s3-vfs: Fix calls of lp_parm_talloc_string
[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: dmapi attribute = 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(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(
118                 tsmd, SNUM(handle->conn), tsmname,
119                 "hsm script", NULL);
120         talloc_steal(tsmd, tsmd->hsmscript);
121         
122         tsmd->attrib_name = lp_parm_talloc_string(
123                 tsmd, SNUM(handle->conn), tsmname,
124                 "dmapi attribute", DM_ATTRIB_OBJECT);
125         talloc_steal(tsmd, tsmd->attrib_name);
126         
127         tsmd->attrib_value = lp_parm_talloc_string(
128                 tsmd, SNUM(handle->conn), tsmname,
129                 "dmapi value", NULL);
130         talloc_steal(tsmd, tsmd->attrib_value);
131         
132         /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
133         fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
134                                     "online ratio", NULL);
135         if (fres == NULL) {
136                 tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
137         } else {
138                 tsmd->online_ratio = strtof(fres, NULL);
139                 if (tsmd->online_ratio > 1.0 ||
140                     tsmd->online_ratio <= 0.0) {
141                         DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
142                                   tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
143                 }
144         }
145
146         /* Store the private data. */
147         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
148                                 struct tsmsm_struct, return -1);
149         return 0;
150 }
151
152 static bool tsmsm_is_offline(struct vfs_handle_struct *handle, 
153                              const struct smb_filename *fname,
154                              SMB_STRUCT_STAT *stbuf)
155 {
156         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
157         const dm_sessid_t *dmsession_id;
158         void *dmhandle = NULL;
159         size_t dmhandle_len = 0;
160         size_t rlen;
161         dm_attrname_t dmname;
162         int ret, lerrno;
163         bool offline;
164         char *buf = NULL;
165         size_t buflen;
166         NTSTATUS status;
167         char *path;
168
169         status = get_full_smb_filename(talloc_tos(), fname, &path);
170         if (!NT_STATUS_IS_OK(status)) {
171                 errno = map_errno_from_nt_status(status);
172                 return false;
173         }
174
175         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
176            then assume it is not offline (it may not be 100%, as it could be sparse) */
177         if (512 * stbuf->st_ex_blocks >=
178             stbuf->st_ex_size * tsmd->online_ratio) {
179                 DEBUG(10,("%s not offline: st_blocks=%llu st_size=%llu "
180                           "online_ratio=%.2f\n", path,
181                           (unsigned long long)stbuf->st_ex_blocks,
182                           (unsigned long long)stbuf->st_ex_size, tsmd->online_ratio));
183                 return false;
184         }
185
186         dmsession_id = dmapi_get_current_session();
187         if (dmsession_id == NULL) {
188                 DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
189                           "Assume file is online.\n"));
190                 return false;
191         }
192
193         /* using POSIX capabilities does not work here. It's a slow path, so 
194          * become_root() is just as good anyway (tridge) 
195          */
196
197         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
198          * we need to be root to do DMAPI manipulations.
199          */
200         become_root();
201
202         /* go the slow DMAPI route */
203         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
204                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
205                          path, strerror(errno)));
206                 offline = true;
207                 goto done;
208         }
209
210         memset(&dmname, 0, sizeof(dmname));
211         strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
212
213         if (tsmd->attrib_value != NULL) {
214                 buflen = strlen(tsmd->attrib_value);
215         } else {
216                 buflen = 1;
217         }
218         buf = talloc_zero_size(tsmd, buflen);
219         if (buf == NULL) {
220                 DEBUG(0,("out of memory in tsmsm_is_offline -- assuming online (%s)\n", path));
221                 errno = ENOMEM;
222                 offline = false;
223                 goto done;
224         }
225
226         do {
227                 lerrno = 0;
228
229                 ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len, 
230                                     DM_NO_TOKEN, &dmname, buflen, buf, &rlen);
231                 if (ret == -1 && errno == EINVAL) {
232                         DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
233                         lerrno = EINVAL;
234                         if (dmapi_new_session()) {
235                                 dmsession_id = dmapi_get_current_session();
236                         } else {
237                                 DEBUG(0, 
238                                       ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n", 
239                                        path, strerror(errno)));
240                                 offline = true;
241                                 dm_handle_free(dmhandle, dmhandle_len);
242                                 goto done;
243                         }
244                 }
245         } while (ret == -1 && lerrno == EINVAL);
246
247         /* check if we need a specific attribute value */
248         if (tsmd->attrib_value != NULL) {
249                 offline = (ret == 0 && rlen == buflen && 
250                             memcmp(buf, tsmd->attrib_value, buflen) == 0);
251         } else {
252                 /* its offline if the specified DMAPI attribute exists */
253                 offline = (ret == 0 || (ret == -1 && errno == E2BIG));
254         }
255
256         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
257
258         ret = 0;
259
260         dm_handle_free(dmhandle, dmhandle_len); 
261
262 done:
263         talloc_free(buf);
264         unbecome_root();
265         return offline;
266 }
267
268
269 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
270 {
271         SMB_STRUCT_STAT sbuf;
272         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
273         /* see if the file might be offline. This is called before each IO
274            to ensure we use AIO if the file is offline. We don't do the full dmapi
275            call as that would be too slow, instead we err on the side of using AIO
276            if the file might be offline
277         */
278         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
279                 DEBUG(10,("tsmsm_aio_force st_blocks=%llu st_size=%llu "
280                           "online_ratio=%.2f\n", (unsigned long long)sbuf.st_ex_blocks,
281                           (unsigned long long)sbuf.st_ex_size, tsmd->online_ratio));
282                 return !(512 * sbuf.st_ex_blocks >=
283                          sbuf.st_ex_size * tsmd->online_ratio);
284         }
285         return false;
286 }
287
288 static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
289                                 SMB_STRUCT_AIOCB *aiocb)
290 {
291         ssize_t result;
292
293         result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
294         if(result >= 0) {
295                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
296                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
297                              fsp->fsp_name->base_name);
298         }
299
300         return result;
301 }
302
303 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
304                               off_t offset, size_t n)
305 {
306         bool file_offline = tsmsm_aio_force(handle, fsp);
307
308         if (file_offline) {
309                 DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
310                 errno = ENOSYS;
311                 return -1;
312         }
313             
314         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
315 }
316
317 /* We do overload pread to allow notification when file becomes online after offline status */
318 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
319 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
320                            void *data, size_t n, off_t offset) {
321         ssize_t result;
322         bool notify_online = tsmsm_aio_force(handle, fsp);
323
324         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
325         if((result != -1) && notify_online) {
326             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
327                what we can do is to send notification that file became online
328             */
329                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
330                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
331                              fsp->fsp_name->base_name);
332         }
333
334         return result;
335 }
336
337 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
338                             const void *data, size_t n, off_t offset) {
339         ssize_t result;
340         bool notify_online = tsmsm_aio_force(handle, fsp);
341
342         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
343         if((result != -1) && notify_online) {
344             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
345                what we can do is to send notification that file became online
346             */
347                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
348                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
349                              fsp->fsp_name->base_name);
350         }
351
352         return result;
353 }
354
355 static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
356                              const struct smb_filename *fname)
357 {
358         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
359         int result = 0;
360         char *command;
361         NTSTATUS status;
362         char *path;
363
364         if (tsmd->hsmscript == NULL) {
365                 /* no script enabled */
366                 DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
367                 return 0;
368         }
369
370         status = get_full_smb_filename(talloc_tos(), fname, &path);
371         if (!NT_STATUS_IS_OK(status)) {
372                 errno = map_errno_from_nt_status(status);
373                 return false;
374         }
375
376         /* Now, call the script */
377         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
378         if(!command) {
379                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
380                 return -1;
381         }
382         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
383         if((result = smbrun(command, NULL)) != 0) {
384                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
385         }
386         TALLOC_FREE(command);
387         return result;
388 }
389
390 static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
391                         enum timestamp_set_resolution *p_ts_res)
392 {
393         return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
394 }
395
396 static struct vfs_fn_pointers tsmsm_fns = {
397         .connect_fn = tsmsm_connect,
398         .fs_capabilities_fn = tsmsm_fs_capabilities,
399         .aio_force_fn = tsmsm_aio_force,
400         .aio_return_fn = tsmsm_aio_return,
401         .pread_fn = tsmsm_pread,
402         .pwrite_fn = tsmsm_pwrite,
403         .sendfile_fn = tsmsm_sendfile,
404         .is_offline_fn = tsmsm_is_offline,
405         .set_offline_fn = tsmsm_set_offline,
406 };
407
408 NTSTATUS vfs_tsmsm_init(void);
409 NTSTATUS vfs_tsmsm_init(void)
410 {
411         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
412                                 "tsmsm", &tsmsm_fns);
413 }