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;
145 * Given a timstamp, build the string to insert into a path
146 * as a path component for creating the local path to the
147 * snapshot at the given timestamp of the input path.
149 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
150 struct vfs_handle_struct *handle,
155 fstring snaptime_string;
158 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
159 "format", GMT_FORMAT);
161 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
162 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
163 (unsigned long)snapshot);
164 if (snaptime_len <= 0) {
165 DEBUG(10, ("snprintf failed\n"));
169 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
170 if (localtime_r(&snapshot, &snap_tm) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
175 if (gmtime_r(&snapshot, &snap_tm) == 0) {
176 DEBUG(10, ("gmtime_r failed\n"));
180 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
182 if (snaptime_len == 0) {
183 DEBUG(10, ("strftime failed\n"));
187 return talloc_asprintf(mem_ctx, "/%s/%s",
188 lp_parm_const_string(
189 SNUM(handle->conn), "shadow", "snapdir",
195 * Strip a snapshot component from an filename as
196 * handed in via the smb layer.
197 * Returns the parsed timestamp and the stripped filename.
199 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
200 struct vfs_handle_struct *handle,
210 size_t rest_len, dst_len;
212 p = strstr_m(name, "@GMT-");
216 if ((p > name) && (p[-1] != '/')) {
219 q = strptime(p, GMT_FORMAT, &tm);
224 timestamp = timegm(&tm);
225 if (timestamp == (time_t)-1) {
228 if ((p == name) && (q[0] == '\0')) {
229 if (pstripped != NULL) {
230 stripped = talloc_strdup(mem_ctx, "");
231 if (stripped == NULL) {
234 *pstripped = stripped;
236 *ptimestamp = timestamp;
244 rest_len = strlen(q);
245 dst_len = (p-name) + rest_len;
247 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
251 insert = shadow_copy2_insert_string(talloc_tos(), handle,
253 if (insert == NULL) {
258 have_insert = (strstr(name, insert+1) != NULL);
265 if (pstripped != NULL) {
266 stripped = talloc_array(mem_ctx, char, dst_len+1);
267 if (stripped == NULL) {
272 memcpy(stripped, name, p-name);
275 memcpy(stripped + (p-name), q, rest_len);
277 stripped[dst_len] = '\0';
278 *pstripped = stripped;
280 *ptimestamp = timestamp;
287 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
288 vfs_handle_struct *handle)
290 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
295 if (stat(path, &st) != 0) {
302 while ((p = strrchr(path, '/')) && p > path) {
304 if (stat(path, &st) != 0) {
308 if (st.st_dev != dev) {
318 * Convert from a name as handed in via the SMB layer
319 * and a timestamp into the local path of the snapshot
320 * of the provided file at the provided time.
322 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
323 struct vfs_handle_struct *handle,
324 const char *name, time_t timestamp)
326 struct smb_filename converted_fname;
328 size_t *slashes = NULL;
329 unsigned num_slashes;
333 char *converted = NULL;
338 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
344 pathlen = talloc_get_size(path)-1;
346 DEBUG(10, ("converting %s\n", path));
348 if (!shadow_copy2_find_slashes(talloc_tos(), path,
349 &slashes, &num_slashes)) {
352 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
353 if (insert == NULL) {
356 insertlen = talloc_get_size(insert)-1;
357 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
358 if (converted == NULL) {
362 if (path[pathlen-1] != '/') {
364 * Append a fake slash to find the snapshot root
367 tmp = talloc_realloc(talloc_tos(), slashes,
368 size_t, num_slashes+1);
373 slashes[num_slashes] = pathlen;
379 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
383 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
385 if (mount_point == NULL) {
388 min_offset = strlen(mount_point);
389 TALLOC_FREE(mount_point);
392 memcpy(converted, path, pathlen+1);
393 converted[pathlen+insertlen] = '\0';
395 ZERO_STRUCT(converted_fname);
396 converted_fname.base_name = converted;
398 for (i = num_slashes-1; i>=0; i--) {
404 if (offset < min_offset) {
409 memcpy(converted+offset, insert, insertlen);
412 memcpy(converted+offset, path + slashes[i],
413 pathlen - slashes[i]);
415 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
417 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
418 ret, ret == 0 ? "ok" : strerror(errno)));
423 if (errno == ENOTDIR) {
425 * This is a valid condition: We appended the
426 * .snaphots/@GMT.. to a file name. Just try
427 * with the upper levels.
431 if (errno != ENOENT) {
432 /* Other problem than "not found" */
441 DEBUG(10, ("Found %s\n", converted));
449 TALLOC_FREE(converted);
451 TALLOC_FREE(slashes);
458 modify a sbuf return to ensure that inodes in the shadow directory
459 are different from those in the main directory
461 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
462 SMB_STRUCT_STAT *sbuf)
464 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
465 /* some snapshot systems, like GPFS, return the name
466 device:inode for the snapshot files as the current
467 files. That breaks the 'restore' button in the shadow copy
468 GUI, as the client gets a sharing violation.
470 This is a crude way of allowing both files to be
471 open at once. It has a slight chance of inode
472 number collision, but I can't see a better approach
473 without significant VFS changes
477 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
481 sbuf->st_ex_ino ^= shash;
485 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
496 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
497 ×tamp, &stripped)) {
500 if (timestamp == 0) {
501 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
503 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
504 TALLOC_FREE(stripped);
508 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
515 static int shadow_copy2_rename(vfs_handle_struct *handle,
516 const struct smb_filename *smb_fname_src,
517 const struct smb_filename *smb_fname_dst)
519 time_t timestamp_src, timestamp_dst;
521 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
522 smb_fname_src->base_name,
523 ×tamp_src, NULL)) {
526 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
527 smb_fname_dst->base_name,
528 ×tamp_dst, NULL)) {
531 if (timestamp_src != 0) {
535 if (timestamp_dst != 0) {
539 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
542 static int shadow_copy2_symlink(vfs_handle_struct *handle,
543 const char *oldname, const char *newname)
545 time_t timestamp_old, timestamp_new;
547 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
548 ×tamp_old, NULL)) {
551 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
552 ×tamp_new, NULL)) {
555 if ((timestamp_old != 0) || (timestamp_new != 0)) {
559 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
562 static int shadow_copy2_link(vfs_handle_struct *handle,
563 const char *oldname, const char *newname)
565 time_t timestamp_old, timestamp_new;
567 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
568 ×tamp_old, NULL)) {
571 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
572 ×tamp_new, NULL)) {
575 if ((timestamp_old != 0) || (timestamp_new != 0)) {
579 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
582 static int shadow_copy2_stat(vfs_handle_struct *handle,
583 struct smb_filename *smb_fname)
586 char *stripped, *tmp;
587 int ret, saved_errno;
589 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
590 smb_fname->base_name,
591 ×tamp, &stripped)) {
594 if (timestamp == 0) {
595 return SMB_VFS_NEXT_STAT(handle, smb_fname);
598 tmp = smb_fname->base_name;
599 smb_fname->base_name = shadow_copy2_convert(
600 talloc_tos(), handle, stripped, timestamp);
601 TALLOC_FREE(stripped);
603 if (smb_fname->base_name == NULL) {
604 smb_fname->base_name = tmp;
608 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
611 TALLOC_FREE(smb_fname->base_name);
612 smb_fname->base_name = tmp;
615 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
621 static int shadow_copy2_lstat(vfs_handle_struct *handle,
622 struct smb_filename *smb_fname)
625 char *stripped, *tmp;
626 int ret, saved_errno;
628 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
629 smb_fname->base_name,
630 ×tamp, &stripped)) {
633 if (timestamp == 0) {
634 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
637 tmp = smb_fname->base_name;
638 smb_fname->base_name = shadow_copy2_convert(
639 talloc_tos(), handle, stripped, timestamp);
640 TALLOC_FREE(stripped);
642 if (smb_fname->base_name == NULL) {
643 smb_fname->base_name = tmp;
647 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
650 TALLOC_FREE(smb_fname->base_name);
651 smb_fname->base_name = tmp;
654 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
660 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
661 SMB_STRUCT_STAT *sbuf)
666 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
670 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
671 fsp->fsp_name->base_name,
675 if (timestamp != 0) {
676 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
681 static int shadow_copy2_open(vfs_handle_struct *handle,
682 struct smb_filename *smb_fname, files_struct *fsp,
683 int flags, mode_t mode)
686 char *stripped, *tmp;
687 int ret, saved_errno;
689 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
690 smb_fname->base_name,
691 ×tamp, &stripped)) {
694 if (timestamp == 0) {
695 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
698 tmp = smb_fname->base_name;
699 smb_fname->base_name = shadow_copy2_convert(
700 talloc_tos(), handle, stripped, timestamp);
701 TALLOC_FREE(stripped);
703 if (smb_fname->base_name == NULL) {
704 smb_fname->base_name = tmp;
708 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
711 TALLOC_FREE(smb_fname->base_name);
712 smb_fname->base_name = tmp;
718 static int shadow_copy2_unlink(vfs_handle_struct *handle,
719 const struct smb_filename *smb_fname)
723 int ret, saved_errno;
724 struct smb_filename *conv;
726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
727 smb_fname->base_name,
728 ×tamp, &stripped)) {
731 if (timestamp == 0) {
732 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
734 conv = cp_smb_filename(talloc_tos(), smb_fname);
739 conv->base_name = shadow_copy2_convert(
740 conv, handle, stripped, timestamp);
741 TALLOC_FREE(stripped);
742 if (conv->base_name == NULL) {
745 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
752 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
757 int ret, saved_errno;
760 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
761 ×tamp, &stripped)) {
764 if (timestamp == 0) {
765 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
767 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
768 TALLOC_FREE(stripped);
772 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
779 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
780 uid_t uid, gid_t gid)
784 int ret, saved_errno;
787 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
788 ×tamp, &stripped)) {
791 if (timestamp == 0) {
792 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
794 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
795 TALLOC_FREE(stripped);
799 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
806 static int shadow_copy2_chdir(vfs_handle_struct *handle,
811 int ret, saved_errno;
814 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
815 ×tamp, &stripped)) {
818 if (timestamp == 0) {
819 return SMB_VFS_NEXT_CHDIR(handle, fname);
821 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
822 TALLOC_FREE(stripped);
826 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
833 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
834 const struct smb_filename *smb_fname,
835 struct smb_file_time *ft)
839 int ret, saved_errno;
840 struct smb_filename *conv;
842 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
843 smb_fname->base_name,
844 ×tamp, &stripped)) {
847 if (timestamp == 0) {
848 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
850 conv = cp_smb_filename(talloc_tos(), smb_fname);
855 conv->base_name = shadow_copy2_convert(
856 conv, handle, stripped, timestamp);
857 TALLOC_FREE(stripped);
858 if (conv->base_name == NULL) {
861 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
868 static int shadow_copy2_readlink(vfs_handle_struct *handle,
869 const char *fname, char *buf, size_t bufsiz)
873 int ret, saved_errno;
876 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
877 ×tamp, &stripped)) {
880 if (timestamp == 0) {
881 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
883 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
884 TALLOC_FREE(stripped);
888 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
895 static int shadow_copy2_mknod(vfs_handle_struct *handle,
896 const char *fname, mode_t mode, SMB_DEV_T dev)
900 int ret, saved_errno;
903 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
904 ×tamp, &stripped)) {
907 if (timestamp == 0) {
908 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
910 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
911 TALLOC_FREE(stripped);
915 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
922 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
926 char *stripped = NULL;
929 char *inserted = NULL;
930 char *inserted_to, *inserted_end;
933 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
934 ×tamp, &stripped)) {
937 if (timestamp == 0) {
938 return SMB_VFS_NEXT_REALPATH(handle, fname);
941 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
946 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
947 if (result == NULL) {
952 * Take away what we've inserted. This removes the @GMT-thingy
953 * completely, but will give a path under the share root.
955 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
956 if (inserted == NULL) {
959 inserted_to = strstr_m(result, inserted);
960 if (inserted_to == NULL) {
961 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
964 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
965 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
969 TALLOC_FREE(inserted);
971 TALLOC_FREE(stripped);
977 * Check whether a given directory contains a
978 * snapshot directory as direct subdirectory.
979 * If yes, return the path of the snapshot-subdir,
980 * otherwise return NULL.
982 static char *have_snapdir(struct vfs_handle_struct *handle,
985 struct smb_filename smb_fname;
988 ZERO_STRUCT(smb_fname);
989 smb_fname.base_name = talloc_asprintf(
990 talloc_tos(), "%s/%s", path,
991 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
993 if (smb_fname.base_name == NULL) {
997 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
998 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
999 return smb_fname.base_name;
1001 TALLOC_FREE(smb_fname.base_name);
1006 * Find the snapshot directory (if any) for the given
1007 * filename (which is relative to the share).
1009 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1010 struct vfs_handle_struct *handle,
1011 struct smb_filename *smb_fname)
1016 path = talloc_asprintf(mem_ctx, "%s/%s",
1017 handle->conn->connectpath,
1018 smb_fname->base_name);
1023 snapdir = have_snapdir(handle, path);
1024 if (snapdir != NULL) {
1029 while ((p = strrchr(path, '/')) && (p > path)) {
1033 snapdir = have_snapdir(handle, path);
1034 if (snapdir != NULL) {
1043 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1045 char *gmt, size_t gmt_len)
1047 struct tm timestamp;
1049 unsigned long int timestamp_long;
1052 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1053 "format", GMT_FORMAT);
1055 ZERO_STRUCT(timestamp);
1056 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1057 if (sscanf(name, fmt, ×tamp_long) != 1) {
1058 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1059 "no sscanf match %s: %s\n",
1063 timestamp_t = timestamp_long;
1064 gmtime_r(×tamp_t, ×tamp);
1066 if (strptime(name, fmt, ×tamp) == NULL) {
1067 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1068 "no match %s: %s\n",
1072 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1075 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1076 timestamp.tm_isdst = -1;
1077 timestamp_t = mktime(×tamp);
1078 gmtime_r(×tamp_t, ×tamp);
1082 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1086 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1088 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1091 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1093 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1097 sort the shadow copy data in ascending or descending order
1099 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1100 struct shadow_copy_data *shadow_copy2_data)
1102 int (*cmpfunc)(const void *, const void *);
1105 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1111 if (strcmp(sort, "asc") == 0) {
1112 cmpfunc = shadow_copy2_label_cmp_asc;
1113 } else if (strcmp(sort, "desc") == 0) {
1114 cmpfunc = shadow_copy2_label_cmp_desc;
1119 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1120 shadow_copy2_data->labels)
1122 TYPESAFE_QSORT(shadow_copy2_data->labels,
1123 shadow_copy2_data->num_volumes,
1128 static int shadow_copy2_get_shadow_copy_data(
1129 vfs_handle_struct *handle, files_struct *fsp,
1130 struct shadow_copy_data *shadow_copy2_data,
1134 const char *snapdir;
1136 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1138 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1139 if (snapdir == NULL) {
1140 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1141 handle->conn->connectpath));
1143 talloc_free(tmp_ctx);
1147 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1150 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1151 " - %s\n", snapdir, strerror(errno)));
1152 talloc_free(tmp_ctx);
1157 shadow_copy2_data->num_volumes = 0;
1158 shadow_copy2_data->labels = NULL;
1160 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1161 char snapshot[GMT_NAME_LEN+1];
1162 SHADOW_COPY_LABEL *tlabels;
1165 * ignore names not of the right form in the snapshot
1168 if (!shadow_copy2_snapshot_to_gmt(
1170 snapshot, sizeof(snapshot))) {
1172 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1173 "ignoring %s\n", d->d_name));
1176 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1177 d->d_name, snapshot));
1180 /* the caller doesn't want the labels */
1181 shadow_copy2_data->num_volumes++;
1185 tlabels = talloc_realloc(shadow_copy2_data,
1186 shadow_copy2_data->labels,
1188 shadow_copy2_data->num_volumes+1);
1189 if (tlabels == NULL) {
1190 DEBUG(0,("shadow_copy2: out of memory\n"));
1191 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1192 talloc_free(tmp_ctx);
1196 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1199 shadow_copy2_data->num_volumes++;
1200 shadow_copy2_data->labels = tlabels;
1203 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1205 shadow_copy2_sort_data(handle, shadow_copy2_data);
1207 talloc_free(tmp_ctx);
1211 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1212 struct files_struct *fsp,
1213 uint32 security_info,
1214 TALLOC_CTX *mem_ctx,
1215 struct security_descriptor **ppdesc)
1222 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1223 fsp->fsp_name->base_name,
1224 ×tamp, &stripped)) {
1225 return map_nt_error_from_unix(errno);
1227 if (timestamp == 0) {
1228 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1232 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1233 TALLOC_FREE(stripped);
1235 return map_nt_error_from_unix(errno);
1237 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1243 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1245 uint32 security_info,
1246 TALLOC_CTX *mem_ctx,
1247 struct security_descriptor **ppdesc)
1254 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1255 ×tamp, &stripped)) {
1256 return map_nt_error_from_unix(errno);
1258 if (timestamp == 0) {
1259 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1262 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1263 TALLOC_FREE(stripped);
1265 return map_nt_error_from_unix(errno);
1267 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1273 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1274 const char *fname, mode_t mode)
1278 int ret, saved_errno;
1281 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1282 ×tamp, &stripped)) {
1285 if (timestamp == 0) {
1286 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1288 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1289 TALLOC_FREE(stripped);
1293 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1294 saved_errno = errno;
1296 errno = saved_errno;
1300 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1304 int ret, saved_errno;
1307 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1308 ×tamp, &stripped)) {
1311 if (timestamp == 0) {
1312 return SMB_VFS_NEXT_RMDIR(handle, fname);
1314 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1315 TALLOC_FREE(stripped);
1319 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1320 saved_errno = errno;
1322 errno = saved_errno;
1326 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1331 int ret, saved_errno;
1334 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1335 ×tamp, &stripped)) {
1338 if (timestamp == 0) {
1339 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1341 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1342 TALLOC_FREE(stripped);
1346 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1347 saved_errno = errno;
1349 errno = saved_errno;
1353 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1354 const char *fname, const char *aname,
1355 void *value, size_t size)
1363 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1364 ×tamp, &stripped)) {
1367 if (timestamp == 0) {
1368 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1371 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1372 TALLOC_FREE(stripped);
1376 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1377 saved_errno = errno;
1379 errno = saved_errno;
1383 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1385 char *list, size_t size)
1393 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1394 ×tamp, &stripped)) {
1397 if (timestamp == 0) {
1398 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1400 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1401 TALLOC_FREE(stripped);
1405 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1406 saved_errno = errno;
1408 errno = saved_errno;
1412 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1413 const char *fname, const char *aname)
1417 int ret, saved_errno;
1420 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1421 ×tamp, &stripped)) {
1424 if (timestamp == 0) {
1425 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1427 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1428 TALLOC_FREE(stripped);
1432 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1433 saved_errno = errno;
1435 errno = saved_errno;
1439 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1441 const char *aname, const void *value,
1442 size_t size, int flags)
1450 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1451 ×tamp, &stripped)) {
1454 if (timestamp == 0) {
1455 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1458 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1459 TALLOC_FREE(stripped);
1463 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1464 saved_errno = errno;
1466 errno = saved_errno;
1470 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1471 const char *fname, mode_t mode)
1479 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1480 ×tamp, &stripped)) {
1483 if (timestamp == 0) {
1484 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1486 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1487 TALLOC_FREE(stripped);
1491 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1492 saved_errno = errno;
1494 errno = saved_errno;
1498 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1501 TALLOC_CTX *mem_ctx,
1510 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1511 ×tamp, &stripped)) {
1514 if (timestamp == 0) {
1515 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1516 mem_ctx, found_name);
1518 if (stripped[0] == '\0') {
1519 *found_name = talloc_strdup(mem_ctx, name);
1520 if (*found_name == NULL) {
1526 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1527 TALLOC_FREE(stripped);
1531 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1532 mem_ctx, found_name);
1533 saved_errno = errno;
1535 errno = saved_errno;
1540 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1541 .opendir_fn = shadow_copy2_opendir,
1542 .rename_fn = shadow_copy2_rename,
1543 .link_fn = shadow_copy2_link,
1544 .symlink_fn = shadow_copy2_symlink,
1545 .stat_fn = shadow_copy2_stat,
1546 .lstat_fn = shadow_copy2_lstat,
1547 .fstat_fn = shadow_copy2_fstat,
1548 .open_fn = shadow_copy2_open,
1549 .unlink_fn = shadow_copy2_unlink,
1550 .chmod_fn = shadow_copy2_chmod,
1551 .chown_fn = shadow_copy2_chown,
1552 .chdir_fn = shadow_copy2_chdir,
1553 .ntimes_fn = shadow_copy2_ntimes,
1554 .readlink_fn = shadow_copy2_readlink,
1555 .mknod_fn = shadow_copy2_mknod,
1556 .realpath_fn = shadow_copy2_realpath,
1557 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1558 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1559 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1560 .mkdir_fn = shadow_copy2_mkdir,
1561 .rmdir_fn = shadow_copy2_rmdir,
1562 .getxattr_fn = shadow_copy2_getxattr,
1563 .listxattr_fn = shadow_copy2_listxattr,
1564 .removexattr_fn = shadow_copy2_removexattr,
1565 .setxattr_fn = shadow_copy2_setxattr,
1566 .chmod_acl_fn = shadow_copy2_chmod_acl,
1567 .chflags_fn = shadow_copy2_chflags,
1568 .get_real_filename_fn = shadow_copy2_get_real_filename,
1571 NTSTATUS vfs_shadow_copy2_init(void);
1572 NTSTATUS vfs_shadow_copy2_init(void)
1574 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1575 "shadow_copy2", &vfs_shadow_copy2_fns);