Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[nivanova/samba-autobuild/.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                             struct connection_struct *conn,
141                             const char *path,
142                             SMB_STRUCT_STAT *stbuf,
143                             bool *offline) {
144         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
145         void *dmhandle = NULL;
146         size_t dmhandle_len = 0;
147         size_t rlen;
148         dm_attrname_t dmname;
149         int ret;
150
151         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
152            then assume it is not offline (it may not be 100%, as it could be sparse) */
153         if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
154                 *offline = false;
155                 DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
156                           path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
157                 return 0;
158         }
159         
160         /* using POSIX capabilities does not work here. It's a slow path, so 
161          * become_root() is just as good anyway (tridge) 
162          */
163
164         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
165          * we need to be root to do DMAPI manipulations.
166          */
167         become_root();
168
169         /* go the slow DMAPI route */
170         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
171                 ret = -1;
172                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
173                          path, strerror(errno)));
174                 *offline = True;
175                 goto done;
176         }
177
178         memset(&dmname, 0, sizeof(dmname));
179         strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars));
180
181         ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len, 
182                             DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
183
184         /* its offline if the IBMObj attribute exists */
185         *offline = (ret == 0 || (ret == -1 && errno == E2BIG));
186
187         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
188
189         ret = 0;
190
191         dm_handle_free(dmhandle, dmhandle_len); 
192
193 done:
194         unbecome_root();
195         return ret;
196 }
197
198
199 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
200 {
201         SMB_STRUCT_STAT sbuf;
202         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
203         /* see if the file might be offline. This is called before each IO
204            to ensure we use AIO if the file is offline. We don't do the full dmapi
205            call as that would be too slow, instead we err on the side of using AIO
206            if the file might be offline
207         */
208         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
209                 DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
210                           sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
211                 return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
212         }
213         return False;
214 }
215
216 static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
217                                 SMB_STRUCT_AIOCB *aiocb)
218 {
219         ssize_t result;
220
221         result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
222         if(result >= 0) {
223                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
224                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
225                              fsp->fsp_name);
226         }
227
228         return result;
229 }
230
231 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
232                               SMB_OFF_T offset, size_t n)
233 {
234         bool file_online = tsmsm_aio_force(handle, fsp);
235
236         if(!file_online) 
237             return ENOSYS;
238             
239         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
240 }
241
242 /* We do overload pread to allow notification when file becomes online after offline status */
243 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
244 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
245                            void *data, size_t n, SMB_OFF_T offset) {
246         ssize_t result;
247         bool notify_online = tsmsm_aio_force(handle, fsp);
248
249         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
250         if((result != -1) && notify_online) {
251             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
252                what we can do is to send notification that file became online
253             */
254                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
255                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
256                              fsp->fsp_name);
257         }
258
259         return result;
260 }
261
262 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
263                            void *data, size_t n, SMB_OFF_T offset) {
264         ssize_t result;
265         bool notify_online = tsmsm_aio_force(handle, fsp);
266
267         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
268         if((result != -1) && notify_online) {
269             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
270                what we can do is to send notification that file became online
271             */
272                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
273                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
274                              fsp->fsp_name);
275         }
276
277         return result;
278 }
279
280 static int tsmsm_set_offline(struct vfs_handle_struct *handle, struct connection_struct *conn, 
281                              const char *path) {
282         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
283         int result = 0;
284         char *command;
285
286         /* Now, call the script */
287         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
288         if(!command) {
289                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
290                 return -1;
291         }
292         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
293         if((result = smbrun(command, NULL)) != 0) {
294                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
295         }
296         TALLOC_FREE(command);
297         return result;
298 }
299
300 static bool tsmsm_is_remotestorage(struct vfs_handle_struct *handle, struct connection_struct *conn, const char *path) {
301         return True;
302 }
303
304 static vfs_op_tuple vfs_tsmsm_ops[] = {
305
306         /* Disk operations */
307
308         {SMB_VFS_OP(tsmsm_connect),     SMB_VFS_OP_CONNECT,
309          SMB_VFS_LAYER_TRANSPARENT},
310         {SMB_VFS_OP(tsmsm_aio_force),   SMB_VFS_OP_AIO_FORCE,
311          SMB_VFS_LAYER_TRANSPARENT},
312         {SMB_VFS_OP(tsmsm_aio_return),  SMB_VFS_OP_AIO_RETURN,
313          SMB_VFS_LAYER_TRANSPARENT},
314         {SMB_VFS_OP(tsmsm_pread),       SMB_VFS_OP_PREAD,
315          SMB_VFS_LAYER_TRANSPARENT},
316         {SMB_VFS_OP(tsmsm_pwrite),      SMB_VFS_OP_PWRITE,
317          SMB_VFS_LAYER_TRANSPARENT},
318         {SMB_VFS_OP(tsmsm_sendfile),    SMB_VFS_OP_SENDFILE,
319          SMB_VFS_LAYER_TRANSPARENT},
320         {SMB_VFS_OP(tsmsm_is_offline),SMB_VFS_OP_IS_OFFLINE,
321          SMB_VFS_LAYER_OPAQUE},
322         {SMB_VFS_OP(tsmsm_set_offline),SMB_VFS_OP_SET_OFFLINE,
323          SMB_VFS_LAYER_OPAQUE},
324         {SMB_VFS_OP(tsmsm_is_remotestorage),SMB_VFS_OP_IS_REMOTESTORAGE,
325          SMB_VFS_LAYER_OPAQUE},
326
327         /* Finish VFS operations definition */
328
329         {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
330          SMB_VFS_LAYER_NOOP}
331 };
332
333 NTSTATUS vfs_tsmsm_init(void);
334 NTSTATUS vfs_tsmsm_init(void)
335 {
336         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
337                                 "tsmsm", vfs_tsmsm_ops);
338 }