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