Merge branch 'ctdb-merge' into dmapi-integration
[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 };
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 /* 
84    called when a client connects to a share
85 */
86 static int tsmsm_connect(struct vfs_handle_struct *handle,
87                          const char *service,
88                          const char *user) {
89         struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
90         const char *fres;
91         const char *tsmname;
92         
93         if (!tsmd) {
94                 DEBUG(0,("tsmsm_connect: out of memory!\n"));
95                 return -1;
96         }
97
98         if (!dmapi_have_session()) {
99                 DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
100                 TALLOC_FREE(tsmd);
101                 return -1;
102         }
103
104         tsmname = (handle->param ? handle->param : "tsmsm");
105         
106         /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
107         tsmd->hsmscript = lp_parm_talloc_string(SNUM(handle->conn), tsmname,
108                                                 "hsm script", NULL);
109         talloc_steal(tsmd, tsmd->hsmscript);
110         
111         tsmd->attrib_name = lp_parm_talloc_string(SNUM(handle->conn), tsmname, 
112                                                   "dmapi attribute", DM_ATTRIB_OBJECT);
113         talloc_steal(tsmd, tsmd->attrib_name);
114
115         /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
116         fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
117                                     "online ratio", NULL);
118         if (fres == NULL) {
119                 tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
120         } else {
121                 tsmd->online_ratio = strtof(fres, NULL);
122                 if (tsmd->online_ration > 1.0 ||
123                     tsmd->online_ration <= 0.0) {
124                         DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
125                                   tsmd->online_ration, (float)FILE_IS_ONLINE_RATIO));
126                 }
127         }
128
129         /* Store the private data. */
130         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
131                                 struct tsmsm_struct, return -1);
132         return SMB_VFS_NEXT_CONNECT(handle, service, user); 
133 }
134
135 static bool tsmsm_is_offline(struct vfs_handle_struct *handle, 
136                             const char *path,
137                             SMB_STRUCT_STAT *stbuf) {
138         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
139         const dm_sessid_t *dmsession_id;
140         void *dmhandle = NULL;
141         size_t dmhandle_len = 0;
142         size_t rlen;
143         dm_attrname_t dmname;
144         int ret;
145         bool offline;
146
147         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
148            then assume it is not offline (it may not be 100%, as it could be sparse) */
149         if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
150                 DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
151                           path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
152                 return false;
153         }
154
155         dmsession_id = dmapi_get_current_session();
156         if (dmsession_id == NULL) {
157                 DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
158                           "Assume file is online.\n"));
159                 return false;
160         }
161
162         /* using POSIX capabilities does not work here. It's a slow path, so 
163          * become_root() is just as good anyway (tridge) 
164          */
165
166         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
167          * we need to be root to do DMAPI manipulations.
168          */
169         become_root();
170
171         /* go the slow DMAPI route */
172         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
173                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
174                          path, strerror(errno)));
175                 offline = true;
176                 goto done;
177         }
178
179         memset(&dmname, 0, sizeof(dmname));
180         strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
181
182         ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len, 
183                             DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
184
185         /* its offline if the specified DMAPI attribute exists */
186         offline = (ret == 0 || (ret == -1 && errno == E2BIG));
187
188         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
189
190         ret = 0;
191
192         dm_handle_free(dmhandle, dmhandle_len); 
193
194 done:
195         unbecome_root();
196         return offline;
197 }
198
199
200 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
201 {
202         SMB_STRUCT_STAT sbuf;
203         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
204         /* see if the file might be offline. This is called before each IO
205            to ensure we use AIO if the file is offline. We don't do the full dmapi
206            call as that would be too slow, instead we err on the side of using AIO
207            if the file might be offline
208         */
209         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
210                 DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
211                           sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
212                 return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
213         }
214         return false;
215 }
216
217 static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
218                                 SMB_STRUCT_AIOCB *aiocb)
219 {
220         ssize_t result;
221
222         result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
223         if(result >= 0) {
224                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
225                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
226                              fsp->fsp_name);
227         }
228
229         return result;
230 }
231
232 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
233                               SMB_OFF_T offset, size_t n)
234 {
235         bool file_online = tsmsm_aio_force(handle, fsp);
236
237         if(!file_online) 
238             return ENOSYS;
239             
240         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
241 }
242
243 /* We do overload pread to allow notification when file becomes online after offline status */
244 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
245 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
246                            void *data, size_t n, SMB_OFF_T offset) {
247         ssize_t result;
248         bool notify_online = tsmsm_aio_force(handle, fsp);
249
250         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
251         if((result != -1) && notify_online) {
252             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
253                what we can do is to send notification that file became online
254             */
255                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
256                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
257                              fsp->fsp_name);
258         }
259
260         return result;
261 }
262
263 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
264                            void *data, size_t n, SMB_OFF_T offset) {
265         ssize_t result;
266         bool notify_online = tsmsm_aio_force(handle, fsp);
267
268         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
269         if((result != -1) && notify_online) {
270             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
271                what we can do is to send notification that file became online
272             */
273                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
274                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
275                              fsp->fsp_name);
276         }
277
278         return result;
279 }
280
281 static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
282                              const char *path) {
283         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
284         int result = 0;
285         char *command;
286
287         if (tsmd->hsmscript == NULL) {
288                 /* no script enabled */
289                 DEBUG(1, ("tsmsm_set_offline: No tsmsm:hsmscript configured\n"));
290                 return 0;
291         }
292
293         /* Now, call the script */
294         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
295         if(!command) {
296                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
297                 return -1;
298         }
299         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
300         if((result = smbrun(command, NULL)) != 0) {
301                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
302         }
303         TALLOC_FREE(command);
304         return result;
305 }
306
307 static bool tsmsm_statvfs(struct vfs_handle_struct *handle,  const char *path, vfs_statvfs_struct *statbuf)
308 {
309         bool result;
310
311         result = SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
312         statbuf->FsCapabilities | = FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
313
314         return result;
315 }
316
317 static vfs_op_tuple vfs_tsmsm_ops[] = {
318
319         /* Disk operations */
320
321         {SMB_VFS_OP(tsmsm_connect),     SMB_VFS_OP_CONNECT,
322          SMB_VFS_LAYER_TRANSPARENT},
323         {SMB_VFS_OP(tsmsm_statvfs),     SMB_VFS_OP_STATVFS,
324          SMB_VFS_LAYER_TRANSPARENT},
325         {SMB_VFS_OP(tsmsm_aio_force),   SMB_VFS_OP_AIO_FORCE,
326          SMB_VFS_LAYER_TRANSPARENT},
327         {SMB_VFS_OP(tsmsm_aio_return),  SMB_VFS_OP_AIO_RETURN,
328          SMB_VFS_LAYER_TRANSPARENT},
329         {SMB_VFS_OP(tsmsm_pread),       SMB_VFS_OP_PREAD,
330          SMB_VFS_LAYER_TRANSPARENT},
331         {SMB_VFS_OP(tsmsm_pwrite),      SMB_VFS_OP_PWRITE,
332          SMB_VFS_LAYER_TRANSPARENT},
333         {SMB_VFS_OP(tsmsm_sendfile),    SMB_VFS_OP_SENDFILE,
334          SMB_VFS_LAYER_TRANSPARENT},
335         {SMB_VFS_OP(tsmsm_is_offline),  SMB_VFS_OP_IS_OFFLINE,
336          SMB_VFS_LAYER_OPAQUE},
337         {SMB_VFS_OP(tsmsm_set_offline), SMB_VFS_OP_SET_OFFLINE,
338          SMB_VFS_LAYER_OPAQUE},
339
340         /* Finish VFS operations definition */
341
342         {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
343          SMB_VFS_LAYER_NOOP}
344 };
345
346 NTSTATUS vfs_tsmsm_init(void);
347 NTSTATUS vfs_tsmsm_init(void)
348 {
349         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
350                                 "tsmsm", vfs_tsmsm_ops);
351 }