smbd: use fdos_mode() in tsmsm_set_dos_attributes()
[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 capablities 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_get_dos_attributes(struct vfs_handle_struct *handle,
272                                          struct smb_filename *fname,
273                                          uint32_t *dosmode)
274 {
275         bool offline;
276
277         offline = tsmsm_is_offline(handle, fname, &fname->st);
278         if (offline) {
279                 *dosmode |= FILE_ATTRIBUTE_OFFLINE;
280         }
281
282         return SMB_VFS_NEXT_GET_DOS_ATTRIBUTES(handle, fname, dosmode);
283 }
284
285 static NTSTATUS tsmsm_fget_dos_attributes(struct vfs_handle_struct *handle,
286                                           files_struct *fsp,
287                                           uint32_t *dosmode)
288 {
289         bool offline;
290
291         offline = tsmsm_is_offline(handle, fsp->fsp_name, &fsp->fsp_name->st);
292         if (offline) {
293                 *dosmode |= FILE_ATTRIBUTE_OFFLINE;
294         }
295
296         return SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, dosmode);
297 }
298
299 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
300 {
301         SMB_STRUCT_STAT sbuf;
302         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
303         /* see if the file might be offline. This is called before each IO
304            to ensure we use AIO if the file is offline. We don't do the full dmapi
305            call as that would be too slow, instead we err on the side of using AIO
306            if the file might be offline
307         */
308         if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
309                 DEBUG(10,("tsmsm_aio_force st_blocks=%llu st_size=%llu "
310                           "online_ratio=%.2f\n", (unsigned long long)sbuf.st_ex_blocks,
311                           (unsigned long long)sbuf.st_ex_size, tsmd->online_ratio));
312                 return !(512 * sbuf.st_ex_blocks >=
313                          sbuf.st_ex_size * tsmd->online_ratio);
314         }
315         return false;
316 }
317
318 struct tsmsm_pread_state {
319         struct files_struct *fsp;
320         ssize_t ret;
321         bool was_offline;
322         struct vfs_aio_state vfs_aio_state;
323 };
324
325 static void tsmsm_pread_done(struct tevent_req *subreq);
326
327 static struct tevent_req *tsmsm_pread_send(struct vfs_handle_struct *handle,
328                                            TALLOC_CTX *mem_ctx,
329                                            struct tevent_context *ev,
330                                            struct files_struct *fsp,
331                                            void *data, size_t n, off_t offset)
332 {
333         struct tevent_req *req, *subreq;
334         struct tsmsm_pread_state *state;
335
336         req = tevent_req_create(mem_ctx, &state, struct tsmsm_pread_state);
337         if (req == NULL) {
338                 return NULL;
339         }
340         state->fsp = fsp;
341         state->was_offline = tsmsm_aio_force(handle, fsp);
342         subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
343                                          n, offset);
344         if (tevent_req_nomem(subreq, req)) {
345                 return tevent_req_post(req, ev);
346         }
347         tevent_req_set_callback(subreq, tsmsm_pread_done, req);
348         return req;
349 }
350
351 static void tsmsm_pread_done(struct tevent_req *subreq)
352 {
353         struct tevent_req *req = tevent_req_callback_data(
354                 subreq, struct tevent_req);
355         struct tsmsm_pread_state *state = tevent_req_data(
356                 req, struct tsmsm_pread_state);
357
358         state->ret = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
359         TALLOC_FREE(subreq);
360         tevent_req_done(req);
361 }
362
363 static ssize_t tsmsm_pread_recv(struct tevent_req *req,
364                                 struct vfs_aio_state *vfs_aio_state)
365 {
366         struct tsmsm_pread_state *state = tevent_req_data(
367                 req, struct tsmsm_pread_state);
368
369         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
370                 return -1;
371         }
372         if (state->ret >= 0 && state->was_offline) {
373                 struct files_struct *fsp = state->fsp;
374                 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
375                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
376                              fsp->fsp_name->base_name);
377         }
378         *vfs_aio_state = state->vfs_aio_state;
379         return state->ret;
380 }
381
382 struct tsmsm_pwrite_state {
383         struct files_struct *fsp;
384         ssize_t ret;
385         bool was_offline;
386         struct vfs_aio_state vfs_aio_state;
387 };
388
389 static void tsmsm_pwrite_done(struct tevent_req *subreq);
390
391 static struct tevent_req *tsmsm_pwrite_send(struct vfs_handle_struct *handle,
392                                             TALLOC_CTX *mem_ctx,
393                                             struct tevent_context *ev,
394                                             struct files_struct *fsp,
395                                             const void *data, size_t n,
396                                             off_t offset)
397 {
398         struct tevent_req *req, *subreq;
399         struct tsmsm_pwrite_state *state;
400
401         req = tevent_req_create(mem_ctx, &state, struct tsmsm_pwrite_state);
402         if (req == NULL) {
403                 return NULL;
404         }
405         state->fsp = fsp;
406         state->was_offline = tsmsm_aio_force(handle, fsp);
407         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
408                                           n, offset);
409         if (tevent_req_nomem(subreq, req)) {
410                 return tevent_req_post(req, ev);
411         }
412         tevent_req_set_callback(subreq, tsmsm_pwrite_done, req);
413         return req;
414 }
415
416 static void tsmsm_pwrite_done(struct tevent_req *subreq)
417 {
418         struct tevent_req *req = tevent_req_callback_data(
419                 subreq, struct tevent_req);
420         struct tsmsm_pwrite_state *state = tevent_req_data(
421                 req, struct tsmsm_pwrite_state);
422
423         state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
424         TALLOC_FREE(subreq);
425         tevent_req_done(req);
426 }
427
428 static ssize_t tsmsm_pwrite_recv(struct tevent_req *req,
429                                  struct vfs_aio_state *vfs_aio_state)
430 {
431         struct tsmsm_pwrite_state *state = tevent_req_data(
432                 req, struct tsmsm_pwrite_state);
433
434         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
435                 return -1;
436         }
437         if (state->ret >= 0 && state->was_offline) {
438                 struct files_struct *fsp = state->fsp;
439                 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
440                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
441                              fsp->fsp_name->base_name);
442         }
443         *vfs_aio_state = state->vfs_aio_state;
444         return state->ret;
445 }
446
447 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
448                               off_t offset, size_t n)
449 {
450         bool file_offline = tsmsm_aio_force(handle, fsp);
451
452         if (file_offline) {
453                 DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
454                 errno = ENOSYS;
455                 return -1;
456         }
457             
458         return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
459 }
460
461 /* We do overload pread to allow notification when file becomes online after offline status */
462 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
463 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
464                            void *data, size_t n, off_t offset) {
465         ssize_t result;
466         bool notify_online = tsmsm_aio_force(handle, fsp);
467
468         result = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
469         if((result != -1) && notify_online) {
470             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
471                what we can do is to send notification that file became online
472             */
473                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
474                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
475                              fsp->fsp_name->base_name);
476         }
477
478         return result;
479 }
480
481 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
482                             const void *data, size_t n, off_t offset) {
483         ssize_t result;
484         bool notify_online = tsmsm_aio_force(handle, fsp);
485
486         result = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
487         if((result != -1) && notify_online) {
488             /* We can't actually force AIO at this point (came here not from reply_read_and_X) 
489                what we can do is to send notification that file became online
490             */
491                 notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
492                              FILE_NOTIFY_CHANGE_ATTRIBUTES,
493                              fsp->fsp_name->base_name);
494         }
495
496         return result;
497 }
498
499 static NTSTATUS tsmsm_set_offline(struct vfs_handle_struct *handle,
500                                   const struct smb_filename *fname)
501 {
502         struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
503         int result = 0;
504         char *command;
505         NTSTATUS status;
506         char *path;
507
508         if (tsmd->hsmscript == NULL) {
509                 /* no script enabled */
510                 DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
511                 return NT_STATUS_OK;
512         }
513
514         status = get_full_smb_filename(talloc_tos(), fname, &path);
515         if (!NT_STATUS_IS_OK(status)) {
516                 return status;
517         }
518
519         /* Now, call the script */
520         command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
521         if(!command) {
522                 DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
523                 return NT_STATUS_NO_MEMORY;
524         }
525         DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
526         result = smbrun(command, NULL, NULL);
527         if(result != 0) {
528                 DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
529                 TALLOC_FREE(command);
530                 return NT_STATUS_INTERNAL_ERROR;
531         }
532         TALLOC_FREE(command);
533         return NT_STATUS_OK;
534 }
535
536 static NTSTATUS tsmsm_set_dos_attributes(struct vfs_handle_struct *handle,
537                                          const struct smb_filename *smb_fname,
538                                          uint32_t dosmode)
539 {
540         NTSTATUS status;
541         uint32_t old_dosmode;
542         struct smb_filename *fname = NULL;
543
544         /* dos_mode() doesn't like const smb_fname */
545         fname = cp_smb_filename(talloc_tos(), smb_fname);
546         if (fname == NULL) {
547                 return NT_STATUS_NO_MEMORY;
548         }
549
550         old_dosmode = fdos_mode(smb_fname->fsp);
551         TALLOC_FREE(fname);
552
553         status = SMB_VFS_NEXT_SET_DOS_ATTRIBUTES(handle, smb_fname, dosmode);
554         if (!NT_STATUS_IS_OK(status)) {
555                 return status;
556         }
557
558         if (!(old_dosmode & FILE_ATTRIBUTE_OFFLINE) &&
559             (dosmode & FILE_ATTRIBUTE_OFFLINE))
560         {
561                 return NT_STATUS_OK;
562         }
563
564         return tsmsm_set_offline(handle, smb_fname);
565 }
566
567 static NTSTATUS tsmsm_fset_dos_attributes(struct vfs_handle_struct *handle,
568                                           struct files_struct *fsp,
569                                           uint32_t dosmode)
570 {
571         NTSTATUS status;
572         uint32_t old_dosmode;
573
574         old_dosmode = dos_mode(handle->conn, fsp->fsp_name);
575
576         status = SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
577         if (!NT_STATUS_IS_OK(status)) {
578                 return status;
579         }
580
581         if (!(old_dosmode & FILE_ATTRIBUTE_OFFLINE) &&
582             (dosmode & FILE_ATTRIBUTE_OFFLINE))
583         {
584                 return NT_STATUS_OK;
585         }
586
587         return tsmsm_set_offline(handle, fsp->fsp_name);
588 }
589
590 static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
591                         enum timestamp_set_resolution *p_ts_res)
592 {
593         return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
594 }
595
596 static struct vfs_fn_pointers tsmsm_fns = {
597         .connect_fn = tsmsm_connect,
598         .fs_capabilities_fn = tsmsm_fs_capabilities,
599         .aio_force_fn = tsmsm_aio_force,
600         .pread_fn = tsmsm_pread,
601         .pread_send_fn = tsmsm_pread_send,
602         .pread_recv_fn = tsmsm_pread_recv,
603         .pwrite_fn = tsmsm_pwrite,
604         .pwrite_send_fn = tsmsm_pwrite_send,
605         .pwrite_recv_fn = tsmsm_pwrite_recv,
606         .sendfile_fn = tsmsm_sendfile,
607         .set_dos_attributes_fn = tsmsm_set_dos_attributes,
608         .fset_dos_attributes_fn = tsmsm_fset_dos_attributes,
609         .get_dos_attributes_fn = tsmsm_get_dos_attributes,
610         .get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
611         .get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
612         .fget_dos_attributes_fn = tsmsm_fget_dos_attributes,
613 };
614
615 static_decl_vfs;
616 NTSTATUS vfs_tsmsm_init(TALLOC_CTX *ctx)
617 {
618         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
619                                 "tsmsm", &tsmsm_fns);
620 }