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