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 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
499 pathlen = talloc_get_size(path)-1;
501 DEBUG(10, ("converting %s\n", path));
503 if (!shadow_copy2_find_slashes(talloc_tos(), path,
504 &slashes, &num_slashes)) {
507 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
508 if (insert == NULL) {
511 insertlen = talloc_get_size(insert)-1;
512 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
513 if (converted == NULL) {
517 if (path[pathlen-1] != '/') {
519 * Append a fake slash to find the snapshot root
522 tmp = talloc_realloc(talloc_tos(), slashes,
523 size_t, num_slashes+1);
528 slashes[num_slashes] = pathlen;
534 if (!config->crossmountpoints) {
537 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
539 if (mount_point == NULL) {
542 min_offset = strlen(mount_point);
543 TALLOC_FREE(mount_point);
546 memcpy(converted, path, pathlen+1);
547 converted[pathlen+insertlen] = '\0';
549 ZERO_STRUCT(converted_fname);
550 converted_fname.base_name = converted;
552 for (i = num_slashes-1; i>=0; i--) {
558 if (offset < min_offset) {
563 memcpy(converted+offset, insert, insertlen);
566 memcpy(converted+offset, path + slashes[i],
567 pathlen - slashes[i]);
569 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
571 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
572 ret, ret == 0 ? "ok" : strerror(errno)));
577 if (errno == ENOTDIR) {
579 * This is a valid condition: We appended the
580 * .snaphots/@GMT.. to a file name. Just try
581 * with the upper levels.
585 if (errno != ENOENT) {
586 /* Other problem than "not found" */
595 DEBUG(10, ("Found %s\n", converted));
603 TALLOC_FREE(converted);
605 TALLOC_FREE(slashes);
612 modify a sbuf return to ensure that inodes in the shadow directory
613 are different from those in the main directory
615 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
616 SMB_STRUCT_STAT *sbuf)
618 struct shadow_copy2_config *config;
620 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
623 if (config->fixinodes) {
624 /* some snapshot systems, like GPFS, return the name
625 device:inode for the snapshot files as the current
626 files. That breaks the 'restore' button in the shadow copy
627 GUI, as the client gets a sharing violation.
629 This is a crude way of allowing both files to be
630 open at once. It has a slight chance of inode
631 number collision, but I can't see a better approach
632 without significant VFS changes
636 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
640 sbuf->st_ex_ino ^= shash;
644 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
656 ×tamp, &stripped)) {
659 if (timestamp == 0) {
660 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
662 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
663 TALLOC_FREE(stripped);
667 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
674 static int shadow_copy2_rename(vfs_handle_struct *handle,
675 const struct smb_filename *smb_fname_src,
676 const struct smb_filename *smb_fname_dst)
678 time_t timestamp_src, timestamp_dst;
680 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
681 smb_fname_src->base_name,
682 ×tamp_src, NULL)) {
685 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
686 smb_fname_dst->base_name,
687 ×tamp_dst, NULL)) {
690 if (timestamp_src != 0) {
694 if (timestamp_dst != 0) {
698 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
701 static int shadow_copy2_symlink(vfs_handle_struct *handle,
702 const char *oldname, const char *newname)
704 time_t timestamp_old, timestamp_new;
706 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
707 ×tamp_old, NULL)) {
710 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
711 ×tamp_new, NULL)) {
714 if ((timestamp_old != 0) || (timestamp_new != 0)) {
718 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
721 static int shadow_copy2_link(vfs_handle_struct *handle,
722 const char *oldname, const char *newname)
724 time_t timestamp_old, timestamp_new;
726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
727 ×tamp_old, NULL)) {
730 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
731 ×tamp_new, NULL)) {
734 if ((timestamp_old != 0) || (timestamp_new != 0)) {
738 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
741 static int shadow_copy2_stat(vfs_handle_struct *handle,
742 struct smb_filename *smb_fname)
745 char *stripped, *tmp;
746 int ret, saved_errno;
748 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
749 smb_fname->base_name,
750 ×tamp, &stripped)) {
753 if (timestamp == 0) {
754 return SMB_VFS_NEXT_STAT(handle, smb_fname);
757 tmp = smb_fname->base_name;
758 smb_fname->base_name = shadow_copy2_convert(
759 talloc_tos(), handle, stripped, timestamp);
760 TALLOC_FREE(stripped);
762 if (smb_fname->base_name == NULL) {
763 smb_fname->base_name = tmp;
767 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
770 TALLOC_FREE(smb_fname->base_name);
771 smb_fname->base_name = tmp;
774 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
780 static int shadow_copy2_lstat(vfs_handle_struct *handle,
781 struct smb_filename *smb_fname)
784 char *stripped, *tmp;
785 int ret, saved_errno;
787 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
788 smb_fname->base_name,
789 ×tamp, &stripped)) {
792 if (timestamp == 0) {
793 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
796 tmp = smb_fname->base_name;
797 smb_fname->base_name = shadow_copy2_convert(
798 talloc_tos(), handle, stripped, timestamp);
799 TALLOC_FREE(stripped);
801 if (smb_fname->base_name == NULL) {
802 smb_fname->base_name = tmp;
806 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
809 TALLOC_FREE(smb_fname->base_name);
810 smb_fname->base_name = tmp;
813 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
819 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
820 SMB_STRUCT_STAT *sbuf)
825 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
829 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
830 fsp->fsp_name->base_name,
834 if (timestamp != 0) {
835 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
840 static int shadow_copy2_open(vfs_handle_struct *handle,
841 struct smb_filename *smb_fname, files_struct *fsp,
842 int flags, mode_t mode)
845 char *stripped, *tmp;
846 int ret, saved_errno;
848 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
849 smb_fname->base_name,
850 ×tamp, &stripped)) {
853 if (timestamp == 0) {
854 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
857 tmp = smb_fname->base_name;
858 smb_fname->base_name = shadow_copy2_convert(
859 talloc_tos(), handle, stripped, timestamp);
860 TALLOC_FREE(stripped);
862 if (smb_fname->base_name == NULL) {
863 smb_fname->base_name = tmp;
867 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
870 TALLOC_FREE(smb_fname->base_name);
871 smb_fname->base_name = tmp;
877 static int shadow_copy2_unlink(vfs_handle_struct *handle,
878 const struct smb_filename *smb_fname)
882 int ret, saved_errno;
883 struct smb_filename *conv;
885 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
886 smb_fname->base_name,
887 ×tamp, &stripped)) {
890 if (timestamp == 0) {
891 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
893 conv = cp_smb_filename(talloc_tos(), smb_fname);
898 conv->base_name = shadow_copy2_convert(
899 conv, handle, stripped, timestamp);
900 TALLOC_FREE(stripped);
901 if (conv->base_name == NULL) {
904 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
911 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
916 int ret, saved_errno;
919 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
920 ×tamp, &stripped)) {
923 if (timestamp == 0) {
924 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
926 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
927 TALLOC_FREE(stripped);
931 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
938 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
939 uid_t uid, gid_t gid)
943 int ret, saved_errno;
946 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
947 ×tamp, &stripped)) {
950 if (timestamp == 0) {
951 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
953 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
954 TALLOC_FREE(stripped);
958 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
965 static int shadow_copy2_chdir(vfs_handle_struct *handle,
970 int ret, saved_errno;
973 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
974 ×tamp, &stripped)) {
977 if (timestamp == 0) {
978 return SMB_VFS_NEXT_CHDIR(handle, fname);
980 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
981 TALLOC_FREE(stripped);
985 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
992 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
993 const struct smb_filename *smb_fname,
994 struct smb_file_time *ft)
998 int ret, saved_errno;
999 struct smb_filename *conv;
1001 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1002 smb_fname->base_name,
1003 ×tamp, &stripped)) {
1006 if (timestamp == 0) {
1007 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1009 conv = cp_smb_filename(talloc_tos(), smb_fname);
1014 conv->base_name = shadow_copy2_convert(
1015 conv, handle, stripped, timestamp);
1016 TALLOC_FREE(stripped);
1017 if (conv->base_name == NULL) {
1020 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1021 saved_errno = errno;
1023 errno = saved_errno;
1027 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1028 const char *fname, char *buf, size_t bufsiz)
1032 int ret, saved_errno;
1035 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1036 ×tamp, &stripped)) {
1039 if (timestamp == 0) {
1040 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1042 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1043 TALLOC_FREE(stripped);
1047 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1048 saved_errno = errno;
1050 errno = saved_errno;
1054 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1055 const char *fname, mode_t mode, SMB_DEV_T dev)
1059 int ret, saved_errno;
1062 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1063 ×tamp, &stripped)) {
1066 if (timestamp == 0) {
1067 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1069 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1070 TALLOC_FREE(stripped);
1074 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1075 saved_errno = errno;
1077 errno = saved_errno;
1081 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1085 char *stripped = NULL;
1087 char *result = NULL;
1088 char *inserted = NULL;
1089 char *inserted_to, *inserted_end;
1092 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1093 ×tamp, &stripped)) {
1096 if (timestamp == 0) {
1097 return SMB_VFS_NEXT_REALPATH(handle, fname);
1100 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1105 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1106 if (result == NULL) {
1111 * Take away what we've inserted. This removes the @GMT-thingy
1112 * completely, but will give a path under the share root.
1114 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1115 if (inserted == NULL) {
1118 inserted_to = strstr_m(result, inserted);
1119 if (inserted_to == NULL) {
1120 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1123 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1124 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1127 saved_errno = errno;
1128 TALLOC_FREE(inserted);
1130 TALLOC_FREE(stripped);
1131 errno = saved_errno;
1136 * Check whether a given directory contains a
1137 * snapshot directory as direct subdirectory.
1138 * If yes, return the path of the snapshot-subdir,
1139 * otherwise return NULL.
1141 static char *have_snapdir(struct vfs_handle_struct *handle,
1144 struct smb_filename smb_fname;
1146 struct shadow_copy2_config *config;
1148 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1151 ZERO_STRUCT(smb_fname);
1152 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1153 path, config->snapdir);
1154 if (smb_fname.base_name == NULL) {
1158 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1159 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1160 return smb_fname.base_name;
1162 TALLOC_FREE(smb_fname.base_name);
1167 * Find the snapshot directory (if any) for the given
1168 * filename (which is relative to the share).
1170 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1171 struct vfs_handle_struct *handle,
1172 struct smb_filename *smb_fname)
1175 const char *snapdir;
1176 struct shadow_copy2_config *config;
1178 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1182 * If the non-snapdisrseverywhere mode, we should not search!
1184 if (!config->snapdirseverywhere) {
1185 return config->snapshot_basepath;
1188 path = talloc_asprintf(mem_ctx, "%s/%s",
1189 handle->conn->connectpath,
1190 smb_fname->base_name);
1195 snapdir = have_snapdir(handle, path);
1196 if (snapdir != NULL) {
1201 while ((p = strrchr(path, '/')) && (p > path)) {
1205 snapdir = have_snapdir(handle, path);
1206 if (snapdir != NULL) {
1215 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1217 char *gmt, size_t gmt_len)
1219 struct tm timestamp;
1221 unsigned long int timestamp_long;
1223 struct shadow_copy2_config *config;
1225 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1228 fmt = config->gmt_format;
1230 ZERO_STRUCT(timestamp);
1231 if (config->use_sscanf) {
1232 if (sscanf(name, fmt, ×tamp_long) != 1) {
1233 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1234 "no sscanf match %s: %s\n",
1238 timestamp_t = timestamp_long;
1239 gmtime_r(×tamp_t, ×tamp);
1241 if (strptime(name, fmt, ×tamp) == NULL) {
1242 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1243 "no match %s: %s\n",
1247 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1250 if (config->use_localtime) {
1251 timestamp.tm_isdst = -1;
1252 timestamp_t = mktime(×tamp);
1253 gmtime_r(×tamp_t, ×tamp);
1257 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1261 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1263 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1266 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1268 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1272 sort the shadow copy data in ascending or descending order
1274 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1275 struct shadow_copy_data *shadow_copy2_data)
1277 int (*cmpfunc)(const void *, const void *);
1279 struct shadow_copy2_config *config;
1281 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1284 sort = config->sort_order;
1289 if (strcmp(sort, "asc") == 0) {
1290 cmpfunc = shadow_copy2_label_cmp_asc;
1291 } else if (strcmp(sort, "desc") == 0) {
1292 cmpfunc = shadow_copy2_label_cmp_desc;
1297 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1298 shadow_copy2_data->labels)
1300 TYPESAFE_QSORT(shadow_copy2_data->labels,
1301 shadow_copy2_data->num_volumes,
1306 static int shadow_copy2_get_shadow_copy_data(
1307 vfs_handle_struct *handle, files_struct *fsp,
1308 struct shadow_copy_data *shadow_copy2_data,
1312 const char *snapdir;
1314 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1316 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1317 if (snapdir == NULL) {
1318 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1319 handle->conn->connectpath));
1321 talloc_free(tmp_ctx);
1325 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1328 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1329 " - %s\n", snapdir, strerror(errno)));
1330 talloc_free(tmp_ctx);
1335 shadow_copy2_data->num_volumes = 0;
1336 shadow_copy2_data->labels = NULL;
1338 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1339 char snapshot[GMT_NAME_LEN+1];
1340 SHADOW_COPY_LABEL *tlabels;
1343 * ignore names not of the right form in the snapshot
1346 if (!shadow_copy2_snapshot_to_gmt(
1348 snapshot, sizeof(snapshot))) {
1350 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1351 "ignoring %s\n", d->d_name));
1354 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1355 d->d_name, snapshot));
1358 /* the caller doesn't want the labels */
1359 shadow_copy2_data->num_volumes++;
1363 tlabels = talloc_realloc(shadow_copy2_data,
1364 shadow_copy2_data->labels,
1366 shadow_copy2_data->num_volumes+1);
1367 if (tlabels == NULL) {
1368 DEBUG(0,("shadow_copy2: out of memory\n"));
1369 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1370 talloc_free(tmp_ctx);
1374 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1377 shadow_copy2_data->num_volumes++;
1378 shadow_copy2_data->labels = tlabels;
1381 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1383 shadow_copy2_sort_data(handle, shadow_copy2_data);
1385 talloc_free(tmp_ctx);
1389 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1390 struct files_struct *fsp,
1391 uint32 security_info,
1392 TALLOC_CTX *mem_ctx,
1393 struct security_descriptor **ppdesc)
1400 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1401 fsp->fsp_name->base_name,
1402 ×tamp, &stripped)) {
1403 return map_nt_error_from_unix(errno);
1405 if (timestamp == 0) {
1406 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1410 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1411 TALLOC_FREE(stripped);
1413 return map_nt_error_from_unix(errno);
1415 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1421 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1423 uint32 security_info,
1424 TALLOC_CTX *mem_ctx,
1425 struct security_descriptor **ppdesc)
1432 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1433 ×tamp, &stripped)) {
1434 return map_nt_error_from_unix(errno);
1436 if (timestamp == 0) {
1437 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1440 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1441 TALLOC_FREE(stripped);
1443 return map_nt_error_from_unix(errno);
1445 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1451 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1452 const char *fname, mode_t mode)
1456 int ret, saved_errno;
1459 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1460 ×tamp, &stripped)) {
1463 if (timestamp == 0) {
1464 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1466 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1467 TALLOC_FREE(stripped);
1471 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1472 saved_errno = errno;
1474 errno = saved_errno;
1478 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1482 int ret, saved_errno;
1485 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1486 ×tamp, &stripped)) {
1489 if (timestamp == 0) {
1490 return SMB_VFS_NEXT_RMDIR(handle, fname);
1492 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1493 TALLOC_FREE(stripped);
1497 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1498 saved_errno = errno;
1500 errno = saved_errno;
1504 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1509 int ret, saved_errno;
1512 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1513 ×tamp, &stripped)) {
1516 if (timestamp == 0) {
1517 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1519 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1520 TALLOC_FREE(stripped);
1524 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1525 saved_errno = errno;
1527 errno = saved_errno;
1531 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1532 const char *fname, const char *aname,
1533 void *value, size_t size)
1541 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1542 ×tamp, &stripped)) {
1545 if (timestamp == 0) {
1546 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1549 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1550 TALLOC_FREE(stripped);
1554 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1555 saved_errno = errno;
1557 errno = saved_errno;
1561 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1563 char *list, size_t size)
1571 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1572 ×tamp, &stripped)) {
1575 if (timestamp == 0) {
1576 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1578 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1579 TALLOC_FREE(stripped);
1583 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1584 saved_errno = errno;
1586 errno = saved_errno;
1590 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1591 const char *fname, const char *aname)
1595 int ret, saved_errno;
1598 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1599 ×tamp, &stripped)) {
1602 if (timestamp == 0) {
1603 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1605 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1606 TALLOC_FREE(stripped);
1610 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1611 saved_errno = errno;
1613 errno = saved_errno;
1617 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1619 const char *aname, const void *value,
1620 size_t size, int flags)
1628 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1629 ×tamp, &stripped)) {
1632 if (timestamp == 0) {
1633 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1636 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1637 TALLOC_FREE(stripped);
1641 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1642 saved_errno = errno;
1644 errno = saved_errno;
1648 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1649 const char *fname, mode_t mode)
1657 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1658 ×tamp, &stripped)) {
1661 if (timestamp == 0) {
1662 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1664 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1665 TALLOC_FREE(stripped);
1669 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1670 saved_errno = errno;
1672 errno = saved_errno;
1676 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1679 TALLOC_CTX *mem_ctx,
1688 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1689 ×tamp, &stripped)) {
1692 if (timestamp == 0) {
1693 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1694 mem_ctx, found_name);
1696 if (stripped[0] == '\0') {
1697 *found_name = talloc_strdup(mem_ctx, name);
1698 if (*found_name == NULL) {
1704 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1705 TALLOC_FREE(stripped);
1709 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1710 mem_ctx, found_name);
1711 saved_errno = errno;
1713 errno = saved_errno;
1717 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1718 const char *path, bool small_query,
1719 uint64_t *bsize, uint64_t *dfree,
1728 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1729 ×tamp, &stripped)) {
1732 if (timestamp == 0) {
1733 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1734 bsize, dfree, dsize);
1737 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1738 TALLOC_FREE(stripped);
1743 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1746 saved_errno = errno;
1748 errno = saved_errno;
1753 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1754 const char *service, const char *user)
1756 struct shadow_copy2_config *config;
1758 const char *snapdir;
1759 const char *gmt_format;
1760 const char *sort_order;
1761 const char *basedir;
1762 const char *mount_point;
1764 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1765 (unsigned)handle->conn->cnum,
1766 handle->conn->connectpath));
1768 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1773 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1774 if (config == NULL) {
1775 DEBUG(0, ("talloc_zero() failed\n"));
1780 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1783 config->gmt_format = talloc_strdup(config, gmt_format);
1784 if (config->gmt_format == NULL) {
1785 DEBUG(0, ("talloc_strdup() failed\n"));
1790 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1791 "shadow", "sscanf", false);
1793 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1794 "shadow", "localtime",
1797 snapdir = lp_parm_const_string(SNUM(handle->conn),
1798 "shadow", "snapdir",
1800 config->snapdir = talloc_strdup(config, snapdir);
1801 if (config->snapdir == NULL) {
1802 DEBUG(0, ("talloc_strdup() failed\n"));
1807 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1809 "snapdirseverywhere",
1812 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1813 "shadow", "crossmountpoints",
1816 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1817 "shadow", "fixinodes",
1820 sort_order = lp_parm_const_string(SNUM(handle->conn),
1821 "shadow", "sort", "desc");
1822 config->sort_order = talloc_strdup(config, sort_order);
1823 if (config->sort_order == NULL) {
1824 DEBUG(0, ("talloc_strdup() failed\n"));
1829 mount_point = lp_parm_const_string(SNUM(handle->conn),
1830 "shadow", "mountpoint", NULL);
1831 if (mount_point != NULL) {
1832 if (mount_point[0] != '/') {
1833 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1834 "relative ('%s'), but it has to be an "
1835 "absolute path. Ignoring provided value.\n",
1840 p = strstr(handle->conn->connectpath, mount_point);
1841 if (p != handle->conn->connectpath) {
1842 DEBUG(1, ("Warning: mount_point (%s) is not a "
1843 "subdirectory of the share root "
1844 "(%s). Ignoring provided value.\n",
1846 handle->conn->connectpath));
1852 if (mount_point != NULL) {
1853 config->mount_point = talloc_strdup(config, mount_point);
1854 if (config->mount_point == NULL) {
1855 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1859 config->mount_point = shadow_copy2_find_mount_point(config,
1861 if (config->mount_point == NULL) {
1862 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1863 " failed: %s\n", strerror(errno)));
1868 basedir = lp_parm_const_string(SNUM(handle->conn),
1869 "shadow", "basedir", NULL);
1871 if (basedir != NULL) {
1872 if (basedir[0] != '/') {
1873 DEBUG(1, (__location__ " Warning: 'basedir' is "
1874 "relative ('%s'), but it has to be an "
1875 "absolute path. Disabling basedir.\n",
1879 p = strstr(basedir, config->mount_point);
1881 DEBUG(1, ("Warning: basedir (%s) is not a "
1882 "subdirectory of the share root's "
1883 "mount point (%s). "
1884 "Disabling basedir\n",
1885 basedir, config->mount_point));
1887 config->basedir = talloc_strdup(config,
1889 if (config->basedir == NULL) {
1890 DEBUG(0, ("talloc_strdup() failed\n"));
1898 if (config->snapdirseverywhere && config->basedir != NULL) {
1899 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1900 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1901 TALLOC_FREE(config->basedir);
1904 if (config->crossmountpoints && config->basedir != NULL) {
1905 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1906 "with 'crossmountpoints'. Disabling basedir.\n"));
1907 TALLOC_FREE(config->basedir);
1910 if (config->basedir == NULL) {
1911 config->basedir = config->mount_point;
1914 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1915 config->rel_connectpath = talloc_strdup(config,
1916 handle->conn->connectpath + strlen(config->basedir));
1917 if (config->rel_connectpath == NULL) {
1918 DEBUG(0, ("talloc_strdup() failed\n"));
1924 if (config->snapdir[0] == '/') {
1925 config->snapdir_absolute = true;
1927 if (config->snapdirseverywhere == true) {
1928 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1929 "is incompatible with 'snapdirseverywhere', "
1930 "setting 'snapdirseverywhere' to false.\n"));
1931 config->snapdirseverywhere = false;
1934 if (config->crossmountpoints == true) {
1935 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1936 "is not supported with an absolute snapdir. "
1937 "Disabling it.\n"));
1938 config->crossmountpoints = false;
1941 config->snapshot_basepath = config->snapdir;
1943 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1944 config->mount_point, config->snapdir);
1945 if (config->snapshot_basepath == NULL) {
1946 DEBUG(0, ("talloc_asprintf() failed\n"));
1952 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1953 " share root: '%s'\n"
1955 " mountpoint: '%s'\n"
1956 " rel share root: '%s'\n"
1958 " snapshot base path: '%s'\n"
1961 " snapdirs everywhere: %s\n"
1962 " cross mountpoints: %s\n"
1966 handle->conn->connectpath,
1968 config->mount_point,
1969 config->rel_connectpath,
1971 config->snapshot_basepath,
1973 config->use_sscanf ? "yes" : "no",
1974 config->snapdirseverywhere ? "yes" : "no",
1975 config->crossmountpoints ? "yes" : "no",
1976 config->fixinodes ? "yes" : "no",
1981 SMB_VFS_HANDLE_SET_DATA(handle, config,
1982 NULL, struct shadow_copy2_config,
1988 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1989 .connect_fn = shadow_copy2_connect,
1990 .opendir_fn = shadow_copy2_opendir,
1991 .disk_free_fn = shadow_copy2_disk_free,
1992 .rename_fn = shadow_copy2_rename,
1993 .link_fn = shadow_copy2_link,
1994 .symlink_fn = shadow_copy2_symlink,
1995 .stat_fn = shadow_copy2_stat,
1996 .lstat_fn = shadow_copy2_lstat,
1997 .fstat_fn = shadow_copy2_fstat,
1998 .open_fn = shadow_copy2_open,
1999 .unlink_fn = shadow_copy2_unlink,
2000 .chmod_fn = shadow_copy2_chmod,
2001 .chown_fn = shadow_copy2_chown,
2002 .chdir_fn = shadow_copy2_chdir,
2003 .ntimes_fn = shadow_copy2_ntimes,
2004 .readlink_fn = shadow_copy2_readlink,
2005 .mknod_fn = shadow_copy2_mknod,
2006 .realpath_fn = shadow_copy2_realpath,
2007 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2008 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2009 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2010 .mkdir_fn = shadow_copy2_mkdir,
2011 .rmdir_fn = shadow_copy2_rmdir,
2012 .getxattr_fn = shadow_copy2_getxattr,
2013 .listxattr_fn = shadow_copy2_listxattr,
2014 .removexattr_fn = shadow_copy2_removexattr,
2015 .setxattr_fn = shadow_copy2_setxattr,
2016 .chmod_acl_fn = shadow_copy2_chmod_acl,
2017 .chflags_fn = shadow_copy2_chflags,
2018 .get_real_filename_fn = shadow_copy2_get_real_filename,
2021 NTSTATUS vfs_shadow_copy2_init(void);
2022 NTSTATUS vfs_shadow_copy2_init(void)
2024 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2025 "shadow_copy2", &vfs_shadow_copy2_fns);