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