s3-vfs: Use the system. namespace for fake ACLs
[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: 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 #include "lib/util/tevent_unix.h"
44
45 #ifndef USE_DMAPI
46 #error "This module requires DMAPI support!"
47 #endif
48
49 #ifdef HAVE_XFS_DMAPI_H
50 #include <xfs/dmapi.h>
51 #elif defined(HAVE_SYS_DMI_H)
52 #include <sys/dmi.h>
53 #elif defined(HAVE_SYS_JFSDMAPI_H)
54 #include <sys/jfsdmapi.h>
55 #elif defined(HAVE_SYS_DMAPI_H)
56 #include <sys/dmapi.h>
57 #elif defined(HAVE_DMAPI_H)
58 #include <dmapi.h>
59 #endif
60
61 #ifndef _ISOC99_SOURCE
62 #define _ISOC99_SOURCE 
63 #endif
64
65 #include <math.h> 
66
67 /* optimisation tunables - used to avoid the DMAPI slow path */
68 #define FILE_IS_ONLINE_RATIO      0.5
69
70 /* default attribute name to look for */
71 #define DM_ATTRIB_OBJECT "IBMObj"
72
73 struct tsmsm_struct {
74         float online_ratio;
75         char *hsmscript;
76         const char *attrib_name;
77         const char *attrib_value;
78 };
79
80 static void tsmsm_free_data(void **pptr) {
81         struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
82         if(!tsmd) return;
83         TALLOC_FREE(*tsmd);
84 }
85
86 /* 
87    called when a client connects to a share
88 */
89 static int tsmsm_connect(struct vfs_handle_struct *handle,
90                          const char *service,
91                          const char *user) {
92         struct tsmsm_struct *tsmd;
93         const char *fres;
94         const char *tsmname;
95         int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
96
97         if (ret < 0) {
98                 return ret;
99         }
100
101         tsmd = talloc_zero(handle, struct tsmsm_struct);
102         if (!tsmd) {
103                 SMB_VFS_NEXT_DISCONNECT(handle);
104                 DEBUG(0,("tsmsm_connect: out of memory!\n"));
105                 return -1;
106         }
107
108         if (!dmapi_have_session()) {
109                 SMB_VFS_NEXT_DISCONNECT(handle);
110                 DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
111                 TALLOC_FREE(tsmd);
112                 return -1;
113         }
114
115         tsmname = (handle->param ? handle->param : "tsmsm");
116         
117         /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
118         tsmd->hsmscript = lp_parm_talloc_string(
119                 tsmd, SNUM(handle->conn), tsmname,
120                 "hsm script", NULL);
121         talloc_steal(tsmd, tsmd->hsmscript);
122         
123         tsmd->attrib_name = lp_parm_talloc_string(
124                 tsmd, SNUM(handle->conn), tsmname,
125                 "dmapi attribute", DM_ATTRIB_OBJECT);
126         talloc_steal(tsmd, tsmd->attrib_name);
127         
128         tsmd->attrib_value = lp_parm_talloc_string(
129                 tsmd, SNUM(handle->conn), tsmname,
130                 "dmapi value", NULL);
131         talloc_steal(tsmd, tsmd->attrib_value);
132         
133         /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
134         fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
135                                     "online ratio", NULL);
136         if (fres == NULL) {
137                 tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
138         } else {
139                 tsmd->online_ratio = strtof(fres, NULL);
140                 if (tsmd->online_ratio > 1.0 ||
141                     tsmd->online_ratio <= 0.0) {
142                         DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
143                                   tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
144                 }
145         }
146
147         /* Store the private data. */
148         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
149                                 struct tsmsm_struct, return -1);
150         return 0;
151 }
152
153 static bool tsmsm_is_offline(struct vfs_handle_struct *handle, 
154                              const struct smb_filename *fname,
155                              SMB_STRUCT_STAT *stbuf)
156 {
157         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
158         const dm_sessid_t *dmsession_id;
159         void *dmhandle = NULL;
160         size_t dmhandle_len = 0;
161         size_t rlen;
162         dm_attrname_t dmname;
163         int ret, lerrno;
164         bool offline;
165         char *buf = NULL;
166         size_t buflen;
167         NTSTATUS status;
168         char *path;
169
170         status = get_full_smb_filename(talloc_tos(), fname, &path);
171         if (!NT_STATUS_IS_OK(status)) {
172                 errno = map_errno_from_nt_status(status);
173                 return false;
174         }
175
176         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
177            then assume it is not offline (it may not be 100%, as it could be sparse) */
178         if (512 * stbuf->st_ex_blocks >=
179             stbuf->st_ex_size * tsmd->online_ratio) {
180                 DEBUG(10,("%s not offline: st_blocks=%llu st_size=%llu "
181                           "online_ratio=%.2f\n", path,
182                           (unsigned long long)stbuf->st_ex_blocks,
183                           (unsigned long long)stbuf->st_ex_size, tsmd->online_ratio));
184                 return false;
185         }
186
187         dmsession_id = dmapi_get_current_session();
188         if (dmsession_id == NULL) {
189                 DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
190                           "Assume file is online.\n"));
191                 return false;
192         }
193
194         /* using POSIX capabilities does not work here. It's a slow path, so 
195          * become_root() is just as good anyway (tridge) 
196          */
197
198         /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
199          * we need to be root to do DMAPI manipulations.
200          */
201         become_root();
202
203         /* go the slow DMAPI route */
204         if (dm_path_to_handle((char*)path, &dmhandle, &dmhandle_len) != 0) {
205                 DEBUG(2,("dm_path_to_handle failed - assuming offline (%s) - %s\n", 
206                          path, strerror(errno)));
207                 offline = true;
208                 goto done;
209         }
210
211         memset(&dmname, 0, sizeof(dmname));
212         strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
213
214         if (tsmd->attrib_value != NULL) {
215                 buflen = strlen(tsmd->attrib_value);
216         } else {
217                 buflen = 1;
218         }
219         buf = talloc_zero_size(tsmd, buflen);
220         if (buf == NULL) {
221                 DEBUG(0,("out of memory in tsmsm_is_offline -- assuming online (%s)\n", path));
222                 errno = ENOMEM;
223                 offline = false;
224                 goto done;
225         }
226
227         do {
228                 lerrno = 0;
229
230                 ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len, 
231                                     DM_NO_TOKEN, &dmname, buflen, buf, &rlen);
232                 if (ret == -1 && errno == EINVAL) {
233                         DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
234                         lerrno = EINVAL;
235                         if (dmapi_new_session()) {
236                                 dmsession_id = dmapi_get_current_session();
237                         } else {
238                                 DEBUG(0, 
239                                       ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n", 
240                                        path, strerror(errno)));
241                                 offline = true;
242                                 dm_handle_free(dmhandle, dmhandle_len);
243                                 goto done;
244                         }
245                 }
246         } while (ret == -1 && lerrno == EINVAL);
247
248         /* check if we need a specific attribute value */
249         if (tsmd->attrib_value != NULL) {
250                 offline = (ret == 0 && rlen == buflen && 
251                             memcmp(buf, tsmd->attrib_value, buflen) == 0);
252         } else {
253                 /* its offline if the specified DMAPI attribute exists */
254                 offline = (ret == 0 || (ret == -1 && errno == E2BIG));
255         }
256
257         DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
258
259         ret = 0;
260
261         dm_handle_free(dmhandle, dmhandle_len); 
262
263 done:
264         talloc_free(buf);
265         unbecome_root();
266         return offline;
267 }
268
269
270 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
271 {
272         SMB_STRUCT_STAT sbuf;
273         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
274         /* see if the file might be offline. This is called before each IO
275            to ensure we use AIO if the file is offline. We don't do the full dmapi
276            call as that would be too slow, instead we err on the side of using AIO
277            if the file might be offline
278         */
279         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
280                 DEBUG(10,("tsmsm_aio_force st_blocks=%llu st_size=%llu "
281                           "online_ratio=%.2f\n", (unsigned long long)sbuf.st_ex_blocks,
282                           (unsigned long long)sbuf.st_ex_size, tsmd->online_ratio));
283                 return !(512 * sbuf.st_ex_blocks >=
284                          sbuf.st_ex_size * tsmd->online_ratio);
285         }
286         return false;
287 }
288
289 struct tsmsm_pread_state {
290         struct files_struct *fsp;
291         ssize_t ret;
292         int err;
293         bool was_offline;
294 };
295
296 static void tsmsm_pread_done(struct tevent_req *subreq);
297
298 static struct tevent_req *tsmsm_pread_send(struct vfs_handle_struct *handle,
299                                            TALLOC_CTX *mem_ctx,
300                                            struct tevent_context *ev,
301                                            struct files_struct *fsp,
302                                            void *data, size_t n, off_t offset)
303 {
304         struct tevent_req *req, *subreq;
305         struct tsmsm_pread_state *state;
306
307         req = tevent_req_create(mem_ctx, &state, struct tsmsm_pread_state);
308         if (req == NULL) {
309                 return NULL;
310         }
311         state->fsp = fsp;
312         state->was_offline = tsmsm_aio_force(handle, fsp);
313         subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
314                                          n, offset);
315         if (tevent_req_nomem(subreq, req)) {
316                 return tevent_req_post(req, ev);
317         }
318         tevent_req_set_callback(subreq, tsmsm_pread_done, req);
319         return req;
320 }
321
322 static void tsmsm_pread_done(struct tevent_req *subreq)
323 {
324         struct tevent_req *req = tevent_req_callback_data(
325                 subreq, struct tevent_req);
326         struct tsmsm_pread_state *state = tevent_req_data(
327                 req, struct tsmsm_pread_state);
328
329         state->ret = SMB_VFS_PREAD_RECV(subreq, &state->err);
330         TALLOC_FREE(subreq);
331         tevent_req_done(req);
332 }
333
334 static ssize_t tsmsm_pread_recv(struct tevent_req *req, int *err)
335 {
336         struct tsmsm_pread_state *state = tevent_req_data(
337                 req, struct tsmsm_pread_state);
338
339         if (tevent_req_is_unix_error(req, err)) {
340                 return -1;
341         }
342         if (state->ret >= 0 && state->was_offline) {
343                 struct files_struct *fsp = state->fsp;
344                 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
345                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
346                              fsp->fsp_name->base_name);
347         }
348         *err = state->err;
349         return state->ret;
350 }
351
352 struct tsmsm_pwrite_state {
353         struct files_struct *fsp;
354         ssize_t ret;
355         int err;
356         bool was_offline;
357 };
358
359 static void tsmsm_pwrite_done(struct tevent_req *subreq);
360
361 static struct tevent_req *tsmsm_pwrite_send(struct vfs_handle_struct *handle,
362                                             TALLOC_CTX *mem_ctx,
363                                             struct tevent_context *ev,
364                                             struct files_struct *fsp,
365                                             const void *data, size_t n,
366                                             off_t offset)
367 {
368         struct tevent_req *req, *subreq;
369         struct tsmsm_pwrite_state *state;
370
371         req = tevent_req_create(mem_ctx, &state, struct tsmsm_pwrite_state);
372         if (req == NULL) {
373                 return NULL;
374         }
375         state->fsp = fsp;
376         state->was_offline = tsmsm_aio_force(handle, fsp);
377         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
378                                           n, offset);
379         if (tevent_req_nomem(subreq, req)) {
380                 return tevent_req_post(req, ev);
381         }
382         tevent_req_set_callback(subreq, tsmsm_pwrite_done, req);
383         return req;
384 }
385
386 static void tsmsm_pwrite_done(struct tevent_req *subreq)
387 {
388         struct tevent_req *req = tevent_req_callback_data(
389                 subreq, struct tevent_req);
390         struct tsmsm_pwrite_state *state = tevent_req_data(
391                 req, struct tsmsm_pwrite_state);
392
393         state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->err);
394         TALLOC_FREE(subreq);
395         tevent_req_done(req);
396 }
397
398 static ssize_t tsmsm_pwrite_recv(struct tevent_req *req, int *err)
399 {
400         struct tsmsm_pwrite_state *state = tevent_req_data(
401                 req, struct tsmsm_pwrite_state);
402
403         if (tevent_req_is_unix_error(req, err)) {
404                 return -1;
405         }
406         if (state->ret >= 0 && state->was_offline) {
407                 struct files_struct *fsp = state->fsp;
408                 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
409                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
410                              fsp->fsp_name->base_name);
411         }
412         *err = state->err;
413         return state->ret;
414 }
415
416 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
417                               off_t offset, size_t n)
418 {
419         bool file_offline = tsmsm_aio_force(handle, fsp);
420
421         if (file_offline) {
422                 DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
423                 errno = ENOSYS;
424                 return -1;
425         }
426             
427         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
428 }
429
430 /* We do overload pread to allow notification when file becomes online after offline status */
431 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
432 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
433                            void *data, size_t n, off_t offset) {
434         ssize_t result;
435         bool notify_online = tsmsm_aio_force(handle, fsp);
436
437         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
438         if((result != -1) && notify_online) {
439             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
440                what we can do is to send notification that file became online
441             */
442                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
443                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
444                              fsp->fsp_name->base_name);
445         }
446
447         return result;
448 }
449
450 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
451                             const void *data, size_t n, off_t offset) {
452         ssize_t result;
453         bool notify_online = tsmsm_aio_force(handle, fsp);
454
455         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
456         if((result != -1) && notify_online) {
457             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
458                what we can do is to send notification that file became online
459             */
460                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
461                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
462                              fsp->fsp_name->base_name);
463         }
464
465         return result;
466 }
467
468 static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
469                              const struct smb_filename *fname)
470 {
471         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
472         int result = 0;
473         char *command;
474         NTSTATUS status;
475         char *path;
476
477         if (tsmd->hsmscript == NULL) {
478                 /* no script enabled */
479                 DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
480                 return 0;
481         }
482
483         status = get_full_smb_filename(talloc_tos(), fname, &path);
484         if (!NT_STATUS_IS_OK(status)) {
485                 errno = map_errno_from_nt_status(status);
486                 return false;
487         }
488
489         /* Now, call the script */
490         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
491         if(!command) {
492                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
493                 return -1;
494         }
495         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
496         if((result = smbrun(command, NULL)) != 0) {
497                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
498         }
499         TALLOC_FREE(command);
500         return result;
501 }
502
503 static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
504                         enum timestamp_set_resolution *p_ts_res)
505 {
506         return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
507 }
508
509 static struct vfs_fn_pointers tsmsm_fns = {
510         .connect_fn = tsmsm_connect,
511         .fs_capabilities_fn = tsmsm_fs_capabilities,
512         .aio_force_fn = tsmsm_aio_force,
513         .pread_fn = tsmsm_pread,
514         .pread_send_fn = tsmsm_pread_send,
515         .pread_recv_fn = tsmsm_pread_recv,
516         .pwrite_fn = tsmsm_pwrite,
517         .pwrite_send_fn = tsmsm_pwrite_send,
518         .pwrite_recv_fn = tsmsm_pwrite_recv,
519         .sendfile_fn = tsmsm_sendfile,
520         .is_offline_fn = tsmsm_is_offline,
521         .set_offline_fn = tsmsm_set_offline,
522 };
523
524 NTSTATUS vfs_tsmsm_init(void);
525 NTSTATUS vfs_tsmsm_init(void)
526 {
527         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
528                                 "tsmsm", &tsmsm_fns);
529 }