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)) {
508 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
509 if (insert == NULL) {
512 insertlen = talloc_get_size(insert)-1;
514 converted = talloc_zero_array(mem_ctx, char, pathlen + insertlen + 1);
515 if (converted == NULL) {
519 if (path[pathlen-1] != '/') {
521 * Append a fake slash to find the snapshot root
524 tmp = talloc_realloc(talloc_tos(), slashes,
525 size_t, num_slashes+1);
530 slashes[num_slashes] = pathlen;
536 if (!config->crossmountpoints) {
539 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
541 if (mount_point == NULL) {
544 min_offset = strlen(mount_point);
545 TALLOC_FREE(mount_point);
548 memcpy(converted, path, pathlen+1);
549 converted[pathlen+insertlen] = '\0';
551 ZERO_STRUCT(converted_fname);
552 converted_fname.base_name = converted;
554 for (i = num_slashes-1; i>=0; i--) {
560 if (offset < min_offset) {
565 memcpy(converted+offset, insert, insertlen);
568 memcpy(converted+offset, path + slashes[i],
569 pathlen - slashes[i]);
571 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
573 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
574 ret, ret == 0 ? "ok" : strerror(errno)));
579 if (errno == ENOTDIR) {
581 * This is a valid condition: We appended the
582 * .snaphots/@GMT.. to a file name. Just try
583 * with the upper levels.
587 if (errno != ENOENT) {
588 /* Other problem than "not found" */
597 DEBUG(10, ("Found %s\n", converted));
605 TALLOC_FREE(converted);
607 TALLOC_FREE(slashes);
614 modify a sbuf return to ensure that inodes in the shadow directory
615 are different from those in the main directory
617 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
618 SMB_STRUCT_STAT *sbuf)
620 struct shadow_copy2_config *config;
622 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
625 if (config->fixinodes) {
626 /* some snapshot systems, like GPFS, return the name
627 device:inode for the snapshot files as the current
628 files. That breaks the 'restore' button in the shadow copy
629 GUI, as the client gets a sharing violation.
631 This is a crude way of allowing both files to be
632 open at once. It has a slight chance of inode
633 number collision, but I can't see a better approach
634 without significant VFS changes
638 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
642 sbuf->st_ex_ino ^= shash;
646 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
657 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
658 ×tamp, &stripped)) {
661 if (timestamp == 0) {
662 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
664 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
665 TALLOC_FREE(stripped);
669 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
676 static int shadow_copy2_rename(vfs_handle_struct *handle,
677 const struct smb_filename *smb_fname_src,
678 const struct smb_filename *smb_fname_dst)
680 time_t timestamp_src, timestamp_dst;
682 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
683 smb_fname_src->base_name,
684 ×tamp_src, NULL)) {
687 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
688 smb_fname_dst->base_name,
689 ×tamp_dst, NULL)) {
692 if (timestamp_src != 0) {
696 if (timestamp_dst != 0) {
700 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
703 static int shadow_copy2_symlink(vfs_handle_struct *handle,
704 const char *oldname, const char *newname)
706 time_t timestamp_old, timestamp_new;
708 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
709 ×tamp_old, NULL)) {
712 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
713 ×tamp_new, NULL)) {
716 if ((timestamp_old != 0) || (timestamp_new != 0)) {
720 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
723 static int shadow_copy2_link(vfs_handle_struct *handle,
724 const char *oldname, const char *newname)
726 time_t timestamp_old, timestamp_new;
728 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
729 ×tamp_old, NULL)) {
732 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
733 ×tamp_new, NULL)) {
736 if ((timestamp_old != 0) || (timestamp_new != 0)) {
740 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
743 static int shadow_copy2_stat(vfs_handle_struct *handle,
744 struct smb_filename *smb_fname)
747 char *stripped, *tmp;
748 int ret, saved_errno;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
751 smb_fname->base_name,
752 ×tamp, &stripped)) {
755 if (timestamp == 0) {
756 return SMB_VFS_NEXT_STAT(handle, smb_fname);
759 tmp = smb_fname->base_name;
760 smb_fname->base_name = shadow_copy2_convert(
761 talloc_tos(), handle, stripped, timestamp);
762 TALLOC_FREE(stripped);
764 if (smb_fname->base_name == NULL) {
765 smb_fname->base_name = tmp;
769 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
772 TALLOC_FREE(smb_fname->base_name);
773 smb_fname->base_name = tmp;
776 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
782 static int shadow_copy2_lstat(vfs_handle_struct *handle,
783 struct smb_filename *smb_fname)
786 char *stripped, *tmp;
787 int ret, saved_errno;
789 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
790 smb_fname->base_name,
791 ×tamp, &stripped)) {
794 if (timestamp == 0) {
795 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
798 tmp = smb_fname->base_name;
799 smb_fname->base_name = shadow_copy2_convert(
800 talloc_tos(), handle, stripped, timestamp);
801 TALLOC_FREE(stripped);
803 if (smb_fname->base_name == NULL) {
804 smb_fname->base_name = tmp;
808 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
811 TALLOC_FREE(smb_fname->base_name);
812 smb_fname->base_name = tmp;
815 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
821 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
822 SMB_STRUCT_STAT *sbuf)
827 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
831 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
832 fsp->fsp_name->base_name,
836 if (timestamp != 0) {
837 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
842 static int shadow_copy2_open(vfs_handle_struct *handle,
843 struct smb_filename *smb_fname, files_struct *fsp,
844 int flags, mode_t mode)
847 char *stripped, *tmp;
848 int ret, saved_errno;
850 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
851 smb_fname->base_name,
852 ×tamp, &stripped)) {
855 if (timestamp == 0) {
856 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
859 tmp = smb_fname->base_name;
860 smb_fname->base_name = shadow_copy2_convert(
861 talloc_tos(), handle, stripped, timestamp);
862 TALLOC_FREE(stripped);
864 if (smb_fname->base_name == NULL) {
865 smb_fname->base_name = tmp;
869 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
872 TALLOC_FREE(smb_fname->base_name);
873 smb_fname->base_name = tmp;
879 static int shadow_copy2_unlink(vfs_handle_struct *handle,
880 const struct smb_filename *smb_fname)
884 int ret, saved_errno;
885 struct smb_filename *conv;
887 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
888 smb_fname->base_name,
889 ×tamp, &stripped)) {
892 if (timestamp == 0) {
893 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
895 conv = cp_smb_filename(talloc_tos(), smb_fname);
900 conv->base_name = shadow_copy2_convert(
901 conv, handle, stripped, timestamp);
902 TALLOC_FREE(stripped);
903 if (conv->base_name == NULL) {
906 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
913 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
918 int ret, saved_errno;
921 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
922 ×tamp, &stripped)) {
925 if (timestamp == 0) {
926 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
928 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
929 TALLOC_FREE(stripped);
933 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
940 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
941 uid_t uid, gid_t gid)
945 int ret, saved_errno;
948 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
949 ×tamp, &stripped)) {
952 if (timestamp == 0) {
953 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
955 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
956 TALLOC_FREE(stripped);
960 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
967 static int shadow_copy2_chdir(vfs_handle_struct *handle,
972 int ret, saved_errno;
975 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
976 ×tamp, &stripped)) {
979 if (timestamp == 0) {
980 return SMB_VFS_NEXT_CHDIR(handle, fname);
982 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
983 TALLOC_FREE(stripped);
987 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
994 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
995 const struct smb_filename *smb_fname,
996 struct smb_file_time *ft)
1000 int ret, saved_errno;
1001 struct smb_filename *conv;
1003 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1004 smb_fname->base_name,
1005 ×tamp, &stripped)) {
1008 if (timestamp == 0) {
1009 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1011 conv = cp_smb_filename(talloc_tos(), smb_fname);
1016 conv->base_name = shadow_copy2_convert(
1017 conv, handle, stripped, timestamp);
1018 TALLOC_FREE(stripped);
1019 if (conv->base_name == NULL) {
1022 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1023 saved_errno = errno;
1025 errno = saved_errno;
1029 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1030 const char *fname, char *buf, size_t bufsiz)
1034 int ret, saved_errno;
1037 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1038 ×tamp, &stripped)) {
1041 if (timestamp == 0) {
1042 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1044 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1045 TALLOC_FREE(stripped);
1049 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1050 saved_errno = errno;
1052 errno = saved_errno;
1056 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1057 const char *fname, mode_t mode, SMB_DEV_T dev)
1061 int ret, saved_errno;
1064 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1065 ×tamp, &stripped)) {
1068 if (timestamp == 0) {
1069 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1071 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1072 TALLOC_FREE(stripped);
1076 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1077 saved_errno = errno;
1079 errno = saved_errno;
1083 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1087 char *stripped = NULL;
1089 char *result = NULL;
1090 char *inserted = NULL;
1091 char *inserted_to, *inserted_end;
1094 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1095 ×tamp, &stripped)) {
1098 if (timestamp == 0) {
1099 return SMB_VFS_NEXT_REALPATH(handle, fname);
1102 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1107 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1108 if (result == NULL) {
1113 * Take away what we've inserted. This removes the @GMT-thingy
1114 * completely, but will give a path under the share root.
1116 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1117 if (inserted == NULL) {
1120 inserted_to = strstr_m(result, inserted);
1121 if (inserted_to == NULL) {
1122 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1125 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1126 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1129 saved_errno = errno;
1130 TALLOC_FREE(inserted);
1132 TALLOC_FREE(stripped);
1133 errno = saved_errno;
1138 * Check whether a given directory contains a
1139 * snapshot directory as direct subdirectory.
1140 * If yes, return the path of the snapshot-subdir,
1141 * otherwise return NULL.
1143 static char *have_snapdir(struct vfs_handle_struct *handle,
1146 struct smb_filename smb_fname;
1148 struct shadow_copy2_config *config;
1150 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1153 ZERO_STRUCT(smb_fname);
1154 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1155 path, config->snapdir);
1156 if (smb_fname.base_name == NULL) {
1160 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1161 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1162 return smb_fname.base_name;
1164 TALLOC_FREE(smb_fname.base_name);
1169 * Find the snapshot directory (if any) for the given
1170 * filename (which is relative to the share).
1172 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1173 struct vfs_handle_struct *handle,
1174 struct smb_filename *smb_fname)
1177 const char *snapdir;
1178 struct shadow_copy2_config *config;
1180 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1184 * If the non-snapdisrseverywhere mode, we should not search!
1186 if (!config->snapdirseverywhere) {
1187 return config->snapshot_basepath;
1190 path = talloc_asprintf(mem_ctx, "%s/%s",
1191 handle->conn->connectpath,
1192 smb_fname->base_name);
1197 snapdir = have_snapdir(handle, path);
1198 if (snapdir != NULL) {
1203 while ((p = strrchr(path, '/')) && (p > path)) {
1207 snapdir = have_snapdir(handle, path);
1208 if (snapdir != NULL) {
1217 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1219 char *gmt, size_t gmt_len)
1221 struct tm timestamp;
1223 unsigned long int timestamp_long;
1225 struct shadow_copy2_config *config;
1227 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1230 fmt = config->gmt_format;
1232 ZERO_STRUCT(timestamp);
1233 if (config->use_sscanf) {
1234 if (sscanf(name, fmt, ×tamp_long) != 1) {
1235 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1236 "no sscanf match %s: %s\n",
1240 timestamp_t = timestamp_long;
1241 gmtime_r(×tamp_t, ×tamp);
1243 if (strptime(name, fmt, ×tamp) == NULL) {
1244 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1245 "no match %s: %s\n",
1249 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1252 if (config->use_localtime) {
1253 timestamp.tm_isdst = -1;
1254 timestamp_t = mktime(×tamp);
1255 gmtime_r(×tamp_t, ×tamp);
1259 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1263 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1265 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1268 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1270 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1274 sort the shadow copy data in ascending or descending order
1276 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1277 struct shadow_copy_data *shadow_copy2_data)
1279 int (*cmpfunc)(const void *, const void *);
1281 struct shadow_copy2_config *config;
1283 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1286 sort = config->sort_order;
1291 if (strcmp(sort, "asc") == 0) {
1292 cmpfunc = shadow_copy2_label_cmp_asc;
1293 } else if (strcmp(sort, "desc") == 0) {
1294 cmpfunc = shadow_copy2_label_cmp_desc;
1299 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1300 shadow_copy2_data->labels)
1302 TYPESAFE_QSORT(shadow_copy2_data->labels,
1303 shadow_copy2_data->num_volumes,
1308 static int shadow_copy2_get_shadow_copy_data(
1309 vfs_handle_struct *handle, files_struct *fsp,
1310 struct shadow_copy_data *shadow_copy2_data,
1314 const char *snapdir;
1316 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1318 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1319 if (snapdir == NULL) {
1320 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1321 handle->conn->connectpath));
1323 talloc_free(tmp_ctx);
1327 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1330 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1331 " - %s\n", snapdir, strerror(errno)));
1332 talloc_free(tmp_ctx);
1337 shadow_copy2_data->num_volumes = 0;
1338 shadow_copy2_data->labels = NULL;
1340 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1341 char snapshot[GMT_NAME_LEN+1];
1342 SHADOW_COPY_LABEL *tlabels;
1345 * ignore names not of the right form in the snapshot
1348 if (!shadow_copy2_snapshot_to_gmt(
1350 snapshot, sizeof(snapshot))) {
1352 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1353 "ignoring %s\n", d->d_name));
1356 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1357 d->d_name, snapshot));
1360 /* the caller doesn't want the labels */
1361 shadow_copy2_data->num_volumes++;
1365 tlabels = talloc_realloc(shadow_copy2_data,
1366 shadow_copy2_data->labels,
1368 shadow_copy2_data->num_volumes+1);
1369 if (tlabels == NULL) {
1370 DEBUG(0,("shadow_copy2: out of memory\n"));
1371 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1372 talloc_free(tmp_ctx);
1376 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1379 shadow_copy2_data->num_volumes++;
1380 shadow_copy2_data->labels = tlabels;
1383 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1385 shadow_copy2_sort_data(handle, shadow_copy2_data);
1387 talloc_free(tmp_ctx);
1391 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1392 struct files_struct *fsp,
1393 uint32 security_info,
1394 TALLOC_CTX *mem_ctx,
1395 struct security_descriptor **ppdesc)
1402 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1403 fsp->fsp_name->base_name,
1404 ×tamp, &stripped)) {
1405 return map_nt_error_from_unix(errno);
1407 if (timestamp == 0) {
1408 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1412 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1413 TALLOC_FREE(stripped);
1415 return map_nt_error_from_unix(errno);
1417 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1423 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1425 uint32 security_info,
1426 TALLOC_CTX *mem_ctx,
1427 struct security_descriptor **ppdesc)
1434 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1435 ×tamp, &stripped)) {
1436 return map_nt_error_from_unix(errno);
1438 if (timestamp == 0) {
1439 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1442 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1443 TALLOC_FREE(stripped);
1445 return map_nt_error_from_unix(errno);
1447 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1453 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1454 const char *fname, mode_t mode)
1458 int ret, saved_errno;
1461 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1462 ×tamp, &stripped)) {
1465 if (timestamp == 0) {
1466 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1468 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1469 TALLOC_FREE(stripped);
1473 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1474 saved_errno = errno;
1476 errno = saved_errno;
1480 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1484 int ret, saved_errno;
1487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1488 ×tamp, &stripped)) {
1491 if (timestamp == 0) {
1492 return SMB_VFS_NEXT_RMDIR(handle, fname);
1494 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1495 TALLOC_FREE(stripped);
1499 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1500 saved_errno = errno;
1502 errno = saved_errno;
1506 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1511 int ret, saved_errno;
1514 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1515 ×tamp, &stripped)) {
1518 if (timestamp == 0) {
1519 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1521 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1522 TALLOC_FREE(stripped);
1526 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1527 saved_errno = errno;
1529 errno = saved_errno;
1533 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1534 const char *fname, const char *aname,
1535 void *value, size_t size)
1543 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1544 ×tamp, &stripped)) {
1547 if (timestamp == 0) {
1548 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1551 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1552 TALLOC_FREE(stripped);
1556 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1557 saved_errno = errno;
1559 errno = saved_errno;
1563 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1565 char *list, size_t size)
1573 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1574 ×tamp, &stripped)) {
1577 if (timestamp == 0) {
1578 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1580 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1581 TALLOC_FREE(stripped);
1585 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1586 saved_errno = errno;
1588 errno = saved_errno;
1592 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1593 const char *fname, const char *aname)
1597 int ret, saved_errno;
1600 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1601 ×tamp, &stripped)) {
1604 if (timestamp == 0) {
1605 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1607 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1608 TALLOC_FREE(stripped);
1612 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1613 saved_errno = errno;
1615 errno = saved_errno;
1619 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1621 const char *aname, const void *value,
1622 size_t size, int flags)
1630 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1631 ×tamp, &stripped)) {
1634 if (timestamp == 0) {
1635 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1638 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1639 TALLOC_FREE(stripped);
1643 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1644 saved_errno = errno;
1646 errno = saved_errno;
1650 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1651 const char *fname, mode_t mode)
1659 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1660 ×tamp, &stripped)) {
1663 if (timestamp == 0) {
1664 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1666 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1667 TALLOC_FREE(stripped);
1671 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1672 saved_errno = errno;
1674 errno = saved_errno;
1678 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1681 TALLOC_CTX *mem_ctx,
1690 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1691 ×tamp, &stripped)) {
1694 if (timestamp == 0) {
1695 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1696 mem_ctx, found_name);
1698 if (stripped[0] == '\0') {
1699 *found_name = talloc_strdup(mem_ctx, name);
1700 if (*found_name == NULL) {
1706 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1707 TALLOC_FREE(stripped);
1711 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1712 mem_ctx, found_name);
1713 saved_errno = errno;
1715 errno = saved_errno;
1719 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1720 const char *path, bool small_query,
1721 uint64_t *bsize, uint64_t *dfree,
1730 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1731 ×tamp, &stripped)) {
1734 if (timestamp == 0) {
1735 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1736 bsize, dfree, dsize);
1739 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1740 TALLOC_FREE(stripped);
1745 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1748 saved_errno = errno;
1750 errno = saved_errno;
1755 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1756 const char *service, const char *user)
1758 struct shadow_copy2_config *config;
1760 const char *snapdir;
1761 const char *gmt_format;
1762 const char *sort_order;
1763 const char *basedir;
1764 const char *mount_point;
1766 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1767 (unsigned)handle->conn->cnum,
1768 handle->conn->connectpath));
1770 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1775 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1776 if (config == NULL) {
1777 DEBUG(0, ("talloc_zero() failed\n"));
1782 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1785 config->gmt_format = talloc_strdup(config, gmt_format);
1786 if (config->gmt_format == NULL) {
1787 DEBUG(0, ("talloc_strdup() failed\n"));
1792 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1793 "shadow", "sscanf", false);
1795 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1796 "shadow", "localtime",
1799 snapdir = lp_parm_const_string(SNUM(handle->conn),
1800 "shadow", "snapdir",
1802 config->snapdir = talloc_strdup(config, snapdir);
1803 if (config->snapdir == NULL) {
1804 DEBUG(0, ("talloc_strdup() failed\n"));
1809 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1811 "snapdirseverywhere",
1814 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1815 "shadow", "crossmountpoints",
1818 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1819 "shadow", "fixinodes",
1822 sort_order = lp_parm_const_string(SNUM(handle->conn),
1823 "shadow", "sort", "desc");
1824 config->sort_order = talloc_strdup(config, sort_order);
1825 if (config->sort_order == NULL) {
1826 DEBUG(0, ("talloc_strdup() failed\n"));
1831 mount_point = lp_parm_const_string(SNUM(handle->conn),
1832 "shadow", "mountpoint", NULL);
1833 if (mount_point != NULL) {
1834 if (mount_point[0] != '/') {
1835 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1836 "relative ('%s'), but it has to be an "
1837 "absolute path. Ignoring provided value.\n",
1842 p = strstr(handle->conn->connectpath, mount_point);
1843 if (p != handle->conn->connectpath) {
1844 DEBUG(1, ("Warning: mount_point (%s) is not a "
1845 "subdirectory of the share root "
1846 "(%s). Ignoring provided value.\n",
1848 handle->conn->connectpath));
1854 if (mount_point != NULL) {
1855 config->mount_point = talloc_strdup(config, mount_point);
1856 if (config->mount_point == NULL) {
1857 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1861 config->mount_point = shadow_copy2_find_mount_point(config,
1863 if (config->mount_point == NULL) {
1864 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1865 " failed: %s\n", strerror(errno)));
1870 basedir = lp_parm_const_string(SNUM(handle->conn),
1871 "shadow", "basedir", NULL);
1873 if (basedir != NULL) {
1874 if (basedir[0] != '/') {
1875 DEBUG(1, (__location__ " Warning: 'basedir' is "
1876 "relative ('%s'), but it has to be an "
1877 "absolute path. Disabling basedir.\n",
1881 p = strstr(basedir, config->mount_point);
1883 DEBUG(1, ("Warning: basedir (%s) is not a "
1884 "subdirectory of the share root's "
1885 "mount point (%s). "
1886 "Disabling basedir\n",
1887 basedir, config->mount_point));
1889 config->basedir = talloc_strdup(config,
1891 if (config->basedir == NULL) {
1892 DEBUG(0, ("talloc_strdup() failed\n"));
1900 if (config->snapdirseverywhere && config->basedir != NULL) {
1901 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1902 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1903 TALLOC_FREE(config->basedir);
1906 if (config->crossmountpoints && config->basedir != NULL) {
1907 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1908 "with 'crossmountpoints'. Disabling basedir.\n"));
1909 TALLOC_FREE(config->basedir);
1912 if (config->basedir == NULL) {
1913 config->basedir = config->mount_point;
1916 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1917 config->rel_connectpath = talloc_strdup(config,
1918 handle->conn->connectpath + strlen(config->basedir));
1919 if (config->rel_connectpath == NULL) {
1920 DEBUG(0, ("talloc_strdup() failed\n"));
1926 if (config->snapdir[0] == '/') {
1927 config->snapdir_absolute = true;
1929 if (config->snapdirseverywhere == true) {
1930 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1931 "is incompatible with 'snapdirseverywhere', "
1932 "setting 'snapdirseverywhere' to false.\n"));
1933 config->snapdirseverywhere = false;
1936 if (config->crossmountpoints == true) {
1937 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1938 "is not supported with an absolute snapdir. "
1939 "Disabling it.\n"));
1940 config->crossmountpoints = false;
1943 config->snapshot_basepath = config->snapdir;
1945 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1946 config->mount_point, config->snapdir);
1947 if (config->snapshot_basepath == NULL) {
1948 DEBUG(0, ("talloc_asprintf() failed\n"));
1954 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1955 " share root: '%s'\n"
1957 " mountpoint: '%s'\n"
1958 " rel share root: '%s'\n"
1960 " snapshot base path: '%s'\n"
1963 " snapdirs everywhere: %s\n"
1964 " cross mountpoints: %s\n"
1968 handle->conn->connectpath,
1970 config->mount_point,
1971 config->rel_connectpath,
1973 config->snapshot_basepath,
1975 config->use_sscanf ? "yes" : "no",
1976 config->snapdirseverywhere ? "yes" : "no",
1977 config->crossmountpoints ? "yes" : "no",
1978 config->fixinodes ? "yes" : "no",
1983 SMB_VFS_HANDLE_SET_DATA(handle, config,
1984 NULL, struct shadow_copy2_config,
1990 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1991 .connect_fn = shadow_copy2_connect,
1992 .opendir_fn = shadow_copy2_opendir,
1993 .disk_free_fn = shadow_copy2_disk_free,
1994 .rename_fn = shadow_copy2_rename,
1995 .link_fn = shadow_copy2_link,
1996 .symlink_fn = shadow_copy2_symlink,
1997 .stat_fn = shadow_copy2_stat,
1998 .lstat_fn = shadow_copy2_lstat,
1999 .fstat_fn = shadow_copy2_fstat,
2000 .open_fn = shadow_copy2_open,
2001 .unlink_fn = shadow_copy2_unlink,
2002 .chmod_fn = shadow_copy2_chmod,
2003 .chown_fn = shadow_copy2_chown,
2004 .chdir_fn = shadow_copy2_chdir,
2005 .ntimes_fn = shadow_copy2_ntimes,
2006 .readlink_fn = shadow_copy2_readlink,
2007 .mknod_fn = shadow_copy2_mknod,
2008 .realpath_fn = shadow_copy2_realpath,
2009 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2010 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2011 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2012 .mkdir_fn = shadow_copy2_mkdir,
2013 .rmdir_fn = shadow_copy2_rmdir,
2014 .getxattr_fn = shadow_copy2_getxattr,
2015 .listxattr_fn = shadow_copy2_listxattr,
2016 .removexattr_fn = shadow_copy2_removexattr,
2017 .setxattr_fn = shadow_copy2_setxattr,
2018 .chmod_acl_fn = shadow_copy2_chmod_acl,
2019 .chflags_fn = shadow_copy2_chflags,
2020 .get_real_filename_fn = shadow_copy2_get_real_filename,
2023 NTSTATUS vfs_shadow_copy2_init(void);
2024 NTSTATUS vfs_shadow_copy2_init(void)
2026 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2027 "shadow_copy2", &vfs_shadow_copy2_fns);