2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 struct shadow_copy2_config {
115 bool snapdirseverywhere;
116 bool crossmountpoints;
119 bool snapdir_absolute;
122 char *rel_connectpath; /* share root, relative to the basedir */
123 char *snapshot_basepath; /* the absolute version of snapdir */
126 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
128 unsigned *pnum_offsets)
130 unsigned num_offsets;
137 while ((p = strchr(p, '/')) != NULL) {
142 offsets = talloc_array(mem_ctx, size_t, num_offsets);
143 if (offsets == NULL) {
149 while ((p = strchr(p, '/')) != NULL) {
150 offsets[num_offsets] = p-str;
156 *pnum_offsets = num_offsets;
161 * Given a timstamp, build the string to insert into a path
162 * as a path component for creating the local path to the
163 * snapshot at the given timestamp of the input path.
165 * In the case of a parallel snapdir (specified with an
166 * absolute path), this is the inital portion of the
167 * local path of any snapshot file. The complete path is
168 * obtained by appending the portion of the file's path
169 * below the share root's mountpoint.
171 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
172 struct vfs_handle_struct *handle,
176 fstring snaptime_string;
178 struct shadow_copy2_config *config;
181 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
184 if (config->use_sscanf) {
185 snaptime_len = snprintf(snaptime_string,
186 sizeof(snaptime_string),
188 (unsigned long)snapshot);
189 if (snaptime_len <= 0) {
190 DEBUG(10, ("snprintf failed\n"));
194 if (config->use_localtime) {
195 if (localtime_r(&snapshot, &snap_tm) == 0) {
196 DEBUG(10, ("gmtime_r failed\n"));
200 if (gmtime_r(&snapshot, &snap_tm) == 0) {
201 DEBUG(10, ("gmtime_r failed\n"));
205 snaptime_len = strftime(snaptime_string,
206 sizeof(snaptime_string),
209 if (snaptime_len == 0) {
210 DEBUG(10, ("strftime failed\n"));
215 if (config->snapdir_absolute) {
216 result = talloc_asprintf(mem_ctx, "%s/%s",
217 config->snapdir, snaptime_string);
219 result = talloc_asprintf(mem_ctx, "/%s/%s",
220 config->snapdir, snaptime_string);
222 if (result == NULL) {
223 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
230 * Strip a snapshot component from an filename as
231 * handed in via the smb layer.
232 * Returns the parsed timestamp and the stripped filename.
234 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
235 struct vfs_handle_struct *handle,
245 size_t rest_len, dst_len;
246 struct shadow_copy2_config *config;
248 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
251 p = strstr_m(name, "@GMT-");
255 if ((p > name) && (p[-1] != '/')) {
258 q = strptime(p, GMT_FORMAT, &tm);
263 timestamp = timegm(&tm);
264 if (timestamp == (time_t)-1) {
267 if ((p == name) && (q[0] == '\0')) {
268 if (pstripped != NULL) {
269 stripped = talloc_strdup(mem_ctx, "");
270 if (stripped == NULL) {
273 *pstripped = stripped;
275 *ptimestamp = timestamp;
283 rest_len = strlen(q);
284 dst_len = (p-name) + rest_len;
286 if (config->snapdirseverywhere) {
289 insert = shadow_copy2_insert_string(talloc_tos(), handle,
291 if (insert == NULL) {
296 have_insert = (strstr(name, insert+1) != NULL);
303 if (pstripped != NULL) {
304 stripped = talloc_array(mem_ctx, char, dst_len+1);
305 if (stripped == NULL) {
310 memcpy(stripped, name, p-name);
313 memcpy(stripped + (p-name), q, rest_len);
315 stripped[dst_len] = '\0';
316 *pstripped = stripped;
318 *ptimestamp = timestamp;
325 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
326 vfs_handle_struct *handle)
328 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
333 if (stat(path, &st) != 0) {
340 while ((p = strrchr(path, '/')) && p > path) {
342 if (stat(path, &st) != 0) {
346 if (st.st_dev != dev) {
356 * Convert from a name as handed in via the SMB layer
357 * and a timestamp into the local path of the snapshot
358 * of the provided file at the provided time.
360 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
361 struct vfs_handle_struct *handle,
362 const char *name, time_t timestamp)
364 struct smb_filename converted_fname;
366 size_t *slashes = NULL;
367 unsigned num_slashes;
371 char *converted = NULL;
375 struct shadow_copy2_config *config;
377 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
380 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
386 pathlen = talloc_get_size(path)-1;
388 DEBUG(10, ("converting %s\n", path));
390 if (!shadow_copy2_find_slashes(talloc_tos(), path,
391 &slashes, &num_slashes)) {
394 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
395 if (insert == NULL) {
398 insertlen = talloc_get_size(insert)-1;
399 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
400 if (converted == NULL) {
404 if (path[pathlen-1] != '/') {
406 * Append a fake slash to find the snapshot root
409 tmp = talloc_realloc(talloc_tos(), slashes,
410 size_t, num_slashes+1);
415 slashes[num_slashes] = pathlen;
421 if (!config->crossmountpoints) {
424 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
426 if (mount_point == NULL) {
429 min_offset = strlen(mount_point);
430 TALLOC_FREE(mount_point);
433 memcpy(converted, path, pathlen+1);
434 converted[pathlen+insertlen] = '\0';
436 ZERO_STRUCT(converted_fname);
437 converted_fname.base_name = converted;
439 for (i = num_slashes-1; i>=0; i--) {
445 if (offset < min_offset) {
450 memcpy(converted+offset, insert, insertlen);
453 memcpy(converted+offset, path + slashes[i],
454 pathlen - slashes[i]);
456 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
458 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
459 ret, ret == 0 ? "ok" : strerror(errno)));
464 if (errno == ENOTDIR) {
466 * This is a valid condition: We appended the
467 * .snaphots/@GMT.. to a file name. Just try
468 * with the upper levels.
472 if (errno != ENOENT) {
473 /* Other problem than "not found" */
482 DEBUG(10, ("Found %s\n", converted));
490 TALLOC_FREE(converted);
492 TALLOC_FREE(slashes);
499 modify a sbuf return to ensure that inodes in the shadow directory
500 are different from those in the main directory
502 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
503 SMB_STRUCT_STAT *sbuf)
505 struct shadow_copy2_config *config;
507 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
510 if (config->fixinodes) {
511 /* some snapshot systems, like GPFS, return the name
512 device:inode for the snapshot files as the current
513 files. That breaks the 'restore' button in the shadow copy
514 GUI, as the client gets a sharing violation.
516 This is a crude way of allowing both files to be
517 open at once. It has a slight chance of inode
518 number collision, but I can't see a better approach
519 without significant VFS changes
523 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
527 sbuf->st_ex_ino ^= shash;
531 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
542 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
543 ×tamp, &stripped)) {
546 if (timestamp == 0) {
547 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
549 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
550 TALLOC_FREE(stripped);
554 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
561 static int shadow_copy2_rename(vfs_handle_struct *handle,
562 const struct smb_filename *smb_fname_src,
563 const struct smb_filename *smb_fname_dst)
565 time_t timestamp_src, timestamp_dst;
567 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
568 smb_fname_src->base_name,
569 ×tamp_src, NULL)) {
572 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
573 smb_fname_dst->base_name,
574 ×tamp_dst, NULL)) {
577 if (timestamp_src != 0) {
581 if (timestamp_dst != 0) {
585 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
588 static int shadow_copy2_symlink(vfs_handle_struct *handle,
589 const char *oldname, const char *newname)
591 time_t timestamp_old, timestamp_new;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
594 ×tamp_old, NULL)) {
597 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
598 ×tamp_new, NULL)) {
601 if ((timestamp_old != 0) || (timestamp_new != 0)) {
605 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
608 static int shadow_copy2_link(vfs_handle_struct *handle,
609 const char *oldname, const char *newname)
611 time_t timestamp_old, timestamp_new;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
614 ×tamp_old, NULL)) {
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
618 ×tamp_new, NULL)) {
621 if ((timestamp_old != 0) || (timestamp_new != 0)) {
625 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
628 static int shadow_copy2_stat(vfs_handle_struct *handle,
629 struct smb_filename *smb_fname)
632 char *stripped, *tmp;
633 int ret, saved_errno;
635 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
636 smb_fname->base_name,
637 ×tamp, &stripped)) {
640 if (timestamp == 0) {
641 return SMB_VFS_NEXT_STAT(handle, smb_fname);
644 tmp = smb_fname->base_name;
645 smb_fname->base_name = shadow_copy2_convert(
646 talloc_tos(), handle, stripped, timestamp);
647 TALLOC_FREE(stripped);
649 if (smb_fname->base_name == NULL) {
650 smb_fname->base_name = tmp;
654 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
657 TALLOC_FREE(smb_fname->base_name);
658 smb_fname->base_name = tmp;
661 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
667 static int shadow_copy2_lstat(vfs_handle_struct *handle,
668 struct smb_filename *smb_fname)
671 char *stripped, *tmp;
672 int ret, saved_errno;
674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
675 smb_fname->base_name,
676 ×tamp, &stripped)) {
679 if (timestamp == 0) {
680 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
683 tmp = smb_fname->base_name;
684 smb_fname->base_name = shadow_copy2_convert(
685 talloc_tos(), handle, stripped, timestamp);
686 TALLOC_FREE(stripped);
688 if (smb_fname->base_name == NULL) {
689 smb_fname->base_name = tmp;
693 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
696 TALLOC_FREE(smb_fname->base_name);
697 smb_fname->base_name = tmp;
700 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
706 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
707 SMB_STRUCT_STAT *sbuf)
712 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
716 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
717 fsp->fsp_name->base_name,
721 if (timestamp != 0) {
722 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
727 static int shadow_copy2_open(vfs_handle_struct *handle,
728 struct smb_filename *smb_fname, files_struct *fsp,
729 int flags, mode_t mode)
732 char *stripped, *tmp;
733 int ret, saved_errno;
735 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
736 smb_fname->base_name,
737 ×tamp, &stripped)) {
740 if (timestamp == 0) {
741 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
744 tmp = smb_fname->base_name;
745 smb_fname->base_name = shadow_copy2_convert(
746 talloc_tos(), handle, stripped, timestamp);
747 TALLOC_FREE(stripped);
749 if (smb_fname->base_name == NULL) {
750 smb_fname->base_name = tmp;
754 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
757 TALLOC_FREE(smb_fname->base_name);
758 smb_fname->base_name = tmp;
764 static int shadow_copy2_unlink(vfs_handle_struct *handle,
765 const struct smb_filename *smb_fname)
769 int ret, saved_errno;
770 struct smb_filename *conv;
772 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
773 smb_fname->base_name,
774 ×tamp, &stripped)) {
777 if (timestamp == 0) {
778 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
780 conv = cp_smb_filename(talloc_tos(), smb_fname);
785 conv->base_name = shadow_copy2_convert(
786 conv, handle, stripped, timestamp);
787 TALLOC_FREE(stripped);
788 if (conv->base_name == NULL) {
791 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
798 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
803 int ret, saved_errno;
806 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
807 ×tamp, &stripped)) {
810 if (timestamp == 0) {
811 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
813 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
814 TALLOC_FREE(stripped);
818 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
825 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
826 uid_t uid, gid_t gid)
830 int ret, saved_errno;
833 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
834 ×tamp, &stripped)) {
837 if (timestamp == 0) {
838 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
840 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
841 TALLOC_FREE(stripped);
845 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
852 static int shadow_copy2_chdir(vfs_handle_struct *handle,
857 int ret, saved_errno;
860 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
861 ×tamp, &stripped)) {
864 if (timestamp == 0) {
865 return SMB_VFS_NEXT_CHDIR(handle, fname);
867 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
868 TALLOC_FREE(stripped);
872 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
879 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
880 const struct smb_filename *smb_fname,
881 struct smb_file_time *ft)
885 int ret, saved_errno;
886 struct smb_filename *conv;
888 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
889 smb_fname->base_name,
890 ×tamp, &stripped)) {
893 if (timestamp == 0) {
894 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
896 conv = cp_smb_filename(talloc_tos(), smb_fname);
901 conv->base_name = shadow_copy2_convert(
902 conv, handle, stripped, timestamp);
903 TALLOC_FREE(stripped);
904 if (conv->base_name == NULL) {
907 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
914 static int shadow_copy2_readlink(vfs_handle_struct *handle,
915 const char *fname, char *buf, size_t bufsiz)
919 int ret, saved_errno;
922 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
923 ×tamp, &stripped)) {
926 if (timestamp == 0) {
927 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
929 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
930 TALLOC_FREE(stripped);
934 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
941 static int shadow_copy2_mknod(vfs_handle_struct *handle,
942 const char *fname, mode_t mode, SMB_DEV_T dev)
946 int ret, saved_errno;
949 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
950 ×tamp, &stripped)) {
953 if (timestamp == 0) {
954 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
956 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
957 TALLOC_FREE(stripped);
961 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
968 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
972 char *stripped = NULL;
975 char *inserted = NULL;
976 char *inserted_to, *inserted_end;
979 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
980 ×tamp, &stripped)) {
983 if (timestamp == 0) {
984 return SMB_VFS_NEXT_REALPATH(handle, fname);
987 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
992 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
993 if (result == NULL) {
998 * Take away what we've inserted. This removes the @GMT-thingy
999 * completely, but will give a path under the share root.
1001 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1002 if (inserted == NULL) {
1005 inserted_to = strstr_m(result, inserted);
1006 if (inserted_to == NULL) {
1007 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1010 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1011 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1014 saved_errno = errno;
1015 TALLOC_FREE(inserted);
1017 TALLOC_FREE(stripped);
1018 errno = saved_errno;
1023 * Check whether a given directory contains a
1024 * snapshot directory as direct subdirectory.
1025 * If yes, return the path of the snapshot-subdir,
1026 * otherwise return NULL.
1028 static char *have_snapdir(struct vfs_handle_struct *handle,
1031 struct smb_filename smb_fname;
1033 struct shadow_copy2_config *config;
1035 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1038 ZERO_STRUCT(smb_fname);
1039 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1040 path, config->snapdir);
1041 if (smb_fname.base_name == NULL) {
1045 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1046 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1047 return smb_fname.base_name;
1049 TALLOC_FREE(smb_fname.base_name);
1054 * Find the snapshot directory (if any) for the given
1055 * filename (which is relative to the share).
1057 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1058 struct vfs_handle_struct *handle,
1059 struct smb_filename *smb_fname)
1062 const char *snapdir;
1063 struct shadow_copy2_config *config;
1065 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1069 * If the non-snapdisrseverywhere mode, we should not search!
1071 if (!config->snapdirseverywhere) {
1072 return config->snapshot_basepath;
1075 path = talloc_asprintf(mem_ctx, "%s/%s",
1076 handle->conn->connectpath,
1077 smb_fname->base_name);
1082 snapdir = have_snapdir(handle, path);
1083 if (snapdir != NULL) {
1088 while ((p = strrchr(path, '/')) && (p > path)) {
1092 snapdir = have_snapdir(handle, path);
1093 if (snapdir != NULL) {
1102 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1104 char *gmt, size_t gmt_len)
1106 struct tm timestamp;
1108 unsigned long int timestamp_long;
1110 struct shadow_copy2_config *config;
1112 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1115 fmt = config->gmt_format;
1117 ZERO_STRUCT(timestamp);
1118 if (config->use_sscanf) {
1119 if (sscanf(name, fmt, ×tamp_long) != 1) {
1120 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1121 "no sscanf match %s: %s\n",
1125 timestamp_t = timestamp_long;
1126 gmtime_r(×tamp_t, ×tamp);
1128 if (strptime(name, fmt, ×tamp) == NULL) {
1129 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1130 "no match %s: %s\n",
1134 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1137 if (config->use_localtime) {
1138 timestamp.tm_isdst = -1;
1139 timestamp_t = mktime(×tamp);
1140 gmtime_r(×tamp_t, ×tamp);
1144 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1148 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1150 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1153 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1155 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1159 sort the shadow copy data in ascending or descending order
1161 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1162 struct shadow_copy_data *shadow_copy2_data)
1164 int (*cmpfunc)(const void *, const void *);
1166 struct shadow_copy2_config *config;
1168 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1171 sort = config->sort_order;
1176 if (strcmp(sort, "asc") == 0) {
1177 cmpfunc = shadow_copy2_label_cmp_asc;
1178 } else if (strcmp(sort, "desc") == 0) {
1179 cmpfunc = shadow_copy2_label_cmp_desc;
1184 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1185 shadow_copy2_data->labels)
1187 TYPESAFE_QSORT(shadow_copy2_data->labels,
1188 shadow_copy2_data->num_volumes,
1193 static int shadow_copy2_get_shadow_copy_data(
1194 vfs_handle_struct *handle, files_struct *fsp,
1195 struct shadow_copy_data *shadow_copy2_data,
1199 const char *snapdir;
1201 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1203 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1204 if (snapdir == NULL) {
1205 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1206 handle->conn->connectpath));
1208 talloc_free(tmp_ctx);
1212 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1215 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1216 " - %s\n", snapdir, strerror(errno)));
1217 talloc_free(tmp_ctx);
1222 shadow_copy2_data->num_volumes = 0;
1223 shadow_copy2_data->labels = NULL;
1225 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1226 char snapshot[GMT_NAME_LEN+1];
1227 SHADOW_COPY_LABEL *tlabels;
1230 * ignore names not of the right form in the snapshot
1233 if (!shadow_copy2_snapshot_to_gmt(
1235 snapshot, sizeof(snapshot))) {
1237 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1238 "ignoring %s\n", d->d_name));
1241 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1242 d->d_name, snapshot));
1245 /* the caller doesn't want the labels */
1246 shadow_copy2_data->num_volumes++;
1250 tlabels = talloc_realloc(shadow_copy2_data,
1251 shadow_copy2_data->labels,
1253 shadow_copy2_data->num_volumes+1);
1254 if (tlabels == NULL) {
1255 DEBUG(0,("shadow_copy2: out of memory\n"));
1256 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1257 talloc_free(tmp_ctx);
1261 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1264 shadow_copy2_data->num_volumes++;
1265 shadow_copy2_data->labels = tlabels;
1268 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1270 shadow_copy2_sort_data(handle, shadow_copy2_data);
1272 talloc_free(tmp_ctx);
1276 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1277 struct files_struct *fsp,
1278 uint32 security_info,
1279 TALLOC_CTX *mem_ctx,
1280 struct security_descriptor **ppdesc)
1287 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1288 fsp->fsp_name->base_name,
1289 ×tamp, &stripped)) {
1290 return map_nt_error_from_unix(errno);
1292 if (timestamp == 0) {
1293 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1297 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1298 TALLOC_FREE(stripped);
1300 return map_nt_error_from_unix(errno);
1302 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1308 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1310 uint32 security_info,
1311 TALLOC_CTX *mem_ctx,
1312 struct security_descriptor **ppdesc)
1319 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1320 ×tamp, &stripped)) {
1321 return map_nt_error_from_unix(errno);
1323 if (timestamp == 0) {
1324 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1327 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1328 TALLOC_FREE(stripped);
1330 return map_nt_error_from_unix(errno);
1332 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1338 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1339 const char *fname, mode_t mode)
1343 int ret, saved_errno;
1346 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1347 ×tamp, &stripped)) {
1350 if (timestamp == 0) {
1351 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1353 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1354 TALLOC_FREE(stripped);
1358 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1359 saved_errno = errno;
1361 errno = saved_errno;
1365 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1369 int ret, saved_errno;
1372 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1373 ×tamp, &stripped)) {
1376 if (timestamp == 0) {
1377 return SMB_VFS_NEXT_RMDIR(handle, fname);
1379 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1380 TALLOC_FREE(stripped);
1384 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1385 saved_errno = errno;
1387 errno = saved_errno;
1391 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1396 int ret, saved_errno;
1399 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1400 ×tamp, &stripped)) {
1403 if (timestamp == 0) {
1404 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1406 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1407 TALLOC_FREE(stripped);
1411 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1412 saved_errno = errno;
1414 errno = saved_errno;
1418 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1419 const char *fname, const char *aname,
1420 void *value, size_t size)
1428 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1429 ×tamp, &stripped)) {
1432 if (timestamp == 0) {
1433 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1436 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1437 TALLOC_FREE(stripped);
1441 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1442 saved_errno = errno;
1444 errno = saved_errno;
1448 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1450 char *list, size_t size)
1458 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1459 ×tamp, &stripped)) {
1462 if (timestamp == 0) {
1463 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1465 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1466 TALLOC_FREE(stripped);
1470 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1471 saved_errno = errno;
1473 errno = saved_errno;
1477 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1478 const char *fname, const char *aname)
1482 int ret, saved_errno;
1485 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1486 ×tamp, &stripped)) {
1489 if (timestamp == 0) {
1490 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1492 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1493 TALLOC_FREE(stripped);
1497 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1498 saved_errno = errno;
1500 errno = saved_errno;
1504 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1506 const char *aname, const void *value,
1507 size_t size, int flags)
1515 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1516 ×tamp, &stripped)) {
1519 if (timestamp == 0) {
1520 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1523 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1524 TALLOC_FREE(stripped);
1528 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1529 saved_errno = errno;
1531 errno = saved_errno;
1535 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1536 const char *fname, mode_t mode)
1544 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1545 ×tamp, &stripped)) {
1548 if (timestamp == 0) {
1549 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1551 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1552 TALLOC_FREE(stripped);
1556 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1557 saved_errno = errno;
1559 errno = saved_errno;
1563 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1566 TALLOC_CTX *mem_ctx,
1575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1576 ×tamp, &stripped)) {
1579 if (timestamp == 0) {
1580 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1581 mem_ctx, found_name);
1583 if (stripped[0] == '\0') {
1584 *found_name = talloc_strdup(mem_ctx, name);
1585 if (*found_name == NULL) {
1591 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1592 TALLOC_FREE(stripped);
1596 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1597 mem_ctx, found_name);
1598 saved_errno = errno;
1600 errno = saved_errno;
1604 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1605 const char *path, bool small_query,
1606 uint64_t *bsize, uint64_t *dfree,
1615 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1616 ×tamp, &stripped)) {
1619 if (timestamp == 0) {
1620 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1621 bsize, dfree, dsize);
1624 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1625 TALLOC_FREE(stripped);
1630 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1633 saved_errno = errno;
1635 errno = saved_errno;
1640 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1641 const char *service, const char *user)
1643 struct shadow_copy2_config *config;
1645 const char *snapdir;
1646 const char *gmt_format;
1647 const char *sort_order;
1648 const char *basedir;
1649 const char *mount_point;
1651 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1652 (unsigned)handle->conn->cnum,
1653 handle->conn->connectpath));
1655 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1660 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1661 if (config == NULL) {
1662 DEBUG(0, ("talloc_zero() failed\n"));
1667 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1670 config->gmt_format = talloc_strdup(config, gmt_format);
1671 if (config->gmt_format == NULL) {
1672 DEBUG(0, ("talloc_strdup() failed\n"));
1677 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1678 "shadow", "sscanf", false);
1680 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1681 "shadow", "localtime",
1684 snapdir = lp_parm_const_string(SNUM(handle->conn),
1685 "shadow", "snapdir",
1687 config->snapdir = talloc_strdup(config, snapdir);
1688 if (config->snapdir == NULL) {
1689 DEBUG(0, ("talloc_strdup() failed\n"));
1694 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1696 "snapdirseverywhere",
1699 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1700 "shadow", "crossmountpoints",
1703 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1704 "shadow", "fixinodes",
1707 sort_order = lp_parm_const_string(SNUM(handle->conn),
1708 "shadow", "sort", "desc");
1709 config->sort_order = talloc_strdup(config, sort_order);
1710 if (config->sort_order == NULL) {
1711 DEBUG(0, ("talloc_strdup() failed\n"));
1716 mount_point = lp_parm_const_string(SNUM(handle->conn),
1717 "shadow", "mountpoint", NULL);
1718 if (mount_point != NULL) {
1719 if (mount_point[0] != '/') {
1720 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1721 "relative ('%s'), but it has to be an "
1722 "absolute path. Ignoring provided value.\n",
1727 p = strstr(handle->conn->connectpath, mount_point);
1728 if (p != handle->conn->connectpath) {
1729 DEBUG(1, ("Warning: mount_point (%s) is not a "
1730 "subdirectory of the share root "
1731 "(%s). Ignoring provided value.\n",
1733 handle->conn->connectpath));
1739 if (mount_point != NULL) {
1740 config->mount_point = talloc_strdup(config, mount_point);
1741 if (config->mount_point == NULL) {
1742 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1746 config->mount_point = shadow_copy2_find_mount_point(config,
1748 if (config->mount_point == NULL) {
1749 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1750 " failed: %s\n", strerror(errno)));
1755 basedir = lp_parm_const_string(SNUM(handle->conn),
1756 "shadow", "basedir", NULL);
1758 if (basedir != NULL) {
1759 if (basedir[0] != '/') {
1760 DEBUG(1, (__location__ " Warning: 'basedir' is "
1761 "relative ('%s'), but it has to be an "
1762 "absolute path. Disabling basedir.\n",
1766 p = strstr(basedir, config->mount_point);
1768 DEBUG(1, ("Warning: basedir (%s) is not a "
1769 "subdirectory of the share root's "
1770 "mount point (%s). "
1771 "Disabling basedir\n",
1772 basedir, config->mount_point));
1774 config->basedir = talloc_strdup(config,
1776 if (config->basedir == NULL) {
1777 DEBUG(0, ("talloc_strdup() failed\n"));
1785 if (config->snapdirseverywhere && config->basedir != NULL) {
1786 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1787 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1788 TALLOC_FREE(config->basedir);
1791 if (config->crossmountpoints && config->basedir != NULL) {
1792 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1793 "with 'crossmountpoints'. Disabling basedir.\n"));
1794 TALLOC_FREE(config->basedir);
1797 if (config->basedir == NULL) {
1798 config->basedir = config->mount_point;
1801 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1802 config->rel_connectpath = talloc_strdup(config,
1803 handle->conn->connectpath + strlen(config->basedir));
1804 if (config->rel_connectpath == NULL) {
1805 DEBUG(0, ("talloc_strdup() failed\n"));
1811 if (config->snapdir[0] == '/') {
1812 config->snapdir_absolute = true;
1814 if (config->snapdirseverywhere == true) {
1815 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1816 "is incompatible with 'snapdirseverywhere', "
1817 "setting 'snapdirseverywhere' to false.\n"));
1818 config->snapdirseverywhere = false;
1821 if (config->crossmountpoints == true) {
1822 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1823 "is not supported with an absolute snapdir. "
1824 "Disabling it.\n"));
1825 config->crossmountpoints = false;
1828 config->snapshot_basepath = config->snapdir;
1830 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1831 config->mount_point, config->snapdir);
1832 if (config->snapshot_basepath == NULL) {
1833 DEBUG(0, ("talloc_asprintf() failed\n"));
1839 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1840 " share root: '%s'\n"
1842 " mountpoint: '%s'\n"
1843 " rel share root: '%s'\n"
1845 " snapshot base path: '%s'\n"
1848 " snapdirs everywhere: %s\n"
1849 " cross mountpoints: %s\n"
1853 handle->conn->connectpath,
1855 config->mount_point,
1856 config->rel_connectpath,
1858 config->snapshot_basepath,
1860 config->use_sscanf ? "yes" : "no",
1861 config->snapdirseverywhere ? "yes" : "no",
1862 config->crossmountpoints ? "yes" : "no",
1863 config->fixinodes ? "yes" : "no",
1868 SMB_VFS_HANDLE_SET_DATA(handle, config,
1869 NULL, struct shadow_copy2_config,
1875 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1876 .connect_fn = shadow_copy2_connect,
1877 .opendir_fn = shadow_copy2_opendir,
1878 .disk_free_fn = shadow_copy2_disk_free,
1879 .rename_fn = shadow_copy2_rename,
1880 .link_fn = shadow_copy2_link,
1881 .symlink_fn = shadow_copy2_symlink,
1882 .stat_fn = shadow_copy2_stat,
1883 .lstat_fn = shadow_copy2_lstat,
1884 .fstat_fn = shadow_copy2_fstat,
1885 .open_fn = shadow_copy2_open,
1886 .unlink_fn = shadow_copy2_unlink,
1887 .chmod_fn = shadow_copy2_chmod,
1888 .chown_fn = shadow_copy2_chown,
1889 .chdir_fn = shadow_copy2_chdir,
1890 .ntimes_fn = shadow_copy2_ntimes,
1891 .readlink_fn = shadow_copy2_readlink,
1892 .mknod_fn = shadow_copy2_mknod,
1893 .realpath_fn = shadow_copy2_realpath,
1894 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1895 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1896 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1897 .mkdir_fn = shadow_copy2_mkdir,
1898 .rmdir_fn = shadow_copy2_rmdir,
1899 .getxattr_fn = shadow_copy2_getxattr,
1900 .listxattr_fn = shadow_copy2_listxattr,
1901 .removexattr_fn = shadow_copy2_removexattr,
1902 .setxattr_fn = shadow_copy2_setxattr,
1903 .chmod_acl_fn = shadow_copy2_chmod_acl,
1904 .chflags_fn = shadow_copy2_chflags,
1905 .get_real_filename_fn = shadow_copy2_get_real_filename,
1908 NTSTATUS vfs_shadow_copy2_init(void);
1909 NTSTATUS vfs_shadow_copy2_init(void)
1911 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1912 "shadow_copy2", &vfs_shadow_copy2_fns);