shadow_copy2: disable "snapdir:crossmountpoints" if the snapdir is absolute.
[mat/samba.git] / source3 / modules / vfs_shadow_copy2.c
index ca33b6d34442ff41a5e97638e70fa6bd251f6bb5..adcc855320d95fe8f569484335de8fe7e41cd79a 100644 (file)
       be compatible with the conversion specifications recognized
       by str[fp]time.  The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
 
+      shadow:sscanf = yes/no (default is no)
+
+      The time is the unsigned long integer (%lu) in the format string
+      rather than a time strptime() can parse.  The result must be a unix time_t
+      time.
+
       shadow:localtime = yes/no (default is no)
 
       This is an optional parameter that indicates whether the
 #include "includes.h"
 #include "system/filesys.h"
 #include "include/ntioctl.h"
-#include "smbd/proto.h"
-#include <tdb.h>
+#include <ccan/hash/hash.h>
 #include "util_tdb.h"
 
-#define GMT_NAME_LEN 24 /* length of a @GMT- name */
-#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
+struct shadow_copy2_config {
+       char *gmt_format;
+       bool use_sscanf;
+       bool use_localtime;
+       char *snapdir;
+       bool snapdirseverywhere;
+       bool crossmountpoints;
+       bool fixinodes;
+       char *sort_order;
+       bool snapdir_absolute;
+};
 
 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
                                      size_t **poffsets,
@@ -139,33 +153,62 @@ static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
        return true;
 }
 
+/**
+ * Given a timstamp, build the string to insert into a path
+ * as a path component for creating the local path to the
+ * snapshot at the given timestamp of the input path.
+ */
 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
                                        struct vfs_handle_struct *handle,
                                        time_t snapshot)
 {
        struct tm snap_tm;
-       fstring gmt;
-       size_t gmt_len;
-
-       if (localtime_r(&snapshot, &snap_tm) == 0) {
-               DEBUG(10, ("gmtime_r failed\n"));
-               return NULL;
-       }
-       gmt_len = strftime(gmt, sizeof(gmt),
-                          lp_parm_const_string(SNUM(handle->conn), "shadow",
-                                               "format", GMT_FORMAT),
-                          &snap_tm);
-       if (gmt_len == 0) {
-               DEBUG(10, ("strftime failed\n"));
-               return NULL;
+       fstring snaptime_string;
+       size_t snaptime_len;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return NULL);
+
+       if (config->use_sscanf) {
+               snaptime_len = snprintf(snaptime_string,
+                                       sizeof(snaptime_string),
+                                       config->gmt_format,
+                                       (unsigned long)snapshot);
+               if (snaptime_len <= 0) {
+                       DEBUG(10, ("snprintf failed\n"));
+                       return NULL;
+               }
+       } else {
+               if (config->use_localtime) {
+                       if (localtime_r(&snapshot, &snap_tm) == 0) {
+                               DEBUG(10, ("gmtime_r failed\n"));
+                               return NULL;
+                       }
+               } else {
+                       if (gmtime_r(&snapshot, &snap_tm) == 0) {
+                               DEBUG(10, ("gmtime_r failed\n"));
+                               return NULL;
+                       }
+               }
+               snaptime_len = strftime(snaptime_string,
+                                       sizeof(snaptime_string),
+                                       config->gmt_format,
+                                       &snap_tm);
+               if (snaptime_len == 0) {
+                       DEBUG(10, ("strftime failed\n"));
+                       return NULL;
+               }
        }
-       return talloc_asprintf(talloc_tos(), "/%s/%s",
-                              lp_parm_const_string(
-                                      SNUM(handle->conn), "shadow", "snapdir",
-                                      ".snapshots"),
-                              gmt);
+       return talloc_asprintf(mem_ctx, "/%s/%s",
+                              config->snapdir, snaptime_string);
 }
 
+/**
+ * Strip a snapshot component from an filename as
+ * handed in via the smb layer.
+ * Returns the parsed timestamp and the stripped filename.
+ */
 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
                                        struct vfs_handle_struct *handle,
                                        const char *name,
@@ -178,6 +221,10 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
        char *q;
        char *stripped;
        size_t rest_len, dst_len;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return false);
 
        p = strstr_m(name, "@GMT-");
        if (p == NULL) {
@@ -191,7 +238,7 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
                goto no_snapshot;
        }
        tm.tm_isdst = -1;
-       timestamp = mktime(&tm);
+       timestamp = timegm(&tm);
        if (timestamp == (time_t)-1) {
                goto no_snapshot;
        }
@@ -214,8 +261,7 @@ static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
        rest_len = strlen(q);
        dst_len = (p-name) + rest_len;
 
-       if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
-                        false)) {
+       if (config->snapdirseverywhere) {
                char *insert;
                bool have_insert;
                insert = shadow_copy2_insert_string(talloc_tos(), handle,
@@ -284,6 +330,11 @@ static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
        return path;
 }
 
+/**
+ * Convert from a name as handed in via the SMB layer
+ * and a timestamp into the local path of the snapshot
+ * of the provided file at the provided time.
+ */
 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
                                  struct vfs_handle_struct *handle,
                                  const char *name, time_t timestamp)
@@ -299,6 +350,10 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
        size_t insertlen;
        int i, saved_errno;
        size_t min_offset;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return NULL);
 
        path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
                               name);
@@ -341,8 +396,7 @@ static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
 
        min_offset = 0;
 
-       if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
-                         false)) {
+       if (!config->crossmountpoints) {
                char *mount_point;
 
                mount_point = shadow_copy2_find_mount_point(talloc_tos(),
@@ -426,7 +480,12 @@ fail:
 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
                         SMB_STRUCT_STAT *sbuf)
 {
-       if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return);
+
+       if (config->fixinodes) {
                /* some snapshot systems, like GPFS, return the name
                   device:inode for the snapshot files as the current
                   files. That breaks the 'restore' button in the shadow copy
@@ -438,9 +497,8 @@ static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
                   without significant VFS changes
                */
                uint32_t shash;
-               TDB_DATA data = string_tdb_data(fname);
 
-               shash = tdb_jenkins_hash(&data) & 0xFF000000;
+               shash = hash(fname, strlen(fname), 0) & 0xFF000000;
                if (shash == 0) {
                        shash = 1;
                }
@@ -448,14 +506,14 @@ static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
        }
 }
 
-static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
+static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
                                            const char *fname,
                                            const char *mask,
                                            uint32 attr)
 {
        time_t timestamp;
        char *stripped;
-       SMB_STRUCT_DIR *ret;
+       DIR *ret;
        int saved_errno;
        char *conv;
 
@@ -688,7 +746,6 @@ static int shadow_copy2_unlink(vfs_handle_struct *handle,
        char *stripped;
        int ret, saved_errno;
        struct smb_filename *conv;
-       NTSTATUS status;
 
        if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
                                         smb_fname->base_name,
@@ -698,8 +755,8 @@ static int shadow_copy2_unlink(vfs_handle_struct *handle,
        if (timestamp == 0) {
                return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
        }
-       status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
-       if (!NT_STATUS_IS_OK(status)) {
+       conv = cp_smb_filename(talloc_tos(), smb_fname);
+       if (conv == NULL) {
                errno = ENOMEM;
                return -1;
        }
@@ -805,7 +862,6 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle,
        char *stripped;
        int ret, saved_errno;
        struct smb_filename *conv;
-       NTSTATUS status;
 
        if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
                                         smb_fname->base_name,
@@ -815,8 +871,8 @@ static int shadow_copy2_ntimes(vfs_handle_struct *handle,
        if (timestamp == 0) {
                return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
        }
-       status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
-       if (!NT_STATUS_IS_OK(status)) {
+       conv = cp_smb_filename(talloc_tos(), smb_fname);
+       if (conv == NULL) {
                errno = ENOMEM;
                return -1;
        }
@@ -941,17 +997,25 @@ done:
        return result;
 }
 
+/**
+ * Check whether a given directory contains a
+ * snapshot directory as direct subdirectory.
+ * If yes, return the path of the snapshot-subdir,
+ * otherwise return NULL.
+ */
 static char *have_snapdir(struct vfs_handle_struct *handle,
                          const char *path)
 {
        struct smb_filename smb_fname;
        int ret;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return NULL);
 
        ZERO_STRUCT(smb_fname);
-       smb_fname.base_name = talloc_asprintf(
-               talloc_tos(), "%s/%s", path,
-               lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
-                                    ".snapshots"));
+       smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
+                                             path, config->snapdir);
        if (smb_fname.base_name == NULL) {
                return NULL;
        }
@@ -964,12 +1028,20 @@ static char *have_snapdir(struct vfs_handle_struct *handle,
        return NULL;
 }
 
+/**
+ * Find the snapshot directory (if any) for the given
+ * filename (which is relative to the share).
+ */
 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
                                       struct vfs_handle_struct *handle,
                                       struct smb_filename *smb_fname)
 {
        char *path, *p;
        char *snapdir;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return NULL);
 
        path = talloc_asprintf(mem_ctx, "%s/%s",
                               handle->conn->connectpath,
@@ -998,32 +1070,48 @@ static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
        return NULL;
 }
 
-static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
-                                        vfs_handle_struct *handle,
+static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
                                         const char *name,
                                         char *gmt, size_t gmt_len)
 {
        struct tm timestamp;
        time_t timestamp_t;
+       unsigned long int timestamp_long;
        const char *fmt;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return NULL);
 
-       fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
-                                  "format", GMT_FORMAT);
+       fmt = config->gmt_format;
 
        ZERO_STRUCT(timestamp);
-       if (strptime(name, fmt, &timestamp) == NULL) {
-               DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
+       if (config->use_sscanf) {
+               if (sscanf(name, fmt, &timestamp_long) != 1) {
+                       DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
+                                  "no sscanf match %s: %s\n",
+                                  fmt, name));
+                       return false;
+               }
+               timestamp_t = timestamp_long;
+               gmtime_r(&timestamp_t, &timestamp);
+       } else {
+               if (strptime(name, fmt, &timestamp) == NULL) {
+                       DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
+                                  "no match %s: %s\n",
+                                  fmt, name));
+                       return false;
+               }
+               DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
                           fmt, name));
-               return false;
+               
+               if (config->use_localtime) {
+                       timestamp.tm_isdst = -1;
+                       timestamp_t = mktime(&timestamp);
+                       gmtime_r(&timestamp_t, &timestamp);
+               }
        }
 
-       DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
-
-       if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
-               timestamp.tm_isdst = -1;
-               timestamp_t = mktime(&timestamp);
-               gmtime_r(&timestamp_t, &timestamp);
-       }
        strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
        return true;
 }
@@ -1046,9 +1134,12 @@ static void shadow_copy2_sort_data(vfs_handle_struct *handle,
 {
        int (*cmpfunc)(const void *, const void *);
        const char *sort;
+       struct shadow_copy2_config *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
+                               return);
 
-       sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
-                                   "sort", NULL);
+       sort = config->sort_order;
        if (sort == NULL) {
                return;
        }
@@ -1068,8 +1159,6 @@ static void shadow_copy2_sort_data(vfs_handle_struct *handle,
                               shadow_copy2_data->num_volumes,
                               cmpfunc);
        }
-
-       return;
 }
 
 static int shadow_copy2_get_shadow_copy_data(
@@ -1077,9 +1166,9 @@ static int shadow_copy2_get_shadow_copy_data(
        struct shadow_copy_data *shadow_copy2_data,
        bool labels)
 {
-       SMB_STRUCT_DIR *p;
+       DIR *p;
        const char *snapdir;
-       SMB_STRUCT_DIRENT *d;
+       struct dirent *d;
        TALLOC_CTX *tmp_ctx = talloc_stackframe();
 
        snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
@@ -1113,7 +1202,7 @@ static int shadow_copy2_get_shadow_copy_data(
                 * directory
                 */
                if (!shadow_copy2_snapshot_to_gmt(
-                           tmp_ctx, handle, d->d_name,
+                           handle, d->d_name,
                            snapshot, sizeof(snapshot))) {
 
                        DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
@@ -1158,6 +1247,7 @@ static int shadow_copy2_get_shadow_copy_data(
 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
                                        struct files_struct *fsp,
                                        uint32 security_info,
+                                        TALLOC_CTX *mem_ctx,
                                        struct security_descriptor **ppdesc)
 {
        time_t timestamp;
@@ -1172,6 +1262,7 @@ static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
        }
        if (timestamp == 0) {
                return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+                                               mem_ctx,
                                                ppdesc);
        }
        conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
@@ -1179,7 +1270,8 @@ static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
        if (conv == NULL) {
                return map_nt_error_from_unix(errno);
        }
-       status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
+       status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
+                                        mem_ctx, ppdesc);
        TALLOC_FREE(conv);
        return status;
 }
@@ -1187,6 +1279,7 @@ static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
                                        const char *fname,
                                        uint32 security_info,
+                                       TALLOC_CTX *mem_ctx,
                                        struct security_descriptor **ppdesc)
 {
        time_t timestamp;
@@ -1200,14 +1293,15 @@ static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
        }
        if (timestamp == 0) {
                return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
-                                              ppdesc);
+                                              mem_ctx, ppdesc);
        }
        conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
        TALLOC_FREE(stripped);
        if (conv == NULL) {
                return map_nt_error_from_unix(errno);
        }
-       status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
+       status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
+                                        mem_ctx, ppdesc);
        TALLOC_FREE(conv);
        return status;
 }
@@ -1322,36 +1416,6 @@ static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
        return ret;
 }
 
-static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
-                                     const char *fname, const char *aname,
-                                     void *value, size_t size)
-{
-       time_t timestamp;
-       char *stripped;
-       ssize_t ret;
-       int saved_errno;
-       char *conv;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
-                                        &timestamp, &stripped)) {
-               return -1;
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_LGETXATTR(handle, fname, aname, value,
-                                             size);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return -1;
-       }
-       ret = SMB_VFS_NEXT_LGETXATTR(handle, conv, aname, value, size);
-       saved_errno = errno;
-       TALLOC_FREE(conv);
-       errno = saved_errno;
-       return ret;
-}
-
 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
                                      const char *fname,
                                      char *list, size_t size)
@@ -1408,33 +1472,6 @@ static int shadow_copy2_removexattr(vfs_handle_struct *handle,
        return ret;
 }
 
-static int shadow_copy2_lremovexattr(vfs_handle_struct *handle,
-                                    const char *fname, const char *aname)
-{
-       time_t timestamp;
-       char *stripped;
-       int ret, saved_errno;
-       char *conv;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
-                                        &timestamp, &stripped)) {
-               return -1;
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_LREMOVEXATTR(handle, fname, aname);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return -1;
-       }
-       ret = SMB_VFS_NEXT_LREMOVEXATTR(handle, conv, aname);
-       saved_errno = errno;
-       TALLOC_FREE(conv);
-       errno = saved_errno;
-       return ret;
-}
-
 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
                                 const char *fname,
                                 const char *aname, const void *value,
@@ -1466,37 +1503,6 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
        return ret;
 }
 
-static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle,
-                                 const char *fname,
-                                 const char *aname, const void *value,
-                                 size_t size, int flags)
-{
-       time_t timestamp;
-       char *stripped;
-       ssize_t ret;
-       int saved_errno;
-       char *conv;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
-                                        &timestamp, &stripped)) {
-               return -1;
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_LSETXATTR(handle, fname, aname, value,
-                                             size, flags);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return -1;
-       }
-       ret = SMB_VFS_NEXT_LSETXATTR(handle, conv, aname, value, size, flags);
-       saved_errno = errno;
-       TALLOC_FREE(conv);
-       errno = saved_errno;
-       return ret;
-}
-
 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
                                  const char *fname, mode_t mode)
 {
@@ -1566,39 +1572,134 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
        return ret;
 }
 
+static int shadow_copy2_connect(struct vfs_handle_struct *handle,
+                               const char *service, const char *user)
+{
+       struct shadow_copy2_config *config;
+       int ret;
+       const char *snapdir;
+       const char *gmt_format;
+       const char *sort_order;
+
+       DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
+                  (unsigned)handle->conn->cnum,
+                  handle->conn->connectpath));
+
+       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (ret < 0) {
+               return ret;
+       }
+
+       config = talloc_zero(handle->conn, struct shadow_copy2_config);
+       if (config == NULL) {
+               DEBUG(0, ("talloc_zero() failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       gmt_format = lp_parm_const_string(SNUM(handle->conn),
+                                         "shadow", "format",
+                                         GMT_FORMAT);
+       config->gmt_format = talloc_strdup(config, gmt_format);
+       if (config->gmt_format == NULL) {
+               DEBUG(0, ("talloc_strdup() failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
+                                         "shadow", "sscanf", false);
+
+       config->use_localtime = lp_parm_bool(SNUM(handle->conn),
+                                            "shadow", "localtime",
+                                            false);
+
+       snapdir = lp_parm_const_string(SNUM(handle->conn),
+                                      "shadow", "snapdir",
+                                      ".snapshots");
+       config->snapdir = talloc_strdup(config, snapdir);
+       if (config->snapdir == NULL) {
+               DEBUG(0, ("talloc_strdup() failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
+                                                 "shadow",
+                                                 "snapdirseverywhere",
+                                                 false);
+
+       config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
+                                               "shadow", "crossmountpoints",
+                                               false);
+
+       config->fixinodes = lp_parm_bool(SNUM(handle->conn),
+                                        "shadow", "fixinodes",
+                                        false);
+
+       sort_order = lp_parm_const_string(SNUM(handle->conn),
+                                         "shadow", "sort", "desc");
+       config->sort_order = talloc_strdup(config, sort_order);
+       if (config->sort_order == NULL) {
+               DEBUG(0, ("talloc_strdup() failed\n"));
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (config->snapdir[0] == '/') {
+               config->snapdir_absolute = true;
+               if (config->snapdirseverywhere == true) {
+                       DEBUG(1, (__location__ " Warning: An absolute snapdir "
+                                 "is incompatible with 'snapdirseverywhere', "
+                                 "setting 'snapdirseverywhere' to false.\n"));
+                       config->snapdirseverywhere = false;
+               }
+
+               if (config->crossmountpoints == true) {
+                       DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
+                                 "is not supported with an absolute snapdir. "
+                                 "Disabling it.\n"));
+                       config->crossmountpoints = false;
+               }
+       }
+
+       SMB_VFS_HANDLE_SET_DATA(handle, config,
+                               NULL, struct shadow_copy2_config,
+                               return -1);
+
+       return 0;
+}
 
 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
-       .opendir = shadow_copy2_opendir,
-       .rename = shadow_copy2_rename,
-       .link = shadow_copy2_link,
-       .symlink = shadow_copy2_symlink,
-       .stat = shadow_copy2_stat,
-       .lstat = shadow_copy2_lstat,
-       .fstat = shadow_copy2_fstat,
+       .connect_fn = shadow_copy2_connect,
+       .opendir_fn = shadow_copy2_opendir,
+       .rename_fn = shadow_copy2_rename,
+       .link_fn = shadow_copy2_link,
+       .symlink_fn = shadow_copy2_symlink,
+       .stat_fn = shadow_copy2_stat,
+       .lstat_fn = shadow_copy2_lstat,
+       .fstat_fn = shadow_copy2_fstat,
        .open_fn = shadow_copy2_open,
-       .unlink = shadow_copy2_unlink,
-       .chmod = shadow_copy2_chmod,
-       .chown = shadow_copy2_chown,
-       .chdir = shadow_copy2_chdir,
-       .ntimes = shadow_copy2_ntimes,
-       .vfs_readlink = shadow_copy2_readlink,
-       .mknod = shadow_copy2_mknod,
-       .realpath = shadow_copy2_realpath,
-       .get_nt_acl = shadow_copy2_get_nt_acl,
-       .fget_nt_acl = shadow_copy2_fget_nt_acl,
-       .get_shadow_copy_data = shadow_copy2_get_shadow_copy_data,
-       .mkdir = shadow_copy2_mkdir,
-       .rmdir = shadow_copy2_rmdir,
-       .getxattr = shadow_copy2_getxattr,
-       .lgetxattr = shadow_copy2_lgetxattr,
-       .listxattr = shadow_copy2_listxattr,
-       .removexattr = shadow_copy2_removexattr,
-       .lremovexattr = shadow_copy2_lremovexattr,
-       .setxattr = shadow_copy2_setxattr,
-       .lsetxattr = shadow_copy2_lsetxattr,
-       .chmod_acl = shadow_copy2_chmod_acl,
-       .chflags = shadow_copy2_chflags,
-       .get_real_filename = shadow_copy2_get_real_filename,
+       .unlink_fn = shadow_copy2_unlink,
+       .chmod_fn = shadow_copy2_chmod,
+       .chown_fn = shadow_copy2_chown,
+       .chdir_fn = shadow_copy2_chdir,
+       .ntimes_fn = shadow_copy2_ntimes,
+       .readlink_fn = shadow_copy2_readlink,
+       .mknod_fn = shadow_copy2_mknod,
+       .realpath_fn = shadow_copy2_realpath,
+       .get_nt_acl_fn = shadow_copy2_get_nt_acl,
+       .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
+       .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
+       .mkdir_fn = shadow_copy2_mkdir,
+       .rmdir_fn = shadow_copy2_rmdir,
+       .getxattr_fn = shadow_copy2_getxattr,
+       .listxattr_fn = shadow_copy2_listxattr,
+       .removexattr_fn = shadow_copy2_removexattr,
+       .setxattr_fn = shadow_copy2_setxattr,
+       .chmod_acl_fn = shadow_copy2_chmod_acl,
+       .chflags_fn = shadow_copy2_chflags,
+       .get_real_filename_fn = shadow_copy2_get_real_filename,
 };
 
 NTSTATUS vfs_shadow_copy2_init(void);