2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 struct shadow_copy2_config {
115 bool snapdirseverywhere;
116 bool crossmountpoints;
119 bool snapdir_absolute;
122 char *rel_connectpath; /* share root, relative to the basedir */
123 char *snapshot_basepath; /* the absolute version of snapdir */
126 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
128 unsigned *pnum_offsets)
130 unsigned num_offsets;
137 while ((p = strchr(p, '/')) != NULL) {
142 offsets = talloc_array(mem_ctx, size_t, num_offsets);
143 if (offsets == NULL) {
149 while ((p = strchr(p, '/')) != NULL) {
150 offsets[num_offsets] = p-str;
156 *pnum_offsets = num_offsets;
161 * Given a timstamp, build the posix level GTM-tag string
162 * based on the configurable format.
164 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct *handle,
166 char *snaptime_string,
171 struct shadow_copy2_config *config;
173 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
176 if (config->use_sscanf) {
177 snaptime_len = snprintf(snaptime_string,
180 (unsigned long)snapshot);
181 if (snaptime_len <= 0) {
182 DEBUG(10, ("snprintf failed\n"));
186 if (config->use_localtime) {
187 if (localtime_r(&snapshot, &snap_tm) == 0) {
188 DEBUG(10, ("gmtime_r failed\n"));
192 if (gmtime_r(&snapshot, &snap_tm) == 0) {
193 DEBUG(10, ("gmtime_r failed\n"));
197 snaptime_len = strftime(snaptime_string,
201 if (snaptime_len == 0) {
202 DEBUG(10, ("strftime failed\n"));
211 * Given a timstamp, build the string to insert into a path
212 * as a path component for creating the local path to the
213 * snapshot at the given timestamp of the input path.
215 * In the case of a parallel snapdir (specified with an
216 * absolute path), this is the inital portion of the
217 * local path of any snapshot file. The complete path is
218 * obtained by appending the portion of the file's path
219 * below the share root's mountpoint.
221 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
222 struct vfs_handle_struct *handle,
225 fstring snaptime_string;
226 size_t snaptime_len = 0;
228 struct shadow_copy2_config *config;
230 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
233 snaptime_len = shadow_copy2_posix_gmt_string(handle,
236 sizeof(snaptime_string));
237 if (snaptime_len <= 0) {
241 if (config->snapdir_absolute) {
242 result = talloc_asprintf(mem_ctx, "%s/%s",
243 config->snapdir, snaptime_string);
245 result = talloc_asprintf(mem_ctx, "/%s/%s",
246 config->snapdir, snaptime_string);
248 if (result == NULL) {
249 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
256 * Build the posix snapshot path for the connection
257 * at the given timestamp, i.e. the absolute posix path
258 * that contains the snapshot for this file system.
260 * This only applies to classical case, i.e. not
261 * to the "snapdirseverywhere" mode.
263 static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx,
264 struct vfs_handle_struct *handle,
267 fstring snaptime_string;
268 size_t snaptime_len = 0;
270 struct shadow_copy2_config *config;
272 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
275 snaptime_len = shadow_copy2_posix_gmt_string(handle,
278 sizeof(snaptime_string));
279 if (snaptime_len <= 0) {
283 result = talloc_asprintf(mem_ctx, "%s/%s",
284 config->snapshot_basepath, snaptime_string);
285 if (result == NULL) {
286 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
293 * Strip a snapshot component from an filename as
294 * handed in via the smb layer.
295 * Returns the parsed timestamp and the stripped filename.
297 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
298 struct vfs_handle_struct *handle,
308 size_t rest_len, dst_len;
309 struct shadow_copy2_config *config;
311 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
314 DEBUG(10, (__location__ ": enter path '%s'\n", name));
316 p = strstr_m(name, "@GMT-");
320 if ((p > name) && (p[-1] != '/')) {
321 /* the GMT-token does not start a path-component */
324 q = strptime(p, GMT_FORMAT, &tm);
329 timestamp = timegm(&tm);
330 if (timestamp == (time_t)-1) {
333 if ((p == name) && (q[0] == '\0')) {
334 /* the name consists of only the GMT token */
335 if (pstripped != NULL) {
336 stripped = talloc_strdup(mem_ctx, "");
337 if (stripped == NULL) {
340 *pstripped = stripped;
342 *ptimestamp = timestamp;
347 * The GMT token is either at the end of the path
348 * or it is not a complete path component, i.e. the
349 * path component continues after the gmt-token.
351 * TODO: Is this correct? Or would the GMT tag as the
352 * last component be a valid input?
358 rest_len = strlen(q);
359 dst_len = (p-name) + rest_len;
361 if (config->snapdirseverywhere) {
364 insert = shadow_copy2_insert_string(talloc_tos(), handle,
366 if (insert == NULL) {
371 DEBUG(10, (__location__ ": snapdirseverywhere mode.\n"
373 "insert string '%s'\n", name, insert));
375 have_insert = (strstr(name, insert+1) != NULL);
377 DEBUG(10, (__location__ ": insert string '%s' found in "
378 "path '%s' found in snapdirseverywhere mode "
379 "==> already converted\n", insert, name));
388 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
391 if (snapshot_path == NULL) {
396 DEBUG(10, (__location__ " path: '%s'.\n"
397 "snapshot path: '%s'\n", name, snapshot_path));
399 s = strstr(name, snapshot_path);
402 * this starts with "snapshot_basepath/GMT-Token"
403 * so it is already a converted absolute
404 * path. Don't process further.
406 DEBUG(10, (__location__ ": path '%s' starts with "
407 "snapshot path '%s' (not in "
408 "snapdirseverywhere mode) ==> "
409 "already converted\n", name, snapshot_path));
410 talloc_free(snapshot_path);
413 talloc_free(snapshot_path);
416 if (pstripped != NULL) {
417 stripped = talloc_array(mem_ctx, char, dst_len+1);
418 if (stripped == NULL) {
423 memcpy(stripped, name, p-name);
426 memcpy(stripped + (p-name), q, rest_len);
428 stripped[dst_len] = '\0';
429 *pstripped = stripped;
431 *ptimestamp = timestamp;
438 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
439 vfs_handle_struct *handle)
441 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
446 if (stat(path, &st) != 0) {
453 while ((p = strrchr(path, '/')) && p > path) {
455 if (stat(path, &st) != 0) {
459 if (st.st_dev != dev) {
469 * Convert from a name as handed in via the SMB layer
470 * and a timestamp into the local path of the snapshot
471 * of the provided file at the provided time.
473 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
474 struct vfs_handle_struct *handle,
475 const char *name, time_t timestamp)
477 struct smb_filename converted_fname;
479 size_t *slashes = NULL;
480 unsigned num_slashes;
484 char *converted = NULL;
488 struct shadow_copy2_config *config;
490 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
493 DEBUG(10, ("converting '%s'\n", name));
495 if (!config->snapdirseverywhere) {
499 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
502 if (snapshot_path == NULL) {
506 if (config->rel_connectpath == NULL) {
507 converted = talloc_asprintf(mem_ctx, "%s/%s",
508 snapshot_path, name);
510 converted = talloc_asprintf(mem_ctx, "%s/%s/%s",
512 config->rel_connectpath,
515 if (converted == NULL) {
519 ZERO_STRUCT(converted_fname);
520 converted_fname.base_name = converted;
522 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
523 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
525 ret, ret == 0 ? "ok" : strerror(errno)));
527 DEBUG(10, ("Found %s\n", converted));
535 /* never reached ... */
538 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
544 pathlen = talloc_get_size(path)-1;
546 if (!shadow_copy2_find_slashes(talloc_tos(), path,
547 &slashes, &num_slashes)) {
551 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
552 if (insert == NULL) {
555 insertlen = talloc_get_size(insert)-1;
557 converted = talloc_zero_array(mem_ctx, char, pathlen + insertlen + 1);
558 if (converted == NULL) {
562 if (path[pathlen-1] != '/') {
564 * Append a fake slash to find the snapshot root
567 tmp = talloc_realloc(talloc_tos(), slashes,
568 size_t, num_slashes+1);
573 slashes[num_slashes] = pathlen;
579 if (!config->crossmountpoints) {
580 min_offset = strlen(config->mount_point);
583 memcpy(converted, path, pathlen+1);
584 converted[pathlen+insertlen] = '\0';
586 ZERO_STRUCT(converted_fname);
587 converted_fname.base_name = converted;
589 for (i = num_slashes-1; i>=0; i--) {
595 if (offset < min_offset) {
600 memcpy(converted+offset, insert, insertlen);
603 memcpy(converted+offset, path + slashes[i],
604 pathlen - slashes[i]);
606 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
608 DEBUG(10, ("Trying[snapdirseverywhere] %s: %d (%s)\n",
610 ret, ret == 0 ? "ok" : strerror(errno)));
615 if (errno == ENOTDIR) {
617 * This is a valid condition: We appended the
618 * .snaphots/@GMT.. to a file name. Just try
619 * with the upper levels.
623 if (errno != ENOENT) {
624 /* Other problem than "not found" */
633 DEBUG(10, ("Found %s\n", converted));
641 TALLOC_FREE(converted);
643 TALLOC_FREE(slashes);
650 modify a sbuf return to ensure that inodes in the shadow directory
651 are different from those in the main directory
653 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
654 SMB_STRUCT_STAT *sbuf)
656 struct shadow_copy2_config *config;
658 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
661 if (config->fixinodes) {
662 /* some snapshot systems, like GPFS, return the name
663 device:inode for the snapshot files as the current
664 files. That breaks the 'restore' button in the shadow copy
665 GUI, as the client gets a sharing violation.
667 This is a crude way of allowing both files to be
668 open at once. It has a slight chance of inode
669 number collision, but I can't see a better approach
670 without significant VFS changes
674 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
678 sbuf->st_ex_ino ^= shash;
682 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
693 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
694 ×tamp, &stripped)) {
697 if (timestamp == 0) {
698 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
700 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
701 TALLOC_FREE(stripped);
705 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
712 static int shadow_copy2_rename(vfs_handle_struct *handle,
713 const struct smb_filename *smb_fname_src,
714 const struct smb_filename *smb_fname_dst)
716 time_t timestamp_src, timestamp_dst;
718 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
719 smb_fname_src->base_name,
720 ×tamp_src, NULL)) {
723 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
724 smb_fname_dst->base_name,
725 ×tamp_dst, NULL)) {
728 if (timestamp_src != 0) {
732 if (timestamp_dst != 0) {
736 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
739 static int shadow_copy2_symlink(vfs_handle_struct *handle,
740 const char *oldname, const char *newname)
742 time_t timestamp_old, timestamp_new;
744 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
745 ×tamp_old, NULL)) {
748 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
749 ×tamp_new, NULL)) {
752 if ((timestamp_old != 0) || (timestamp_new != 0)) {
756 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
759 static int shadow_copy2_link(vfs_handle_struct *handle,
760 const char *oldname, const char *newname)
762 time_t timestamp_old, timestamp_new;
764 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
765 ×tamp_old, NULL)) {
768 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
769 ×tamp_new, NULL)) {
772 if ((timestamp_old != 0) || (timestamp_new != 0)) {
776 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
779 static int shadow_copy2_stat(vfs_handle_struct *handle,
780 struct smb_filename *smb_fname)
783 char *stripped, *tmp;
784 int ret, saved_errno;
786 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
787 smb_fname->base_name,
788 ×tamp, &stripped)) {
791 if (timestamp == 0) {
792 return SMB_VFS_NEXT_STAT(handle, smb_fname);
795 tmp = smb_fname->base_name;
796 smb_fname->base_name = shadow_copy2_convert(
797 talloc_tos(), handle, stripped, timestamp);
798 TALLOC_FREE(stripped);
800 if (smb_fname->base_name == NULL) {
801 smb_fname->base_name = tmp;
805 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
808 TALLOC_FREE(smb_fname->base_name);
809 smb_fname->base_name = tmp;
812 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
818 static int shadow_copy2_lstat(vfs_handle_struct *handle,
819 struct smb_filename *smb_fname)
822 char *stripped, *tmp;
823 int ret, saved_errno;
825 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
826 smb_fname->base_name,
827 ×tamp, &stripped)) {
830 if (timestamp == 0) {
831 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
834 tmp = smb_fname->base_name;
835 smb_fname->base_name = shadow_copy2_convert(
836 talloc_tos(), handle, stripped, timestamp);
837 TALLOC_FREE(stripped);
839 if (smb_fname->base_name == NULL) {
840 smb_fname->base_name = tmp;
844 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
847 TALLOC_FREE(smb_fname->base_name);
848 smb_fname->base_name = tmp;
851 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
857 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
858 SMB_STRUCT_STAT *sbuf)
863 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
867 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
868 fsp->fsp_name->base_name,
872 if (timestamp != 0) {
873 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
878 static int shadow_copy2_open(vfs_handle_struct *handle,
879 struct smb_filename *smb_fname, files_struct *fsp,
880 int flags, mode_t mode)
883 char *stripped, *tmp;
884 int ret, saved_errno;
886 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
887 smb_fname->base_name,
888 ×tamp, &stripped)) {
891 if (timestamp == 0) {
892 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
895 tmp = smb_fname->base_name;
896 smb_fname->base_name = shadow_copy2_convert(
897 talloc_tos(), handle, stripped, timestamp);
898 TALLOC_FREE(stripped);
900 if (smb_fname->base_name == NULL) {
901 smb_fname->base_name = tmp;
905 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
908 TALLOC_FREE(smb_fname->base_name);
909 smb_fname->base_name = tmp;
915 static int shadow_copy2_unlink(vfs_handle_struct *handle,
916 const struct smb_filename *smb_fname)
920 int ret, saved_errno;
921 struct smb_filename *conv;
923 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
924 smb_fname->base_name,
925 ×tamp, &stripped)) {
928 if (timestamp == 0) {
929 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
931 conv = cp_smb_filename(talloc_tos(), smb_fname);
936 conv->base_name = shadow_copy2_convert(
937 conv, handle, stripped, timestamp);
938 TALLOC_FREE(stripped);
939 if (conv->base_name == NULL) {
942 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
949 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
954 int ret, saved_errno;
957 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
958 ×tamp, &stripped)) {
961 if (timestamp == 0) {
962 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
964 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
965 TALLOC_FREE(stripped);
969 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
976 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
977 uid_t uid, gid_t gid)
981 int ret, saved_errno;
984 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
985 ×tamp, &stripped)) {
988 if (timestamp == 0) {
989 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
991 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
992 TALLOC_FREE(stripped);
996 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1003 static int shadow_copy2_chdir(vfs_handle_struct *handle,
1008 int ret, saved_errno;
1011 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1012 ×tamp, &stripped)) {
1015 if (timestamp == 0) {
1016 return SMB_VFS_NEXT_CHDIR(handle, fname);
1018 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1019 TALLOC_FREE(stripped);
1023 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1024 saved_errno = errno;
1026 errno = saved_errno;
1030 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
1031 const struct smb_filename *smb_fname,
1032 struct smb_file_time *ft)
1036 int ret, saved_errno;
1037 struct smb_filename *conv;
1039 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1040 smb_fname->base_name,
1041 ×tamp, &stripped)) {
1044 if (timestamp == 0) {
1045 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1047 conv = cp_smb_filename(talloc_tos(), smb_fname);
1052 conv->base_name = shadow_copy2_convert(
1053 conv, handle, stripped, timestamp);
1054 TALLOC_FREE(stripped);
1055 if (conv->base_name == NULL) {
1058 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1059 saved_errno = errno;
1061 errno = saved_errno;
1065 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1066 const char *fname, char *buf, size_t bufsiz)
1070 int ret, saved_errno;
1073 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1074 ×tamp, &stripped)) {
1077 if (timestamp == 0) {
1078 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1080 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1081 TALLOC_FREE(stripped);
1085 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1086 saved_errno = errno;
1088 errno = saved_errno;
1092 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1093 const char *fname, mode_t mode, SMB_DEV_T dev)
1097 int ret, saved_errno;
1100 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1101 ×tamp, &stripped)) {
1104 if (timestamp == 0) {
1105 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1107 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1108 TALLOC_FREE(stripped);
1112 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1113 saved_errno = errno;
1115 errno = saved_errno;
1119 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1123 char *stripped = NULL;
1125 char *result = NULL;
1126 char *inserted = NULL;
1127 char *inserted_to, *inserted_end;
1130 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1131 ×tamp, &stripped)) {
1134 if (timestamp == 0) {
1135 return SMB_VFS_NEXT_REALPATH(handle, fname);
1138 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1143 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1144 if (result == NULL) {
1149 * Take away what we've inserted. This removes the @GMT-thingy
1150 * completely, but will give a path under the share root.
1152 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1153 if (inserted == NULL) {
1156 inserted_to = strstr_m(result, inserted);
1157 if (inserted_to == NULL) {
1158 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1161 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1162 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1165 saved_errno = errno;
1166 TALLOC_FREE(inserted);
1168 TALLOC_FREE(stripped);
1169 errno = saved_errno;
1174 * Check whether a given directory contains a
1175 * snapshot directory as direct subdirectory.
1176 * If yes, return the path of the snapshot-subdir,
1177 * otherwise return NULL.
1179 static char *have_snapdir(struct vfs_handle_struct *handle,
1182 struct smb_filename smb_fname;
1184 struct shadow_copy2_config *config;
1186 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1189 ZERO_STRUCT(smb_fname);
1190 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1191 path, config->snapdir);
1192 if (smb_fname.base_name == NULL) {
1196 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1197 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1198 return smb_fname.base_name;
1200 TALLOC_FREE(smb_fname.base_name);
1205 * Find the snapshot directory (if any) for the given
1206 * filename (which is relative to the share).
1208 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1209 struct vfs_handle_struct *handle,
1210 struct smb_filename *smb_fname)
1213 const char *snapdir;
1214 struct shadow_copy2_config *config;
1216 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1220 * If the non-snapdisrseverywhere mode, we should not search!
1222 if (!config->snapdirseverywhere) {
1223 return config->snapshot_basepath;
1226 path = talloc_asprintf(mem_ctx, "%s/%s",
1227 handle->conn->connectpath,
1228 smb_fname->base_name);
1233 snapdir = have_snapdir(handle, path);
1234 if (snapdir != NULL) {
1239 while ((p = strrchr(path, '/')) && (p > path)) {
1243 snapdir = have_snapdir(handle, path);
1244 if (snapdir != NULL) {
1253 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1255 char *gmt, size_t gmt_len)
1257 struct tm timestamp;
1259 unsigned long int timestamp_long;
1261 struct shadow_copy2_config *config;
1263 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1266 fmt = config->gmt_format;
1268 ZERO_STRUCT(timestamp);
1269 if (config->use_sscanf) {
1270 if (sscanf(name, fmt, ×tamp_long) != 1) {
1271 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1272 "no sscanf match %s: %s\n",
1276 timestamp_t = timestamp_long;
1277 gmtime_r(×tamp_t, ×tamp);
1279 if (strptime(name, fmt, ×tamp) == NULL) {
1280 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1281 "no match %s: %s\n",
1285 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1288 if (config->use_localtime) {
1289 timestamp.tm_isdst = -1;
1290 timestamp_t = mktime(×tamp);
1291 gmtime_r(×tamp_t, ×tamp);
1295 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1299 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1301 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1304 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1306 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1310 sort the shadow copy data in ascending or descending order
1312 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1313 struct shadow_copy_data *shadow_copy2_data)
1315 int (*cmpfunc)(const void *, const void *);
1317 struct shadow_copy2_config *config;
1319 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1322 sort = config->sort_order;
1327 if (strcmp(sort, "asc") == 0) {
1328 cmpfunc = shadow_copy2_label_cmp_asc;
1329 } else if (strcmp(sort, "desc") == 0) {
1330 cmpfunc = shadow_copy2_label_cmp_desc;
1335 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1336 shadow_copy2_data->labels)
1338 TYPESAFE_QSORT(shadow_copy2_data->labels,
1339 shadow_copy2_data->num_volumes,
1344 static int shadow_copy2_get_shadow_copy_data(
1345 vfs_handle_struct *handle, files_struct *fsp,
1346 struct shadow_copy_data *shadow_copy2_data,
1350 const char *snapdir;
1352 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1354 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1355 if (snapdir == NULL) {
1356 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1357 handle->conn->connectpath));
1359 talloc_free(tmp_ctx);
1363 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1366 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1367 " - %s\n", snapdir, strerror(errno)));
1368 talloc_free(tmp_ctx);
1373 shadow_copy2_data->num_volumes = 0;
1374 shadow_copy2_data->labels = NULL;
1376 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1377 char snapshot[GMT_NAME_LEN+1];
1378 SHADOW_COPY_LABEL *tlabels;
1381 * ignore names not of the right form in the snapshot
1384 if (!shadow_copy2_snapshot_to_gmt(
1386 snapshot, sizeof(snapshot))) {
1388 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1389 "ignoring %s\n", d->d_name));
1392 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1393 d->d_name, snapshot));
1396 /* the caller doesn't want the labels */
1397 shadow_copy2_data->num_volumes++;
1401 tlabels = talloc_realloc(shadow_copy2_data,
1402 shadow_copy2_data->labels,
1404 shadow_copy2_data->num_volumes+1);
1405 if (tlabels == NULL) {
1406 DEBUG(0,("shadow_copy2: out of memory\n"));
1407 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1408 talloc_free(tmp_ctx);
1412 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1415 shadow_copy2_data->num_volumes++;
1416 shadow_copy2_data->labels = tlabels;
1419 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1421 shadow_copy2_sort_data(handle, shadow_copy2_data);
1423 talloc_free(tmp_ctx);
1427 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1428 struct files_struct *fsp,
1429 uint32 security_info,
1430 TALLOC_CTX *mem_ctx,
1431 struct security_descriptor **ppdesc)
1438 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1439 fsp->fsp_name->base_name,
1440 ×tamp, &stripped)) {
1441 return map_nt_error_from_unix(errno);
1443 if (timestamp == 0) {
1444 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1448 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1449 TALLOC_FREE(stripped);
1451 return map_nt_error_from_unix(errno);
1453 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1459 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1461 uint32 security_info,
1462 TALLOC_CTX *mem_ctx,
1463 struct security_descriptor **ppdesc)
1470 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1471 ×tamp, &stripped)) {
1472 return map_nt_error_from_unix(errno);
1474 if (timestamp == 0) {
1475 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1478 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1479 TALLOC_FREE(stripped);
1481 return map_nt_error_from_unix(errno);
1483 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1489 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1490 const char *fname, mode_t mode)
1494 int ret, saved_errno;
1497 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1498 ×tamp, &stripped)) {
1501 if (timestamp == 0) {
1502 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1504 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1505 TALLOC_FREE(stripped);
1509 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1510 saved_errno = errno;
1512 errno = saved_errno;
1516 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1520 int ret, saved_errno;
1523 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1524 ×tamp, &stripped)) {
1527 if (timestamp == 0) {
1528 return SMB_VFS_NEXT_RMDIR(handle, fname);
1530 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1531 TALLOC_FREE(stripped);
1535 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1536 saved_errno = errno;
1538 errno = saved_errno;
1542 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1547 int ret, saved_errno;
1550 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1551 ×tamp, &stripped)) {
1554 if (timestamp == 0) {
1555 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1557 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1558 TALLOC_FREE(stripped);
1562 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1563 saved_errno = errno;
1565 errno = saved_errno;
1569 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1570 const char *fname, const char *aname,
1571 void *value, size_t size)
1579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1580 ×tamp, &stripped)) {
1583 if (timestamp == 0) {
1584 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1587 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1588 TALLOC_FREE(stripped);
1592 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1593 saved_errno = errno;
1595 errno = saved_errno;
1599 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1601 char *list, size_t size)
1609 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1610 ×tamp, &stripped)) {
1613 if (timestamp == 0) {
1614 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1616 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1617 TALLOC_FREE(stripped);
1621 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1622 saved_errno = errno;
1624 errno = saved_errno;
1628 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1629 const char *fname, const char *aname)
1633 int ret, saved_errno;
1636 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1637 ×tamp, &stripped)) {
1640 if (timestamp == 0) {
1641 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1643 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1644 TALLOC_FREE(stripped);
1648 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1649 saved_errno = errno;
1651 errno = saved_errno;
1655 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1657 const char *aname, const void *value,
1658 size_t size, int flags)
1666 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1667 ×tamp, &stripped)) {
1670 if (timestamp == 0) {
1671 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1674 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1675 TALLOC_FREE(stripped);
1679 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1680 saved_errno = errno;
1682 errno = saved_errno;
1686 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1687 const char *fname, mode_t mode)
1695 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1696 ×tamp, &stripped)) {
1699 if (timestamp == 0) {
1700 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1702 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1703 TALLOC_FREE(stripped);
1707 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1708 saved_errno = errno;
1710 errno = saved_errno;
1714 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1717 TALLOC_CTX *mem_ctx,
1726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1727 ×tamp, &stripped)) {
1730 if (timestamp == 0) {
1731 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1732 mem_ctx, found_name);
1734 if (stripped[0] == '\0') {
1735 *found_name = talloc_strdup(mem_ctx, name);
1736 if (*found_name == NULL) {
1742 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1743 TALLOC_FREE(stripped);
1747 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1748 mem_ctx, found_name);
1749 saved_errno = errno;
1751 errno = saved_errno;
1755 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1756 const char *path, bool small_query,
1757 uint64_t *bsize, uint64_t *dfree,
1766 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1767 ×tamp, &stripped)) {
1770 if (timestamp == 0) {
1771 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1772 bsize, dfree, dsize);
1775 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1776 TALLOC_FREE(stripped);
1781 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1784 saved_errno = errno;
1786 errno = saved_errno;
1791 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1792 const char *service, const char *user)
1794 struct shadow_copy2_config *config;
1796 const char *snapdir;
1797 const char *gmt_format;
1798 const char *sort_order;
1799 const char *basedir;
1800 const char *mount_point;
1802 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1803 (unsigned)handle->conn->cnum,
1804 handle->conn->connectpath));
1806 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1811 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1812 if (config == NULL) {
1813 DEBUG(0, ("talloc_zero() failed\n"));
1818 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1821 config->gmt_format = talloc_strdup(config, gmt_format);
1822 if (config->gmt_format == NULL) {
1823 DEBUG(0, ("talloc_strdup() failed\n"));
1828 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1829 "shadow", "sscanf", false);
1831 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1832 "shadow", "localtime",
1835 snapdir = lp_parm_const_string(SNUM(handle->conn),
1836 "shadow", "snapdir",
1838 config->snapdir = talloc_strdup(config, snapdir);
1839 if (config->snapdir == NULL) {
1840 DEBUG(0, ("talloc_strdup() failed\n"));
1845 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1847 "snapdirseverywhere",
1850 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1851 "shadow", "crossmountpoints",
1854 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1855 "shadow", "fixinodes",
1858 sort_order = lp_parm_const_string(SNUM(handle->conn),
1859 "shadow", "sort", "desc");
1860 config->sort_order = talloc_strdup(config, sort_order);
1861 if (config->sort_order == NULL) {
1862 DEBUG(0, ("talloc_strdup() failed\n"));
1867 mount_point = lp_parm_const_string(SNUM(handle->conn),
1868 "shadow", "mountpoint", NULL);
1869 if (mount_point != NULL) {
1870 if (mount_point[0] != '/') {
1871 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1872 "relative ('%s'), but it has to be an "
1873 "absolute path. Ignoring provided value.\n",
1878 p = strstr(handle->conn->connectpath, mount_point);
1879 if (p != handle->conn->connectpath) {
1880 DEBUG(1, ("Warning: mount_point (%s) is not a "
1881 "subdirectory of the share root "
1882 "(%s). Ignoring provided value.\n",
1884 handle->conn->connectpath));
1890 if (mount_point != NULL) {
1891 config->mount_point = talloc_strdup(config, mount_point);
1892 if (config->mount_point == NULL) {
1893 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1897 config->mount_point = shadow_copy2_find_mount_point(config,
1899 if (config->mount_point == NULL) {
1900 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1901 " failed: %s\n", strerror(errno)));
1906 basedir = lp_parm_const_string(SNUM(handle->conn),
1907 "shadow", "basedir", NULL);
1909 if (basedir != NULL) {
1910 if (basedir[0] != '/') {
1911 DEBUG(1, (__location__ " Warning: 'basedir' is "
1912 "relative ('%s'), but it has to be an "
1913 "absolute path. Disabling basedir.\n",
1917 p = strstr(basedir, config->mount_point);
1919 DEBUG(1, ("Warning: basedir (%s) is not a "
1920 "subdirectory of the share root's "
1921 "mount point (%s). "
1922 "Disabling basedir\n",
1923 basedir, config->mount_point));
1925 config->basedir = talloc_strdup(config,
1927 if (config->basedir == NULL) {
1928 DEBUG(0, ("talloc_strdup() failed\n"));
1936 if (config->snapdirseverywhere && config->basedir != NULL) {
1937 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1938 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1939 TALLOC_FREE(config->basedir);
1942 if (config->crossmountpoints && config->basedir != NULL) {
1943 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1944 "with 'crossmountpoints'. Disabling basedir.\n"));
1945 TALLOC_FREE(config->basedir);
1948 if (config->basedir == NULL) {
1949 config->basedir = config->mount_point;
1952 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1953 config->rel_connectpath = talloc_strdup(config,
1954 handle->conn->connectpath + strlen(config->basedir));
1955 if (config->rel_connectpath == NULL) {
1956 DEBUG(0, ("talloc_strdup() failed\n"));
1962 if (config->snapdir[0] == '/') {
1963 config->snapdir_absolute = true;
1965 if (config->snapdirseverywhere == true) {
1966 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1967 "is incompatible with 'snapdirseverywhere', "
1968 "setting 'snapdirseverywhere' to false.\n"));
1969 config->snapdirseverywhere = false;
1972 if (config->crossmountpoints == true) {
1973 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1974 "is not supported with an absolute snapdir. "
1975 "Disabling it.\n"));
1976 config->crossmountpoints = false;
1979 config->snapshot_basepath = config->snapdir;
1981 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1982 config->mount_point, config->snapdir);
1983 if (config->snapshot_basepath == NULL) {
1984 DEBUG(0, ("talloc_asprintf() failed\n"));
1990 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1991 " share root: '%s'\n"
1993 " mountpoint: '%s'\n"
1994 " rel share root: '%s'\n"
1996 " snapshot base path: '%s'\n"
1999 " snapdirs everywhere: %s\n"
2000 " cross mountpoints: %s\n"
2004 handle->conn->connectpath,
2006 config->mount_point,
2007 config->rel_connectpath,
2009 config->snapshot_basepath,
2011 config->use_sscanf ? "yes" : "no",
2012 config->snapdirseverywhere ? "yes" : "no",
2013 config->crossmountpoints ? "yes" : "no",
2014 config->fixinodes ? "yes" : "no",
2019 SMB_VFS_HANDLE_SET_DATA(handle, config,
2020 NULL, struct shadow_copy2_config,
2026 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
2027 .connect_fn = shadow_copy2_connect,
2028 .opendir_fn = shadow_copy2_opendir,
2029 .disk_free_fn = shadow_copy2_disk_free,
2030 .rename_fn = shadow_copy2_rename,
2031 .link_fn = shadow_copy2_link,
2032 .symlink_fn = shadow_copy2_symlink,
2033 .stat_fn = shadow_copy2_stat,
2034 .lstat_fn = shadow_copy2_lstat,
2035 .fstat_fn = shadow_copy2_fstat,
2036 .open_fn = shadow_copy2_open,
2037 .unlink_fn = shadow_copy2_unlink,
2038 .chmod_fn = shadow_copy2_chmod,
2039 .chown_fn = shadow_copy2_chown,
2040 .chdir_fn = shadow_copy2_chdir,
2041 .ntimes_fn = shadow_copy2_ntimes,
2042 .readlink_fn = shadow_copy2_readlink,
2043 .mknod_fn = shadow_copy2_mknod,
2044 .realpath_fn = shadow_copy2_realpath,
2045 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2046 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2047 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2048 .mkdir_fn = shadow_copy2_mkdir,
2049 .rmdir_fn = shadow_copy2_rmdir,
2050 .getxattr_fn = shadow_copy2_getxattr,
2051 .listxattr_fn = shadow_copy2_listxattr,
2052 .removexattr_fn = shadow_copy2_removexattr,
2053 .setxattr_fn = shadow_copy2_setxattr,
2054 .chmod_acl_fn = shadow_copy2_chmod_acl,
2055 .chflags_fn = shadow_copy2_chflags,
2056 .get_real_filename_fn = shadow_copy2_get_real_filename,
2059 NTSTATUS vfs_shadow_copy2_init(void);
2060 NTSTATUS vfs_shadow_copy2_init(void)
2062 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2063 "shadow_copy2", &vfs_shadow_copy2_fns);