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
;
727 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
728 smb_fname
->base_name
,
729 ×tamp
, &stripped
)) {
732 if (timestamp
== 0) {
733 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
735 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
736 if (!NT_STATUS_IS_OK(status
)) {
740 conv
->base_name
= shadow_copy2_convert(
741 conv
, handle
, stripped
, timestamp
);
742 TALLOC_FREE(stripped
);
743 if (conv
->base_name
== NULL
) {
746 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
753 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
758 int ret
, saved_errno
;
761 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
762 ×tamp
, &stripped
)) {
765 if (timestamp
== 0) {
766 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
768 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
769 TALLOC_FREE(stripped
);
773 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
780 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
781 uid_t uid
, gid_t gid
)
785 int ret
, saved_errno
;
788 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
789 ×tamp
, &stripped
)) {
792 if (timestamp
== 0) {
793 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
795 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
796 TALLOC_FREE(stripped
);
800 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
807 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
812 int ret
, saved_errno
;
815 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
816 ×tamp
, &stripped
)) {
819 if (timestamp
== 0) {
820 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
822 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
823 TALLOC_FREE(stripped
);
827 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
834 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
835 const struct smb_filename
*smb_fname
,
836 struct smb_file_time
*ft
)
840 int ret
, saved_errno
;
841 struct smb_filename
*conv
;
844 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
845 smb_fname
->base_name
,
846 ×tamp
, &stripped
)) {
849 if (timestamp
== 0) {
850 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
852 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
853 if (!NT_STATUS_IS_OK(status
)) {
857 conv
->base_name
= shadow_copy2_convert(
858 conv
, handle
, stripped
, timestamp
);
859 TALLOC_FREE(stripped
);
860 if (conv
->base_name
== NULL
) {
863 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
870 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
871 const char *fname
, char *buf
, size_t bufsiz
)
875 int ret
, saved_errno
;
878 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
879 ×tamp
, &stripped
)) {
882 if (timestamp
== 0) {
883 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
885 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
886 TALLOC_FREE(stripped
);
890 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
897 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
898 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
902 int ret
, saved_errno
;
905 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
906 ×tamp
, &stripped
)) {
909 if (timestamp
== 0) {
910 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
912 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
913 TALLOC_FREE(stripped
);
917 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
924 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
928 char *stripped
= NULL
;
931 char *inserted
= NULL
;
932 char *inserted_to
, *inserted_end
;
935 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
936 ×tamp
, &stripped
)) {
939 if (timestamp
== 0) {
940 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
943 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
948 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
949 if (result
== NULL
) {
954 * Take away what we've inserted. This removes the @GMT-thingy
955 * completely, but will give a path under the share root.
957 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
958 if (inserted
== NULL
) {
961 inserted_to
= strstr_m(result
, inserted
);
962 if (inserted_to
== NULL
) {
963 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
966 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
967 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
971 TALLOC_FREE(inserted
);
973 TALLOC_FREE(stripped
);
979 * Check whether a given directory contains a
980 * snapshot directory as direct subdirectory.
981 * If yes, return the path of the snapshot-subdir,
982 * otherwise return NULL.
984 static char *have_snapdir(struct vfs_handle_struct
*handle
,
987 struct smb_filename smb_fname
;
990 ZERO_STRUCT(smb_fname
);
991 smb_fname
.base_name
= talloc_asprintf(
992 talloc_tos(), "%s/%s", path
,
993 lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir",
995 if (smb_fname
.base_name
== NULL
) {
999 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1000 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1001 return smb_fname
.base_name
;
1003 TALLOC_FREE(smb_fname
.base_name
);
1008 * Find the snapshot directory (if any) for the given
1009 * filename (which is relative to the share).
1011 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1012 struct vfs_handle_struct
*handle
,
1013 struct smb_filename
*smb_fname
)
1018 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1019 handle
->conn
->connectpath
,
1020 smb_fname
->base_name
);
1025 snapdir
= have_snapdir(handle
, path
);
1026 if (snapdir
!= NULL
) {
1031 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1035 snapdir
= have_snapdir(handle
, path
);
1036 if (snapdir
!= NULL
) {
1045 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1047 char *gmt
, size_t gmt_len
)
1049 struct tm timestamp
;
1051 unsigned long int timestamp_long
;
1054 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1055 "format", GMT_FORMAT
);
1057 ZERO_STRUCT(timestamp
);
1058 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "sscanf", false)) {
1059 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1060 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1061 "no sscanf match %s: %s\n",
1065 timestamp_t
= timestamp_long
;
1066 gmtime_r(×tamp_t
, ×tamp
);
1068 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1069 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1070 "no match %s: %s\n",
1074 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1077 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1078 timestamp
.tm_isdst
= -1;
1079 timestamp_t
= mktime(×tamp
);
1080 gmtime_r(×tamp_t
, ×tamp
);
1084 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1088 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1090 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1093 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1095 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1099 sort the shadow copy data in ascending or descending order
1101 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1102 struct shadow_copy_data
*shadow_copy2_data
)
1104 int (*cmpfunc
)(const void *, const void *);
1107 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1113 if (strcmp(sort
, "asc") == 0) {
1114 cmpfunc
= shadow_copy2_label_cmp_asc
;
1115 } else if (strcmp(sort
, "desc") == 0) {
1116 cmpfunc
= shadow_copy2_label_cmp_desc
;
1121 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1122 shadow_copy2_data
->labels
)
1124 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1125 shadow_copy2_data
->num_volumes
,
1130 static int shadow_copy2_get_shadow_copy_data(
1131 vfs_handle_struct
*handle
, files_struct
*fsp
,
1132 struct shadow_copy_data
*shadow_copy2_data
,
1136 const char *snapdir
;
1138 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1140 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1141 if (snapdir
== NULL
) {
1142 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1143 handle
->conn
->connectpath
));
1145 talloc_free(tmp_ctx
);
1149 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1152 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1153 " - %s\n", snapdir
, strerror(errno
)));
1154 talloc_free(tmp_ctx
);
1159 shadow_copy2_data
->num_volumes
= 0;
1160 shadow_copy2_data
->labels
= NULL
;
1162 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1163 char snapshot
[GMT_NAME_LEN
+1];
1164 SHADOW_COPY_LABEL
*tlabels
;
1167 * ignore names not of the right form in the snapshot
1170 if (!shadow_copy2_snapshot_to_gmt(
1172 snapshot
, sizeof(snapshot
))) {
1174 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1175 "ignoring %s\n", d
->d_name
));
1178 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1179 d
->d_name
, snapshot
));
1182 /* the caller doesn't want the labels */
1183 shadow_copy2_data
->num_volumes
++;
1187 tlabels
= talloc_realloc(shadow_copy2_data
,
1188 shadow_copy2_data
->labels
,
1190 shadow_copy2_data
->num_volumes
+1);
1191 if (tlabels
== NULL
) {
1192 DEBUG(0,("shadow_copy2: out of memory\n"));
1193 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1194 talloc_free(tmp_ctx
);
1198 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1201 shadow_copy2_data
->num_volumes
++;
1202 shadow_copy2_data
->labels
= tlabels
;
1205 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1207 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1209 talloc_free(tmp_ctx
);
1213 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1214 struct files_struct
*fsp
,
1215 uint32 security_info
,
1216 TALLOC_CTX
*mem_ctx
,
1217 struct security_descriptor
**ppdesc
)
1224 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1225 fsp
->fsp_name
->base_name
,
1226 ×tamp
, &stripped
)) {
1227 return map_nt_error_from_unix(errno
);
1229 if (timestamp
== 0) {
1230 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1234 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1235 TALLOC_FREE(stripped
);
1237 return map_nt_error_from_unix(errno
);
1239 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1245 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1247 uint32 security_info
,
1248 TALLOC_CTX
*mem_ctx
,
1249 struct security_descriptor
**ppdesc
)
1256 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1257 ×tamp
, &stripped
)) {
1258 return map_nt_error_from_unix(errno
);
1260 if (timestamp
== 0) {
1261 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1264 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1265 TALLOC_FREE(stripped
);
1267 return map_nt_error_from_unix(errno
);
1269 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1275 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1276 const char *fname
, mode_t mode
)
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_MKDIR(handle
, fname
, mode
);
1290 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1291 TALLOC_FREE(stripped
);
1295 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1296 saved_errno
= errno
;
1298 errno
= saved_errno
;
1302 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1306 int ret
, saved_errno
;
1309 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1310 ×tamp
, &stripped
)) {
1313 if (timestamp
== 0) {
1314 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1316 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1317 TALLOC_FREE(stripped
);
1321 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1322 saved_errno
= errno
;
1324 errno
= saved_errno
;
1328 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1333 int ret
, saved_errno
;
1336 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1337 ×tamp
, &stripped
)) {
1340 if (timestamp
== 0) {
1341 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1343 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1344 TALLOC_FREE(stripped
);
1348 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1349 saved_errno
= errno
;
1351 errno
= saved_errno
;
1355 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1356 const char *fname
, const char *aname
,
1357 void *value
, size_t size
)
1365 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1366 ×tamp
, &stripped
)) {
1369 if (timestamp
== 0) {
1370 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1373 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1374 TALLOC_FREE(stripped
);
1378 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1379 saved_errno
= errno
;
1381 errno
= saved_errno
;
1385 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1387 char *list
, size_t size
)
1395 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1396 ×tamp
, &stripped
)) {
1399 if (timestamp
== 0) {
1400 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1402 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1403 TALLOC_FREE(stripped
);
1407 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1408 saved_errno
= errno
;
1410 errno
= saved_errno
;
1414 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1415 const char *fname
, const char *aname
)
1419 int ret
, saved_errno
;
1422 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1423 ×tamp
, &stripped
)) {
1426 if (timestamp
== 0) {
1427 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1429 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1430 TALLOC_FREE(stripped
);
1434 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1435 saved_errno
= errno
;
1437 errno
= saved_errno
;
1441 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1443 const char *aname
, const void *value
,
1444 size_t size
, int flags
)
1452 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1453 ×tamp
, &stripped
)) {
1456 if (timestamp
== 0) {
1457 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1460 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1461 TALLOC_FREE(stripped
);
1465 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1466 saved_errno
= errno
;
1468 errno
= saved_errno
;
1472 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1473 const char *fname
, mode_t mode
)
1481 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1482 ×tamp
, &stripped
)) {
1485 if (timestamp
== 0) {
1486 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1488 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1489 TALLOC_FREE(stripped
);
1493 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1494 saved_errno
= errno
;
1496 errno
= saved_errno
;
1500 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1503 TALLOC_CTX
*mem_ctx
,
1512 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1513 ×tamp
, &stripped
)) {
1516 if (timestamp
== 0) {
1517 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1518 mem_ctx
, found_name
);
1520 if (stripped
[0] == '\0') {
1521 *found_name
= talloc_strdup(mem_ctx
, name
);
1522 if (*found_name
== NULL
) {
1528 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1529 TALLOC_FREE(stripped
);
1533 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1534 mem_ctx
, found_name
);
1535 saved_errno
= errno
;
1537 errno
= saved_errno
;
1542 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1543 .opendir_fn
= shadow_copy2_opendir
,
1544 .rename_fn
= shadow_copy2_rename
,
1545 .link_fn
= shadow_copy2_link
,
1546 .symlink_fn
= shadow_copy2_symlink
,
1547 .stat_fn
= shadow_copy2_stat
,
1548 .lstat_fn
= shadow_copy2_lstat
,
1549 .fstat_fn
= shadow_copy2_fstat
,
1550 .open_fn
= shadow_copy2_open
,
1551 .unlink_fn
= shadow_copy2_unlink
,
1552 .chmod_fn
= shadow_copy2_chmod
,
1553 .chown_fn
= shadow_copy2_chown
,
1554 .chdir_fn
= shadow_copy2_chdir
,
1555 .ntimes_fn
= shadow_copy2_ntimes
,
1556 .readlink_fn
= shadow_copy2_readlink
,
1557 .mknod_fn
= shadow_copy2_mknod
,
1558 .realpath_fn
= shadow_copy2_realpath
,
1559 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1560 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1561 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1562 .mkdir_fn
= shadow_copy2_mkdir
,
1563 .rmdir_fn
= shadow_copy2_rmdir
,
1564 .getxattr_fn
= shadow_copy2_getxattr
,
1565 .listxattr_fn
= shadow_copy2_listxattr
,
1566 .removexattr_fn
= shadow_copy2_removexattr
,
1567 .setxattr_fn
= shadow_copy2_setxattr
,
1568 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1569 .chflags_fn
= shadow_copy2_chflags
,
1570 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1573 NTSTATUS
vfs_shadow_copy2_init(void);
1574 NTSTATUS
vfs_shadow_copy2_init(void)
1576 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1577 "shadow_copy2", &vfs_shadow_copy2_fns
);