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 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
111 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
113 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
115 unsigned *pnum_offsets)
117 unsigned num_offsets;
124 while ((p = strchr(p, '/')) != NULL) {
129 offsets = talloc_array(mem_ctx, size_t, num_offsets);
130 if (offsets == NULL) {
136 while ((p = strchr(p, '/')) != NULL) {
137 offsets[num_offsets] = p-str;
143 *pnum_offsets = num_offsets;
147 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
148 struct vfs_handle_struct *handle,
153 fstring snaptime_string;
156 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
157 "format", GMT_FORMAT);
159 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
160 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
161 (unsigned long)snapshot);
162 if (snaptime_len <= 0) {
163 DEBUG(10, ("snprintf failed\n"));
167 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
168 if (localtime_r(&snapshot, &snap_tm) == 0) {
169 DEBUG(10, ("gmtime_r failed\n"));
173 if (gmtime_r(&snapshot, &snap_tm) == 0) {
174 DEBUG(10, ("gmtime_r failed\n"));
178 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
180 if (snaptime_len == 0) {
181 DEBUG(10, ("strftime failed\n"));
185 return talloc_asprintf(mem_ctx, "/%s/%s",
186 lp_parm_const_string(
187 SNUM(handle->conn), "shadow", "snapdir",
192 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
193 struct vfs_handle_struct *handle,
203 size_t rest_len, dst_len;
205 p = strstr_m(name, "@GMT-");
209 if ((p > name) && (p[-1] != '/')) {
212 q = strptime(p, GMT_FORMAT, &tm);
217 timestamp = timegm(&tm);
218 if (timestamp == (time_t)-1) {
221 if ((p == name) && (q[0] == '\0')) {
222 if (pstripped != NULL) {
223 stripped = talloc_strdup(mem_ctx, "");
224 if (stripped == NULL) {
227 *pstripped = stripped;
229 *ptimestamp = timestamp;
237 rest_len = strlen(q);
238 dst_len = (p-name) + rest_len;
240 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
244 insert = shadow_copy2_insert_string(talloc_tos(), handle,
246 if (insert == NULL) {
251 have_insert = (strstr(name, insert+1) != NULL);
258 if (pstripped != NULL) {
259 stripped = talloc_array(mem_ctx, char, dst_len+1);
260 if (stripped == NULL) {
265 memcpy(stripped, name, p-name);
268 memcpy(stripped + (p-name), q, rest_len);
270 stripped[dst_len] = '\0';
271 *pstripped = stripped;
273 *ptimestamp = timestamp;
280 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
281 vfs_handle_struct *handle)
283 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
288 if (stat(path, &st) != 0) {
295 while ((p = strrchr(path, '/')) && p > path) {
297 if (stat(path, &st) != 0) {
301 if (st.st_dev != dev) {
310 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
311 struct vfs_handle_struct *handle,
312 const char *name, time_t timestamp)
314 struct smb_filename converted_fname;
316 size_t *slashes = NULL;
317 unsigned num_slashes;
321 char *converted = NULL;
326 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
332 pathlen = talloc_get_size(path)-1;
334 DEBUG(10, ("converting %s\n", path));
336 if (!shadow_copy2_find_slashes(talloc_tos(), path,
337 &slashes, &num_slashes)) {
340 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
341 if (insert == NULL) {
344 insertlen = talloc_get_size(insert)-1;
345 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
346 if (converted == NULL) {
350 if (path[pathlen-1] != '/') {
352 * Append a fake slash to find the snapshot root
355 tmp = talloc_realloc(talloc_tos(), slashes,
356 size_t, num_slashes+1);
361 slashes[num_slashes] = pathlen;
367 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
371 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
373 if (mount_point == NULL) {
376 min_offset = strlen(mount_point);
377 TALLOC_FREE(mount_point);
380 memcpy(converted, path, pathlen+1);
381 converted[pathlen+insertlen] = '\0';
383 ZERO_STRUCT(converted_fname);
384 converted_fname.base_name = converted;
386 for (i = num_slashes-1; i>=0; i--) {
392 if (offset < min_offset) {
397 memcpy(converted+offset, insert, insertlen);
400 memcpy(converted+offset, path + slashes[i],
401 pathlen - slashes[i]);
403 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
405 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
406 ret, ret == 0 ? "ok" : strerror(errno)));
411 if (errno == ENOTDIR) {
413 * This is a valid condition: We appended the
414 * .snaphots/@GMT.. to a file name. Just try
415 * with the upper levels.
419 if (errno != ENOENT) {
420 /* Other problem than "not found" */
429 DEBUG(10, ("Found %s\n", converted));
437 TALLOC_FREE(converted);
439 TALLOC_FREE(slashes);
446 modify a sbuf return to ensure that inodes in the shadow directory
447 are different from those in the main directory
449 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
450 SMB_STRUCT_STAT *sbuf)
452 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
453 /* some snapshot systems, like GPFS, return the name
454 device:inode for the snapshot files as the current
455 files. That breaks the 'restore' button in the shadow copy
456 GUI, as the client gets a sharing violation.
458 This is a crude way of allowing both files to be
459 open at once. It has a slight chance of inode
460 number collision, but I can't see a better approach
461 without significant VFS changes
465 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
469 sbuf->st_ex_ino ^= shash;
473 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
485 ×tamp, &stripped)) {
488 if (timestamp == 0) {
489 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
491 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
492 TALLOC_FREE(stripped);
496 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
503 static int shadow_copy2_rename(vfs_handle_struct *handle,
504 const struct smb_filename *smb_fname_src,
505 const struct smb_filename *smb_fname_dst)
507 time_t timestamp_src, timestamp_dst;
509 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
510 smb_fname_src->base_name,
511 ×tamp_src, NULL)) {
514 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
515 smb_fname_dst->base_name,
516 ×tamp_dst, NULL)) {
519 if (timestamp_src != 0) {
523 if (timestamp_dst != 0) {
527 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
530 static int shadow_copy2_symlink(vfs_handle_struct *handle,
531 const char *oldname, const char *newname)
533 time_t timestamp_old, timestamp_new;
535 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
536 ×tamp_old, NULL)) {
539 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
540 ×tamp_new, NULL)) {
543 if ((timestamp_old != 0) || (timestamp_new != 0)) {
547 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
550 static int shadow_copy2_link(vfs_handle_struct *handle,
551 const char *oldname, const char *newname)
553 time_t timestamp_old, timestamp_new;
555 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
556 ×tamp_old, NULL)) {
559 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
560 ×tamp_new, NULL)) {
563 if ((timestamp_old != 0) || (timestamp_new != 0)) {
567 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
570 static int shadow_copy2_stat(vfs_handle_struct *handle,
571 struct smb_filename *smb_fname)
574 char *stripped, *tmp;
575 int ret, saved_errno;
577 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
578 smb_fname->base_name,
579 ×tamp, &stripped)) {
582 if (timestamp == 0) {
583 return SMB_VFS_NEXT_STAT(handle, smb_fname);
586 tmp = smb_fname->base_name;
587 smb_fname->base_name = shadow_copy2_convert(
588 talloc_tos(), handle, stripped, timestamp);
589 TALLOC_FREE(stripped);
591 if (smb_fname->base_name == NULL) {
592 smb_fname->base_name = tmp;
596 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
599 TALLOC_FREE(smb_fname->base_name);
600 smb_fname->base_name = tmp;
603 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
609 static int shadow_copy2_lstat(vfs_handle_struct *handle,
610 struct smb_filename *smb_fname)
613 char *stripped, *tmp;
614 int ret, saved_errno;
616 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
617 smb_fname->base_name,
618 ×tamp, &stripped)) {
621 if (timestamp == 0) {
622 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
625 tmp = smb_fname->base_name;
626 smb_fname->base_name = shadow_copy2_convert(
627 talloc_tos(), handle, stripped, timestamp);
628 TALLOC_FREE(stripped);
630 if (smb_fname->base_name == NULL) {
631 smb_fname->base_name = tmp;
635 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
638 TALLOC_FREE(smb_fname->base_name);
639 smb_fname->base_name = tmp;
642 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
648 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
649 SMB_STRUCT_STAT *sbuf)
654 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
658 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
659 fsp->fsp_name->base_name,
663 if (timestamp != 0) {
664 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
669 static int shadow_copy2_open(vfs_handle_struct *handle,
670 struct smb_filename *smb_fname, files_struct *fsp,
671 int flags, mode_t mode)
674 char *stripped, *tmp;
675 int ret, saved_errno;
677 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
678 smb_fname->base_name,
679 ×tamp, &stripped)) {
682 if (timestamp == 0) {
683 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
686 tmp = smb_fname->base_name;
687 smb_fname->base_name = shadow_copy2_convert(
688 talloc_tos(), handle, stripped, timestamp);
689 TALLOC_FREE(stripped);
691 if (smb_fname->base_name == NULL) {
692 smb_fname->base_name = tmp;
696 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
699 TALLOC_FREE(smb_fname->base_name);
700 smb_fname->base_name = tmp;
706 static int shadow_copy2_unlink(vfs_handle_struct *handle,
707 const struct smb_filename *smb_fname)
711 int ret, saved_errno;
712 struct smb_filename *conv;
714 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
715 smb_fname->base_name,
716 ×tamp, &stripped)) {
719 if (timestamp == 0) {
720 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
722 conv = cp_smb_filename(talloc_tos(), smb_fname);
727 conv->base_name = shadow_copy2_convert(
728 conv, handle, stripped, timestamp);
729 TALLOC_FREE(stripped);
730 if (conv->base_name == NULL) {
733 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
740 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
745 int ret, saved_errno;
748 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
749 ×tamp, &stripped)) {
752 if (timestamp == 0) {
753 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
755 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
756 TALLOC_FREE(stripped);
760 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
767 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
768 uid_t uid, gid_t gid)
772 int ret, saved_errno;
775 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
776 ×tamp, &stripped)) {
779 if (timestamp == 0) {
780 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
782 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
783 TALLOC_FREE(stripped);
787 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
794 static int shadow_copy2_chdir(vfs_handle_struct *handle,
799 int ret, saved_errno;
802 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
803 ×tamp, &stripped)) {
806 if (timestamp == 0) {
807 return SMB_VFS_NEXT_CHDIR(handle, fname);
809 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
810 TALLOC_FREE(stripped);
814 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
821 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
822 const struct smb_filename *smb_fname,
823 struct smb_file_time *ft)
827 int ret, saved_errno;
828 struct smb_filename *conv;
831 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
832 smb_fname->base_name,
833 ×tamp, &stripped)) {
836 if (timestamp == 0) {
837 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
839 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
840 if (!NT_STATUS_IS_OK(status)) {
844 conv->base_name = shadow_copy2_convert(
845 conv, handle, stripped, timestamp);
846 TALLOC_FREE(stripped);
847 if (conv->base_name == NULL) {
850 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
857 static int shadow_copy2_readlink(vfs_handle_struct *handle,
858 const char *fname, char *buf, size_t bufsiz)
862 int ret, saved_errno;
865 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
866 ×tamp, &stripped)) {
869 if (timestamp == 0) {
870 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
872 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
873 TALLOC_FREE(stripped);
877 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
884 static int shadow_copy2_mknod(vfs_handle_struct *handle,
885 const char *fname, mode_t mode, SMB_DEV_T dev)
889 int ret, saved_errno;
892 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
893 ×tamp, &stripped)) {
896 if (timestamp == 0) {
897 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
899 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
900 TALLOC_FREE(stripped);
904 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
911 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
915 char *stripped = NULL;
918 char *inserted = NULL;
919 char *inserted_to, *inserted_end;
922 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
923 ×tamp, &stripped)) {
926 if (timestamp == 0) {
927 return SMB_VFS_NEXT_REALPATH(handle, fname);
930 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
935 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
936 if (result == NULL) {
941 * Take away what we've inserted. This removes the @GMT-thingy
942 * completely, but will give a path under the share root.
944 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
945 if (inserted == NULL) {
948 inserted_to = strstr_m(result, inserted);
949 if (inserted_to == NULL) {
950 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
953 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
954 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
958 TALLOC_FREE(inserted);
960 TALLOC_FREE(stripped);
965 static char *have_snapdir(struct vfs_handle_struct *handle,
968 struct smb_filename smb_fname;
971 ZERO_STRUCT(smb_fname);
972 smb_fname.base_name = talloc_asprintf(
973 talloc_tos(), "%s/%s", path,
974 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
976 if (smb_fname.base_name == NULL) {
980 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
981 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
982 return smb_fname.base_name;
984 TALLOC_FREE(smb_fname.base_name);
988 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
989 struct vfs_handle_struct *handle,
990 struct smb_filename *smb_fname)
995 path = talloc_asprintf(mem_ctx, "%s/%s",
996 handle->conn->connectpath,
997 smb_fname->base_name);
1002 snapdir = have_snapdir(handle, path);
1003 if (snapdir != NULL) {
1008 while ((p = strrchr(path, '/')) && (p > path)) {
1012 snapdir = have_snapdir(handle, path);
1013 if (snapdir != NULL) {
1022 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1024 char *gmt, size_t gmt_len)
1026 struct tm timestamp;
1028 unsigned long int timestamp_long;
1031 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1032 "format", GMT_FORMAT);
1034 ZERO_STRUCT(timestamp);
1035 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1036 if (sscanf(name, fmt, ×tamp_long) != 1) {
1037 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no sscanf match %s: %s\n",
1041 timestamp_t = timestamp_long;
1042 gmtime_r(×tamp_t, ×tamp);
1044 if (strptime(name, fmt, ×tamp) == NULL) {
1045 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1049 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1051 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1052 timestamp.tm_isdst = -1;
1053 timestamp_t = mktime(×tamp);
1054 gmtime_r(×tamp_t, ×tamp);
1058 strftime(gmt, gmt_len, GMT_FORMAT, ×tamp);
1062 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1064 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1067 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1069 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1073 sort the shadow copy data in ascending or descending order
1075 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1076 struct shadow_copy_data *shadow_copy2_data)
1078 int (*cmpfunc)(const void *, const void *);
1081 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1087 if (strcmp(sort, "asc") == 0) {
1088 cmpfunc = shadow_copy2_label_cmp_asc;
1089 } else if (strcmp(sort, "desc") == 0) {
1090 cmpfunc = shadow_copy2_label_cmp_desc;
1095 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1096 shadow_copy2_data->labels)
1098 TYPESAFE_QSORT(shadow_copy2_data->labels,
1099 shadow_copy2_data->num_volumes,
1104 static int shadow_copy2_get_shadow_copy_data(
1105 vfs_handle_struct *handle, files_struct *fsp,
1106 struct shadow_copy_data *shadow_copy2_data,
1110 const char *snapdir;
1112 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1114 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1115 if (snapdir == NULL) {
1116 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1117 handle->conn->connectpath));
1119 talloc_free(tmp_ctx);
1123 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1126 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1127 " - %s\n", snapdir, strerror(errno)));
1128 talloc_free(tmp_ctx);
1133 shadow_copy2_data->num_volumes = 0;
1134 shadow_copy2_data->labels = NULL;
1136 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1137 char snapshot[GMT_NAME_LEN+1];
1138 SHADOW_COPY_LABEL *tlabels;
1141 * ignore names not of the right form in the snapshot
1144 if (!shadow_copy2_snapshot_to_gmt(
1146 snapshot, sizeof(snapshot))) {
1148 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1149 "ignoring %s\n", d->d_name));
1152 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1153 d->d_name, snapshot));
1156 /* the caller doesn't want the labels */
1157 shadow_copy2_data->num_volumes++;
1161 tlabels = talloc_realloc(shadow_copy2_data,
1162 shadow_copy2_data->labels,
1164 shadow_copy2_data->num_volumes+1);
1165 if (tlabels == NULL) {
1166 DEBUG(0,("shadow_copy2: out of memory\n"));
1167 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1168 talloc_free(tmp_ctx);
1172 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1175 shadow_copy2_data->num_volumes++;
1176 shadow_copy2_data->labels = tlabels;
1179 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1181 shadow_copy2_sort_data(handle, shadow_copy2_data);
1183 talloc_free(tmp_ctx);
1187 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1188 struct files_struct *fsp,
1189 uint32 security_info,
1190 TALLOC_CTX *mem_ctx,
1191 struct security_descriptor **ppdesc)
1198 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1199 fsp->fsp_name->base_name,
1200 ×tamp, &stripped)) {
1201 return map_nt_error_from_unix(errno);
1203 if (timestamp == 0) {
1204 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1208 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1209 TALLOC_FREE(stripped);
1211 return map_nt_error_from_unix(errno);
1213 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1219 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1221 uint32 security_info,
1222 TALLOC_CTX *mem_ctx,
1223 struct security_descriptor **ppdesc)
1230 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1231 ×tamp, &stripped)) {
1232 return map_nt_error_from_unix(errno);
1234 if (timestamp == 0) {
1235 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1238 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1239 TALLOC_FREE(stripped);
1241 return map_nt_error_from_unix(errno);
1243 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1249 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1250 const char *fname, mode_t mode)
1254 int ret, saved_errno;
1257 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1258 ×tamp, &stripped)) {
1261 if (timestamp == 0) {
1262 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1264 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1265 TALLOC_FREE(stripped);
1269 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1270 saved_errno = errno;
1272 errno = saved_errno;
1276 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1280 int ret, saved_errno;
1283 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1284 ×tamp, &stripped)) {
1287 if (timestamp == 0) {
1288 return SMB_VFS_NEXT_RMDIR(handle, fname);
1290 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1291 TALLOC_FREE(stripped);
1295 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1296 saved_errno = errno;
1298 errno = saved_errno;
1302 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1307 int ret, saved_errno;
1310 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1311 ×tamp, &stripped)) {
1314 if (timestamp == 0) {
1315 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1317 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1318 TALLOC_FREE(stripped);
1322 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1323 saved_errno = errno;
1325 errno = saved_errno;
1329 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1330 const char *fname, const char *aname,
1331 void *value, size_t size)
1339 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1340 ×tamp, &stripped)) {
1343 if (timestamp == 0) {
1344 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1347 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1348 TALLOC_FREE(stripped);
1352 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1353 saved_errno = errno;
1355 errno = saved_errno;
1359 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1361 char *list, size_t size)
1369 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1370 ×tamp, &stripped)) {
1373 if (timestamp == 0) {
1374 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1376 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1377 TALLOC_FREE(stripped);
1381 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1382 saved_errno = errno;
1384 errno = saved_errno;
1388 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1389 const char *fname, const char *aname)
1393 int ret, saved_errno;
1396 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1397 ×tamp, &stripped)) {
1400 if (timestamp == 0) {
1401 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1403 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1404 TALLOC_FREE(stripped);
1408 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1409 saved_errno = errno;
1411 errno = saved_errno;
1415 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1417 const char *aname, const void *value,
1418 size_t size, int flags)
1426 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1427 ×tamp, &stripped)) {
1430 if (timestamp == 0) {
1431 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1434 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1435 TALLOC_FREE(stripped);
1439 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1440 saved_errno = errno;
1442 errno = saved_errno;
1446 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1447 const char *fname, mode_t mode)
1455 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1456 ×tamp, &stripped)) {
1459 if (timestamp == 0) {
1460 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1462 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1463 TALLOC_FREE(stripped);
1467 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1468 saved_errno = errno;
1470 errno = saved_errno;
1474 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1477 TALLOC_CTX *mem_ctx,
1486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1487 ×tamp, &stripped)) {
1490 if (timestamp == 0) {
1491 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1492 mem_ctx, found_name);
1494 if (stripped[0] == '\0') {
1495 *found_name = talloc_strdup(mem_ctx, name);
1496 if (*found_name == NULL) {
1502 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1503 TALLOC_FREE(stripped);
1507 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1508 mem_ctx, found_name);
1509 saved_errno = errno;
1511 errno = saved_errno;
1516 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1517 .opendir_fn = shadow_copy2_opendir,
1518 .rename_fn = shadow_copy2_rename,
1519 .link_fn = shadow_copy2_link,
1520 .symlink_fn = shadow_copy2_symlink,
1521 .stat_fn = shadow_copy2_stat,
1522 .lstat_fn = shadow_copy2_lstat,
1523 .fstat_fn = shadow_copy2_fstat,
1524 .open_fn = shadow_copy2_open,
1525 .unlink_fn = shadow_copy2_unlink,
1526 .chmod_fn = shadow_copy2_chmod,
1527 .chown_fn = shadow_copy2_chown,
1528 .chdir_fn = shadow_copy2_chdir,
1529 .ntimes_fn = shadow_copy2_ntimes,
1530 .readlink_fn = shadow_copy2_readlink,
1531 .mknod_fn = shadow_copy2_mknod,
1532 .realpath_fn = shadow_copy2_realpath,
1533 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1534 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1535 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1536 .mkdir_fn = shadow_copy2_mkdir,
1537 .rmdir_fn = shadow_copy2_rmdir,
1538 .getxattr_fn = shadow_copy2_getxattr,
1539 .listxattr_fn = shadow_copy2_listxattr,
1540 .removexattr_fn = shadow_copy2_removexattr,
1541 .setxattr_fn = shadow_copy2_setxattr,
1542 .chmod_acl_fn = shadow_copy2_chmod_acl,
1543 .chflags_fn = shadow_copy2_chflags,
1544 .get_real_filename_fn = shadow_copy2_get_real_filename,
1547 NTSTATUS vfs_shadow_copy2_init(void);
1548 NTSTATUS vfs_shadow_copy2_init(void)
1550 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1551 "shadow_copy2", &vfs_shadow_copy2_fns);