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) {
582 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
584 if (mount_point == NULL) {
587 min_offset = strlen(mount_point);
588 TALLOC_FREE(mount_point);
591 memcpy(converted, path, pathlen+1);
592 converted[pathlen+insertlen] = '\0';
594 ZERO_STRUCT(converted_fname);
595 converted_fname.base_name = converted;
597 for (i = num_slashes-1; i>=0; i--) {
603 if (offset < min_offset) {
608 memcpy(converted+offset, insert, insertlen);
611 memcpy(converted+offset, path + slashes[i],
612 pathlen - slashes[i]);
614 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
616 DEBUG(10, ("Trying[snapdirseverywhere] %s: %d (%s)\n",
618 ret, ret == 0 ? "ok" : strerror(errno)));
623 if (errno == ENOTDIR) {
625 * This is a valid condition: We appended the
626 * .snaphots/@GMT.. to a file name. Just try
627 * with the upper levels.
631 if (errno != ENOENT) {
632 /* Other problem than "not found" */
641 DEBUG(10, ("Found %s\n", converted));
649 TALLOC_FREE(converted);
651 TALLOC_FREE(slashes);
658 modify a sbuf return to ensure that inodes in the shadow directory
659 are different from those in the main directory
661 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
662 SMB_STRUCT_STAT *sbuf)
664 struct shadow_copy2_config *config;
666 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
669 if (config->fixinodes) {
670 /* some snapshot systems, like GPFS, return the name
671 device:inode for the snapshot files as the current
672 files. That breaks the 'restore' button in the shadow copy
673 GUI, as the client gets a sharing violation.
675 This is a crude way of allowing both files to be
676 open at once. It has a slight chance of inode
677 number collision, but I can't see a better approach
678 without significant VFS changes
682 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
686 sbuf->st_ex_ino ^= shash;
690 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
701 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
702 ×tamp, &stripped)) {
705 if (timestamp == 0) {
706 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
708 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
709 TALLOC_FREE(stripped);
713 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
720 static int shadow_copy2_rename(vfs_handle_struct *handle,
721 const struct smb_filename *smb_fname_src,
722 const struct smb_filename *smb_fname_dst)
724 time_t timestamp_src, timestamp_dst;
726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
727 smb_fname_src->base_name,
728 ×tamp_src, NULL)) {
731 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
732 smb_fname_dst->base_name,
733 ×tamp_dst, NULL)) {
736 if (timestamp_src != 0) {
740 if (timestamp_dst != 0) {
744 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
747 static int shadow_copy2_symlink(vfs_handle_struct *handle,
748 const char *oldname, const char *newname)
750 time_t timestamp_old, timestamp_new;
752 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
753 ×tamp_old, NULL)) {
756 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
757 ×tamp_new, NULL)) {
760 if ((timestamp_old != 0) || (timestamp_new != 0)) {
764 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
767 static int shadow_copy2_link(vfs_handle_struct *handle,
768 const char *oldname, const char *newname)
770 time_t timestamp_old, timestamp_new;
772 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
773 ×tamp_old, NULL)) {
776 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
777 ×tamp_new, NULL)) {
780 if ((timestamp_old != 0) || (timestamp_new != 0)) {
784 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
787 static int shadow_copy2_stat(vfs_handle_struct *handle,
788 struct smb_filename *smb_fname)
791 char *stripped, *tmp;
792 int ret, saved_errno;
794 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
795 smb_fname->base_name,
796 ×tamp, &stripped)) {
799 if (timestamp == 0) {
800 return SMB_VFS_NEXT_STAT(handle, smb_fname);
803 tmp = smb_fname->base_name;
804 smb_fname->base_name = shadow_copy2_convert(
805 talloc_tos(), handle, stripped, timestamp);
806 TALLOC_FREE(stripped);
808 if (smb_fname->base_name == NULL) {
809 smb_fname->base_name = tmp;
813 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
816 TALLOC_FREE(smb_fname->base_name);
817 smb_fname->base_name = tmp;
820 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
826 static int shadow_copy2_lstat(vfs_handle_struct *handle,
827 struct smb_filename *smb_fname)
830 char *stripped, *tmp;
831 int ret, saved_errno;
833 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
834 smb_fname->base_name,
835 ×tamp, &stripped)) {
838 if (timestamp == 0) {
839 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
842 tmp = smb_fname->base_name;
843 smb_fname->base_name = shadow_copy2_convert(
844 talloc_tos(), handle, stripped, timestamp);
845 TALLOC_FREE(stripped);
847 if (smb_fname->base_name == NULL) {
848 smb_fname->base_name = tmp;
852 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
855 TALLOC_FREE(smb_fname->base_name);
856 smb_fname->base_name = tmp;
859 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
865 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
866 SMB_STRUCT_STAT *sbuf)
871 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
875 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
876 fsp->fsp_name->base_name,
880 if (timestamp != 0) {
881 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
886 static int shadow_copy2_open(vfs_handle_struct *handle,
887 struct smb_filename *smb_fname, files_struct *fsp,
888 int flags, mode_t mode)
891 char *stripped, *tmp;
892 int ret, saved_errno;
894 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
895 smb_fname->base_name,
896 ×tamp, &stripped)) {
899 if (timestamp == 0) {
900 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
903 tmp = smb_fname->base_name;
904 smb_fname->base_name = shadow_copy2_convert(
905 talloc_tos(), handle, stripped, timestamp);
906 TALLOC_FREE(stripped);
908 if (smb_fname->base_name == NULL) {
909 smb_fname->base_name = tmp;
913 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
916 TALLOC_FREE(smb_fname->base_name);
917 smb_fname->base_name = tmp;
923 static int shadow_copy2_unlink(vfs_handle_struct *handle,
924 const struct smb_filename *smb_fname)
928 int ret, saved_errno;
929 struct smb_filename *conv;
931 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
932 smb_fname->base_name,
933 ×tamp, &stripped)) {
936 if (timestamp == 0) {
937 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
939 conv = cp_smb_filename(talloc_tos(), smb_fname);
944 conv->base_name = shadow_copy2_convert(
945 conv, handle, stripped, timestamp);
946 TALLOC_FREE(stripped);
947 if (conv->base_name == NULL) {
950 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
957 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
962 int ret, saved_errno;
965 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
966 ×tamp, &stripped)) {
969 if (timestamp == 0) {
970 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
972 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
973 TALLOC_FREE(stripped);
977 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
984 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
985 uid_t uid, gid_t gid)
989 int ret, saved_errno;
992 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
993 ×tamp, &stripped)) {
996 if (timestamp == 0) {
997 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
999 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1000 TALLOC_FREE(stripped);
1004 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1005 saved_errno = errno;
1007 errno = saved_errno;
1011 static int shadow_copy2_chdir(vfs_handle_struct *handle,
1016 int ret, saved_errno;
1019 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1020 ×tamp, &stripped)) {
1023 if (timestamp == 0) {
1024 return SMB_VFS_NEXT_CHDIR(handle, fname);
1026 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1027 TALLOC_FREE(stripped);
1031 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1032 saved_errno = errno;
1034 errno = saved_errno;
1038 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
1039 const struct smb_filename *smb_fname,
1040 struct smb_file_time *ft)
1044 int ret, saved_errno;
1045 struct smb_filename *conv;
1047 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1048 smb_fname->base_name,
1049 ×tamp, &stripped)) {
1052 if (timestamp == 0) {
1053 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1055 conv = cp_smb_filename(talloc_tos(), smb_fname);
1060 conv->base_name = shadow_copy2_convert(
1061 conv, handle, stripped, timestamp);
1062 TALLOC_FREE(stripped);
1063 if (conv->base_name == NULL) {
1066 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1067 saved_errno = errno;
1069 errno = saved_errno;
1073 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1074 const char *fname, char *buf, size_t bufsiz)
1078 int ret, saved_errno;
1081 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1082 ×tamp, &stripped)) {
1085 if (timestamp == 0) {
1086 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1088 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1089 TALLOC_FREE(stripped);
1093 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1094 saved_errno = errno;
1096 errno = saved_errno;
1100 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1101 const char *fname, mode_t mode, SMB_DEV_T dev)
1105 int ret, saved_errno;
1108 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1109 ×tamp, &stripped)) {
1112 if (timestamp == 0) {
1113 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1115 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1116 TALLOC_FREE(stripped);
1120 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1121 saved_errno = errno;
1123 errno = saved_errno;
1127 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1131 char *stripped = NULL;
1133 char *result = NULL;
1134 char *inserted = NULL;
1135 char *inserted_to, *inserted_end;
1138 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1139 ×tamp, &stripped)) {
1142 if (timestamp == 0) {
1143 return SMB_VFS_NEXT_REALPATH(handle, fname);
1146 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1151 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1152 if (result == NULL) {
1157 * Take away what we've inserted. This removes the @GMT-thingy
1158 * completely, but will give a path under the share root.
1160 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1161 if (inserted == NULL) {
1164 inserted_to = strstr_m(result, inserted);
1165 if (inserted_to == NULL) {
1166 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1169 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1170 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1173 saved_errno = errno;
1174 TALLOC_FREE(inserted);
1176 TALLOC_FREE(stripped);
1177 errno = saved_errno;
1182 * Check whether a given directory contains a
1183 * snapshot directory as direct subdirectory.
1184 * If yes, return the path of the snapshot-subdir,
1185 * otherwise return NULL.
1187 static char *have_snapdir(struct vfs_handle_struct *handle,
1190 struct smb_filename smb_fname;
1192 struct shadow_copy2_config *config;
1194 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1197 ZERO_STRUCT(smb_fname);
1198 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1199 path, config->snapdir);
1200 if (smb_fname.base_name == NULL) {
1204 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1205 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1206 return smb_fname.base_name;
1208 TALLOC_FREE(smb_fname.base_name);
1213 * Find the snapshot directory (if any) for the given
1214 * filename (which is relative to the share).
1216 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1217 struct vfs_handle_struct *handle,
1218 struct smb_filename *smb_fname)
1221 const char *snapdir;
1222 struct shadow_copy2_config *config;
1224 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1228 * If the non-snapdisrseverywhere mode, we should not search!
1230 if (!config->snapdirseverywhere) {
1231 return config->snapshot_basepath;
1234 path = talloc_asprintf(mem_ctx, "%s/%s",
1235 handle->conn->connectpath,
1236 smb_fname->base_name);
1241 snapdir = have_snapdir(handle, path);
1242 if (snapdir != NULL) {
1247 while ((p = strrchr(path, '/')) && (p > path)) {
1251 snapdir = have_snapdir(handle, path);
1252 if (snapdir != NULL) {
1261 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1263 char *gmt, size_t gmt_len)
1265 struct tm timestamp;
1267 unsigned long int timestamp_long;
1269 struct shadow_copy2_config *config;
1271 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1274 fmt = config->gmt_format;
1276 ZERO_STRUCT(timestamp);
1277 if (config->use_sscanf) {
1278 if (sscanf(name, fmt, ×tamp_long) != 1) {
1279 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1280 "no sscanf match %s: %s\n",
1284 timestamp_t = timestamp_long;
1285 gmtime_r(×tamp_t, ×tamp);
1287 if (strptime(name, fmt, ×tamp) == NULL) {
1288 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1289 "no match %s: %s\n",
1293 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1296 if (config->use_localtime) {
1297 timestamp.tm_isdst = -1;
1298 timestamp_t = mktime(×tamp);
1299 gmtime_r(×tamp_t, ×tamp);
1303 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1307 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1309 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1312 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1314 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1318 sort the shadow copy data in ascending or descending order
1320 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1321 struct shadow_copy_data *shadow_copy2_data)
1323 int (*cmpfunc)(const void *, const void *);
1325 struct shadow_copy2_config *config;
1327 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1330 sort = config->sort_order;
1335 if (strcmp(sort, "asc") == 0) {
1336 cmpfunc = shadow_copy2_label_cmp_asc;
1337 } else if (strcmp(sort, "desc") == 0) {
1338 cmpfunc = shadow_copy2_label_cmp_desc;
1343 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1344 shadow_copy2_data->labels)
1346 TYPESAFE_QSORT(shadow_copy2_data->labels,
1347 shadow_copy2_data->num_volumes,
1352 static int shadow_copy2_get_shadow_copy_data(
1353 vfs_handle_struct *handle, files_struct *fsp,
1354 struct shadow_copy_data *shadow_copy2_data,
1358 const char *snapdir;
1360 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1362 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1363 if (snapdir == NULL) {
1364 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1365 handle->conn->connectpath));
1367 talloc_free(tmp_ctx);
1371 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1374 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1375 " - %s\n", snapdir, strerror(errno)));
1376 talloc_free(tmp_ctx);
1381 shadow_copy2_data->num_volumes = 0;
1382 shadow_copy2_data->labels = NULL;
1384 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1385 char snapshot[GMT_NAME_LEN+1];
1386 SHADOW_COPY_LABEL *tlabels;
1389 * ignore names not of the right form in the snapshot
1392 if (!shadow_copy2_snapshot_to_gmt(
1394 snapshot, sizeof(snapshot))) {
1396 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1397 "ignoring %s\n", d->d_name));
1400 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1401 d->d_name, snapshot));
1404 /* the caller doesn't want the labels */
1405 shadow_copy2_data->num_volumes++;
1409 tlabels = talloc_realloc(shadow_copy2_data,
1410 shadow_copy2_data->labels,
1412 shadow_copy2_data->num_volumes+1);
1413 if (tlabels == NULL) {
1414 DEBUG(0,("shadow_copy2: out of memory\n"));
1415 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1416 talloc_free(tmp_ctx);
1420 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1423 shadow_copy2_data->num_volumes++;
1424 shadow_copy2_data->labels = tlabels;
1427 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1429 shadow_copy2_sort_data(handle, shadow_copy2_data);
1431 talloc_free(tmp_ctx);
1435 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1436 struct files_struct *fsp,
1437 uint32 security_info,
1438 TALLOC_CTX *mem_ctx,
1439 struct security_descriptor **ppdesc)
1446 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1447 fsp->fsp_name->base_name,
1448 ×tamp, &stripped)) {
1449 return map_nt_error_from_unix(errno);
1451 if (timestamp == 0) {
1452 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1456 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1457 TALLOC_FREE(stripped);
1459 return map_nt_error_from_unix(errno);
1461 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1467 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1469 uint32 security_info,
1470 TALLOC_CTX *mem_ctx,
1471 struct security_descriptor **ppdesc)
1478 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1479 ×tamp, &stripped)) {
1480 return map_nt_error_from_unix(errno);
1482 if (timestamp == 0) {
1483 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1486 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1487 TALLOC_FREE(stripped);
1489 return map_nt_error_from_unix(errno);
1491 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1497 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1498 const char *fname, mode_t mode)
1502 int ret, saved_errno;
1505 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1506 ×tamp, &stripped)) {
1509 if (timestamp == 0) {
1510 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1512 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1513 TALLOC_FREE(stripped);
1517 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1518 saved_errno = errno;
1520 errno = saved_errno;
1524 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1528 int ret, saved_errno;
1531 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1532 ×tamp, &stripped)) {
1535 if (timestamp == 0) {
1536 return SMB_VFS_NEXT_RMDIR(handle, fname);
1538 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1539 TALLOC_FREE(stripped);
1543 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1544 saved_errno = errno;
1546 errno = saved_errno;
1550 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1555 int ret, saved_errno;
1558 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1559 ×tamp, &stripped)) {
1562 if (timestamp == 0) {
1563 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1565 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1566 TALLOC_FREE(stripped);
1570 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1571 saved_errno = errno;
1573 errno = saved_errno;
1577 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1578 const char *fname, const char *aname,
1579 void *value, size_t size)
1587 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1588 ×tamp, &stripped)) {
1591 if (timestamp == 0) {
1592 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1595 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1596 TALLOC_FREE(stripped);
1600 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1601 saved_errno = errno;
1603 errno = saved_errno;
1607 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1609 char *list, size_t size)
1617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1618 ×tamp, &stripped)) {
1621 if (timestamp == 0) {
1622 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1624 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1625 TALLOC_FREE(stripped);
1629 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1630 saved_errno = errno;
1632 errno = saved_errno;
1636 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1637 const char *fname, const char *aname)
1641 int ret, saved_errno;
1644 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1645 ×tamp, &stripped)) {
1648 if (timestamp == 0) {
1649 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1651 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1652 TALLOC_FREE(stripped);
1656 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1657 saved_errno = errno;
1659 errno = saved_errno;
1663 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1665 const char *aname, const void *value,
1666 size_t size, int flags)
1674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1675 ×tamp, &stripped)) {
1678 if (timestamp == 0) {
1679 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1682 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1683 TALLOC_FREE(stripped);
1687 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1688 saved_errno = errno;
1690 errno = saved_errno;
1694 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1695 const char *fname, mode_t mode)
1703 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1704 ×tamp, &stripped)) {
1707 if (timestamp == 0) {
1708 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1710 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1711 TALLOC_FREE(stripped);
1715 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1716 saved_errno = errno;
1718 errno = saved_errno;
1722 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1725 TALLOC_CTX *mem_ctx,
1734 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1735 ×tamp, &stripped)) {
1738 if (timestamp == 0) {
1739 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1740 mem_ctx, found_name);
1742 if (stripped[0] == '\0') {
1743 *found_name = talloc_strdup(mem_ctx, name);
1744 if (*found_name == NULL) {
1750 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1751 TALLOC_FREE(stripped);
1755 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1756 mem_ctx, found_name);
1757 saved_errno = errno;
1759 errno = saved_errno;
1763 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1764 const char *path, bool small_query,
1765 uint64_t *bsize, uint64_t *dfree,
1774 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1775 ×tamp, &stripped)) {
1778 if (timestamp == 0) {
1779 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1780 bsize, dfree, dsize);
1783 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1784 TALLOC_FREE(stripped);
1789 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1792 saved_errno = errno;
1794 errno = saved_errno;
1799 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1800 const char *service, const char *user)
1802 struct shadow_copy2_config *config;
1804 const char *snapdir;
1805 const char *gmt_format;
1806 const char *sort_order;
1807 const char *basedir;
1808 const char *mount_point;
1810 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1811 (unsigned)handle->conn->cnum,
1812 handle->conn->connectpath));
1814 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1819 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1820 if (config == NULL) {
1821 DEBUG(0, ("talloc_zero() failed\n"));
1826 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1829 config->gmt_format = talloc_strdup(config, gmt_format);
1830 if (config->gmt_format == NULL) {
1831 DEBUG(0, ("talloc_strdup() failed\n"));
1836 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1837 "shadow", "sscanf", false);
1839 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1840 "shadow", "localtime",
1843 snapdir = lp_parm_const_string(SNUM(handle->conn),
1844 "shadow", "snapdir",
1846 config->snapdir = talloc_strdup(config, snapdir);
1847 if (config->snapdir == NULL) {
1848 DEBUG(0, ("talloc_strdup() failed\n"));
1853 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1855 "snapdirseverywhere",
1858 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1859 "shadow", "crossmountpoints",
1862 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1863 "shadow", "fixinodes",
1866 sort_order = lp_parm_const_string(SNUM(handle->conn),
1867 "shadow", "sort", "desc");
1868 config->sort_order = talloc_strdup(config, sort_order);
1869 if (config->sort_order == NULL) {
1870 DEBUG(0, ("talloc_strdup() failed\n"));
1875 mount_point = lp_parm_const_string(SNUM(handle->conn),
1876 "shadow", "mountpoint", NULL);
1877 if (mount_point != NULL) {
1878 if (mount_point[0] != '/') {
1879 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1880 "relative ('%s'), but it has to be an "
1881 "absolute path. Ignoring provided value.\n",
1886 p = strstr(handle->conn->connectpath, mount_point);
1887 if (p != handle->conn->connectpath) {
1888 DEBUG(1, ("Warning: mount_point (%s) is not a "
1889 "subdirectory of the share root "
1890 "(%s). Ignoring provided value.\n",
1892 handle->conn->connectpath));
1898 if (mount_point != NULL) {
1899 config->mount_point = talloc_strdup(config, mount_point);
1900 if (config->mount_point == NULL) {
1901 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1905 config->mount_point = shadow_copy2_find_mount_point(config,
1907 if (config->mount_point == NULL) {
1908 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1909 " failed: %s\n", strerror(errno)));
1914 basedir = lp_parm_const_string(SNUM(handle->conn),
1915 "shadow", "basedir", NULL);
1917 if (basedir != NULL) {
1918 if (basedir[0] != '/') {
1919 DEBUG(1, (__location__ " Warning: 'basedir' is "
1920 "relative ('%s'), but it has to be an "
1921 "absolute path. Disabling basedir.\n",
1925 p = strstr(basedir, config->mount_point);
1927 DEBUG(1, ("Warning: basedir (%s) is not a "
1928 "subdirectory of the share root's "
1929 "mount point (%s). "
1930 "Disabling basedir\n",
1931 basedir, config->mount_point));
1933 config->basedir = talloc_strdup(config,
1935 if (config->basedir == NULL) {
1936 DEBUG(0, ("talloc_strdup() failed\n"));
1944 if (config->snapdirseverywhere && config->basedir != NULL) {
1945 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1946 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1947 TALLOC_FREE(config->basedir);
1950 if (config->crossmountpoints && config->basedir != NULL) {
1951 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1952 "with 'crossmountpoints'. Disabling basedir.\n"));
1953 TALLOC_FREE(config->basedir);
1956 if (config->basedir == NULL) {
1957 config->basedir = config->mount_point;
1960 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1961 config->rel_connectpath = talloc_strdup(config,
1962 handle->conn->connectpath + strlen(config->basedir));
1963 if (config->rel_connectpath == NULL) {
1964 DEBUG(0, ("talloc_strdup() failed\n"));
1970 if (config->snapdir[0] == '/') {
1971 config->snapdir_absolute = true;
1973 if (config->snapdirseverywhere == true) {
1974 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1975 "is incompatible with 'snapdirseverywhere', "
1976 "setting 'snapdirseverywhere' to false.\n"));
1977 config->snapdirseverywhere = false;
1980 if (config->crossmountpoints == true) {
1981 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1982 "is not supported with an absolute snapdir. "
1983 "Disabling it.\n"));
1984 config->crossmountpoints = false;
1987 config->snapshot_basepath = config->snapdir;
1989 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1990 config->mount_point, config->snapdir);
1991 if (config->snapshot_basepath == NULL) {
1992 DEBUG(0, ("talloc_asprintf() failed\n"));
1998 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1999 " share root: '%s'\n"
2001 " mountpoint: '%s'\n"
2002 " rel share root: '%s'\n"
2004 " snapshot base path: '%s'\n"
2007 " snapdirs everywhere: %s\n"
2008 " cross mountpoints: %s\n"
2012 handle->conn->connectpath,
2014 config->mount_point,
2015 config->rel_connectpath,
2017 config->snapshot_basepath,
2019 config->use_sscanf ? "yes" : "no",
2020 config->snapdirseverywhere ? "yes" : "no",
2021 config->crossmountpoints ? "yes" : "no",
2022 config->fixinodes ? "yes" : "no",
2027 SMB_VFS_HANDLE_SET_DATA(handle, config,
2028 NULL, struct shadow_copy2_config,
2034 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
2035 .connect_fn = shadow_copy2_connect,
2036 .opendir_fn = shadow_copy2_opendir,
2037 .disk_free_fn = shadow_copy2_disk_free,
2038 .rename_fn = shadow_copy2_rename,
2039 .link_fn = shadow_copy2_link,
2040 .symlink_fn = shadow_copy2_symlink,
2041 .stat_fn = shadow_copy2_stat,
2042 .lstat_fn = shadow_copy2_lstat,
2043 .fstat_fn = shadow_copy2_fstat,
2044 .open_fn = shadow_copy2_open,
2045 .unlink_fn = shadow_copy2_unlink,
2046 .chmod_fn = shadow_copy2_chmod,
2047 .chown_fn = shadow_copy2_chown,
2048 .chdir_fn = shadow_copy2_chdir,
2049 .ntimes_fn = shadow_copy2_ntimes,
2050 .readlink_fn = shadow_copy2_readlink,
2051 .mknod_fn = shadow_copy2_mknod,
2052 .realpath_fn = shadow_copy2_realpath,
2053 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2054 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2055 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2056 .mkdir_fn = shadow_copy2_mkdir,
2057 .rmdir_fn = shadow_copy2_rmdir,
2058 .getxattr_fn = shadow_copy2_getxattr,
2059 .listxattr_fn = shadow_copy2_listxattr,
2060 .removexattr_fn = shadow_copy2_removexattr,
2061 .setxattr_fn = shadow_copy2_setxattr,
2062 .chmod_acl_fn = shadow_copy2_chmod_acl,
2063 .chflags_fn = shadow_copy2_chflags,
2064 .get_real_filename_fn = shadow_copy2_get_real_filename,
2067 NTSTATUS vfs_shadow_copy2_init(void);
2068 NTSTATUS vfs_shadow_copy2_init(void)
2070 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2071 "shadow_copy2", &vfs_shadow_copy2_fns);