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 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
112 unsigned *pnum_offsets)
114 unsigned num_offsets;
121 while ((p = strchr(p, '/')) != NULL) {
126 offsets = talloc_array(mem_ctx, size_t, num_offsets);
127 if (offsets == NULL) {
133 while ((p = strchr(p, '/')) != NULL) {
134 offsets[num_offsets] = p-str;
140 *pnum_offsets = num_offsets;
144 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
145 struct vfs_handle_struct *handle,
150 fstring snaptime_string;
153 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
154 "format", GMT_FORMAT);
156 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
157 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
158 (unsigned long)snapshot);
159 if (snaptime_len <= 0) {
160 DEBUG(10, ("snprintf failed\n"));
164 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
165 if (localtime_r(&snapshot, &snap_tm) == 0) {
166 DEBUG(10, ("gmtime_r failed\n"));
170 if (gmtime_r(&snapshot, &snap_tm) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
175 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
177 if (snaptime_len == 0) {
178 DEBUG(10, ("strftime failed\n"));
182 return talloc_asprintf(mem_ctx, "/%s/%s",
183 lp_parm_const_string(
184 SNUM(handle->conn), "shadow", "snapdir",
190 * Strip a snapshot component from an filename as
191 * handed in via the smb layer.
192 * Returns the parsed timestamp and the stripped filename.
194 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
195 struct vfs_handle_struct *handle,
205 size_t rest_len, dst_len;
207 p = strstr_m(name, "@GMT-");
211 if ((p > name) && (p[-1] != '/')) {
214 q = strptime(p, GMT_FORMAT, &tm);
219 timestamp = timegm(&tm);
220 if (timestamp == (time_t)-1) {
223 if ((p == name) && (q[0] == '\0')) {
224 if (pstripped != NULL) {
225 stripped = talloc_strdup(mem_ctx, "");
226 if (stripped == NULL) {
229 *pstripped = stripped;
231 *ptimestamp = timestamp;
239 rest_len = strlen(q);
240 dst_len = (p-name) + rest_len;
242 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
246 insert = shadow_copy2_insert_string(talloc_tos(), handle,
248 if (insert == NULL) {
253 have_insert = (strstr(name, insert+1) != NULL);
260 if (pstripped != NULL) {
261 stripped = talloc_array(mem_ctx, char, dst_len+1);
262 if (stripped == NULL) {
267 memcpy(stripped, name, p-name);
270 memcpy(stripped + (p-name), q, rest_len);
272 stripped[dst_len] = '\0';
273 *pstripped = stripped;
275 *ptimestamp = timestamp;
282 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
283 vfs_handle_struct *handle)
285 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
290 if (stat(path, &st) != 0) {
297 while ((p = strrchr(path, '/')) && p > path) {
299 if (stat(path, &st) != 0) {
303 if (st.st_dev != dev) {
312 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
313 struct vfs_handle_struct *handle,
314 const char *name, time_t timestamp)
316 struct smb_filename converted_fname;
318 size_t *slashes = NULL;
319 unsigned num_slashes;
323 char *converted = NULL;
328 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
334 pathlen = talloc_get_size(path)-1;
336 DEBUG(10, ("converting %s\n", path));
338 if (!shadow_copy2_find_slashes(talloc_tos(), path,
339 &slashes, &num_slashes)) {
342 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
343 if (insert == NULL) {
346 insertlen = talloc_get_size(insert)-1;
347 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
348 if (converted == NULL) {
352 if (path[pathlen-1] != '/') {
354 * Append a fake slash to find the snapshot root
357 tmp = talloc_realloc(talloc_tos(), slashes,
358 size_t, num_slashes+1);
363 slashes[num_slashes] = pathlen;
369 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
373 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
375 if (mount_point == NULL) {
378 min_offset = strlen(mount_point);
379 TALLOC_FREE(mount_point);
382 memcpy(converted, path, pathlen+1);
383 converted[pathlen+insertlen] = '\0';
385 ZERO_STRUCT(converted_fname);
386 converted_fname.base_name = converted;
388 for (i = num_slashes-1; i>=0; i--) {
394 if (offset < min_offset) {
399 memcpy(converted+offset, insert, insertlen);
402 memcpy(converted+offset, path + slashes[i],
403 pathlen - slashes[i]);
405 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
407 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
408 ret, ret == 0 ? "ok" : strerror(errno)));
413 if (errno == ENOTDIR) {
415 * This is a valid condition: We appended the
416 * .snaphots/@GMT.. to a file name. Just try
417 * with the upper levels.
421 if (errno != ENOENT) {
422 /* Other problem than "not found" */
431 DEBUG(10, ("Found %s\n", converted));
439 TALLOC_FREE(converted);
441 TALLOC_FREE(slashes);
448 modify a sbuf return to ensure that inodes in the shadow directory
449 are different from those in the main directory
451 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
452 SMB_STRUCT_STAT *sbuf)
454 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
455 /* some snapshot systems, like GPFS, return the name
456 device:inode for the snapshot files as the current
457 files. That breaks the 'restore' button in the shadow copy
458 GUI, as the client gets a sharing violation.
460 This is a crude way of allowing both files to be
461 open at once. It has a slight chance of inode
462 number collision, but I can't see a better approach
463 without significant VFS changes
467 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
471 sbuf->st_ex_ino ^= shash;
475 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
487 ×tamp, &stripped)) {
490 if (timestamp == 0) {
491 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
493 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
494 TALLOC_FREE(stripped);
498 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
505 static int shadow_copy2_rename(vfs_handle_struct *handle,
506 const struct smb_filename *smb_fname_src,
507 const struct smb_filename *smb_fname_dst)
509 time_t timestamp_src, timestamp_dst;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
512 smb_fname_src->base_name,
513 ×tamp_src, NULL)) {
516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
517 smb_fname_dst->base_name,
518 ×tamp_dst, NULL)) {
521 if (timestamp_src != 0) {
525 if (timestamp_dst != 0) {
529 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
532 static int shadow_copy2_symlink(vfs_handle_struct *handle,
533 const char *oldname, const char *newname)
535 time_t timestamp_old, timestamp_new;
537 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
538 ×tamp_old, NULL)) {
541 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
542 ×tamp_new, NULL)) {
545 if ((timestamp_old != 0) || (timestamp_new != 0)) {
549 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
552 static int shadow_copy2_link(vfs_handle_struct *handle,
553 const char *oldname, const char *newname)
555 time_t timestamp_old, timestamp_new;
557 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
558 ×tamp_old, NULL)) {
561 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
562 ×tamp_new, NULL)) {
565 if ((timestamp_old != 0) || (timestamp_new != 0)) {
569 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
572 static int shadow_copy2_stat(vfs_handle_struct *handle,
573 struct smb_filename *smb_fname)
576 char *stripped, *tmp;
577 int ret, saved_errno;
579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
580 smb_fname->base_name,
581 ×tamp, &stripped)) {
584 if (timestamp == 0) {
585 return SMB_VFS_NEXT_STAT(handle, smb_fname);
588 tmp = smb_fname->base_name;
589 smb_fname->base_name = shadow_copy2_convert(
590 talloc_tos(), handle, stripped, timestamp);
591 TALLOC_FREE(stripped);
593 if (smb_fname->base_name == NULL) {
594 smb_fname->base_name = tmp;
598 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
601 TALLOC_FREE(smb_fname->base_name);
602 smb_fname->base_name = tmp;
605 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
611 static int shadow_copy2_lstat(vfs_handle_struct *handle,
612 struct smb_filename *smb_fname)
615 char *stripped, *tmp;
616 int ret, saved_errno;
618 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
619 smb_fname->base_name,
620 ×tamp, &stripped)) {
623 if (timestamp == 0) {
624 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
627 tmp = smb_fname->base_name;
628 smb_fname->base_name = shadow_copy2_convert(
629 talloc_tos(), handle, stripped, timestamp);
630 TALLOC_FREE(stripped);
632 if (smb_fname->base_name == NULL) {
633 smb_fname->base_name = tmp;
637 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
640 TALLOC_FREE(smb_fname->base_name);
641 smb_fname->base_name = tmp;
644 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
650 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
651 SMB_STRUCT_STAT *sbuf)
656 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
660 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
661 fsp->fsp_name->base_name,
665 if (timestamp != 0) {
666 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
671 static int shadow_copy2_open(vfs_handle_struct *handle,
672 struct smb_filename *smb_fname, files_struct *fsp,
673 int flags, mode_t mode)
676 char *stripped, *tmp;
677 int ret, saved_errno;
679 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
680 smb_fname->base_name,
681 ×tamp, &stripped)) {
684 if (timestamp == 0) {
685 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
688 tmp = smb_fname->base_name;
689 smb_fname->base_name = shadow_copy2_convert(
690 talloc_tos(), handle, stripped, timestamp);
691 TALLOC_FREE(stripped);
693 if (smb_fname->base_name == NULL) {
694 smb_fname->base_name = tmp;
698 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
701 TALLOC_FREE(smb_fname->base_name);
702 smb_fname->base_name = tmp;
708 static int shadow_copy2_unlink(vfs_handle_struct *handle,
709 const struct smb_filename *smb_fname)
713 int ret, saved_errno;
714 struct smb_filename *conv;
716 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
717 smb_fname->base_name,
718 ×tamp, &stripped)) {
721 if (timestamp == 0) {
722 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
724 conv = cp_smb_filename(talloc_tos(), smb_fname);
729 conv->base_name = shadow_copy2_convert(
730 conv, handle, stripped, timestamp);
731 TALLOC_FREE(stripped);
732 if (conv->base_name == NULL) {
735 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
742 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
747 int ret, saved_errno;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
751 ×tamp, &stripped)) {
754 if (timestamp == 0) {
755 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
757 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
758 TALLOC_FREE(stripped);
762 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
769 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
770 uid_t uid, gid_t gid)
774 int ret, saved_errno;
777 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
778 ×tamp, &stripped)) {
781 if (timestamp == 0) {
782 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
784 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
785 TALLOC_FREE(stripped);
789 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
796 static int shadow_copy2_chdir(vfs_handle_struct *handle,
801 int ret, saved_errno;
804 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
805 ×tamp, &stripped)) {
808 if (timestamp == 0) {
809 return SMB_VFS_NEXT_CHDIR(handle, fname);
811 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
812 TALLOC_FREE(stripped);
816 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
823 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
824 const struct smb_filename *smb_fname,
825 struct smb_file_time *ft)
829 int ret, saved_errno;
830 struct smb_filename *conv;
832 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
833 smb_fname->base_name,
834 ×tamp, &stripped)) {
837 if (timestamp == 0) {
838 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
840 conv = cp_smb_filename(talloc_tos(), smb_fname);
845 conv->base_name = shadow_copy2_convert(
846 conv, handle, stripped, timestamp);
847 TALLOC_FREE(stripped);
848 if (conv->base_name == NULL) {
851 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
858 static int shadow_copy2_readlink(vfs_handle_struct *handle,
859 const char *fname, char *buf, size_t bufsiz)
863 int ret, saved_errno;
866 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
867 ×tamp, &stripped)) {
870 if (timestamp == 0) {
871 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
873 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
874 TALLOC_FREE(stripped);
878 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
885 static int shadow_copy2_mknod(vfs_handle_struct *handle,
886 const char *fname, mode_t mode, SMB_DEV_T dev)
890 int ret, saved_errno;
893 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
894 ×tamp, &stripped)) {
897 if (timestamp == 0) {
898 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
900 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
901 TALLOC_FREE(stripped);
905 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
912 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
916 char *stripped = NULL;
919 char *inserted = NULL;
920 char *inserted_to, *inserted_end;
923 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
924 ×tamp, &stripped)) {
927 if (timestamp == 0) {
928 return SMB_VFS_NEXT_REALPATH(handle, fname);
931 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
936 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
937 if (result == NULL) {
942 * Take away what we've inserted. This removes the @GMT-thingy
943 * completely, but will give a path under the share root.
945 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
946 if (inserted == NULL) {
949 inserted_to = strstr_m(result, inserted);
950 if (inserted_to == NULL) {
951 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
954 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
955 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
959 TALLOC_FREE(inserted);
961 TALLOC_FREE(stripped);
967 * Check whether a given directory contains a
968 * snapshot directory as direct subdirectory.
969 * If yes, return the path of the snapshot-subdir,
970 * otherwise return NULL.
972 static char *have_snapdir(struct vfs_handle_struct *handle,
975 struct smb_filename smb_fname;
978 ZERO_STRUCT(smb_fname);
979 smb_fname.base_name = talloc_asprintf(
980 talloc_tos(), "%s/%s", path,
981 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
983 if (smb_fname.base_name == NULL) {
987 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
988 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
989 return smb_fname.base_name;
991 TALLOC_FREE(smb_fname.base_name);
996 * Find the snapshot directory (if any) for the given
997 * filename (which is relative to the share).
999 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1000 struct vfs_handle_struct *handle,
1001 struct smb_filename *smb_fname)
1006 path = talloc_asprintf(mem_ctx, "%s/%s",
1007 handle->conn->connectpath,
1008 smb_fname->base_name);
1013 snapdir = have_snapdir(handle, path);
1014 if (snapdir != NULL) {
1019 while ((p = strrchr(path, '/')) && (p > path)) {
1023 snapdir = have_snapdir(handle, path);
1024 if (snapdir != NULL) {
1033 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1035 char *gmt, size_t gmt_len)
1037 struct tm timestamp;
1039 unsigned long int timestamp_long;
1042 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1043 "format", GMT_FORMAT);
1045 ZERO_STRUCT(timestamp);
1046 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1047 if (sscanf(name, fmt, ×tamp_long) != 1) {
1048 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1049 "no sscanf match %s: %s\n",
1053 timestamp_t = timestamp_long;
1054 gmtime_r(×tamp_t, ×tamp);
1056 if (strptime(name, fmt, ×tamp) == NULL) {
1057 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1058 "no match %s: %s\n",
1062 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1065 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1066 timestamp.tm_isdst = -1;
1067 timestamp_t = mktime(×tamp);
1068 gmtime_r(×tamp_t, ×tamp);
1072 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1076 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1078 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1081 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1083 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1087 sort the shadow copy data in ascending or descending order
1089 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1090 struct shadow_copy_data *shadow_copy2_data)
1092 int (*cmpfunc)(const void *, const void *);
1095 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1101 if (strcmp(sort, "asc") == 0) {
1102 cmpfunc = shadow_copy2_label_cmp_asc;
1103 } else if (strcmp(sort, "desc") == 0) {
1104 cmpfunc = shadow_copy2_label_cmp_desc;
1109 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1110 shadow_copy2_data->labels)
1112 TYPESAFE_QSORT(shadow_copy2_data->labels,
1113 shadow_copy2_data->num_volumes,
1118 static int shadow_copy2_get_shadow_copy_data(
1119 vfs_handle_struct *handle, files_struct *fsp,
1120 struct shadow_copy_data *shadow_copy2_data,
1124 const char *snapdir;
1126 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1128 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1129 if (snapdir == NULL) {
1130 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1131 handle->conn->connectpath));
1133 talloc_free(tmp_ctx);
1137 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1140 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1141 " - %s\n", snapdir, strerror(errno)));
1142 talloc_free(tmp_ctx);
1147 shadow_copy2_data->num_volumes = 0;
1148 shadow_copy2_data->labels = NULL;
1150 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1151 char snapshot[GMT_NAME_LEN+1];
1152 SHADOW_COPY_LABEL *tlabels;
1155 * ignore names not of the right form in the snapshot
1158 if (!shadow_copy2_snapshot_to_gmt(
1160 snapshot, sizeof(snapshot))) {
1162 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1163 "ignoring %s\n", d->d_name));
1166 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1167 d->d_name, snapshot));
1170 /* the caller doesn't want the labels */
1171 shadow_copy2_data->num_volumes++;
1175 tlabels = talloc_realloc(shadow_copy2_data,
1176 shadow_copy2_data->labels,
1178 shadow_copy2_data->num_volumes+1);
1179 if (tlabels == NULL) {
1180 DEBUG(0,("shadow_copy2: out of memory\n"));
1181 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1182 talloc_free(tmp_ctx);
1186 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1189 shadow_copy2_data->num_volumes++;
1190 shadow_copy2_data->labels = tlabels;
1193 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1195 shadow_copy2_sort_data(handle, shadow_copy2_data);
1197 talloc_free(tmp_ctx);
1201 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1202 struct files_struct *fsp,
1203 uint32 security_info,
1204 TALLOC_CTX *mem_ctx,
1205 struct security_descriptor **ppdesc)
1212 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1213 fsp->fsp_name->base_name,
1214 ×tamp, &stripped)) {
1215 return map_nt_error_from_unix(errno);
1217 if (timestamp == 0) {
1218 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1222 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1223 TALLOC_FREE(stripped);
1225 return map_nt_error_from_unix(errno);
1227 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1233 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1235 uint32 security_info,
1236 TALLOC_CTX *mem_ctx,
1237 struct security_descriptor **ppdesc)
1244 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1245 ×tamp, &stripped)) {
1246 return map_nt_error_from_unix(errno);
1248 if (timestamp == 0) {
1249 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1252 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1253 TALLOC_FREE(stripped);
1255 return map_nt_error_from_unix(errno);
1257 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1263 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1264 const char *fname, mode_t mode)
1268 int ret, saved_errno;
1271 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1272 ×tamp, &stripped)) {
1275 if (timestamp == 0) {
1276 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1278 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1279 TALLOC_FREE(stripped);
1283 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1284 saved_errno = errno;
1286 errno = saved_errno;
1290 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1294 int ret, saved_errno;
1297 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1298 ×tamp, &stripped)) {
1301 if (timestamp == 0) {
1302 return SMB_VFS_NEXT_RMDIR(handle, fname);
1304 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1305 TALLOC_FREE(stripped);
1309 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1310 saved_errno = errno;
1312 errno = saved_errno;
1316 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1321 int ret, saved_errno;
1324 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1325 ×tamp, &stripped)) {
1328 if (timestamp == 0) {
1329 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1331 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1332 TALLOC_FREE(stripped);
1336 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1337 saved_errno = errno;
1339 errno = saved_errno;
1343 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1344 const char *fname, const char *aname,
1345 void *value, size_t size)
1353 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1354 ×tamp, &stripped)) {
1357 if (timestamp == 0) {
1358 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1361 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1362 TALLOC_FREE(stripped);
1366 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1367 saved_errno = errno;
1369 errno = saved_errno;
1373 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1375 char *list, size_t size)
1383 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1384 ×tamp, &stripped)) {
1387 if (timestamp == 0) {
1388 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1390 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1391 TALLOC_FREE(stripped);
1395 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1396 saved_errno = errno;
1398 errno = saved_errno;
1402 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1403 const char *fname, const char *aname)
1407 int ret, saved_errno;
1410 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1411 ×tamp, &stripped)) {
1414 if (timestamp == 0) {
1415 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1417 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1418 TALLOC_FREE(stripped);
1422 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1423 saved_errno = errno;
1425 errno = saved_errno;
1429 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1431 const char *aname, const void *value,
1432 size_t size, int flags)
1440 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1441 ×tamp, &stripped)) {
1444 if (timestamp == 0) {
1445 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1448 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1449 TALLOC_FREE(stripped);
1453 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1454 saved_errno = errno;
1456 errno = saved_errno;
1460 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1461 const char *fname, mode_t mode)
1469 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1470 ×tamp, &stripped)) {
1473 if (timestamp == 0) {
1474 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1476 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1477 TALLOC_FREE(stripped);
1481 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1482 saved_errno = errno;
1484 errno = saved_errno;
1488 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1491 TALLOC_CTX *mem_ctx,
1500 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1501 ×tamp, &stripped)) {
1504 if (timestamp == 0) {
1505 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1506 mem_ctx, found_name);
1508 if (stripped[0] == '\0') {
1509 *found_name = talloc_strdup(mem_ctx, name);
1510 if (*found_name == NULL) {
1516 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1517 TALLOC_FREE(stripped);
1521 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1522 mem_ctx, found_name);
1523 saved_errno = errno;
1525 errno = saved_errno;
1530 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1531 .opendir_fn = shadow_copy2_opendir,
1532 .rename_fn = shadow_copy2_rename,
1533 .link_fn = shadow_copy2_link,
1534 .symlink_fn = shadow_copy2_symlink,
1535 .stat_fn = shadow_copy2_stat,
1536 .lstat_fn = shadow_copy2_lstat,
1537 .fstat_fn = shadow_copy2_fstat,
1538 .open_fn = shadow_copy2_open,
1539 .unlink_fn = shadow_copy2_unlink,
1540 .chmod_fn = shadow_copy2_chmod,
1541 .chown_fn = shadow_copy2_chown,
1542 .chdir_fn = shadow_copy2_chdir,
1543 .ntimes_fn = shadow_copy2_ntimes,
1544 .readlink_fn = shadow_copy2_readlink,
1545 .mknod_fn = shadow_copy2_mknod,
1546 .realpath_fn = shadow_copy2_realpath,
1547 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1548 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1549 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1550 .mkdir_fn = shadow_copy2_mkdir,
1551 .rmdir_fn = shadow_copy2_rmdir,
1552 .getxattr_fn = shadow_copy2_getxattr,
1553 .listxattr_fn = shadow_copy2_listxattr,
1554 .removexattr_fn = shadow_copy2_removexattr,
1555 .setxattr_fn = shadow_copy2_setxattr,
1556 .chmod_acl_fn = shadow_copy2_chmod_acl,
1557 .chflags_fn = shadow_copy2_chflags,
1558 .get_real_filename_fn = shadow_copy2_get_real_filename,
1561 NTSTATUS vfs_shadow_copy2_init(void);
1562 NTSTATUS vfs_shadow_copy2_init(void)
1564 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1565 "shadow_copy2", &vfs_shadow_copy2_fns);