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 * Strip a snapshot component from an filename as
257 * handed in via the smb layer.
258 * Returns the parsed timestamp and the stripped filename.
260 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
261 struct vfs_handle_struct *handle,
271 size_t rest_len, dst_len;
272 struct shadow_copy2_config *config;
274 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
277 p = strstr_m(name, "@GMT-");
281 if ((p > name) && (p[-1] != '/')) {
284 q = strptime(p, GMT_FORMAT, &tm);
289 timestamp = timegm(&tm);
290 if (timestamp == (time_t)-1) {
293 if ((p == name) && (q[0] == '\0')) {
294 if (pstripped != NULL) {
295 stripped = talloc_strdup(mem_ctx, "");
296 if (stripped == NULL) {
299 *pstripped = stripped;
301 *ptimestamp = timestamp;
309 rest_len = strlen(q);
310 dst_len = (p-name) + rest_len;
312 if (config->snapdirseverywhere) {
315 insert = shadow_copy2_insert_string(talloc_tos(), handle,
317 if (insert == NULL) {
322 have_insert = (strstr(name, insert+1) != NULL);
329 if (pstripped != NULL) {
330 stripped = talloc_array(mem_ctx, char, dst_len+1);
331 if (stripped == NULL) {
336 memcpy(stripped, name, p-name);
339 memcpy(stripped + (p-name), q, rest_len);
341 stripped[dst_len] = '\0';
342 *pstripped = stripped;
344 *ptimestamp = timestamp;
351 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
352 vfs_handle_struct *handle)
354 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
359 if (stat(path, &st) != 0) {
366 while ((p = strrchr(path, '/')) && p > path) {
368 if (stat(path, &st) != 0) {
372 if (st.st_dev != dev) {
382 * Convert from a name as handed in via the SMB layer
383 * and a timestamp into the local path of the snapshot
384 * of the provided file at the provided time.
386 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
387 struct vfs_handle_struct *handle,
388 const char *name, time_t timestamp)
390 struct smb_filename converted_fname;
392 size_t *slashes = NULL;
393 unsigned num_slashes;
397 char *converted = NULL;
401 struct shadow_copy2_config *config;
403 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
406 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
412 pathlen = talloc_get_size(path)-1;
414 DEBUG(10, ("converting %s\n", path));
416 if (!shadow_copy2_find_slashes(talloc_tos(), path,
417 &slashes, &num_slashes)) {
420 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
421 if (insert == NULL) {
424 insertlen = talloc_get_size(insert)-1;
425 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
426 if (converted == NULL) {
430 if (path[pathlen-1] != '/') {
432 * Append a fake slash to find the snapshot root
435 tmp = talloc_realloc(talloc_tos(), slashes,
436 size_t, num_slashes+1);
441 slashes[num_slashes] = pathlen;
447 if (!config->crossmountpoints) {
450 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
452 if (mount_point == NULL) {
455 min_offset = strlen(mount_point);
456 TALLOC_FREE(mount_point);
459 memcpy(converted, path, pathlen+1);
460 converted[pathlen+insertlen] = '\0';
462 ZERO_STRUCT(converted_fname);
463 converted_fname.base_name = converted;
465 for (i = num_slashes-1; i>=0; i--) {
471 if (offset < min_offset) {
476 memcpy(converted+offset, insert, insertlen);
479 memcpy(converted+offset, path + slashes[i],
480 pathlen - slashes[i]);
482 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
484 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
485 ret, ret == 0 ? "ok" : strerror(errno)));
490 if (errno == ENOTDIR) {
492 * This is a valid condition: We appended the
493 * .snaphots/@GMT.. to a file name. Just try
494 * with the upper levels.
498 if (errno != ENOENT) {
499 /* Other problem than "not found" */
508 DEBUG(10, ("Found %s\n", converted));
516 TALLOC_FREE(converted);
518 TALLOC_FREE(slashes);
525 modify a sbuf return to ensure that inodes in the shadow directory
526 are different from those in the main directory
528 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
529 SMB_STRUCT_STAT *sbuf)
531 struct shadow_copy2_config *config;
533 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
536 if (config->fixinodes) {
537 /* some snapshot systems, like GPFS, return the name
538 device:inode for the snapshot files as the current
539 files. That breaks the 'restore' button in the shadow copy
540 GUI, as the client gets a sharing violation.
542 This is a crude way of allowing both files to be
543 open at once. It has a slight chance of inode
544 number collision, but I can't see a better approach
545 without significant VFS changes
549 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
553 sbuf->st_ex_ino ^= shash;
557 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
568 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
569 ×tamp, &stripped)) {
572 if (timestamp == 0) {
573 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
575 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
576 TALLOC_FREE(stripped);
580 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
587 static int shadow_copy2_rename(vfs_handle_struct *handle,
588 const struct smb_filename *smb_fname_src,
589 const struct smb_filename *smb_fname_dst)
591 time_t timestamp_src, timestamp_dst;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
594 smb_fname_src->base_name,
595 ×tamp_src, NULL)) {
598 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
599 smb_fname_dst->base_name,
600 ×tamp_dst, NULL)) {
603 if (timestamp_src != 0) {
607 if (timestamp_dst != 0) {
611 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
614 static int shadow_copy2_symlink(vfs_handle_struct *handle,
615 const char *oldname, const char *newname)
617 time_t timestamp_old, timestamp_new;
619 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
620 ×tamp_old, NULL)) {
623 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
624 ×tamp_new, NULL)) {
627 if ((timestamp_old != 0) || (timestamp_new != 0)) {
631 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
634 static int shadow_copy2_link(vfs_handle_struct *handle,
635 const char *oldname, const char *newname)
637 time_t timestamp_old, timestamp_new;
639 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
640 ×tamp_old, NULL)) {
643 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
644 ×tamp_new, NULL)) {
647 if ((timestamp_old != 0) || (timestamp_new != 0)) {
651 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
654 static int shadow_copy2_stat(vfs_handle_struct *handle,
655 struct smb_filename *smb_fname)
658 char *stripped, *tmp;
659 int ret, saved_errno;
661 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
662 smb_fname->base_name,
663 ×tamp, &stripped)) {
666 if (timestamp == 0) {
667 return SMB_VFS_NEXT_STAT(handle, smb_fname);
670 tmp = smb_fname->base_name;
671 smb_fname->base_name = shadow_copy2_convert(
672 talloc_tos(), handle, stripped, timestamp);
673 TALLOC_FREE(stripped);
675 if (smb_fname->base_name == NULL) {
676 smb_fname->base_name = tmp;
680 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
683 TALLOC_FREE(smb_fname->base_name);
684 smb_fname->base_name = tmp;
687 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
693 static int shadow_copy2_lstat(vfs_handle_struct *handle,
694 struct smb_filename *smb_fname)
697 char *stripped, *tmp;
698 int ret, saved_errno;
700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
701 smb_fname->base_name,
702 ×tamp, &stripped)) {
705 if (timestamp == 0) {
706 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
709 tmp = smb_fname->base_name;
710 smb_fname->base_name = shadow_copy2_convert(
711 talloc_tos(), handle, stripped, timestamp);
712 TALLOC_FREE(stripped);
714 if (smb_fname->base_name == NULL) {
715 smb_fname->base_name = tmp;
719 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
722 TALLOC_FREE(smb_fname->base_name);
723 smb_fname->base_name = tmp;
726 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
732 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
733 SMB_STRUCT_STAT *sbuf)
738 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
742 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
743 fsp->fsp_name->base_name,
747 if (timestamp != 0) {
748 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
753 static int shadow_copy2_open(vfs_handle_struct *handle,
754 struct smb_filename *smb_fname, files_struct *fsp,
755 int flags, mode_t mode)
758 char *stripped, *tmp;
759 int ret, saved_errno;
761 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
762 smb_fname->base_name,
763 ×tamp, &stripped)) {
766 if (timestamp == 0) {
767 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
770 tmp = smb_fname->base_name;
771 smb_fname->base_name = shadow_copy2_convert(
772 talloc_tos(), handle, stripped, timestamp);
773 TALLOC_FREE(stripped);
775 if (smb_fname->base_name == NULL) {
776 smb_fname->base_name = tmp;
780 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
783 TALLOC_FREE(smb_fname->base_name);
784 smb_fname->base_name = tmp;
790 static int shadow_copy2_unlink(vfs_handle_struct *handle,
791 const struct smb_filename *smb_fname)
795 int ret, saved_errno;
796 struct smb_filename *conv;
798 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
799 smb_fname->base_name,
800 ×tamp, &stripped)) {
803 if (timestamp == 0) {
804 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
806 conv = cp_smb_filename(talloc_tos(), smb_fname);
811 conv->base_name = shadow_copy2_convert(
812 conv, handle, stripped, timestamp);
813 TALLOC_FREE(stripped);
814 if (conv->base_name == NULL) {
817 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
824 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
829 int ret, saved_errno;
832 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
833 ×tamp, &stripped)) {
836 if (timestamp == 0) {
837 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
839 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
840 TALLOC_FREE(stripped);
844 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
851 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
852 uid_t uid, gid_t gid)
856 int ret, saved_errno;
859 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
860 ×tamp, &stripped)) {
863 if (timestamp == 0) {
864 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
866 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
867 TALLOC_FREE(stripped);
871 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
878 static int shadow_copy2_chdir(vfs_handle_struct *handle,
883 int ret, saved_errno;
886 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
887 ×tamp, &stripped)) {
890 if (timestamp == 0) {
891 return SMB_VFS_NEXT_CHDIR(handle, fname);
893 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
894 TALLOC_FREE(stripped);
898 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
905 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
906 const struct smb_filename *smb_fname,
907 struct smb_file_time *ft)
911 int ret, saved_errno;
912 struct smb_filename *conv;
914 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
915 smb_fname->base_name,
916 ×tamp, &stripped)) {
919 if (timestamp == 0) {
920 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
922 conv = cp_smb_filename(talloc_tos(), smb_fname);
927 conv->base_name = shadow_copy2_convert(
928 conv, handle, stripped, timestamp);
929 TALLOC_FREE(stripped);
930 if (conv->base_name == NULL) {
933 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
940 static int shadow_copy2_readlink(vfs_handle_struct *handle,
941 const char *fname, char *buf, size_t bufsiz)
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_READLINK(handle, fname, buf, bufsiz);
955 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
956 TALLOC_FREE(stripped);
960 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
967 static int shadow_copy2_mknod(vfs_handle_struct *handle,
968 const char *fname, mode_t mode, SMB_DEV_T dev)
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_MKNOD(handle, fname, mode, dev);
982 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
983 TALLOC_FREE(stripped);
987 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
994 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
998 char *stripped = NULL;
1000 char *result = NULL;
1001 char *inserted = NULL;
1002 char *inserted_to, *inserted_end;
1005 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1006 ×tamp, &stripped)) {
1009 if (timestamp == 0) {
1010 return SMB_VFS_NEXT_REALPATH(handle, fname);
1013 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1018 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1019 if (result == NULL) {
1024 * Take away what we've inserted. This removes the @GMT-thingy
1025 * completely, but will give a path under the share root.
1027 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1028 if (inserted == NULL) {
1031 inserted_to = strstr_m(result, inserted);
1032 if (inserted_to == NULL) {
1033 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1036 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1037 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1040 saved_errno = errno;
1041 TALLOC_FREE(inserted);
1043 TALLOC_FREE(stripped);
1044 errno = saved_errno;
1049 * Check whether a given directory contains a
1050 * snapshot directory as direct subdirectory.
1051 * If yes, return the path of the snapshot-subdir,
1052 * otherwise return NULL.
1054 static char *have_snapdir(struct vfs_handle_struct *handle,
1057 struct smb_filename smb_fname;
1059 struct shadow_copy2_config *config;
1061 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1064 ZERO_STRUCT(smb_fname);
1065 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1066 path, config->snapdir);
1067 if (smb_fname.base_name == NULL) {
1071 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1072 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1073 return smb_fname.base_name;
1075 TALLOC_FREE(smb_fname.base_name);
1080 * Find the snapshot directory (if any) for the given
1081 * filename (which is relative to the share).
1083 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1084 struct vfs_handle_struct *handle,
1085 struct smb_filename *smb_fname)
1088 const char *snapdir;
1089 struct shadow_copy2_config *config;
1091 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1095 * If the non-snapdisrseverywhere mode, we should not search!
1097 if (!config->snapdirseverywhere) {
1098 return config->snapshot_basepath;
1101 path = talloc_asprintf(mem_ctx, "%s/%s",
1102 handle->conn->connectpath,
1103 smb_fname->base_name);
1108 snapdir = have_snapdir(handle, path);
1109 if (snapdir != NULL) {
1114 while ((p = strrchr(path, '/')) && (p > path)) {
1118 snapdir = have_snapdir(handle, path);
1119 if (snapdir != NULL) {
1128 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1130 char *gmt, size_t gmt_len)
1132 struct tm timestamp;
1134 unsigned long int timestamp_long;
1136 struct shadow_copy2_config *config;
1138 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1141 fmt = config->gmt_format;
1143 ZERO_STRUCT(timestamp);
1144 if (config->use_sscanf) {
1145 if (sscanf(name, fmt, ×tamp_long) != 1) {
1146 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1147 "no sscanf match %s: %s\n",
1151 timestamp_t = timestamp_long;
1152 gmtime_r(×tamp_t, ×tamp);
1154 if (strptime(name, fmt, ×tamp) == NULL) {
1155 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1156 "no match %s: %s\n",
1160 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1163 if (config->use_localtime) {
1164 timestamp.tm_isdst = -1;
1165 timestamp_t = mktime(×tamp);
1166 gmtime_r(×tamp_t, ×tamp);
1170 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1174 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1176 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1179 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1181 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1185 sort the shadow copy data in ascending or descending order
1187 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1188 struct shadow_copy_data *shadow_copy2_data)
1190 int (*cmpfunc)(const void *, const void *);
1192 struct shadow_copy2_config *config;
1194 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1197 sort = config->sort_order;
1202 if (strcmp(sort, "asc") == 0) {
1203 cmpfunc = shadow_copy2_label_cmp_asc;
1204 } else if (strcmp(sort, "desc") == 0) {
1205 cmpfunc = shadow_copy2_label_cmp_desc;
1210 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1211 shadow_copy2_data->labels)
1213 TYPESAFE_QSORT(shadow_copy2_data->labels,
1214 shadow_copy2_data->num_volumes,
1219 static int shadow_copy2_get_shadow_copy_data(
1220 vfs_handle_struct *handle, files_struct *fsp,
1221 struct shadow_copy_data *shadow_copy2_data,
1225 const char *snapdir;
1227 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1229 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1230 if (snapdir == NULL) {
1231 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1232 handle->conn->connectpath));
1234 talloc_free(tmp_ctx);
1238 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1241 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1242 " - %s\n", snapdir, strerror(errno)));
1243 talloc_free(tmp_ctx);
1248 shadow_copy2_data->num_volumes = 0;
1249 shadow_copy2_data->labels = NULL;
1251 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1252 char snapshot[GMT_NAME_LEN+1];
1253 SHADOW_COPY_LABEL *tlabels;
1256 * ignore names not of the right form in the snapshot
1259 if (!shadow_copy2_snapshot_to_gmt(
1261 snapshot, sizeof(snapshot))) {
1263 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1264 "ignoring %s\n", d->d_name));
1267 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1268 d->d_name, snapshot));
1271 /* the caller doesn't want the labels */
1272 shadow_copy2_data->num_volumes++;
1276 tlabels = talloc_realloc(shadow_copy2_data,
1277 shadow_copy2_data->labels,
1279 shadow_copy2_data->num_volumes+1);
1280 if (tlabels == NULL) {
1281 DEBUG(0,("shadow_copy2: out of memory\n"));
1282 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1283 talloc_free(tmp_ctx);
1287 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1290 shadow_copy2_data->num_volumes++;
1291 shadow_copy2_data->labels = tlabels;
1294 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1296 shadow_copy2_sort_data(handle, shadow_copy2_data);
1298 talloc_free(tmp_ctx);
1302 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1303 struct files_struct *fsp,
1304 uint32 security_info,
1305 TALLOC_CTX *mem_ctx,
1306 struct security_descriptor **ppdesc)
1313 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1314 fsp->fsp_name->base_name,
1315 ×tamp, &stripped)) {
1316 return map_nt_error_from_unix(errno);
1318 if (timestamp == 0) {
1319 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1323 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1324 TALLOC_FREE(stripped);
1326 return map_nt_error_from_unix(errno);
1328 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1334 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1336 uint32 security_info,
1337 TALLOC_CTX *mem_ctx,
1338 struct security_descriptor **ppdesc)
1345 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1346 ×tamp, &stripped)) {
1347 return map_nt_error_from_unix(errno);
1349 if (timestamp == 0) {
1350 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1353 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1354 TALLOC_FREE(stripped);
1356 return map_nt_error_from_unix(errno);
1358 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1364 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1365 const char *fname, mode_t mode)
1369 int ret, saved_errno;
1372 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1373 ×tamp, &stripped)) {
1376 if (timestamp == 0) {
1377 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1379 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1380 TALLOC_FREE(stripped);
1384 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1385 saved_errno = errno;
1387 errno = saved_errno;
1391 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1395 int ret, saved_errno;
1398 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1399 ×tamp, &stripped)) {
1402 if (timestamp == 0) {
1403 return SMB_VFS_NEXT_RMDIR(handle, fname);
1405 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1406 TALLOC_FREE(stripped);
1410 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1411 saved_errno = errno;
1413 errno = saved_errno;
1417 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1422 int ret, saved_errno;
1425 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1426 ×tamp, &stripped)) {
1429 if (timestamp == 0) {
1430 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1432 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1433 TALLOC_FREE(stripped);
1437 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1438 saved_errno = errno;
1440 errno = saved_errno;
1444 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1445 const char *fname, const char *aname,
1446 void *value, size_t size)
1454 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1455 ×tamp, &stripped)) {
1458 if (timestamp == 0) {
1459 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1462 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1463 TALLOC_FREE(stripped);
1467 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1468 saved_errno = errno;
1470 errno = saved_errno;
1474 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1476 char *list, size_t size)
1484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1485 ×tamp, &stripped)) {
1488 if (timestamp == 0) {
1489 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1491 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1492 TALLOC_FREE(stripped);
1496 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1497 saved_errno = errno;
1499 errno = saved_errno;
1503 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1504 const char *fname, const char *aname)
1508 int ret, saved_errno;
1511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1512 ×tamp, &stripped)) {
1515 if (timestamp == 0) {
1516 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1518 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1519 TALLOC_FREE(stripped);
1523 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1524 saved_errno = errno;
1526 errno = saved_errno;
1530 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1532 const char *aname, const void *value,
1533 size_t size, int flags)
1541 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1542 ×tamp, &stripped)) {
1545 if (timestamp == 0) {
1546 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1549 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1550 TALLOC_FREE(stripped);
1554 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1555 saved_errno = errno;
1557 errno = saved_errno;
1561 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1562 const char *fname, mode_t mode)
1570 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1571 ×tamp, &stripped)) {
1574 if (timestamp == 0) {
1575 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1577 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1578 TALLOC_FREE(stripped);
1582 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1583 saved_errno = errno;
1585 errno = saved_errno;
1589 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1592 TALLOC_CTX *mem_ctx,
1601 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1602 ×tamp, &stripped)) {
1605 if (timestamp == 0) {
1606 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1607 mem_ctx, found_name);
1609 if (stripped[0] == '\0') {
1610 *found_name = talloc_strdup(mem_ctx, name);
1611 if (*found_name == NULL) {
1617 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1618 TALLOC_FREE(stripped);
1622 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1623 mem_ctx, found_name);
1624 saved_errno = errno;
1626 errno = saved_errno;
1630 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1631 const char *path, bool small_query,
1632 uint64_t *bsize, uint64_t *dfree,
1641 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1642 ×tamp, &stripped)) {
1645 if (timestamp == 0) {
1646 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1647 bsize, dfree, dsize);
1650 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1651 TALLOC_FREE(stripped);
1656 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1659 saved_errno = errno;
1661 errno = saved_errno;
1666 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1667 const char *service, const char *user)
1669 struct shadow_copy2_config *config;
1671 const char *snapdir;
1672 const char *gmt_format;
1673 const char *sort_order;
1674 const char *basedir;
1675 const char *mount_point;
1677 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1678 (unsigned)handle->conn->cnum,
1679 handle->conn->connectpath));
1681 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1686 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1687 if (config == NULL) {
1688 DEBUG(0, ("talloc_zero() failed\n"));
1693 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1696 config->gmt_format = talloc_strdup(config, gmt_format);
1697 if (config->gmt_format == NULL) {
1698 DEBUG(0, ("talloc_strdup() failed\n"));
1703 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1704 "shadow", "sscanf", false);
1706 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1707 "shadow", "localtime",
1710 snapdir = lp_parm_const_string(SNUM(handle->conn),
1711 "shadow", "snapdir",
1713 config->snapdir = talloc_strdup(config, snapdir);
1714 if (config->snapdir == NULL) {
1715 DEBUG(0, ("talloc_strdup() failed\n"));
1720 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1722 "snapdirseverywhere",
1725 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1726 "shadow", "crossmountpoints",
1729 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1730 "shadow", "fixinodes",
1733 sort_order = lp_parm_const_string(SNUM(handle->conn),
1734 "shadow", "sort", "desc");
1735 config->sort_order = talloc_strdup(config, sort_order);
1736 if (config->sort_order == NULL) {
1737 DEBUG(0, ("talloc_strdup() failed\n"));
1742 mount_point = lp_parm_const_string(SNUM(handle->conn),
1743 "shadow", "mountpoint", NULL);
1744 if (mount_point != NULL) {
1745 if (mount_point[0] != '/') {
1746 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1747 "relative ('%s'), but it has to be an "
1748 "absolute path. Ignoring provided value.\n",
1753 p = strstr(handle->conn->connectpath, mount_point);
1754 if (p != handle->conn->connectpath) {
1755 DEBUG(1, ("Warning: mount_point (%s) is not a "
1756 "subdirectory of the share root "
1757 "(%s). Ignoring provided value.\n",
1759 handle->conn->connectpath));
1765 if (mount_point != NULL) {
1766 config->mount_point = talloc_strdup(config, mount_point);
1767 if (config->mount_point == NULL) {
1768 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1772 config->mount_point = shadow_copy2_find_mount_point(config,
1774 if (config->mount_point == NULL) {
1775 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1776 " failed: %s\n", strerror(errno)));
1781 basedir = lp_parm_const_string(SNUM(handle->conn),
1782 "shadow", "basedir", NULL);
1784 if (basedir != NULL) {
1785 if (basedir[0] != '/') {
1786 DEBUG(1, (__location__ " Warning: 'basedir' is "
1787 "relative ('%s'), but it has to be an "
1788 "absolute path. Disabling basedir.\n",
1792 p = strstr(basedir, config->mount_point);
1794 DEBUG(1, ("Warning: basedir (%s) is not a "
1795 "subdirectory of the share root's "
1796 "mount point (%s). "
1797 "Disabling basedir\n",
1798 basedir, config->mount_point));
1800 config->basedir = talloc_strdup(config,
1802 if (config->basedir == NULL) {
1803 DEBUG(0, ("talloc_strdup() failed\n"));
1811 if (config->snapdirseverywhere && config->basedir != NULL) {
1812 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1813 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1814 TALLOC_FREE(config->basedir);
1817 if (config->crossmountpoints && config->basedir != NULL) {
1818 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1819 "with 'crossmountpoints'. Disabling basedir.\n"));
1820 TALLOC_FREE(config->basedir);
1823 if (config->basedir == NULL) {
1824 config->basedir = config->mount_point;
1827 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1828 config->rel_connectpath = talloc_strdup(config,
1829 handle->conn->connectpath + strlen(config->basedir));
1830 if (config->rel_connectpath == NULL) {
1831 DEBUG(0, ("talloc_strdup() failed\n"));
1837 if (config->snapdir[0] == '/') {
1838 config->snapdir_absolute = true;
1840 if (config->snapdirseverywhere == true) {
1841 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1842 "is incompatible with 'snapdirseverywhere', "
1843 "setting 'snapdirseverywhere' to false.\n"));
1844 config->snapdirseverywhere = false;
1847 if (config->crossmountpoints == true) {
1848 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1849 "is not supported with an absolute snapdir. "
1850 "Disabling it.\n"));
1851 config->crossmountpoints = false;
1854 config->snapshot_basepath = config->snapdir;
1856 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1857 config->mount_point, config->snapdir);
1858 if (config->snapshot_basepath == NULL) {
1859 DEBUG(0, ("talloc_asprintf() failed\n"));
1865 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1866 " share root: '%s'\n"
1868 " mountpoint: '%s'\n"
1869 " rel share root: '%s'\n"
1871 " snapshot base path: '%s'\n"
1874 " snapdirs everywhere: %s\n"
1875 " cross mountpoints: %s\n"
1879 handle->conn->connectpath,
1881 config->mount_point,
1882 config->rel_connectpath,
1884 config->snapshot_basepath,
1886 config->use_sscanf ? "yes" : "no",
1887 config->snapdirseverywhere ? "yes" : "no",
1888 config->crossmountpoints ? "yes" : "no",
1889 config->fixinodes ? "yes" : "no",
1894 SMB_VFS_HANDLE_SET_DATA(handle, config,
1895 NULL, struct shadow_copy2_config,
1901 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1902 .connect_fn = shadow_copy2_connect,
1903 .opendir_fn = shadow_copy2_opendir,
1904 .disk_free_fn = shadow_copy2_disk_free,
1905 .rename_fn = shadow_copy2_rename,
1906 .link_fn = shadow_copy2_link,
1907 .symlink_fn = shadow_copy2_symlink,
1908 .stat_fn = shadow_copy2_stat,
1909 .lstat_fn = shadow_copy2_lstat,
1910 .fstat_fn = shadow_copy2_fstat,
1911 .open_fn = shadow_copy2_open,
1912 .unlink_fn = shadow_copy2_unlink,
1913 .chmod_fn = shadow_copy2_chmod,
1914 .chown_fn = shadow_copy2_chown,
1915 .chdir_fn = shadow_copy2_chdir,
1916 .ntimes_fn = shadow_copy2_ntimes,
1917 .readlink_fn = shadow_copy2_readlink,
1918 .mknod_fn = shadow_copy2_mknod,
1919 .realpath_fn = shadow_copy2_realpath,
1920 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1921 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1922 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1923 .mkdir_fn = shadow_copy2_mkdir,
1924 .rmdir_fn = shadow_copy2_rmdir,
1925 .getxattr_fn = shadow_copy2_getxattr,
1926 .listxattr_fn = shadow_copy2_listxattr,
1927 .removexattr_fn = shadow_copy2_removexattr,
1928 .setxattr_fn = shadow_copy2_setxattr,
1929 .chmod_acl_fn = shadow_copy2_chmod_acl,
1930 .chflags_fn = shadow_copy2_chflags,
1931 .get_real_filename_fn = shadow_copy2_get_real_filename,
1934 NTSTATUS vfs_shadow_copy2_init(void);
1935 NTSTATUS vfs_shadow_copy2_init(void)
1937 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1938 "shadow_copy2", &vfs_shadow_copy2_fns);