selftest/Samba4: make use of get_cmd_env_vars() to setup all relevant env variables
[samba.git] / source3 / modules / vfs_tsmsm.c
index 2805488e8b5c3b33d001a38a48154fd42c21fef0..89191f7e3ae55be0d38083365fc33754efcacc3e 100644 (file)
@@ -3,8 +3,8 @@
   Samba VFS module for handling offline files
   with Tivoli Storage Manager Space Management
 
-  (c) Alexander Bokovoy, 2007
-  (c) Andrew Tridgell, 2007
+  (c) Alexander Bokovoy, 2007, 2008
+  (c) Andrew Tridgell, 2007, 2008
   
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
  */
 /*
   This VFS module accepts following options:
-  tsmsm: hsm script = <path to hsm script> (/bin/true by default, i.e. does nothing)
+  tsmsm: hsm script = <path to hsm script> (default does nothing)
          hsm script should point to a shell script which accepts two arguments:
         <operation> <filepath>
         where <operation> is currently 'offline' to set offline status of the <filepath>
 
   tsmsm: online ratio = ratio to check reported size against actual file size (0.5 by default)
+  tsmsm: dmapi attribute = name of DMAPI attribute that is present when a file is offline.
+  Default is "IBMobj" (which is what GPFS uses)
 
   The TSMSM VFS module tries to avoid calling expensive DMAPI calls with some heuristics
   based on the fact that number of blocks reported of a file multiplied by 512 will be
   bigger than 'online ratio' of actual size for online (non-migrated) files.
 
-  If checks fail, we call DMAPI and ask for specific IBM attribute which present for
+  If checks fail, we call DMAPI and ask for specific attribute which present for
   offline (migrated) files. If this attribute presents, we consider file offline.
  */
 
 #include "includes.h"
+#include "smbd/smbd.h"
+#include "lib/util/tevent_unix.h"
 
 #ifndef USE_DMAPI
 #error "This module requires DMAPI support!"
 
 /* optimisation tunables - used to avoid the DMAPI slow path */
 #define FILE_IS_ONLINE_RATIO      0.5
+
+/* default attribute name to look for */
 #define DM_ATTRIB_OBJECT "IBMObj"
-#define DM_ATTRIB_MIGRATED "IBMMig"
 
 struct tsmsm_struct {
-       dm_sessid_t sid;
        float online_ratio;
        char *hsmscript;
+       const char *attrib_name;
+       const char *attrib_value;
 };
 
-#define TSM_STRINGIFY(a) #a
-#define TSM_TOSTRING(a) TSM_STRINGIFY(a)
-
 static void tsmsm_free_data(void **pptr) {
        struct tsmsm_struct **tsmd = (struct tsmsm_struct **)pptr;
        if(!tsmd) return;
        TALLOC_FREE(*tsmd);
 }
 
+/* 
+   called when a client connects to a share
+*/
 static int tsmsm_connect(struct vfs_handle_struct *handle,
                         const char *service,
                         const char *user) {
-       struct tsmsm_struct *tsmd = TALLOC_ZERO_P(handle, struct tsmsm_struct);
-       const char *hsmscript, *tsmname;
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
+       struct tsmsm_struct *tsmd;
        const char *fres;
-       
+       const char *tsmname;
+        int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+
+       if (ret < 0) {
+               return ret;
+       }
+
+       tsmd = talloc_zero(handle, struct tsmsm_struct);
        if (!tsmd) {
+               SMB_VFS_NEXT_DISCONNECT(handle);
                DEBUG(0,("tsmsm_connect: out of memory!\n"));
                return -1;
        }
 
-       tsmd->sid = *(dm_sessid_t*) dmapi_get_current_session();
-
-       if (tsmd->sid == DM_NO_SESSION) {
+       if (!dmapi_have_session()) {
+               SMB_VFS_NEXT_DISCONNECT(handle);
                DEBUG(0,("tsmsm_connect: no DMAPI session for Samba is available!\n"));
                TALLOC_FREE(tsmd);
                return -1;
        }
 
        tsmname = (handle->param ? handle->param : "tsmsm");
-       hsmscript = lp_parm_const_string(SNUM(handle->conn), tsmname,
-                                        "hsm script", NULL);
-       if (hsmscript) {
-               tsmd->hsmscript = talloc_strdup(tsmd, hsmscript);
-               if(!tsmd->hsmscript) {
-                       DEBUG(1, ("tsmsm_connect: can't allocate memory for hsm script path"));
-                       TALLOC_FREE(tsmd);
-                       return -1;
-               }
-       } else {
-               DEBUG(1, ("tsmsm_connect: can't call hsm script because it "
-                         "is not set to anything in the smb.conf\n"
-                         "Use %s: 'hsm script = path' to set it\n",
-                         tsmname));
-               TALLOC_FREE(tsmd);
-               return -1;
-       }
-
+       
+       /* Get 'hsm script' and 'dmapi attribute' parameters to tsmd context */
+       tsmd->hsmscript = lp_parm_substituted_string(
+               tsmd, lp_sub, SNUM(handle->conn), tsmname,
+               "hsm script", NULL);
+       talloc_steal(tsmd, tsmd->hsmscript);
+       
+       tsmd->attrib_name = lp_parm_substituted_string(
+               tsmd, lp_sub, SNUM(handle->conn), tsmname,
+               "dmapi attribute", DM_ATTRIB_OBJECT);
+       talloc_steal(tsmd, tsmd->attrib_name);
+       
+       tsmd->attrib_value = lp_parm_substituted_string(
+               tsmd, lp_sub, SNUM(handle->conn), tsmname,
+               "dmapi value", NULL);
+       talloc_steal(tsmd, tsmd->attrib_value);
+       
+       /* retrieve 'online ratio'. In case of error default to FILE_IS_ONLINE_RATIO */
        fres = lp_parm_const_string(SNUM(handle->conn), tsmname, 
-                                   "online ratio", TSM_TOSTRING(FILE_IS_ONLINE_RATIO));
-       tsmd->online_ratio = strtof(fres, NULL);
-       if((tsmd->online_ratio == (float)0) || ((errno == ERANGE) &&
-                                               ((tsmd->online_ratio == HUGE_VALF) ||
-                                                (tsmd->online_ratio == HUGE_VALL)))) {
-               DEBUG(1, ("tsmsm_connect: error while getting online ratio from smb.conf."
-                         "Default to %s.\n", TSM_TOSTRING(FILE_IS_ONLINE_RATIO)));
+                                   "online ratio", NULL);
+       if (fres == NULL) {
                tsmd->online_ratio = FILE_IS_ONLINE_RATIO;
+       } else {
+               tsmd->online_ratio = strtof(fres, NULL);
+               if (tsmd->online_ratio > 1.0 ||
+                   tsmd->online_ratio <= 0.0) {
+                       DEBUG(1, ("tsmsm_connect: invalid online ration %f - using %f.\n",
+                                 tsmd->online_ratio, (float)FILE_IS_ONLINE_RATIO));
+               }
        }
 
         /* Store the private data. */
         SMB_VFS_HANDLE_SET_DATA(handle, tsmd, tsmsm_free_data,
                                 struct tsmsm_struct, return -1);
-        return SMB_VFS_NEXT_CONNECT(handle, service, user); 
+        return 0;
 }
 
 static bool tsmsm_is_offline(struct vfs_handle_struct *handle, 
-                           const char *path,
-                           SMB_STRUCT_STAT *stbuf) {
+                            const struct smb_filename *fname,
+                            SMB_STRUCT_STAT *stbuf)
+{
        struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
+       const dm_sessid_t *dmsession_id;
        void *dmhandle = NULL;
        size_t dmhandle_len = 0;
        size_t rlen;
        dm_attrname_t dmname;
-       int ret;
+       int ret, lerrno;
        bool offline;
+       char *buf = NULL;
+       size_t buflen;
+       NTSTATUS status;
+       char *path;
+
+        status = get_full_smb_filename(talloc_tos(), fname, &path);
+        if (!NT_STATUS_IS_OK(status)) {
+                errno = map_errno_from_nt_status(status);
+                return false;
+        }
 
         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
           then assume it is not offline (it may not be 100%, as it could be sparse) */
-       if (512 * (off_t)stbuf->st_blocks >= stbuf->st_size * tsmd->online_ratio) {
-               DEBUG(10,("%s not offline: st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
-                         path, stbuf->st_blocks, stbuf->st_size, tsmd->online_ratio));
+       if (512 * stbuf->st_ex_blocks >=
+           stbuf->st_ex_size * tsmd->online_ratio) {
+               DEBUG(10,("%s not offline: st_blocks=%llu st_size=%llu "
+                         "online_ratio=%.2f\n", path,
+                         (unsigned long long)stbuf->st_ex_blocks,
+                         (unsigned long long)stbuf->st_ex_size, tsmd->online_ratio));
                return false;
        }
-       
+
+       dmsession_id = dmapi_get_current_session();
+       if (dmsession_id == NULL) {
+               DEBUG(2, ("tsmsm_is_offline: no DMAPI session available? "
+                         "Assume file is online.\n"));
+               return false;
+       }
+
         /* using POSIX capabilities does not work here. It's a slow path, so 
         * become_root() is just as good anyway (tridge) 
         */
 
-       /* Also, AIX has DMAPI but no POSIX capablities support. In this case,
+       /* Also, AIX has DMAPI but no POSIX capabilities support. In this case,
         * we need to be root to do DMAPI manipulations.
         */
        become_root();
@@ -173,13 +211,50 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
        }
 
        memset(&dmname, 0, sizeof(dmname));
-       strlcpy((char *)&dmname.an_chars[0], DM_ATTRIB_OBJECT, sizeof(dmname.an_chars));
+       strlcpy((char *)&dmname.an_chars[0], tsmd->attrib_name, sizeof(dmname.an_chars));
+
+       if (tsmd->attrib_value != NULL) {
+               buflen = strlen(tsmd->attrib_value);
+       } else {
+               buflen = 1;
+       }
+       buf = talloc_zero_size(tsmd, buflen);
+       if (buf == NULL) {
+               DEBUG(0,("out of memory in tsmsm_is_offline -- assuming online (%s)\n", path));
+               errno = ENOMEM;
+               offline = false;
+               goto done;
+       }
 
-       ret = dm_get_dmattr(tsmd->sid, dmhandle, dmhandle_len, 
-                           DM_NO_TOKEN, &dmname, 0, NULL, &rlen);
+       do {
+               lerrno = 0;
+
+               ret = dm_get_dmattr(*dmsession_id, dmhandle, dmhandle_len, 
+                                   DM_NO_TOKEN, &dmname, buflen, buf, &rlen);
+               if (ret == -1 && errno == EINVAL) {
+                       DEBUG(0, ("Stale DMAPI session, re-creating it.\n"));
+                       lerrno = EINVAL;
+                       if (dmapi_new_session()) {
+                               dmsession_id = dmapi_get_current_session();
+                       } else {
+                               DEBUG(0, 
+                                     ("Unable to re-create DMAPI session, assuming offline (%s) - %s\n", 
+                                      path, strerror(errno)));
+                               offline = true;
+                               dm_handle_free(dmhandle, dmhandle_len);
+                               goto done;
+                       }
+               }
+       } while (ret == -1 && lerrno == EINVAL);
 
-       /* its offline if the IBMObj attribute exists */
-       offline = (ret == 0 || (ret == -1 && errno == E2BIG));
+       /* check if we need a specific attribute value */
+       if (tsmd->attrib_value != NULL) {
+               offline = (ret == 0 && rlen == buflen && 
+                           memcmp(buf, tsmd->attrib_value, buflen) == 0);
+       } else {
+               /* its offline if the specified DMAPI attribute exists */
+               offline = (ret == 0 || (ret == -1 && errno == E2BIG));
+       }
 
        DEBUG(10,("dm_get_dmattr %s ret=%d (%s)\n", path, ret, strerror(errno)));
 
@@ -188,10 +263,24 @@ static bool tsmsm_is_offline(struct vfs_handle_struct *handle,
        dm_handle_free(dmhandle, dmhandle_len); 
 
 done:
+       talloc_free(buf);
        unbecome_root();
        return offline;
 }
 
+static NTSTATUS tsmsm_fget_dos_attributes(struct vfs_handle_struct *handle,
+                                         files_struct *fsp,
+                                         uint32_t *dosmode)
+{
+       bool offline;
+
+       offline = tsmsm_is_offline(handle, fsp->fsp_name, &fsp->fsp_name->st);
+       if (offline) {
+               *dosmode |= FILE_ATTRIBUTE_OFFLINE;
+       }
+
+       return SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, dosmode);
+}
 
 static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struct *fsp)
 {
@@ -203,35 +292,154 @@ static bool tsmsm_aio_force(struct vfs_handle_struct *handle, struct files_struc
           if the file might be offline
        */
        if(SMB_VFS_FSTAT(fsp, &sbuf) == 0) {
-               DEBUG(10,("tsmsm_aio_force st_blocks=%ld st_size=%ld online_ratio=%.2f\n", 
-                         sbuf.st_blocks, sbuf.st_size, tsmd->online_ratio));
-               return !(512 * (off_t)sbuf.st_blocks >= sbuf.st_size * tsmd->online_ratio);
+               DEBUG(10,("tsmsm_aio_force st_blocks=%llu st_size=%llu "
+                         "online_ratio=%.2f\n", (unsigned long long)sbuf.st_ex_blocks,
+                         (unsigned long long)sbuf.st_ex_size, tsmd->online_ratio));
+               return !(512 * sbuf.st_ex_blocks >=
+                        sbuf.st_ex_size * tsmd->online_ratio);
        }
        return false;
 }
 
-static ssize_t tsmsm_aio_return(struct vfs_handle_struct *handle, struct files_struct *fsp, 
-                               SMB_STRUCT_AIOCB *aiocb)
+struct tsmsm_pread_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       bool was_offline;
+       struct vfs_aio_state vfs_aio_state;
+};
+
+static void tsmsm_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *tsmsm_pread_send(struct vfs_handle_struct *handle,
+                                          TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct files_struct *fsp,
+                                          void *data, size_t n, off_t offset)
 {
-       ssize_t result;
+       struct tevent_req *req, *subreq;
+       struct tsmsm_pread_state *state;
 
-       result = SMB_VFS_NEXT_AIO_RETURN(handle, fsp, aiocb);
-       if(result >= 0) {
-               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+       req = tevent_req_create(mem_ctx, &state, struct tsmsm_pread_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->fsp = fsp;
+       state->was_offline = tsmsm_aio_force(handle, fsp);
+       subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
+                                        n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, tsmsm_pread_done, req);
+       return req;
+}
+
+static void tsmsm_pread_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct tsmsm_pread_state *state = tevent_req_data(
+               req, struct tsmsm_pread_state);
+
+       state->ret = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t tsmsm_pread_recv(struct tevent_req *req,
+                               struct vfs_aio_state *vfs_aio_state)
+{
+       struct tsmsm_pread_state *state = tevent_req_data(
+               req, struct tsmsm_pread_state);
+
+       if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+               return -1;
+       }
+       if (state->ret >= 0 && state->was_offline) {
+               struct files_struct *fsp = state->fsp;
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
                             FILE_NOTIFY_CHANGE_ATTRIBUTES,
-                            fsp->fsp_name);
+                            fsp->fsp_name->base_name);
        }
+       *vfs_aio_state = state->vfs_aio_state;
+       return state->ret;
+}
 
-       return result;
+struct tsmsm_pwrite_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       bool was_offline;
+       struct vfs_aio_state vfs_aio_state;
+};
+
+static void tsmsm_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *tsmsm_pwrite_send(struct vfs_handle_struct *handle,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct files_struct *fsp,
+                                           const void *data, size_t n,
+                                           off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct tsmsm_pwrite_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct tsmsm_pwrite_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->fsp = fsp;
+       state->was_offline = tsmsm_aio_force(handle, fsp);
+       subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
+                                         n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, tsmsm_pwrite_done, req);
+       return req;
+}
+
+static void tsmsm_pwrite_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct tsmsm_pwrite_state *state = tevent_req_data(
+               req, struct tsmsm_pwrite_state);
+
+       state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t tsmsm_pwrite_recv(struct tevent_req *req,
+                                struct vfs_aio_state *vfs_aio_state)
+{
+       struct tsmsm_pwrite_state *state = tevent_req_data(
+               req, struct tsmsm_pwrite_state);
+
+       if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+               return -1;
+       }
+       if (state->ret >= 0 && state->was_offline) {
+               struct files_struct *fsp = state->fsp;
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+       *vfs_aio_state = state->vfs_aio_state;
+       return state->ret;
 }
 
 static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct *fsp, const DATA_BLOB *hdr,
-                             SMB_OFF_T offset, size_t n)
+                             off_t offset, size_t n)
 {
-       bool file_online = tsmsm_aio_force(handle, fsp);
+       bool file_offline = tsmsm_aio_force(handle, fsp);
 
-       if(!file_online) 
-           return ENOSYS;
+       if (file_offline) {
+               DEBUG(10,("tsmsm_sendfile on offline file - rejecting\n"));
+               errno = ENOSYS;
+               return -1;
+       }
            
        return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
 }
@@ -239,7 +447,7 @@ static ssize_t tsmsm_sendfile(vfs_handle_struct *handle, int tofd, files_struct
 /* We do overload pread to allow notification when file becomes online after offline status */
 /* We don't intercept SMB_VFS_READ here because all file I/O now goes through SMB_VFS_PREAD instead */
 static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct *fsp, 
-                          void *data, size_t n, SMB_OFF_T offset) {
+                          void *data, size_t n, off_t offset) {
        ssize_t result;
        bool notify_online = tsmsm_aio_force(handle, fsp);
 
@@ -250,14 +458,14 @@ static ssize_t tsmsm_pread(struct vfs_handle_struct *handle, struct files_struct
            */
                notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
                             FILE_NOTIFY_CHANGE_ATTRIBUTES,
-                            fsp->fsp_name);
+                            fsp->fsp_name->base_name);
        }
 
        return result;
 }
 
 static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struct *fsp, 
-                          void *data, size_t n, SMB_OFF_T offset) {
+                           const void *data, size_t n, off_t offset) {
        ssize_t result;
        bool notify_online = tsmsm_aio_force(handle, fsp);
 
@@ -268,74 +476,98 @@ static ssize_t tsmsm_pwrite(struct vfs_handle_struct *handle, struct files_struc
            */
                notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
                             FILE_NOTIFY_CHANGE_ATTRIBUTES,
-                            fsp->fsp_name);
+                            fsp->fsp_name->base_name);
        }
 
        return result;
 }
 
-static int tsmsm_set_offline(struct vfs_handle_struct *handle, 
-                            const char *path) {
+static NTSTATUS tsmsm_set_offline(struct vfs_handle_struct *handle,
+                                 const struct smb_filename *fname)
+{
        struct tsmsm_struct *tsmd = (struct tsmsm_struct *) handle->data;
        int result = 0;
        char *command;
+       NTSTATUS status;
+       char *path;
+
+       if (tsmd->hsmscript == NULL) {
+               /* no script enabled */
+               DEBUG(1, ("tsmsm_set_offline: No 'tsmsm:hsm script' configured\n"));
+               return NT_STATUS_OK;
+       }
+
+        status = get_full_smb_filename(talloc_tos(), fname, &path);
+        if (!NT_STATUS_IS_OK(status)) {
+               return status;
+        }
 
        /* Now, call the script */
        command = talloc_asprintf(tsmd, "%s offline \"%s\"", tsmd->hsmscript, path);
        if(!command) {
-               DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script"));
-               return -1;
+               DEBUG(1, ("tsmsm_set_offline: can't allocate memory to run hsm script\n"));
+               return NT_STATUS_NO_MEMORY;
        }
        DEBUG(10, ("tsmsm_set_offline: Running [%s]\n", command));
-       if((result = smbrun(command, NULL)) != 0) {
+       result = smbrun(command, NULL, NULL);
+       if(result != 0) {
                DEBUG(1,("tsmsm_set_offline: Running [%s] returned %d\n", command, result));
+               TALLOC_FREE(command);
+               return NT_STATUS_INTERNAL_ERROR;
        }
        TALLOC_FREE(command);
-       return result;
+       return NT_STATUS_OK;
 }
 
-static bool tsmsm_statvfs(struct vfs_handle_struct *handle,  const char *path, vfs_statvfs_struct *statbuf)
+static NTSTATUS tsmsm_fset_dos_attributes(struct vfs_handle_struct *handle,
+                                         struct files_struct *fsp,
+                                         uint32_t dosmode)
 {
-       bool result;
+       NTSTATUS status;
+       uint32_t old_dosmode;
 
-       result = SMB_VFS_NEXT_STATVFS(handle, path, statbuf);
-       statbuf->FsCapabilities | = FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
+       old_dosmode = fdos_mode(fsp);
 
-       return result;
+       status = SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (!(old_dosmode & FILE_ATTRIBUTE_OFFLINE) &&
+           (dosmode & FILE_ATTRIBUTE_OFFLINE))
+       {
+               return NT_STATUS_OK;
+       }
+
+       return tsmsm_set_offline(handle, fsp->fsp_name);
+}
+
+static uint32_t tsmsm_fs_capabilities(struct vfs_handle_struct *handle,
+                       enum timestamp_set_resolution *p_ts_res)
+{
+       return SMB_VFS_NEXT_FS_CAPABILITIES(handle, p_ts_res) | FILE_SUPPORTS_REMOTE_STORAGE | FILE_SUPPORTS_REPARSE_POINTS;
 }
 
-static vfs_op_tuple vfs_tsmsm_ops[] = {
-
-       /* Disk operations */
-
-       {SMB_VFS_OP(tsmsm_connect),     SMB_VFS_OP_CONNECT,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_statvfs),     SMB_VFS_OP_STATVFS,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_aio_force),   SMB_VFS_OP_AIO_FORCE,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_aio_return),  SMB_VFS_OP_AIO_RETURN,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_pread),       SMB_VFS_OP_PREAD,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_pwrite),      SMB_VFS_OP_PWRITE,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_sendfile),    SMB_VFS_OP_SENDFILE,
-        SMB_VFS_LAYER_TRANSPARENT},
-       {SMB_VFS_OP(tsmsm_is_offline),  SMB_VFS_OP_IS_OFFLINE,
-        SMB_VFS_LAYER_OPAQUE},
-       {SMB_VFS_OP(tsmsm_set_offline), SMB_VFS_OP_SET_OFFLINE,
-        SMB_VFS_LAYER_OPAQUE},
-
-       /* Finish VFS operations definition */
-
-       {SMB_VFS_OP(NULL),              SMB_VFS_OP_NOOP,
-        SMB_VFS_LAYER_NOOP}
+static struct vfs_fn_pointers tsmsm_fns = {
+       .connect_fn = tsmsm_connect,
+       .fs_capabilities_fn = tsmsm_fs_capabilities,
+       .aio_force_fn = tsmsm_aio_force,
+       .pread_fn = tsmsm_pread,
+       .pread_send_fn = tsmsm_pread_send,
+       .pread_recv_fn = tsmsm_pread_recv,
+       .pwrite_fn = tsmsm_pwrite,
+       .pwrite_send_fn = tsmsm_pwrite_send,
+       .pwrite_recv_fn = tsmsm_pwrite_recv,
+       .sendfile_fn = tsmsm_sendfile,
+       .fset_dos_attributes_fn = tsmsm_fset_dos_attributes,
+       .get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+       .get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
+       .fget_dos_attributes_fn = tsmsm_fget_dos_attributes,
 };
 
-NTSTATUS vfs_tsmsm_init(void);
-NTSTATUS vfs_tsmsm_init(void)
+static_decl_vfs;
+NTSTATUS vfs_tsmsm_init(TALLOC_CTX *ctx)
 {
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
-                               "tsmsm", vfs_tsmsm_ops);
+                               "tsmsm", &tsmsm_fns);
 }