diff options
Diffstat (limited to 'recipes-connectivity/samba/samba-3.6.8/shadow_copy2_backport.patch')
-rw-r--r-- | recipes-connectivity/samba/samba-3.6.8/shadow_copy2_backport.patch | 2101 |
1 files changed, 2101 insertions, 0 deletions
diff --git a/recipes-connectivity/samba/samba-3.6.8/shadow_copy2_backport.patch b/recipes-connectivity/samba/samba-3.6.8/shadow_copy2_backport.patch new file mode 100644 index 0000000..dbd1048 --- /dev/null +++ b/recipes-connectivity/samba/samba-3.6.8/shadow_copy2_backport.patch @@ -0,0 +1,2101 @@ +Description: Backport new shadow_copy2 implementation from master + The shadow_copy2 vfs module in samba 3.6 doesn't work if wide links is + disabled. This problem is fixed by a rewrite in the master branch. + This patch is a backport of this new version to samba 3.6. + It is based on these commits in the upstream samba git: + dc461cade5becec21f8d1f2bb74fcf1a977a5ec2 + 617b63658b02957422359a76fd8b8e4748d228ee +Author: Ivo De Decker <ivo.dedecker@ugent.be> +Origin: upstream +Bug: https://bugzilla.samba.org/show_bug.cgi?id=7287 +Forwarded: not-needed +Last-Update: 2012-05-27 + +--- samba-3.6.5.orig/source3/modules/vfs_shadow_copy2.c ++++ samba-3.6.5/source3/modules/vfs_shadow_copy2.c +@@ -1,32 +1,29 @@ +-/* +- * implementation of an Shadow Copy module - version 2 ++/* ++ * Third attempt at a shadow copy module + * +- * Copyright (C) Andrew Tridgell 2007 +- * Copyright (C) Ed Plese 2009 ++ * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2) ++ * Copyright (C) Ed Plese 2009 ++ * Copyright (C) Volker Lendecke 2011 ++ * Copyright (C) Christian Ambach 2011 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. +- * ++ * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +- * ++ * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +-#include "includes.h" +-#include "smbd/smbd.h" +-#include "system/filesys.h" +-#include "ntioctl.h" +- + /* + +- This is a 2nd implemetation of a shadow copy module for exposing ++ This is a 3rd implemetation of a shadow copy module for exposing + snapshots to windows clients as shadow copies. This version has the + following features: + +@@ -96,243 +93,169 @@ + The following command would generate a correctly formatted directory name + for use with the default parameters: + date -u +@GMT-%Y.%m.%d-%H.%M.%S +- + */ + +-static int vfs_shadow_copy2_debug_level = DBGC_VFS; +- +-#undef DBGC_CLASS +-#define DBGC_CLASS vfs_shadow_copy2_debug_level ++#include "includes.h" ++#include "system/filesys.h" ++#include "include/ntioctl.h" ++#include "smbd/proto.h" ++#include <tdb.h> ++#include "util_tdb.h" + + #define GMT_NAME_LEN 24 /* length of a @GMT- name */ +-#define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S" +- +-#define SHADOW_COPY2_DEFAULT_SORT NULL +-#define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S" +-#define SHADOW_COPY2_DEFAULT_LOCALTIME false ++#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S" + +-/* +- make very sure it is one of our special names +- */ +-static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start) ++static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str, ++ size_t **poffsets, ++ unsigned *pnum_offsets) + { +- unsigned year, month, day, hr, min, sec; ++ unsigned num_offsets; ++ size_t *offsets; + const char *p; +- if (gmt_start) { +- (*gmt_start) = NULL; +- } +- p = strstr_m(name, "@GMT-"); +- if (p == NULL) return false; +- if (p > name && p[-1] != '/') return False; +- if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month, +- &day, &hr, &min, &sec) != 6) { +- return False; +- } +- if (p[24] != 0 && p[24] != '/') { +- return False; +- } +- if (gmt_start) { +- (*gmt_start) = p; +- } +- return True; +-} + +-static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx, +- vfs_handle_struct *handle, const char *name) +-{ +- struct tm timestamp; +- time_t timestamp_t; +- char gmt[GMT_NAME_LEN + 1]; +- const char *fmt; ++ num_offsets = 0; + +- fmt = lp_parm_const_string(SNUM(handle->conn), "shadow", +- "format", SHADOW_COPY2_DEFAULT_FORMAT); ++ p = str; ++ while ((p = strchr(p, '/')) != NULL) { ++ num_offsets += 1; ++ p += 1; ++ } + +- ZERO_STRUCT(timestamp); +- if (strptime(name, fmt, ×tamp) == NULL) { +- DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n", +- fmt, name)); +- return NULL; ++ offsets = talloc_array(mem_ctx, size_t, num_offsets); ++ if (offsets == NULL) { ++ return false; + } + +- DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name)); +- if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", +- SHADOW_COPY2_DEFAULT_LOCALTIME)) +- { +- timestamp.tm_isdst = -1; +- timestamp_t = mktime(×tamp); +- gmtime_r(×tamp_t, ×tamp); ++ p = str; ++ num_offsets = 0; ++ while ((p = strchr(p, '/')) != NULL) { ++ offsets[num_offsets] = p-str; ++ num_offsets += 1; ++ p += 1; + } +- strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, ×tamp); + +- return talloc_strdup(mem_ctx, gmt); ++ *poffsets = offsets; ++ *pnum_offsets = num_offsets; ++ return true; + } + +-/* +- shadow copy paths can also come into the server in this form: +- +- /foo/bar/@GMT-XXXXX/some/file +- +- This function normalises the filename to be of the form: +- +- @GMT-XXXX/foo/bar/some/file +- */ +-static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start) ++static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx, ++ struct vfs_handle_struct *handle, ++ time_t snapshot) + { +- char *pcopy; +- char buf[GMT_NAME_LEN]; +- size_t prefix_len; ++ struct tm snap_tm; ++ fstring gmt; ++ size_t gmt_len; + +- if (path == gmt_start) { +- return path; ++ if (localtime_r(&snapshot, &snap_tm) == 0) { ++ DEBUG(10, ("gmtime_r failed\n")); ++ return NULL; + } +- +- prefix_len = gmt_start - path - 1; +- +- DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start, +- (int)prefix_len)); +- +- /* +- * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to +- * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further +- * processing. As many VFS calls provide a const char *, +- * unfortunately we have to make a copy. +- */ +- +- pcopy = talloc_strdup(talloc_tos(), path); +- if (pcopy == 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; + } +- +- gmt_start = pcopy + prefix_len; +- +- /* +- * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS" +- */ +- memcpy(buf, gmt_start+1, GMT_NAME_LEN); +- +- /* +- * Make space for it including a trailing / +- */ +- memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len); +- +- /* +- * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again +- */ +- memcpy(pcopy, buf, GMT_NAME_LEN); +- pcopy[GMT_NAME_LEN] = '/'; +- +- DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy)); +- +- return pcopy; ++ return talloc_asprintf(talloc_tos(), "/%s/%s", ++ lp_parm_const_string( ++ SNUM(handle->conn), "shadow", "snapdir", ++ ".snapshots"), ++ gmt); + } + +-/* +- convert a name to the shadow directory +- */ ++static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx, ++ struct vfs_handle_struct *handle, ++ const char *name, ++ time_t *ptimestamp, ++ char **pstripped) ++{ ++ struct tm tm; ++ time_t timestamp; ++ const char *p; ++ char *q; ++ char *stripped; ++ size_t rest_len, dst_len; + +-#define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \ +- const char *name = fname; \ +- const char *gmt_start; \ +- if (shadow_copy2_match_name(fname, &gmt_start)) { \ +- char *name2; \ +- rtype ret; \ +- name2 = convert_shadow2_name(handle, fname, gmt_start); \ +- if (name2 == NULL) { \ +- errno = EINVAL; \ +- return eret; \ +- } \ +- name = name2; \ +- ret = SMB_VFS_NEXT_ ## op args; \ +- talloc_free(name2); \ +- if (ret != eret) extra; \ +- return ret; \ +- } else { \ +- return SMB_VFS_NEXT_ ## op args; \ +- } \ +-} while (0) +- +-#define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \ +- const char *gmt_start; \ +- if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \ +- char *name2; \ +- char *smb_base_name_tmp = NULL; \ +- rtype ret; \ +- name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \ +- if (name2 == NULL) { \ +- errno = EINVAL; \ +- return eret; \ +- } \ +- smb_base_name_tmp = smb_fname->base_name; \ +- smb_fname->base_name = name2; \ +- ret = SMB_VFS_NEXT_ ## op args; \ +- smb_fname->base_name = smb_base_name_tmp; \ +- talloc_free(name2); \ +- if (ret != eret) extra; \ +- return ret; \ +- } else { \ +- return SMB_VFS_NEXT_ ## op args; \ +- } \ +-} while (0) ++ p = strstr_m(name, "@GMT-"); ++ if (p == NULL) { ++ goto no_snapshot; ++ } ++ if ((p > name) && (p[-1] != '/')) { ++ goto no_snapshot; ++ } ++ q = strptime(p, GMT_FORMAT, &tm); ++ if (q == NULL) { ++ goto no_snapshot; ++ } ++ tm.tm_isdst = -1; ++ timestamp = mktime(&tm); ++ if (timestamp == (time_t)-1) { ++ goto no_snapshot; ++ } ++ if ((p == name) && (q[0] == '\0')) { ++ if (pstripped != NULL) { ++ stripped = talloc_strdup(mem_ctx, ""); ++ if (stripped == NULL) { ++ return false; ++ } ++ *pstripped = stripped; ++ } ++ *ptimestamp = timestamp; ++ return true; ++ } ++ if (q[0] != '/') { ++ goto no_snapshot; ++ } ++ q += 1; + +-/* +- convert a name to the shadow directory: NTSTATUS-specific handling +- */ ++ rest_len = strlen(q); ++ dst_len = (p-name) + rest_len; ++ ++ if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere", ++ false)) { ++ char *insert; ++ bool have_insert; ++ insert = shadow_copy2_insert_string(talloc_tos(), handle, ++ timestamp); ++ if (insert == NULL) { ++ errno = ENOMEM; ++ return false; ++ } + +-#define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \ +- const char *name = fname; \ +- const char *gmt_start; \ +- if (shadow_copy2_match_name(fname, &gmt_start)) { \ +- char *name2; \ +- NTSTATUS ret; \ +- name2 = convert_shadow2_name(handle, fname, gmt_start); \ +- if (name2 == NULL) { \ +- errno = EINVAL; \ +- return eret; \ +- } \ +- name = name2; \ +- ret = SMB_VFS_NEXT_ ## op args; \ +- talloc_free(name2); \ +- if (!NT_STATUS_EQUAL(ret, eret)) extra; \ +- return ret; \ +- } else { \ +- return SMB_VFS_NEXT_ ## op args; \ +- } \ +-} while (0) +- +-#define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, ) +- +-#define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, ) +- +-#define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, ) +- +-#define SHADOW2_NEXT2(op, args) do { \ +- const char *gmt_start1, *gmt_start2; \ +- if (shadow_copy2_match_name(oldname, &gmt_start1) || \ +- shadow_copy2_match_name(newname, &gmt_start2)) { \ +- errno = EROFS; \ +- return -1; \ +- } else { \ +- return SMB_VFS_NEXT_ ## op args; \ +- } \ +-} while (0) +- +-#define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \ +- const char *gmt_start1, *gmt_start2; \ +- if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \ +- shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \ +- errno = EROFS; \ +- return -1; \ +- } else { \ +- return SMB_VFS_NEXT_ ## op args; \ +- } \ +-} while (0) ++ have_insert = (strstr(name, insert+1) != NULL); ++ TALLOC_FREE(insert); ++ if (have_insert) { ++ goto no_snapshot; ++ } ++ } + ++ if (pstripped != NULL) { ++ stripped = talloc_array(mem_ctx, char, dst_len+1); ++ if (stripped == NULL) { ++ errno = ENOMEM; ++ return false; ++ } ++ if (p > name) { ++ memcpy(stripped, name, p-name); ++ } ++ if (rest_len > 0) { ++ memcpy(stripped + (p-name), q, rest_len); ++ } ++ stripped[dst_len] = '\0'; ++ *pstripped = stripped; ++ } ++ *ptimestamp = timestamp; ++ return true; ++no_snapshot: ++ *ptimestamp = 0; ++ return true; ++} + +-/* +- find the mount point of a filesystem +- */ +-static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) ++static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx, ++ vfs_handle_struct *handle) + { + char *path = talloc_strdup(mem_ctx, handle->conn->connectpath); + dev_t dev; +@@ -358,164 +281,152 @@ static char *find_mount_point(TALLOC_CTX + } + } + +- return path; ++ return path; + } + +-/* +- work out the location of the snapshot for this share +- */ +-static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) +-{ +- const char *snapdir; +- char *mount_point; +- const char *ret; +- +- snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL); +- if (snapdir == NULL) { +- return NULL; +- } +- /* if its an absolute path, we're done */ +- if (*snapdir == '/') { +- return snapdir; ++static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx, ++ struct vfs_handle_struct *handle, ++ const char *name, time_t timestamp) ++{ ++ struct smb_filename converted_fname; ++ char *result = NULL; ++ size_t *slashes = NULL; ++ unsigned num_slashes; ++ char *path = NULL; ++ size_t pathlen; ++ char *insert = NULL; ++ char *converted = NULL; ++ size_t insertlen; ++ int i, saved_errno; ++ size_t min_offset; ++ ++ path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath, ++ name); ++ if (path == NULL) { ++ errno = ENOMEM; ++ goto fail; ++ } ++ pathlen = talloc_get_size(path)-1; ++ ++ DEBUG(10, ("converting %s\n", path)); ++ ++ if (!shadow_copy2_find_slashes(talloc_tos(), path, ++ &slashes, &num_slashes)) { ++ goto fail; ++ } ++ insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp); ++ if (insert == NULL) { ++ goto fail; ++ } ++ insertlen = talloc_get_size(insert)-1; ++ converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1); ++ if (converted == NULL) { ++ goto fail; ++ } ++ ++ if (path[pathlen-1] != '/') { ++ /* ++ * Append a fake slash to find the snapshot root ++ */ ++ size_t *tmp; ++ tmp = talloc_realloc(talloc_tos(), slashes, ++ size_t, num_slashes+1); ++ if (tmp == NULL) { ++ goto fail; ++ } ++ slashes = tmp; ++ slashes[num_slashes] = pathlen; ++ num_slashes += 1; + } + +- /* other its relative to the filesystem mount point */ +- mount_point = find_mount_point(mem_ctx, handle); +- if (mount_point == NULL) { +- return NULL; +- } ++ min_offset = 0; + +- ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir); +- talloc_free(mount_point); +- return ret; +-} +- +-/* +- work out the location of the base directory for snapshots of this share +- */ +-static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle) +-{ +- const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL); ++ if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints", ++ false)) { ++ char *mount_point; + +- /* other its the filesystem mount point */ +- if (basedir == NULL) { +- basedir = find_mount_point(mem_ctx, handle); ++ mount_point = shadow_copy2_find_mount_point(talloc_tos(), ++ handle); ++ if (mount_point == NULL) { ++ goto fail; ++ } ++ min_offset = strlen(mount_point); ++ TALLOC_FREE(mount_point); + } + +- return basedir; +-} ++ memcpy(converted, path, pathlen+1); ++ converted[pathlen+insertlen] = '\0'; + +-/* +- convert a filename from a share relative path, to a path in the +- snapshot directory +- */ +-static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path) +-{ +- TALLOC_CTX *tmp_ctx = talloc_new(handle->data); +- const char *snapdir, *relpath, *baseoffset, *basedir; +- size_t baselen; +- char *ret, *prefix; ++ ZERO_STRUCT(converted_fname); ++ converted_fname.base_name = converted; + +- struct tm timestamp; +- time_t timestamp_t; +- char snapshot[MAXPATHLEN]; +- const char *fmt; ++ for (i = num_slashes-1; i>=0; i--) { ++ int ret; ++ size_t offset; + +- fmt = lp_parm_const_string(SNUM(handle->conn), "shadow", +- "format", SHADOW_COPY2_DEFAULT_FORMAT); ++ offset = slashes[i]; + +- snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle); +- if (snapdir == NULL) { +- DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath)); +- talloc_free(tmp_ctx); +- return NULL; +- } +- +- basedir = shadow_copy2_find_basedir(tmp_ctx, handle); +- if (basedir == NULL) { +- DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath)); +- talloc_free(tmp_ctx); +- return NULL; +- } +- +- prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir); +- if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) { +- /* this looks like as we have already normalized it, leave it untouched*/ +- talloc_free(tmp_ctx); +- return talloc_strdup(handle->data, fname); +- } +- +- if (strncmp(fname, "@GMT-", 5) != 0) { +- fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path); +- if (fname == NULL) { +- talloc_free(tmp_ctx); +- return NULL; ++ if (offset < min_offset) { ++ errno = ENOENT; ++ goto fail; + } +- } + +- ZERO_STRUCT(timestamp); +- relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, ×tamp); +- if (relpath == NULL) { +- talloc_free(tmp_ctx); +- return NULL; +- } ++ memcpy(converted+offset, insert, insertlen); + +- /* relpath is the remaining portion of the path after the @GMT-xxx */ +- +- if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", +- SHADOW_COPY2_DEFAULT_LOCALTIME)) +- { +- timestamp_t = timegm(×tamp); +- localtime_r(×tamp_t, ×tamp); ++ offset += insertlen; ++ memcpy(converted+offset, path + slashes[i], ++ pathlen - slashes[i]); ++ ++ ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname); ++ ++ DEBUG(10, ("Trying %s: %d (%s)\n", converted, ++ ret, ret == 0 ? "ok" : strerror(errno))); ++ if (ret == 0) { ++ /* success */ ++ break; ++ } ++ if (errno == ENOTDIR) { ++ /* ++ * This is a valid condition: We appended the ++ * .snaphots/@GMT.. to a file name. Just try ++ * with the upper levels. ++ */ ++ continue; ++ } ++ if (errno != ENOENT) { ++ /* Other problem than "not found" */ ++ goto fail; ++ } + } + +- strftime(snapshot, MAXPATHLEN, fmt, ×tamp); +- +- baselen = strlen(basedir); +- baseoffset = handle->conn->connectpath + baselen; +- +- /* some sanity checks */ +- if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 || +- (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) { +- DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n", +- basedir, handle->conn->connectpath)); +- talloc_free(tmp_ctx); +- return NULL; ++ if (i >= 0) { ++ /* ++ * Found something ++ */ ++ DEBUG(10, ("Found %s\n", converted)); ++ result = converted; ++ converted = NULL; ++ } else { ++ errno = ENOENT; + } +- +- if (*relpath == '/') relpath++; +- if (*baseoffset == '/') baseoffset++; +- +- ret = talloc_asprintf(handle->data, "%s/%s/%s/%s", +- snapdir, +- snapshot, +- baseoffset, +- relpath); +- DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret)); +- talloc_free(tmp_ctx); +- return ret; +-} +- +- +-/* +- simple string hash +- */ +-static uint32 string_hash(const char *s) +-{ +- uint32 n = 0; +- while (*s) { +- n = ((n << 5) + n) ^ (uint32)(*s++); +- } +- return n; ++fail: ++ saved_errno = errno; ++ TALLOC_FREE(converted); ++ TALLOC_FREE(insert); ++ TALLOC_FREE(slashes); ++ TALLOC_FREE(path); ++ errno = saved_errno; ++ return result; + } + + /* + modify a sbuf return to ensure that inodes in the shadow directory + are different from those in the main directory + */ +-static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf) ++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)) { ++ if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) { + /* 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 +@@ -526,7 +437,10 @@ static void convert_sbuf(vfs_handle_stru + number collision, but I can't see a better approach + without significant VFS changes + */ +- uint32_t shash = string_hash(fname) & 0xFF000000; ++ uint32_t shash; ++ TDB_DATA data = string_tdb_data(fname); ++ ++ shash = tdb_jenkins_hash(&data) & 0xFF000000; + if (shash == 0) { + shash = 1; + } +@@ -534,303 +448,594 @@ static void convert_sbuf(vfs_handle_stru + } + } + ++static SMB_STRUCT_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; ++ int saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return NULL; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return NULL; ++ } ++ ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ + static int shadow_copy2_rename(vfs_handle_struct *handle, + const struct smb_filename *smb_fname_src, + const struct smb_filename *smb_fname_dst) + { +- if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) { ++ time_t timestamp_src, timestamp_dst; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname_src->base_name, ++ ×tamp_src, NULL)) { ++ return -1; ++ } ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname_dst->base_name, ++ ×tamp_dst, NULL)) { ++ return -1; ++ } ++ if (timestamp_src != 0) { + errno = EXDEV; + return -1; + } +- SHADOW2_NEXT2_SMB_FNAME(RENAME, +- (handle, smb_fname_src, smb_fname_dst)); ++ if (timestamp_dst != 0) { ++ errno = EROFS; ++ return -1; ++ } ++ return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); + } + + static int shadow_copy2_symlink(vfs_handle_struct *handle, + const char *oldname, const char *newname) + { +- SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname)); +-} ++ time_t timestamp_old, timestamp_new; + +-static int shadow_copy2_link(vfs_handle_struct *handle, +- const char *oldname, const char *newname) +-{ +- SHADOW2_NEXT2(LINK, (handle, oldname, newname)); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, ++ ×tamp_old, NULL)) { ++ return -1; ++ } ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname, ++ ×tamp_new, NULL)) { ++ return -1; ++ } ++ if ((timestamp_old != 0) || (timestamp_new != 0)) { ++ errno = EROFS; ++ return -1; ++ } ++ return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname); + } + +-static int shadow_copy2_open(vfs_handle_struct *handle, +- struct smb_filename *smb_fname, files_struct *fsp, +- int flags, mode_t mode) ++static int shadow_copy2_link(vfs_handle_struct *handle, ++ const char *oldname, const char *newname) + { +- SHADOW2_NEXT_SMB_FNAME(OPEN, +- (handle, smb_fname, fsp, flags, mode), +- int, -1); +-} ++ time_t timestamp_old, timestamp_new; + +-static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle, +- const char *fname, const char *mask, uint32 attr) +-{ +- SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname, ++ ×tamp_old, NULL)) { ++ return -1; ++ } ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname, ++ ×tamp_new, NULL)) { ++ return -1; ++ } ++ if ((timestamp_old != 0) || (timestamp_new != 0)) { ++ errno = EROFS; ++ return -1; ++ } ++ return SMB_VFS_NEXT_LINK(handle, oldname, newname); + } + + static int shadow_copy2_stat(vfs_handle_struct *handle, + struct smb_filename *smb_fname) + { +- _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1, +- convert_sbuf(handle, smb_fname->base_name, +- &smb_fname->st)); ++ time_t timestamp; ++ char *stripped, *tmp; ++ int ret, saved_errno; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname->base_name, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_STAT(handle, smb_fname); ++ } ++ ++ tmp = smb_fname->base_name; ++ smb_fname->base_name = shadow_copy2_convert( ++ talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ ++ if (smb_fname->base_name == NULL) { ++ smb_fname->base_name = tmp; ++ return -1; ++ } ++ ++ ret = SMB_VFS_NEXT_STAT(handle, smb_fname); ++ saved_errno = errno; ++ ++ TALLOC_FREE(smb_fname->base_name); ++ smb_fname->base_name = tmp; ++ ++ if (ret == 0) { ++ convert_sbuf(handle, smb_fname->base_name, &smb_fname->st); ++ } ++ errno = saved_errno; ++ return ret; + } + + static int shadow_copy2_lstat(vfs_handle_struct *handle, + struct smb_filename *smb_fname) + { +- _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1, +- convert_sbuf(handle, smb_fname->base_name, +- &smb_fname->st)); ++ time_t timestamp; ++ char *stripped, *tmp; ++ int ret, saved_errno; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname->base_name, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_LSTAT(handle, smb_fname); ++ } ++ ++ tmp = smb_fname->base_name; ++ smb_fname->base_name = shadow_copy2_convert( ++ talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ ++ if (smb_fname->base_name == NULL) { ++ smb_fname->base_name = tmp; ++ return -1; ++ } ++ ++ ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname); ++ saved_errno = errno; ++ ++ TALLOC_FREE(smb_fname->base_name); ++ smb_fname->base_name = tmp; ++ ++ if (ret == 0) { ++ convert_sbuf(handle, smb_fname->base_name, &smb_fname->st); ++ } ++ errno = saved_errno; ++ return ret; + } + +-static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf) ++static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, ++ SMB_STRUCT_STAT *sbuf) + { +- int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); +- if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) { ++ time_t timestamp; ++ int ret; ++ ++ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf); ++ if (ret == -1) { ++ return ret; ++ } ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ fsp->fsp_name->base_name, ++ ×tamp, NULL)) { ++ return 0; ++ } ++ if (timestamp != 0) { + convert_sbuf(handle, fsp->fsp_name->base_name, sbuf); + } ++ return 0; ++} ++ ++static int shadow_copy2_open(vfs_handle_struct *handle, ++ struct smb_filename *smb_fname, files_struct *fsp, ++ int flags, mode_t mode) ++{ ++ time_t timestamp; ++ char *stripped, *tmp; ++ int ret, saved_errno; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname->base_name, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); ++ } ++ ++ tmp = smb_fname->base_name; ++ smb_fname->base_name = shadow_copy2_convert( ++ talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ ++ if (smb_fname->base_name == NULL) { ++ smb_fname->base_name = tmp; ++ return -1; ++ } ++ ++ ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); ++ saved_errno = errno; ++ ++ TALLOC_FREE(smb_fname->base_name); ++ smb_fname->base_name = tmp; ++ ++ errno = saved_errno; + return ret; + } + + static int shadow_copy2_unlink(vfs_handle_struct *handle, +- const struct smb_filename *smb_fname_in) ++ const struct smb_filename *smb_fname) + { +- struct smb_filename *smb_fname = NULL; ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ struct smb_filename *conv; + NTSTATUS status; + +- status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname->base_name, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ 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)) { +- errno = map_errno_from_nt_status(status); ++ errno = ENOMEM; + return -1; + } +- +- SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1); ++ conv->base_name = shadow_copy2_convert( ++ conv, handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv->base_name == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_UNLINK(handle, conv); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + +-static int shadow_copy2_chmod(vfs_handle_struct *handle, +- const char *fname, mode_t mode) ++static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname, ++ mode_t mode) + { +- SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1); ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_CHMOD(handle, fname, mode); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + +-static int shadow_copy2_chown(vfs_handle_struct *handle, +- const char *fname, uid_t uid, gid_t gid) ++static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname, ++ uid_t uid, gid_t gid) + { +- SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1); ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + + static int shadow_copy2_chdir(vfs_handle_struct *handle, +- const char *fname) ++ const char *fname) + { +- SHADOW2_NEXT(CHDIR, (handle, name), int, -1); ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_CHDIR(handle, fname); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_CHDIR(handle, conv); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + + static int shadow_copy2_ntimes(vfs_handle_struct *handle, +- const struct smb_filename *smb_fname_in, ++ const struct smb_filename *smb_fname, + struct smb_file_time *ft) + { +- struct smb_filename *smb_fname = NULL; ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ struct smb_filename *conv; + NTSTATUS status; + +- status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ smb_fname->base_name, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ 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)) { +- errno = map_errno_from_nt_status(status); ++ errno = ENOMEM; + return -1; + } +- +- SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1); ++ conv->base_name = shadow_copy2_convert( ++ conv, handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv->base_name == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + + static int shadow_copy2_readlink(vfs_handle_struct *handle, + const char *fname, char *buf, size_t bufsiz) + { +- SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1); +-} ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; + +-static int shadow_copy2_mknod(vfs_handle_struct *handle, +- const char *fname, mode_t mode, SMB_DEV_T dev) +-{ +- SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + +-static char *shadow_copy2_realpath(vfs_handle_struct *handle, +- const char *fname) ++static int shadow_copy2_mknod(vfs_handle_struct *handle, ++ const char *fname, mode_t mode, SMB_DEV_T dev) + { +- const char *gmt; ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; + +- if (shadow_copy2_match_name(fname, &gmt) +- && (gmt[GMT_NAME_LEN] == '\0')) { +- char *copy; +- +- copy = talloc_strdup(talloc_tos(), fname); +- if (copy == NULL) { +- errno = ENOMEM; +- return NULL; +- } +- +- copy[gmt - fname] = '.'; +- copy[gmt - fname + 1] = '\0'; +- +- DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy)); +- SHADOW2_NEXT(REALPATH, (handle, name), char *, +- NULL); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; + } +- SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL); ++ ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; + } + +-static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle, +- const char *fname) ++static char *shadow_copy2_realpath(vfs_handle_struct *handle, ++ const char *fname) + { +- TALLOC_CTX *tmp_ctx; +- const char *snapdir, *baseoffset, *basedir, *gmt_start; +- size_t baselen; +- char *ret; ++ time_t timestamp; ++ char *stripped = NULL; ++ char *tmp = NULL; ++ char *result = NULL; ++ char *inserted = NULL; ++ char *inserted_to, *inserted_end; ++ int saved_errno; + +- DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname)); +- +- if (!shadow_copy2_match_name(fname, &gmt_start)) { +- return handle->conn->connectpath; ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ goto done; + } +- +- /* +- * We have to create a real temporary context because we have +- * to put our result on talloc_tos(). Thus we can't use a +- * talloc_stackframe() here. +- */ +- tmp_ctx = talloc_new(talloc_tos()); +- +- fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start); +- if (fname == NULL) { +- TALLOC_FREE(tmp_ctx); +- return NULL; ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_REALPATH(handle, fname); + } + +- snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle); +- if (snapdir == NULL) { +- DEBUG(2,("no snapdir found for share at %s\n", +- handle->conn->connectpath)); +- TALLOC_FREE(tmp_ctx); +- return NULL; ++ tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ if (tmp == NULL) { ++ goto done; + } + +- basedir = shadow_copy2_find_basedir(tmp_ctx, handle); +- if (basedir == NULL) { +- DEBUG(2,("no basedir found for share at %s\n", +- handle->conn->connectpath)); +- TALLOC_FREE(tmp_ctx); +- return NULL; ++ result = SMB_VFS_NEXT_REALPATH(handle, tmp); ++ if (result == NULL) { ++ goto done; + } + +- baselen = strlen(basedir); +- baseoffset = handle->conn->connectpath + baselen; +- +- /* some sanity checks */ +- if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 || +- (handle->conn->connectpath[baselen] != 0 +- && handle->conn->connectpath[baselen] != '/')) { +- DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a " +- "parent of %s\n", basedir, +- handle->conn->connectpath)); +- TALLOC_FREE(tmp_ctx); ++ /* ++ * Take away what we've inserted. This removes the @GMT-thingy ++ * completely, but will give a path under the share root. ++ */ ++ inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp); ++ if (inserted == NULL) { ++ goto done; ++ } ++ inserted_to = strstr_m(result, inserted); ++ if (inserted_to == NULL) { ++ DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted)); ++ goto done; ++ } ++ inserted_end = inserted_to + talloc_get_size(inserted) - 1; ++ memmove(inserted_to, inserted_end, strlen(inserted_end)+1); ++ ++done: ++ saved_errno = errno; ++ TALLOC_FREE(inserted); ++ TALLOC_FREE(tmp); ++ TALLOC_FREE(stripped); ++ errno = saved_errno; ++ return result; ++} ++ ++static char *have_snapdir(struct vfs_handle_struct *handle, ++ const char *path) ++{ ++ struct smb_filename smb_fname; ++ int ret; ++ ++ 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")); ++ if (smb_fname.base_name == NULL) { + return NULL; + } + +- if (*baseoffset == '/') baseoffset++; +- +- ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s", +- snapdir, +- GMT_NAME_LEN, fname, +- baseoffset); +- DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret)); +- TALLOC_FREE(tmp_ctx); +- return ret; +-} +- +-static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle, +- const char *fname, uint32 security_info, +- struct security_descriptor **ppdesc) +-{ +- SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED); ++ ret = SMB_VFS_NEXT_STAT(handle, &smb_fname); ++ if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) { ++ return smb_fname.base_name; ++ } ++ TALLOC_FREE(smb_fname.base_name); ++ return NULL; + } + +-static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode) ++static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, ++ struct vfs_handle_struct *handle, ++ struct smb_filename *smb_fname) + { +- SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1); +-} ++ char *path, *p; ++ char *snapdir; + +-static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) +-{ +- SHADOW2_NEXT(RMDIR, (handle, name), int, -1); +-} ++ path = talloc_asprintf(mem_ctx, "%s/%s", ++ handle->conn->connectpath, ++ smb_fname->base_name); ++ if (path == NULL) { ++ return NULL; ++ } + +-static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, +- unsigned int flags) +-{ +- SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1); +-} ++ snapdir = have_snapdir(handle, path); ++ if (snapdir != NULL) { ++ TALLOC_FREE(path); ++ return snapdir; ++ } + +-static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle, +- const char *fname, const char *aname, void *value, size_t size) +-{ +- SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1); +-} ++ while ((p = strrchr(path, '/')) && (p > path)) { + +-static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle, +- const char *fname, const char *aname, void *value, size_t size) +-{ +- SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1); +-} ++ p[0] = '\0'; + +-static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, +- char *list, size_t size) +-{ +- SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1); ++ snapdir = have_snapdir(handle, path); ++ if (snapdir != NULL) { ++ TALLOC_FREE(path); ++ return snapdir; ++ } ++ } ++ TALLOC_FREE(path); ++ return NULL; + } + +-static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, +- const char *aname) ++static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx, ++ vfs_handle_struct *handle, ++ const char *name, ++ char *gmt, size_t gmt_len) + { +- SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1); +-} ++ struct tm timestamp; ++ time_t timestamp_t; ++ const char *fmt; + +-static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, +- const char *aname) +-{ +- SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1); +-} ++ fmt = lp_parm_const_string(SNUM(handle->conn), "shadow", ++ "format", GMT_FORMAT); + +-static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, +- const char *aname, const void *value, size_t size, int flags) +-{ +- SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1); +-} ++ ZERO_STRUCT(timestamp); ++ if (strptime(name, fmt, ×tamp) == NULL) { ++ DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n", ++ fmt, name)); ++ return false; ++ } + +-static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, +- const char *aname, const void *value, size_t size, int flags) +-{ +- SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1); +-} ++ DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name)); + +-static int shadow_copy2_chmod_acl(vfs_handle_struct *handle, +- const char *fname, mode_t mode) +-{ +- SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1); ++ if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) { ++ timestamp.tm_isdst = -1; ++ timestamp_t = mktime(×tamp); ++ gmtime_r(×tamp_t, ×tamp); ++ } ++ strftime(gmt, gmt_len, GMT_FORMAT, ×tamp); ++ return true; + } + + static int shadow_copy2_label_cmp_asc(const void *x, const void *y) + { +- return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL)); ++ return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL)); + } + + static int shadow_copy2_label_cmp_desc(const void *x, const void *y) + { +- return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL)); ++ return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL)); + } + + /* +@@ -843,7 +1048,7 @@ static void shadow_copy2_sort_data(vfs_h + const char *sort; + + sort = lp_parm_const_string(SNUM(handle->conn), "shadow", +- "sort", SHADOW_COPY2_DEFAULT_SORT); ++ "sort", "desc"); + if (sort == NULL) { + return; + } +@@ -867,18 +1072,17 @@ static void shadow_copy2_sort_data(vfs_h + return; + } + +-static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, +- files_struct *fsp, +- struct shadow_copy_data *shadow_copy2_data, +- bool labels) ++static int shadow_copy2_get_shadow_copy_data( ++ vfs_handle_struct *handle, files_struct *fsp, ++ struct shadow_copy_data *shadow_copy2_data, ++ bool labels) + { + SMB_STRUCT_DIR *p; + const char *snapdir; + SMB_STRUCT_DIRENT *d; +- TALLOC_CTX *tmp_ctx = talloc_new(handle->data); +- char *snapshot; ++ TALLOC_CTX *tmp_ctx = talloc_stackframe(); + +- snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle); ++ snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name); + if (snapdir == NULL) { + DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n", + handle->conn->connectpath)); +@@ -901,16 +1105,23 @@ static int shadow_copy2_get_shadow_copy2 + shadow_copy2_data->labels = NULL; + + while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) { ++ char snapshot[GMT_NAME_LEN+1]; + SHADOW_COPY_LABEL *tlabels; + +- /* ignore names not of the right form in the snapshot directory */ +- snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle, +- d->d_name); +- DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n", +- d->d_name, snapshot)); +- if (!snapshot) { ++ /* ++ * ignore names not of the right form in the snapshot ++ * directory ++ */ ++ if (!shadow_copy2_snapshot_to_gmt( ++ tmp_ctx, handle, d->d_name, ++ snapshot, sizeof(snapshot))) { ++ ++ DEBUG(6, ("shadow_copy2_get_shadow_copy_data: " ++ "ignoring %s\n", d->d_name)); + continue; + } ++ DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n", ++ d->d_name, snapshot)); + + if (!labels) { + /* the caller doesn't want the labels */ +@@ -920,7 +1131,8 @@ static int shadow_copy2_get_shadow_copy2 + + tlabels = talloc_realloc(shadow_copy2_data, + shadow_copy2_data->labels, +- SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1); ++ SHADOW_COPY_LABEL, ++ shadow_copy2_data->num_volumes+1); + if (tlabels == NULL) { + DEBUG(0,("shadow_copy2: out of memory\n")); + SMB_VFS_NEXT_CLOSEDIR(handle, p); +@@ -930,7 +1142,6 @@ static int shadow_copy2_get_shadow_copy2 + + strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot, + sizeof(*tlabels)); +- talloc_free(snapshot); + + shadow_copy2_data->num_volumes++; + shadow_copy2_data->labels = tlabels; +@@ -944,59 +1155,455 @@ static int shadow_copy2_get_shadow_copy2 + return 0; + } + +-static struct vfs_fn_pointers vfs_shadow_copy2_fns = { +- .opendir = shadow_copy2_opendir, +- .mkdir = shadow_copy2_mkdir, +- .rmdir = shadow_copy2_rmdir, +- .chflags = shadow_copy2_chflags, +- .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, +- .open_fn = shadow_copy2_open, +- .rename = shadow_copy2_rename, +- .stat = shadow_copy2_stat, +- .lstat = shadow_copy2_lstat, +- .fstat = shadow_copy2_fstat, +- .unlink = shadow_copy2_unlink, +- .chmod = shadow_copy2_chmod, +- .chown = shadow_copy2_chown, +- .chdir = shadow_copy2_chdir, +- .ntimes = shadow_copy2_ntimes, +- .symlink = shadow_copy2_symlink, +- .vfs_readlink = shadow_copy2_readlink, +- .link = shadow_copy2_link, +- .mknod = shadow_copy2_mknod, +- .realpath = shadow_copy2_realpath, +- .connectpath = shadow_copy2_connectpath, +- .get_nt_acl = shadow_copy2_get_nt_acl, +- .chmod_acl = shadow_copy2_chmod_acl, +- .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data, +-}; ++static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle, ++ struct files_struct *fsp, ++ uint32 security_info, ++ struct security_descriptor **ppdesc) ++{ ++ time_t timestamp; ++ char *stripped; ++ NTSTATUS status; ++ char *conv; + +-NTSTATUS vfs_shadow_copy2_init(void); +-NTSTATUS vfs_shadow_copy2_init(void) ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, ++ fsp->fsp_name->base_name, ++ ×tamp, &stripped)) { ++ return map_nt_error_from_unix(errno); ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, ++ 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); ++ TALLOC_FREE(conv); ++ return status; ++} ++ ++static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle, ++ const char *fname, ++ uint32 security_info, ++ struct security_descriptor **ppdesc) + { +- NTSTATUS ret; ++ time_t timestamp; ++ char *stripped; ++ NTSTATUS status; ++ char *conv; + +- ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", +- &vfs_shadow_copy2_fns); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return map_nt_error_from_unix(errno); ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info, ++ 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); ++ TALLOC_FREE(conv); ++ return status; ++} + +- if (!NT_STATUS_IS_OK(ret)) +- return ret; ++static int shadow_copy2_mkdir(vfs_handle_struct *handle, ++ const char *fname, mode_t mode) ++{ ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; + +- vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2"); +- if (vfs_shadow_copy2_debug_level == -1) { +- vfs_shadow_copy2_debug_level = DBGC_VFS; +- DEBUG(0, ("%s: Couldn't register custom debugging class!\n", +- "vfs_shadow_copy2_init")); +- } else { +- DEBUG(10, ("%s: Debug class number of '%s': %d\n", +- "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level)); ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_MKDIR(handle, fname, mode); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ ++static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname) ++{ ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; + } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_RMDIR(handle, fname); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_RMDIR(handle, conv); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ ++static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, ++ unsigned int flags) ++{ ++ time_t timestamp; ++ char *stripped; ++ int ret, saved_errno; ++ char *conv; + ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; + return ret; + } ++ ++static ssize_t shadow_copy2_getxattr(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, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_GETXATTR(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_GETXATTR(handle, conv, aname, value, size); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ 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, ++ ×tamp, &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) ++{ ++ time_t timestamp; ++ char *stripped; ++ ssize_t ret; ++ int saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ ++static int shadow_copy2_removexattr(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, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ 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, ++ ×tamp, &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, ++ 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, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_SETXATTR(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_SETXATTR(handle, conv, aname, value, size, flags); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ 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, ++ ×tamp, &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) ++{ ++ time_t timestamp; ++ char *stripped; ++ ssize_t ret; ++ int saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode); ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ ++static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle, ++ const char *path, ++ const char *name, ++ TALLOC_CTX *mem_ctx, ++ char **found_name) ++{ ++ time_t timestamp; ++ char *stripped; ++ ssize_t ret; ++ int saved_errno; ++ char *conv; ++ ++ if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path, ++ ×tamp, &stripped)) { ++ return -1; ++ } ++ if (timestamp == 0) { ++ return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name, ++ mem_ctx, found_name); ++ } ++ if (stripped[0] == '\0') { ++ *found_name = talloc_strdup(mem_ctx, name); ++ if (*found_name == NULL) { ++ errno = ENOMEM; ++ return -1; ++ } ++ return 0; ++ } ++ conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp); ++ TALLOC_FREE(stripped); ++ if (conv == NULL) { ++ return -1; ++ } ++ ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name, ++ mem_ctx, found_name); ++ saved_errno = errno; ++ TALLOC_FREE(conv); ++ errno = saved_errno; ++ return ret; ++} ++ ++ ++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, ++ .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, ++}; ++ ++NTSTATUS vfs_shadow_copy2_init(void); ++NTSTATUS vfs_shadow_copy2_init(void) ++{ ++ return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, ++ "shadow_copy2", &vfs_shadow_copy2_fns); ++} |