s3:vfs: Fix the build of vfs_tsmsm after the VFS rewrite
[kai/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, 2008
7   (c) Andrew Tridgell, 2007, 2008
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> (default 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   tsmsm: attribute name = name of DMAPI attribute that is present when a file is offline. 
31   Default is "IBMobj" (which is what GPFS uses)
32
33   The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
34   based on the fact that number of blocks reported of a file multiplied by 512 will be
35   bigger than 'online ratio' of actual size for online (non-migrated) files.
36
37   If checks fail, we call DMAPI and ask for specific attribute which present for
38   offline (migrated) files. If this attribute presents, we consider file offline.
39  */
40
41 #include "includes.h"
42
43 #ifndef USE_DMAPI
44 #error "This module requires DMAPI support!"
45 #endif
46
47 #ifdef HAVE_XFS_DMAPI_H
48 #include <xfs/dmapi.h>
49 #elif defined(HAVE_SYS_DMI_H)
50 #include <sys/dmi.h>
51 #elif defined(HAVE_SYS_JFSDMAPI_H)
52 #include <sys/jfsdmapi.h>
53 #elif defined(HAVE_SYS_DMAPI_H)
54 #include <sys/dmapi.h>
55 #elif defined(HAVE_DMAPI_H)
56 #include <dmapi.h>
57 #endif
58
59 #ifndef _ISOC99_SOURCE
60 #define _ISOC99_SOURCE 
61 #endif
62
63 #include <math.h> 
64
65 /* optimisation tunables - used to avoid the DMAPI slow path */
66 #define FILE_IS_ONLINE_RATIO      0.5
67
68 /* default attribute name to look for */
69 #define DM_ATTRIB_OBJECT "IBMObj"
70
71 struct tsmsm_struct {
72         float online_ratio;
73         char *hsmscript;
74         const char *attrib_name;
75         const char *attrib_value;
76 };
77
78 static void tsmsm_free_data(void **pptr) {
79         struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
80         if(!tsmd) return;
81         TALLOC_FREE(*tsmd);
82 }
83
84 /* 
85    called when a client connects to a share
86 */
87 static int tsmsm_connect(struct vfs_handle_struct *handle,
88                          const char *service,
89                          const char *user) {
90         struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
91         const char *fres;
92         const char *tsmname;
93         
94         if (!tsmd) {
95                 DEBUG(0,("tsmsm_connect: out of memory!\n"));
96                 return -1;
97         }
98
99         if (!dmapi_have_session()) {
100                 DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
101                 TALLOC_FREE(tsmd);
102                 return -1;
103         }
104
105         tsmname = (handle->param ? handle->param : "tsmsm");
106         
107         /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
108         tsmd->hsmscript = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
109                                                 "hsm script", NULL);
110         talloc_steal(tsmd, tsmd->hsmscript);
111         
112         tsmd->attrib_name = lp_parm_talloc_string(SNUM(handle->conn), tsmname, 
113                                                   "dmapi attribute", DM_ATTRIB_OBJECT);
114         talloc_steal(tsmd, tsmd->attrib_name);
115         
116         tsmd->attrib_value = lp_parm_talloc_string(SNUM(handle->conn), "tsmsm", 
117                                                    "dmapi value", NULL);
118         talloc_steal(tsmd, tsmd->attrib_value);
119         
120         /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
121         fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
122                                     "online ratio", NULL);
123         if (fres == NULL) {
124                 tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
125         } else {
126                 tsmd->online_ratio = strtof(fres, NULL);
127                 if (tsmd->online_ratio > 1.0 ||
128                     tsmd->online_ratio <= 0.0) {
129                         DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
130                                   tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
131                 }
132         }
133
134         /* Store the private data. */
135         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
136                                 struct tsmsm_struct, return -1);
137         return SMB_VFS_NEXT_CONNECT(handle, service, user); 
138 }
139
140 static bool tsmsm_is_offline(struct vfs_handle_struct *handle, 
141                             const char *path,
142                             SMB_STRUCT_STAT *stbuf) {
143         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
144         const dm_sessid_t *dmsession_id;
145         void *dmhandle = NULL;
146         size_t dmhandle_len = 0;
147         size_t rlen;
148         dm_attrname_t dmname;
149         int ret, lerrno;
150         bool offline;
151         char *buf = NULL;
152         size_t buflen;
153
154         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
155            then assume it is not offline (it may not be 100%, as it could be sparse) */
156         if (512 * (off_t)stbuf->st_ex_blocks >=
157             stbuf->st_ex_size * tsmd->online_ratio) {
158                 DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld "
159                           "online_ratio=%.2f\n", path,
160                           (long)stbuf->st_ex_blocks,
161                           (long)stbuf->st_ex_size, tsmd->online_ratio));
162                 return false;
163         }
164
165         dmsession_id = dmapi_get_current_session();
166         if (dmsession_id == NULL) {
167                 DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
168                           "Assume file is online.\n"));
169                 return false;
170         }
171
172         /* using POSIX capabilities does not work here. It's a slow path, so 
173          * become_root() is just as good anyway (tridge) 
174          */
175
176         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
177          * we need to be root to do DMAPI manipulations.
178          */
179         become_root();
180
181         /* go the slow DMAPI route */
182         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
183                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
184                          path, strerror(errno)));
185                 offline = true;
186                 goto done;
187         }
188
189         memset(&dmname, 0, sizeof(dmname));
190         strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
191
192         if (tsmd->attrib_value != NULL) {
193                 buflen = strlen(tsmd->attrib_value);
194         } else {
195                 buflen = 1;
196         }
197         buf = talloc_zero_size(tsmd, buflen);
198         if (buf == NULL) {
199                 DEBUG(0,("out of memory in tsmsm_is_offline -- assuming online (%s)\n", path));
200                 errno = ENOMEM;
201                 offline = false;
202                 goto done;
203         }
204
205         do {
206                 lerrno = 0;
207
208                 ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len, 
209                                     DM_NO_TOKEN, &dmname, buflen, buf, &rlen);
210                 if (ret == -1 && errno == EINVAL) {
211                         DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
212                         lerrno = EINVAL;
213                         if (dmapi_new_session()) {
214                                 dmsession_id = dmapi_get_current_session();
215                         } else {
216                                 DEBUG(0, 
217                                       ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n", 
218                                        path, strerror(errno)));
219                                 offline = true;
220                                 dm_handle_free(dmhandle, dmhandle_len);
221                                 goto done;
222                         }
223                 }
224         } while (ret == -1 && lerrno == EINVAL);
225
226         /* check if we need a specific attribute value */
227         if (tsmd->attrib_value != NULL) {
228                 offline = (ret == 0 && rlen == buflen && 
229                             memcmp(buf, tsmd->attrib_value, buflen) == 0);
230         } else {
231                 /* its offline if the specified DMAPI attribute exists */
232                 offline = (ret == 0 || (ret == -1 && errno == E2BIG));
233         }
234
235         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
236
237         ret = 0;
238
239         dm_handle_free(dmhandle, dmhandle_len); 
240
241 done:
242         talloc_free(buf);
243         unbecome_root();
244         return offline;
245 }
246
247
248 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
249 {
250         SMB_STRUCT_STAT sbuf;
251         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
252         /* see if the file might be offline. This is called before each IO
253            to ensure we use AIO if the file is offline. We don't do the full dmapi
254            call as that would be too slow, instead we err on the side of using AIO
255            if the file might be offline
256         */
257         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
258                 DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld "
259                           "online_ratio=%.2f\n", (long)sbuf.st_ex_blocks,
260                           (long)sbuf.st_ex_size, tsmd->online_ratio));
261                 return !(512 * (off_t)sbuf.st_ex_blocks >=
262                          sbuf.st_ex_size * tsmd->online_ratio);
263         }
264         return false;
265 }
266
267 static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
268                                 SMB_STRUCT_AIOCB *aiocb)
269 {
270         ssize_t result;
271
272         result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
273         if(result >= 0) {
274                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
275                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
276                              fsp->fsp_name->base_name);
277         }
278
279         return result;
280 }
281
282 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
283                               SMB_OFF_T offset, size_t n)
284 {
285         bool file_offline = tsmsm_aio_force(handle, fsp);
286
287         if (file_offline) {
288                 DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
289                 errno = ENOSYS;
290                 return -1;
291         }
292             
293         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
294 }
295
296 /* We do overload pread to allow notification when file becomes online after offline status */
297 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
298 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
299                            void *data, size_t n, SMB_OFF_T offset) {
300         ssize_t result;
301         bool notify_online = tsmsm_aio_force(handle, fsp);
302
303         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
304         if((result != -1) && notify_online) {
305             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
306                what we can do is to send notification that file became online
307             */
308                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
309                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
310                              fsp->fsp_name->base_name);
311         }
312
313         return result;
314 }
315
316 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
317                             const void *data, size_t n, SMB_OFF_T offset) {
318         ssize_t result;
319         bool notify_online = tsmsm_aio_force(handle, fsp);
320
321         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
322         if((result != -1) && notify_online) {
323             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
324                what we can do is to send notification that file became online
325             */
326                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
327                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
328                              fsp->fsp_name->base_name);
329         }
330
331         return result;
332 }
333
334 static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
335                              const char *path) {
336         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
337         int result = 0;
338         char *command;
339
340         if (tsmd->hsmscript == NULL) {
341                 /* no script enabled */
342                 DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
343                 return 0;
344         }
345
346         /* Now, call the script */
347         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
348         if(!command) {
349                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
350                 return -1;
351         }
352         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
353         if((result = smbrun(command, NULL)) != 0) {
354                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
355         }
356         TALLOC_FREE(command);
357         return result;
358 }
359
360 static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
361                         enum timestamp_set_resolution *p_ts_res)
362 {
363         return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
364 }
365
366 static struct vfs_fn_pointers tsmsm_fns = {
367         .connect_fn = tsmsm_connect,
368         .fs_capabilities = tsmsm_fs_capabilities,
369         .aio_force = tsmsm_aio_force,
370         .aio_return_fn = tsmsm_aio_return,
371         .pread = tsmsm_pread,
372         .pwrite = tsmsm_pwrite,
373         .sendfile = tsmsm_sendfile,
374         .is_offline = tsmsm_is_offline,
375         .set_offline = tsmsm_set_offline,
376 };
377
378 NTSTATUS vfs_tsmsm_init(void);
379 NTSTATUS vfs_tsmsm_init(void)
380 {
381         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
382                                 "tsmsm", &tsmsm_fns);
383 }