Fix more VFS API mixup with offline files
[samba.git] / source3 / modules / vfs_tsmsm.c
1 /*
2   Unix SMB/CIFS implementation.
3   Samba VFS module for handling offline files
4   with Tivoli Storage Manager Space Management
5
6   (c) Alexander Bokovoy, 2007
7   (c) Andrew Tridgell, 2007
8   
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 /*
23   This VFS module accepts following options:
24   tsmsm: hsm script = <path to hsm script> (/bin/true by default, i.e. does nothing)
25          hsm script should point to a shell script which accepts two arguments:
26          <operation> <filepath>
27          where <operation> is currently 'offline' to set offline status of the <filepath>
28
29   tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
30
31   The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
32   based on the fact that number of blocks reported of a file multiplied by 512 will be
33   bigger than 'online ratio' of actual size for online (non-migrated) files.
34
35   If checks fail, we call DMAPI and ask for specific IBM attribute which present for
36   offline (migrated) files. If this attribute presents, we consider file offline.
37  */
38
39 #include "includes.h"
40
41 #ifndef USE_DMAPI
42 #error "This module requires DMAPI support!"
43 #endif
44
45 #ifdef HAVE_XFS_DMAPI_H
46 #include <xfs/dmapi.h>
47 #elif defined(HAVE_SYS_DMI_H)
48 #include <sys/dmi.h>
49 #elif defined(HAVE_SYS_JFSDMAPI_H)
50 #include <sys/jfsdmapi.h>
51 #elif defined(HAVE_SYS_DMAPI_H)
52 #include <sys/dmapi.h>
53 #elif defined(HAVE_DMAPI_H)
54 #include <dmapi.h>
55 #endif
56
57 #ifndef _ISOC99_SOURCE
58 #define _ISOC99_SOURCE 
59 #endif
60
61 #include <math.h> 
62
63 /* optimisation tunables - used to avoid the DMAPI slow path */
64 #define FILE_IS_ONLINE_RATIO      0.5
65 #define DM_ATTRIB_OBJECT "IBMObj"
66 #define DM_ATTRIB_MIGRATED "IBMMig"
67
68 struct tsmsm_struct {
69         dm_sessid_t sid;
70         float online_ratio;
71         char *hsmscript;
72 };
73
74 #define TSM_STRINGIFY(a) #a
75 #define TSM_TOSTRING(a) TSM_STRINGIFY(a)
76
77 static void tsmsm_free_data(void **pptr) {
78         struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
79         if(!tsmd) return;
80         TALLOC_FREE(*tsmd);
81 }
82
83 static int tsmsm_connect(struct vfs_handle_struct *handle,
84                          const char *service,
85                          const char *user) {
86         struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
87         const char *hsmscript, *tsmname;
88         const char *fres;
89         
90         if (!tsmd) {
91                 DEBUG(0,("tsmsm_connect: out of memory!\n"));
92                 return -1;
93         }
94
95         tsmd->sid = *(dm_sessid_t*) dmapi_get_current_session();
96
97         if (tsmd->sid == DM_NO_SESSION) {
98                 DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
99                 TALLOC_FREE(tsmd);
100                 return -1;
101         }
102
103         tsmname = (handle->param ? handle->param : "tsmsm");
104         hsmscript = lp_parm_const_string(SNUM(handle->conn), tsmname,
105                                          "hsm script", NULL);
106         if (hsmscript) {
107                 tsmd->hsmscript = talloc_strdup(tsmd, hsmscript);
108                 if(!tsmd->hsmscript) {
109                         DEBUG(1, ("tsmsm_connect: can't allocate memory for hsm script path"));
110                         TALLOC_FREE(tsmd);
111                         return -1;
112                 }
113         } else {
114                 DEBUG(1, ("tsmsm_connect: can't call hsm script because it "
115                           "is not set to anything in the smb.conf\n"
116                           "Use %s: 'hsm script = path' to set it\n",
117                           tsmname));
118                 TALLOC_FREE(tsmd);
119                 return -1;
120         }
121
122         fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
123                                     "online ratio", TSM_TOSTRING(FILE_IS_ONLINE_RATIO));
124         tsmd->online_ratio = strtof(fres, NULL);
125         if((tsmd->online_ratio == (float)0) || ((errno == ERANGE) &&
126                                                 ((tsmd->online_ratio == HUGE_VALF) ||
127                                                  (tsmd->online_ratio == HUGE_VALL)))) {
128                 DEBUG(1, ("tsmsm_connect: error while getting online ratio from smb.conf."
129                           "Default to %s.\n", TSM_TOSTRING(FILE_IS_ONLINE_RATIO)));
130                 tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
131         }
132
133         /* Store the private data. */
134         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
135                                 struct tsmsm_struct, return -1);
136         return SMB_VFS_NEXT_CONNECT(handle, service, user); 
137 }
138
139 static int tsmsm_is_offline(struct vfs_handle_struct *handle, 
140                             const char *path,
141                             SMB_STRUCT_STAT *stbuf,
142                             bool *offline) {
143         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
144         void *dmhandle = NULL;
145         size_t dmhandle_len = 0;
146         size_t rlen;
147         dm_attrname_t dmname;
148         int ret;
149
150         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
151            then assume it is not offline (it may not be 100%, as it could be sparse) */
152         if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
153                 *offline = false;
154                 DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
155                           path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
156                 return 0;
157         }
158         
159         /* using POSIX capabilities does not work here. It's a slow path, so 
160          * become_root() is just as good anyway (tridge) 
161          */
162
163         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
164          * we need to be root to do DMAPI manipulations.
165          */
166         become_root();
167
168         /* go the slow DMAPI route */
169         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
170                 ret = -1;
171                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
172                          path, strerror(errno)));
173                 *offline = true;
174                 goto done;
175         }
176
177         memset(&dmname, 0, sizeof(dmname));
178         strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars));
179
180         ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len, 
181                             DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
182
183         /* its offline if the IBMObj attribute exists */
184         *offline = (ret == 0 || (ret == -1 && errno == E2BIG));
185
186         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
187
188         ret = 0;
189
190         dm_handle_free(dmhandle, dmhandle_len); 
191
192 done:
193         unbecome_root();
194         return ret;
195 }
196
197
198 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
199 {
200         SMB_STRUCT_STAT sbuf;
201         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
202         /* see if the file might be offline. This is called before each IO
203            to ensure we use AIO if the file is offline. We don't do the full dmapi
204            call as that would be too slow, instead we err on the side of using AIO
205            if the file might be offline
206         */
207         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
208                 DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
209                           sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
210                 return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
211         }
212         return false;
213 }
214
215 static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
216                                 SMB_STRUCT_AIOCB *aiocb)
217 {
218         ssize_t result;
219
220         result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
221         if(result >= 0) {
222                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
223                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
224                              fsp->fsp_name);
225         }
226
227         return result;
228 }
229
230 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
231                               SMB_OFF_T offset, size_t n)
232 {
233         bool file_online = tsmsm_aio_force(handle, fsp);
234
235         if(!file_online) 
236             return ENOSYS;
237             
238         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
239 }
240
241 /* We do overload pread to allow notification when file becomes online after offline status */
242 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
243 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
244                            void *data, size_t n, SMB_OFF_T offset) {
245         ssize_t result;
246         bool notify_online = tsmsm_aio_force(handle, fsp);
247
248         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
249         if((result != -1) && notify_online) {
250             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
251                what we can do is to send notification that file became online
252             */
253                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
254                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
255                              fsp->fsp_name);
256         }
257
258         return result;
259 }
260
261 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
262                            void *data, size_t n, SMB_OFF_T offset) {
263         ssize_t result;
264         bool notify_online = tsmsm_aio_force(handle, fsp);
265
266         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
267         if((result != -1) && notify_online) {
268             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
269                what we can do is to send notification that file became online
270             */
271                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
272                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
273                              fsp->fsp_name);
274         }
275
276         return result;
277 }
278
279 static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
280                              const char *path) {
281         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
282         int result = 0;
283         char *command;
284
285         /* Now, call the script */
286         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
287         if(!command) {
288                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
289                 return -1;
290         }
291         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
292         if((result = smbrun(command, NULL)) != 0) {
293                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
294         }
295         TALLOC_FREE(command);
296         return result;
297 }
298
299 static bool tsmsm_is_remotestorage(struct vfs_handle_struct *handle, const char *path) {
300         return True;
301 }
302
303 static vfs_op_tuple vfs_tsmsm_ops[] = {
304
305         /* Disk operations */
306
307         {SMB_VFS_OP(tsmsm_connect),     SMB_VFS_OP_CONNECT,
308          SMB_VFS_LAYER_TRANSPARENT},
309         {SMB_VFS_OP(tsmsm_aio_force),   SMB_VFS_OP_AIO_FORCE,
310          SMB_VFS_LAYER_TRANSPARENT},
311         {SMB_VFS_OP(tsmsm_aio_return),  SMB_VFS_OP_AIO_RETURN,
312          SMB_VFS_LAYER_TRANSPARENT},
313         {SMB_VFS_OP(tsmsm_pread),       SMB_VFS_OP_PREAD,
314          SMB_VFS_LAYER_TRANSPARENT},
315         {SMB_VFS_OP(tsmsm_pwrite),      SMB_VFS_OP_PWRITE,
316          SMB_VFS_LAYER_TRANSPARENT},
317         {SMB_VFS_OP(tsmsm_sendfile),    SMB_VFS_OP_SENDFILE,
318          SMB_VFS_LAYER_TRANSPARENT},
319         {SMB_VFS_OP(tsmsm_is_offline),SMB_VFS_OP_IS_OFFLINE,
320          SMB_VFS_LAYER_OPAQUE},
321         {SMB_VFS_OP(tsmsm_set_offline),SMB_VFS_OP_SET_OFFLINE,
322          SMB_VFS_LAYER_OPAQUE},
323         {SMB_VFS_OP(tsmsm_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE,
324          SMB_VFS_LAYER_OPAQUE},
325
326         /* Finish VFS operations definition */
327
328         {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
329          SMB_VFS_LAYER_NOOP}
330 };
331
332 NTSTATUS vfs_tsmsm_init(void);
333 NTSTATUS vfs_tsmsm_init(void)
334 {
335         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
336                                 "tsmsm", vfs_tsmsm_ops);
337 }