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 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
124 unsigned *pnum_offsets)
126 unsigned num_offsets;
133 while ((p = strchr(p, '/')) != NULL) {
138 offsets = talloc_array(mem_ctx, size_t, num_offsets);
139 if (offsets == NULL) {
145 while ((p = strchr(p, '/')) != NULL) {
146 offsets[num_offsets] = p-str;
152 *pnum_offsets = num_offsets;
157 * Given a timstamp, build the string to insert into a path
158 * as a path component for creating the local path to the
159 * snapshot at the given timestamp of the input path.
161 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
162 struct vfs_handle_struct *handle,
166 fstring snaptime_string;
168 struct shadow_copy2_config *config;
170 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
173 if (config->use_sscanf) {
174 snaptime_len = snprintf(snaptime_string,
175 sizeof(snaptime_string),
177 (unsigned long)snapshot);
178 if (snaptime_len <= 0) {
179 DEBUG(10, ("snprintf failed\n"));
183 if (config->use_localtime) {
184 if (localtime_r(&snapshot, &snap_tm) == 0) {
185 DEBUG(10, ("gmtime_r failed\n"));
189 if (gmtime_r(&snapshot, &snap_tm) == 0) {
190 DEBUG(10, ("gmtime_r failed\n"));
194 snaptime_len = strftime(snaptime_string,
195 sizeof(snaptime_string),
198 if (snaptime_len == 0) {
199 DEBUG(10, ("strftime failed\n"));
203 return talloc_asprintf(mem_ctx, "/%s/%s",
204 config->snapdir, snaptime_string);
208 * Strip a snapshot component from an filename as
209 * handed in via the smb layer.
210 * Returns the parsed timestamp and the stripped filename.
212 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
213 struct vfs_handle_struct *handle,
223 size_t rest_len, dst_len;
224 struct shadow_copy2_config *config;
226 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
229 p = strstr_m(name, "@GMT-");
233 if ((p > name) && (p[-1] != '/')) {
236 q = strptime(p, GMT_FORMAT, &tm);
241 timestamp = timegm(&tm);
242 if (timestamp == (time_t)-1) {
245 if ((p == name) && (q[0] == '\0')) {
246 if (pstripped != NULL) {
247 stripped = talloc_strdup(mem_ctx, "");
248 if (stripped == NULL) {
251 *pstripped = stripped;
253 *ptimestamp = timestamp;
261 rest_len = strlen(q);
262 dst_len = (p-name) + rest_len;
264 if (config->snapdirseverywhere) {
267 insert = shadow_copy2_insert_string(talloc_tos(), handle,
269 if (insert == NULL) {
274 have_insert = (strstr(name, insert+1) != NULL);
281 if (pstripped != NULL) {
282 stripped = talloc_array(mem_ctx, char, dst_len+1);
283 if (stripped == NULL) {
288 memcpy(stripped, name, p-name);
291 memcpy(stripped + (p-name), q, rest_len);
293 stripped[dst_len] = '\0';
294 *pstripped = stripped;
296 *ptimestamp = timestamp;
303 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
304 vfs_handle_struct *handle)
306 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
311 if (stat(path, &st) != 0) {
318 while ((p = strrchr(path, '/')) && p > path) {
320 if (stat(path, &st) != 0) {
324 if (st.st_dev != dev) {
334 * Convert from a name as handed in via the SMB layer
335 * and a timestamp into the local path of the snapshot
336 * of the provided file at the provided time.
338 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
339 struct vfs_handle_struct *handle,
340 const char *name, time_t timestamp)
342 struct smb_filename converted_fname;
344 size_t *slashes = NULL;
345 unsigned num_slashes;
349 char *converted = NULL;
353 struct shadow_copy2_config *config;
355 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
358 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
364 pathlen = talloc_get_size(path)-1;
366 DEBUG(10, ("converting %s\n", path));
368 if (!shadow_copy2_find_slashes(talloc_tos(), path,
369 &slashes, &num_slashes)) {
372 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
373 if (insert == NULL) {
376 insertlen = talloc_get_size(insert)-1;
377 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
378 if (converted == NULL) {
382 if (path[pathlen-1] != '/') {
384 * Append a fake slash to find the snapshot root
387 tmp = talloc_realloc(talloc_tos(), slashes,
388 size_t, num_slashes+1);
393 slashes[num_slashes] = pathlen;
399 if (!config->crossmountpoints) {
402 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
404 if (mount_point == NULL) {
407 min_offset = strlen(mount_point);
408 TALLOC_FREE(mount_point);
411 memcpy(converted, path, pathlen+1);
412 converted[pathlen+insertlen] = '\0';
414 ZERO_STRUCT(converted_fname);
415 converted_fname.base_name = converted;
417 for (i = num_slashes-1; i>=0; i--) {
423 if (offset < min_offset) {
428 memcpy(converted+offset, insert, insertlen);
431 memcpy(converted+offset, path + slashes[i],
432 pathlen - slashes[i]);
434 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
436 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
437 ret, ret == 0 ? "ok" : strerror(errno)));
442 if (errno == ENOTDIR) {
444 * This is a valid condition: We appended the
445 * .snaphots/@GMT.. to a file name. Just try
446 * with the upper levels.
450 if (errno != ENOENT) {
451 /* Other problem than "not found" */
460 DEBUG(10, ("Found %s\n", converted));
468 TALLOC_FREE(converted);
470 TALLOC_FREE(slashes);
477 modify a sbuf return to ensure that inodes in the shadow directory
478 are different from those in the main directory
480 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
481 SMB_STRUCT_STAT *sbuf)
483 struct shadow_copy2_config *config;
485 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
488 if (config->fixinodes) {
489 /* some snapshot systems, like GPFS, return the name
490 device:inode for the snapshot files as the current
491 files. That breaks the 'restore' button in the shadow copy
492 GUI, as the client gets a sharing violation.
494 This is a crude way of allowing both files to be
495 open at once. It has a slight chance of inode
496 number collision, but I can't see a better approach
497 without significant VFS changes
501 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
505 sbuf->st_ex_ino ^= shash;
509 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
520 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
521 ×tamp, &stripped)) {
524 if (timestamp == 0) {
525 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
527 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
528 TALLOC_FREE(stripped);
532 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
539 static int shadow_copy2_rename(vfs_handle_struct *handle,
540 const struct smb_filename *smb_fname_src,
541 const struct smb_filename *smb_fname_dst)
543 time_t timestamp_src, timestamp_dst;
545 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
546 smb_fname_src->base_name,
547 ×tamp_src, NULL)) {
550 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
551 smb_fname_dst->base_name,
552 ×tamp_dst, NULL)) {
555 if (timestamp_src != 0) {
559 if (timestamp_dst != 0) {
563 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
566 static int shadow_copy2_symlink(vfs_handle_struct *handle,
567 const char *oldname, const char *newname)
569 time_t timestamp_old, timestamp_new;
571 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
572 ×tamp_old, NULL)) {
575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
576 ×tamp_new, NULL)) {
579 if ((timestamp_old != 0) || (timestamp_new != 0)) {
583 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
586 static int shadow_copy2_link(vfs_handle_struct *handle,
587 const char *oldname, const char *newname)
589 time_t timestamp_old, timestamp_new;
591 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
592 ×tamp_old, NULL)) {
595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
596 ×tamp_new, NULL)) {
599 if ((timestamp_old != 0) || (timestamp_new != 0)) {
603 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
606 static int shadow_copy2_stat(vfs_handle_struct *handle,
607 struct smb_filename *smb_fname)
610 char *stripped, *tmp;
611 int ret, saved_errno;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
614 smb_fname->base_name,
615 ×tamp, &stripped)) {
618 if (timestamp == 0) {
619 return SMB_VFS_NEXT_STAT(handle, smb_fname);
622 tmp = smb_fname->base_name;
623 smb_fname->base_name = shadow_copy2_convert(
624 talloc_tos(), handle, stripped, timestamp);
625 TALLOC_FREE(stripped);
627 if (smb_fname->base_name == NULL) {
628 smb_fname->base_name = tmp;
632 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
635 TALLOC_FREE(smb_fname->base_name);
636 smb_fname->base_name = tmp;
639 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
645 static int shadow_copy2_lstat(vfs_handle_struct *handle,
646 struct smb_filename *smb_fname)
649 char *stripped, *tmp;
650 int ret, saved_errno;
652 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
653 smb_fname->base_name,
654 ×tamp, &stripped)) {
657 if (timestamp == 0) {
658 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
661 tmp = smb_fname->base_name;
662 smb_fname->base_name = shadow_copy2_convert(
663 talloc_tos(), handle, stripped, timestamp);
664 TALLOC_FREE(stripped);
666 if (smb_fname->base_name == NULL) {
667 smb_fname->base_name = tmp;
671 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
674 TALLOC_FREE(smb_fname->base_name);
675 smb_fname->base_name = tmp;
678 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
684 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
685 SMB_STRUCT_STAT *sbuf)
690 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
694 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
695 fsp->fsp_name->base_name,
699 if (timestamp != 0) {
700 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
705 static int shadow_copy2_open(vfs_handle_struct *handle,
706 struct smb_filename *smb_fname, files_struct *fsp,
707 int flags, mode_t mode)
710 char *stripped, *tmp;
711 int ret, saved_errno;
713 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
714 smb_fname->base_name,
715 ×tamp, &stripped)) {
718 if (timestamp == 0) {
719 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
722 tmp = smb_fname->base_name;
723 smb_fname->base_name = shadow_copy2_convert(
724 talloc_tos(), handle, stripped, timestamp);
725 TALLOC_FREE(stripped);
727 if (smb_fname->base_name == NULL) {
728 smb_fname->base_name = tmp;
732 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
735 TALLOC_FREE(smb_fname->base_name);
736 smb_fname->base_name = tmp;
742 static int shadow_copy2_unlink(vfs_handle_struct *handle,
743 const struct smb_filename *smb_fname)
747 int ret, saved_errno;
748 struct smb_filename *conv;
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_UNLINK(handle, smb_fname);
758 conv = cp_smb_filename(talloc_tos(), smb_fname);
763 conv->base_name = shadow_copy2_convert(
764 conv, handle, stripped, timestamp);
765 TALLOC_FREE(stripped);
766 if (conv->base_name == NULL) {
769 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
776 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
781 int ret, saved_errno;
784 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
785 ×tamp, &stripped)) {
788 if (timestamp == 0) {
789 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
791 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
792 TALLOC_FREE(stripped);
796 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
803 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
804 uid_t uid, gid_t gid)
808 int ret, saved_errno;
811 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
812 ×tamp, &stripped)) {
815 if (timestamp == 0) {
816 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
818 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
819 TALLOC_FREE(stripped);
823 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
830 static int shadow_copy2_chdir(vfs_handle_struct *handle,
835 int ret, saved_errno;
838 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
839 ×tamp, &stripped)) {
842 if (timestamp == 0) {
843 return SMB_VFS_NEXT_CHDIR(handle, fname);
845 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
846 TALLOC_FREE(stripped);
850 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
857 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
858 const struct smb_filename *smb_fname,
859 struct smb_file_time *ft)
863 int ret, saved_errno;
864 struct smb_filename *conv;
866 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
867 smb_fname->base_name,
868 ×tamp, &stripped)) {
871 if (timestamp == 0) {
872 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
874 conv = cp_smb_filename(talloc_tos(), smb_fname);
879 conv->base_name = shadow_copy2_convert(
880 conv, handle, stripped, timestamp);
881 TALLOC_FREE(stripped);
882 if (conv->base_name == NULL) {
885 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
892 static int shadow_copy2_readlink(vfs_handle_struct *handle,
893 const char *fname, char *buf, size_t bufsiz)
897 int ret, saved_errno;
900 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
901 ×tamp, &stripped)) {
904 if (timestamp == 0) {
905 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
907 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
908 TALLOC_FREE(stripped);
912 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
919 static int shadow_copy2_mknod(vfs_handle_struct *handle,
920 const char *fname, mode_t mode, SMB_DEV_T dev)
924 int ret, saved_errno;
927 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
928 ×tamp, &stripped)) {
931 if (timestamp == 0) {
932 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
934 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
935 TALLOC_FREE(stripped);
939 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
946 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
950 char *stripped = NULL;
953 char *inserted = NULL;
954 char *inserted_to, *inserted_end;
957 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
958 ×tamp, &stripped)) {
961 if (timestamp == 0) {
962 return SMB_VFS_NEXT_REALPATH(handle, fname);
965 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
970 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
971 if (result == NULL) {
976 * Take away what we've inserted. This removes the @GMT-thingy
977 * completely, but will give a path under the share root.
979 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
980 if (inserted == NULL) {
983 inserted_to = strstr_m(result, inserted);
984 if (inserted_to == NULL) {
985 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
988 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
989 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
993 TALLOC_FREE(inserted);
995 TALLOC_FREE(stripped);
1001 * Check whether a given directory contains a
1002 * snapshot directory as direct subdirectory.
1003 * If yes, return the path of the snapshot-subdir,
1004 * otherwise return NULL.
1006 static char *have_snapdir(struct vfs_handle_struct *handle,
1009 struct smb_filename smb_fname;
1011 struct shadow_copy2_config *config;
1013 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1016 ZERO_STRUCT(smb_fname);
1017 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1018 path, config->snapdir);
1019 if (smb_fname.base_name == NULL) {
1023 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1024 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1025 return smb_fname.base_name;
1027 TALLOC_FREE(smb_fname.base_name);
1032 * Find the snapshot directory (if any) for the given
1033 * filename (which is relative to the share).
1035 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1036 struct vfs_handle_struct *handle,
1037 struct smb_filename *smb_fname)
1041 struct shadow_copy2_config *config;
1043 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1046 path = talloc_asprintf(mem_ctx, "%s/%s",
1047 handle->conn->connectpath,
1048 smb_fname->base_name);
1053 snapdir = have_snapdir(handle, path);
1054 if (snapdir != NULL) {
1059 while ((p = strrchr(path, '/')) && (p > path)) {
1063 snapdir = have_snapdir(handle, path);
1064 if (snapdir != NULL) {
1073 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1075 char *gmt, size_t gmt_len)
1077 struct tm timestamp;
1079 unsigned long int timestamp_long;
1081 struct shadow_copy2_config *config;
1083 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1086 fmt = config->gmt_format;
1088 ZERO_STRUCT(timestamp);
1089 if (config->use_sscanf) {
1090 if (sscanf(name, fmt, ×tamp_long) != 1) {
1091 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1092 "no sscanf match %s: %s\n",
1096 timestamp_t = timestamp_long;
1097 gmtime_r(×tamp_t, ×tamp);
1099 if (strptime(name, fmt, ×tamp) == NULL) {
1100 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1101 "no match %s: %s\n",
1105 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1108 if (config->use_localtime) {
1109 timestamp.tm_isdst = -1;
1110 timestamp_t = mktime(×tamp);
1111 gmtime_r(×tamp_t, ×tamp);
1115 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1119 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1121 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1124 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1126 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1130 sort the shadow copy data in ascending or descending order
1132 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1133 struct shadow_copy_data *shadow_copy2_data)
1135 int (*cmpfunc)(const void *, const void *);
1137 struct shadow_copy2_config *config;
1139 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1142 sort = config->sort_order;
1147 if (strcmp(sort, "asc") == 0) {
1148 cmpfunc = shadow_copy2_label_cmp_asc;
1149 } else if (strcmp(sort, "desc") == 0) {
1150 cmpfunc = shadow_copy2_label_cmp_desc;
1155 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1156 shadow_copy2_data->labels)
1158 TYPESAFE_QSORT(shadow_copy2_data->labels,
1159 shadow_copy2_data->num_volumes,
1164 static int shadow_copy2_get_shadow_copy_data(
1165 vfs_handle_struct *handle, files_struct *fsp,
1166 struct shadow_copy_data *shadow_copy2_data,
1170 const char *snapdir;
1172 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1174 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1175 if (snapdir == NULL) {
1176 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1177 handle->conn->connectpath));
1179 talloc_free(tmp_ctx);
1183 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1186 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1187 " - %s\n", snapdir, strerror(errno)));
1188 talloc_free(tmp_ctx);
1193 shadow_copy2_data->num_volumes = 0;
1194 shadow_copy2_data->labels = NULL;
1196 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1197 char snapshot[GMT_NAME_LEN+1];
1198 SHADOW_COPY_LABEL *tlabels;
1201 * ignore names not of the right form in the snapshot
1204 if (!shadow_copy2_snapshot_to_gmt(
1206 snapshot, sizeof(snapshot))) {
1208 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1209 "ignoring %s\n", d->d_name));
1212 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1213 d->d_name, snapshot));
1216 /* the caller doesn't want the labels */
1217 shadow_copy2_data->num_volumes++;
1221 tlabels = talloc_realloc(shadow_copy2_data,
1222 shadow_copy2_data->labels,
1224 shadow_copy2_data->num_volumes+1);
1225 if (tlabels == NULL) {
1226 DEBUG(0,("shadow_copy2: out of memory\n"));
1227 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1228 talloc_free(tmp_ctx);
1232 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1235 shadow_copy2_data->num_volumes++;
1236 shadow_copy2_data->labels = tlabels;
1239 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1241 shadow_copy2_sort_data(handle, shadow_copy2_data);
1243 talloc_free(tmp_ctx);
1247 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1248 struct files_struct *fsp,
1249 uint32 security_info,
1250 TALLOC_CTX *mem_ctx,
1251 struct security_descriptor **ppdesc)
1258 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1259 fsp->fsp_name->base_name,
1260 ×tamp, &stripped)) {
1261 return map_nt_error_from_unix(errno);
1263 if (timestamp == 0) {
1264 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1268 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1269 TALLOC_FREE(stripped);
1271 return map_nt_error_from_unix(errno);
1273 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1279 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1281 uint32 security_info,
1282 TALLOC_CTX *mem_ctx,
1283 struct security_descriptor **ppdesc)
1290 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1291 ×tamp, &stripped)) {
1292 return map_nt_error_from_unix(errno);
1294 if (timestamp == 0) {
1295 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1298 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1299 TALLOC_FREE(stripped);
1301 return map_nt_error_from_unix(errno);
1303 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1309 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1310 const char *fname, mode_t mode)
1314 int ret, saved_errno;
1317 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1318 ×tamp, &stripped)) {
1321 if (timestamp == 0) {
1322 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1324 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1325 TALLOC_FREE(stripped);
1329 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1330 saved_errno = errno;
1332 errno = saved_errno;
1336 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1340 int ret, saved_errno;
1343 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1344 ×tamp, &stripped)) {
1347 if (timestamp == 0) {
1348 return SMB_VFS_NEXT_RMDIR(handle, fname);
1350 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1351 TALLOC_FREE(stripped);
1355 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1356 saved_errno = errno;
1358 errno = saved_errno;
1362 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1367 int ret, saved_errno;
1370 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1371 ×tamp, &stripped)) {
1374 if (timestamp == 0) {
1375 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1377 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1378 TALLOC_FREE(stripped);
1382 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1383 saved_errno = errno;
1385 errno = saved_errno;
1389 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1390 const char *fname, const char *aname,
1391 void *value, size_t size)
1399 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1400 ×tamp, &stripped)) {
1403 if (timestamp == 0) {
1404 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1407 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1408 TALLOC_FREE(stripped);
1412 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1413 saved_errno = errno;
1415 errno = saved_errno;
1419 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1421 char *list, size_t size)
1429 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1430 ×tamp, &stripped)) {
1433 if (timestamp == 0) {
1434 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1436 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1437 TALLOC_FREE(stripped);
1441 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1442 saved_errno = errno;
1444 errno = saved_errno;
1448 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1449 const char *fname, const char *aname)
1453 int ret, saved_errno;
1456 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1457 ×tamp, &stripped)) {
1460 if (timestamp == 0) {
1461 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1463 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1464 TALLOC_FREE(stripped);
1468 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1469 saved_errno = errno;
1471 errno = saved_errno;
1475 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1477 const char *aname, const void *value,
1478 size_t size, int flags)
1486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1487 ×tamp, &stripped)) {
1490 if (timestamp == 0) {
1491 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1494 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1495 TALLOC_FREE(stripped);
1499 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1500 saved_errno = errno;
1502 errno = saved_errno;
1506 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1507 const char *fname, mode_t mode)
1515 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1516 ×tamp, &stripped)) {
1519 if (timestamp == 0) {
1520 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1522 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1523 TALLOC_FREE(stripped);
1527 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1528 saved_errno = errno;
1530 errno = saved_errno;
1534 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1537 TALLOC_CTX *mem_ctx,
1546 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1547 ×tamp, &stripped)) {
1550 if (timestamp == 0) {
1551 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1552 mem_ctx, found_name);
1554 if (stripped[0] == '\0') {
1555 *found_name = talloc_strdup(mem_ctx, name);
1556 if (*found_name == NULL) {
1562 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1563 TALLOC_FREE(stripped);
1567 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1568 mem_ctx, found_name);
1569 saved_errno = errno;
1571 errno = saved_errno;
1575 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1576 const char *service, const char *user)
1578 struct shadow_copy2_config *config;
1580 const char *snapdir;
1581 const char *gmt_format;
1582 const char *sort_order;
1584 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1585 (unsigned)handle->conn->cnum,
1586 handle->conn->connectpath));
1588 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1593 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1594 if (config == NULL) {
1595 DEBUG(0, ("talloc_zero() failed\n"));
1600 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1603 config->gmt_format = talloc_strdup(config, gmt_format);
1604 if (config->gmt_format == NULL) {
1605 DEBUG(0, ("talloc_strdup() failed\n"));
1610 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1611 "shadow", "sscanf", false);
1613 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1614 "shadow", "localtime",
1617 snapdir = lp_parm_const_string(SNUM(handle->conn),
1618 "shadow", "snapdir",
1620 config->snapdir = talloc_strdup(config, snapdir);
1621 if (config->snapdir == NULL) {
1622 DEBUG(0, ("talloc_strdup() failed\n"));
1627 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1629 "snapdirseverywhere",
1632 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1633 "shadow", "crossmountpoints",
1636 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1637 "shadow", "fixinodes",
1640 sort_order = lp_parm_const_string(SNUM(handle->conn),
1641 "shadow", "sort", "desc");
1642 config->sort_order = talloc_strdup(config, sort_order);
1643 if (config->sort_order == NULL) {
1644 DEBUG(0, ("talloc_strdup() failed\n"));
1649 if (config->snapdir[0] == '/') {
1650 config->snapdir_absolute = true;
1651 if (config->snapdirseverywhere == true) {
1652 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1653 "is incompatible with 'snapdirseverywhere', "
1654 "setting 'snapdirseverywhere' to false.\n"));
1655 config->snapdirseverywhere = false;
1659 SMB_VFS_HANDLE_SET_DATA(handle, config,
1660 NULL, struct shadow_copy2_config,
1666 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1667 .connect_fn = shadow_copy2_connect,
1668 .opendir_fn = shadow_copy2_opendir,
1669 .rename_fn = shadow_copy2_rename,
1670 .link_fn = shadow_copy2_link,
1671 .symlink_fn = shadow_copy2_symlink,
1672 .stat_fn = shadow_copy2_stat,
1673 .lstat_fn = shadow_copy2_lstat,
1674 .fstat_fn = shadow_copy2_fstat,
1675 .open_fn = shadow_copy2_open,
1676 .unlink_fn = shadow_copy2_unlink,
1677 .chmod_fn = shadow_copy2_chmod,
1678 .chown_fn = shadow_copy2_chown,
1679 .chdir_fn = shadow_copy2_chdir,
1680 .ntimes_fn = shadow_copy2_ntimes,
1681 .readlink_fn = shadow_copy2_readlink,
1682 .mknod_fn = shadow_copy2_mknod,
1683 .realpath_fn = shadow_copy2_realpath,
1684 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1685 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1686 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1687 .mkdir_fn = shadow_copy2_mkdir,
1688 .rmdir_fn = shadow_copy2_rmdir,
1689 .getxattr_fn = shadow_copy2_getxattr,
1690 .listxattr_fn = shadow_copy2_listxattr,
1691 .removexattr_fn = shadow_copy2_removexattr,
1692 .setxattr_fn = shadow_copy2_setxattr,
1693 .chmod_acl_fn = shadow_copy2_chmod_acl,
1694 .chflags_fn = shadow_copy2_chflags,
1695 .get_real_filename_fn = shadow_copy2_get_real_filename,
1698 NTSTATUS vfs_shadow_copy2_init(void);
1699 NTSTATUS vfs_shadow_copy2_init(void)
1701 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1702 "shadow_copy2", &vfs_shadow_copy2_fns);