2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 static bool shadow_copy2_find_slashes(TALLOC_CTX
*mem_ctx
, const char *str
,
112 unsigned *pnum_offsets
)
114 unsigned num_offsets
;
121 while ((p
= strchr(p
, '/')) != NULL
) {
126 offsets
= talloc_array(mem_ctx
, size_t, num_offsets
);
127 if (offsets
== NULL
) {
133 while ((p
= strchr(p
, '/')) != NULL
) {
134 offsets
[num_offsets
] = p
-str
;
140 *pnum_offsets
= num_offsets
;
144 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
145 struct vfs_handle_struct
*handle
,
150 fstring snaptime_string
;
153 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
154 "format", GMT_FORMAT
);
156 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "sscanf", false)) {
157 snaptime_len
= snprintf(snaptime_string
, sizeof(snaptime_string
), fmt
,
158 (unsigned long)snapshot
);
159 if (snaptime_len
<= 0) {
160 DEBUG(10, ("snprintf failed\n"));
164 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
165 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
166 DEBUG(10, ("gmtime_r failed\n"));
170 if (gmtime_r(&snapshot
, &snap_tm
) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
175 snaptime_len
= strftime(snaptime_string
, sizeof(snaptime_string
), fmt
,
177 if (snaptime_len
== 0) {
178 DEBUG(10, ("strftime failed\n"));
182 return talloc_asprintf(mem_ctx
, "/%s/%s",
183 lp_parm_const_string(
184 SNUM(handle
->conn
), "shadow", "snapdir",
189 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
190 struct vfs_handle_struct
*handle
,
200 size_t rest_len
, dst_len
;
202 p
= strstr_m(name
, "@GMT-");
206 if ((p
> name
) && (p
[-1] != '/')) {
209 q
= strptime(p
, GMT_FORMAT
, &tm
);
214 timestamp
= timegm(&tm
);
215 if (timestamp
== (time_t)-1) {
218 if ((p
== name
) && (q
[0] == '\0')) {
219 if (pstripped
!= NULL
) {
220 stripped
= talloc_strdup(mem_ctx
, "");
221 if (stripped
== NULL
) {
224 *pstripped
= stripped
;
226 *ptimestamp
= timestamp
;
234 rest_len
= strlen(q
);
235 dst_len
= (p
-name
) + rest_len
;
237 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "snapdirseverywhere",
241 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
243 if (insert
== NULL
) {
248 have_insert
= (strstr(name
, insert
+1) != NULL
);
255 if (pstripped
!= NULL
) {
256 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
257 if (stripped
== NULL
) {
262 memcpy(stripped
, name
, p
-name
);
265 memcpy(stripped
+ (p
-name
), q
, rest_len
);
267 stripped
[dst_len
] = '\0';
268 *pstripped
= stripped
;
270 *ptimestamp
= timestamp
;
277 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
278 vfs_handle_struct
*handle
)
280 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
285 if (stat(path
, &st
) != 0) {
292 while ((p
= strrchr(path
, '/')) && p
> path
) {
294 if (stat(path
, &st
) != 0) {
298 if (st
.st_dev
!= dev
) {
307 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
308 struct vfs_handle_struct
*handle
,
309 const char *name
, time_t timestamp
)
311 struct smb_filename converted_fname
;
313 size_t *slashes
= NULL
;
314 unsigned num_slashes
;
318 char *converted
= NULL
;
323 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
329 pathlen
= talloc_get_size(path
)-1;
331 DEBUG(10, ("converting %s\n", path
));
333 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
334 &slashes
, &num_slashes
)) {
337 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
338 if (insert
== NULL
) {
341 insertlen
= talloc_get_size(insert
)-1;
342 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
343 if (converted
== NULL
) {
347 if (path
[pathlen
-1] != '/') {
349 * Append a fake slash to find the snapshot root
352 tmp
= talloc_realloc(talloc_tos(), slashes
,
353 size_t, num_slashes
+1);
358 slashes
[num_slashes
] = pathlen
;
364 if (!lp_parm_bool(SNUM(handle
->conn
), "shadow", "crossmountpoints",
368 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
370 if (mount_point
== NULL
) {
373 min_offset
= strlen(mount_point
);
374 TALLOC_FREE(mount_point
);
377 memcpy(converted
, path
, pathlen
+1);
378 converted
[pathlen
+insertlen
] = '\0';
380 ZERO_STRUCT(converted_fname
);
381 converted_fname
.base_name
= converted
;
383 for (i
= num_slashes
-1; i
>=0; i
--) {
389 if (offset
< min_offset
) {
394 memcpy(converted
+offset
, insert
, insertlen
);
397 memcpy(converted
+offset
, path
+ slashes
[i
],
398 pathlen
- slashes
[i
]);
400 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
402 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
403 ret
, ret
== 0 ? "ok" : strerror(errno
)));
408 if (errno
== ENOTDIR
) {
410 * This is a valid condition: We appended the
411 * .snaphots/@GMT.. to a file name. Just try
412 * with the upper levels.
416 if (errno
!= ENOENT
) {
417 /* Other problem than "not found" */
426 DEBUG(10, ("Found %s\n", converted
));
434 TALLOC_FREE(converted
);
436 TALLOC_FREE(slashes
);
443 modify a sbuf return to ensure that inodes in the shadow directory
444 are different from those in the main directory
446 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
447 SMB_STRUCT_STAT
*sbuf
)
449 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
450 /* some snapshot systems, like GPFS, return the name
451 device:inode for the snapshot files as the current
452 files. That breaks the 'restore' button in the shadow copy
453 GUI, as the client gets a sharing violation.
455 This is a crude way of allowing both files to be
456 open at once. It has a slight chance of inode
457 number collision, but I can't see a better approach
458 without significant VFS changes
462 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
466 sbuf
->st_ex_ino
^= shash
;
470 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
481 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
482 ×tamp
, &stripped
)) {
485 if (timestamp
== 0) {
486 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
488 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
489 TALLOC_FREE(stripped
);
493 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
500 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
501 const struct smb_filename
*smb_fname_src
,
502 const struct smb_filename
*smb_fname_dst
)
504 time_t timestamp_src
, timestamp_dst
;
506 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
507 smb_fname_src
->base_name
,
508 ×tamp_src
, NULL
)) {
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
512 smb_fname_dst
->base_name
,
513 ×tamp_dst
, NULL
)) {
516 if (timestamp_src
!= 0) {
520 if (timestamp_dst
!= 0) {
524 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
527 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
528 const char *oldname
, const char *newname
)
530 time_t timestamp_old
, timestamp_new
;
532 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
533 ×tamp_old
, NULL
)) {
536 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
537 ×tamp_new
, NULL
)) {
540 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
544 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
547 static int shadow_copy2_link(vfs_handle_struct
*handle
,
548 const char *oldname
, const char *newname
)
550 time_t timestamp_old
, timestamp_new
;
552 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
553 ×tamp_old
, NULL
)) {
556 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
557 ×tamp_new
, NULL
)) {
560 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
564 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
567 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
568 struct smb_filename
*smb_fname
)
571 char *stripped
, *tmp
;
572 int ret
, saved_errno
;
574 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
575 smb_fname
->base_name
,
576 ×tamp
, &stripped
)) {
579 if (timestamp
== 0) {
580 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
583 tmp
= smb_fname
->base_name
;
584 smb_fname
->base_name
= shadow_copy2_convert(
585 talloc_tos(), handle
, stripped
, timestamp
);
586 TALLOC_FREE(stripped
);
588 if (smb_fname
->base_name
== NULL
) {
589 smb_fname
->base_name
= tmp
;
593 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
596 TALLOC_FREE(smb_fname
->base_name
);
597 smb_fname
->base_name
= tmp
;
600 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
606 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
607 struct smb_filename
*smb_fname
)
610 char *stripped
, *tmp
;
611 int ret
, saved_errno
;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
614 smb_fname
->base_name
,
615 ×tamp
, &stripped
)) {
618 if (timestamp
== 0) {
619 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
622 tmp
= smb_fname
->base_name
;
623 smb_fname
->base_name
= shadow_copy2_convert(
624 talloc_tos(), handle
, stripped
, timestamp
);
625 TALLOC_FREE(stripped
);
627 if (smb_fname
->base_name
== NULL
) {
628 smb_fname
->base_name
= tmp
;
632 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
635 TALLOC_FREE(smb_fname
->base_name
);
636 smb_fname
->base_name
= tmp
;
639 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
645 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
646 SMB_STRUCT_STAT
*sbuf
)
651 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
656 fsp
->fsp_name
->base_name
,
660 if (timestamp
!= 0) {
661 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
666 static int shadow_copy2_open(vfs_handle_struct
*handle
,
667 struct smb_filename
*smb_fname
, files_struct
*fsp
,
668 int flags
, mode_t mode
)
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_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
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_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
696 TALLOC_FREE(smb_fname
->base_name
);
697 smb_fname
->base_name
= tmp
;
703 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
704 const struct smb_filename
*smb_fname
)
708 int ret
, saved_errno
;
709 struct smb_filename
*conv
;
712 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
713 smb_fname
->base_name
,
714 ×tamp
, &stripped
)) {
717 if (timestamp
== 0) {
718 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
720 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
721 if (!NT_STATUS_IS_OK(status
)) {
725 conv
->base_name
= shadow_copy2_convert(
726 conv
, handle
, stripped
, timestamp
);
727 TALLOC_FREE(stripped
);
728 if (conv
->base_name
== NULL
) {
731 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
738 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
743 int ret
, saved_errno
;
746 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
747 ×tamp
, &stripped
)) {
750 if (timestamp
== 0) {
751 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
753 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
754 TALLOC_FREE(stripped
);
758 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
765 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
766 uid_t uid
, gid_t gid
)
770 int ret
, saved_errno
;
773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
774 ×tamp
, &stripped
)) {
777 if (timestamp
== 0) {
778 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
780 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
781 TALLOC_FREE(stripped
);
785 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
792 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
797 int ret
, saved_errno
;
800 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
801 ×tamp
, &stripped
)) {
804 if (timestamp
== 0) {
805 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
807 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
808 TALLOC_FREE(stripped
);
812 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
819 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
820 const struct smb_filename
*smb_fname
,
821 struct smb_file_time
*ft
)
825 int ret
, saved_errno
;
826 struct smb_filename
*conv
;
829 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
830 smb_fname
->base_name
,
831 ×tamp
, &stripped
)) {
834 if (timestamp
== 0) {
835 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
837 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
838 if (!NT_STATUS_IS_OK(status
)) {
842 conv
->base_name
= shadow_copy2_convert(
843 conv
, handle
, stripped
, timestamp
);
844 TALLOC_FREE(stripped
);
845 if (conv
->base_name
== NULL
) {
848 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
855 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
856 const char *fname
, char *buf
, size_t bufsiz
)
860 int ret
, saved_errno
;
863 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
864 ×tamp
, &stripped
)) {
867 if (timestamp
== 0) {
868 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
870 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
871 TALLOC_FREE(stripped
);
875 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
882 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
883 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
887 int ret
, saved_errno
;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
891 ×tamp
, &stripped
)) {
894 if (timestamp
== 0) {
895 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
897 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
898 TALLOC_FREE(stripped
);
902 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
909 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
913 char *stripped
= NULL
;
916 char *inserted
= NULL
;
917 char *inserted_to
, *inserted_end
;
920 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
921 ×tamp
, &stripped
)) {
924 if (timestamp
== 0) {
925 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
928 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
933 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
934 if (result
== NULL
) {
939 * Take away what we've inserted. This removes the @GMT-thingy
940 * completely, but will give a path under the share root.
942 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
943 if (inserted
== NULL
) {
946 inserted_to
= strstr_m(result
, inserted
);
947 if (inserted_to
== NULL
) {
948 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
951 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
952 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
956 TALLOC_FREE(inserted
);
958 TALLOC_FREE(stripped
);
963 static char *have_snapdir(struct vfs_handle_struct
*handle
,
966 struct smb_filename smb_fname
;
969 ZERO_STRUCT(smb_fname
);
970 smb_fname
.base_name
= talloc_asprintf(
971 talloc_tos(), "%s/%s", path
,
972 lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir",
974 if (smb_fname
.base_name
== NULL
) {
978 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
979 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
980 return smb_fname
.base_name
;
982 TALLOC_FREE(smb_fname
.base_name
);
986 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
987 struct vfs_handle_struct
*handle
,
988 struct smb_filename
*smb_fname
)
993 path
= talloc_asprintf(mem_ctx
, "%s/%s",
994 handle
->conn
->connectpath
,
995 smb_fname
->base_name
);
1000 snapdir
= have_snapdir(handle
, path
);
1001 if (snapdir
!= NULL
) {
1006 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1010 snapdir
= have_snapdir(handle
, path
);
1011 if (snapdir
!= NULL
) {
1020 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1022 char *gmt
, size_t gmt_len
)
1024 struct tm timestamp
;
1026 unsigned long int timestamp_long
;
1029 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1030 "format", GMT_FORMAT
);
1032 ZERO_STRUCT(timestamp
);
1033 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "sscanf", false)) {
1034 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1035 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no sscanf match %s: %s\n",
1039 timestamp_t
= timestamp_long
;
1040 gmtime_r(×tamp_t
, ×tamp
);
1042 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1043 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1047 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
1049 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1050 timestamp
.tm_isdst
= -1;
1051 timestamp_t
= mktime(×tamp
);
1052 gmtime_r(×tamp_t
, ×tamp
);
1056 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1060 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1062 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1065 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1067 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1071 sort the shadow copy data in ascending or descending order
1073 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1074 struct shadow_copy_data
*shadow_copy2_data
)
1076 int (*cmpfunc
)(const void *, const void *);
1079 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1085 if (strcmp(sort
, "asc") == 0) {
1086 cmpfunc
= shadow_copy2_label_cmp_asc
;
1087 } else if (strcmp(sort
, "desc") == 0) {
1088 cmpfunc
= shadow_copy2_label_cmp_desc
;
1093 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1094 shadow_copy2_data
->labels
)
1096 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1097 shadow_copy2_data
->num_volumes
,
1102 static int shadow_copy2_get_shadow_copy_data(
1103 vfs_handle_struct
*handle
, files_struct
*fsp
,
1104 struct shadow_copy_data
*shadow_copy2_data
,
1108 const char *snapdir
;
1110 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1112 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1113 if (snapdir
== NULL
) {
1114 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1115 handle
->conn
->connectpath
));
1117 talloc_free(tmp_ctx
);
1121 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1124 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1125 " - %s\n", snapdir
, strerror(errno
)));
1126 talloc_free(tmp_ctx
);
1131 shadow_copy2_data
->num_volumes
= 0;
1132 shadow_copy2_data
->labels
= NULL
;
1134 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1135 char snapshot
[GMT_NAME_LEN
+1];
1136 SHADOW_COPY_LABEL
*tlabels
;
1139 * ignore names not of the right form in the snapshot
1142 if (!shadow_copy2_snapshot_to_gmt(
1144 snapshot
, sizeof(snapshot
))) {
1146 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1147 "ignoring %s\n", d
->d_name
));
1150 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1151 d
->d_name
, snapshot
));
1154 /* the caller doesn't want the labels */
1155 shadow_copy2_data
->num_volumes
++;
1159 tlabels
= talloc_realloc(shadow_copy2_data
,
1160 shadow_copy2_data
->labels
,
1162 shadow_copy2_data
->num_volumes
+1);
1163 if (tlabels
== NULL
) {
1164 DEBUG(0,("shadow_copy2: out of memory\n"));
1165 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1166 talloc_free(tmp_ctx
);
1170 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1173 shadow_copy2_data
->num_volumes
++;
1174 shadow_copy2_data
->labels
= tlabels
;
1177 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1179 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1181 talloc_free(tmp_ctx
);
1185 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1186 struct files_struct
*fsp
,
1187 uint32 security_info
,
1188 TALLOC_CTX
*mem_ctx
,
1189 struct security_descriptor
**ppdesc
)
1196 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1197 fsp
->fsp_name
->base_name
,
1198 ×tamp
, &stripped
)) {
1199 return map_nt_error_from_unix(errno
);
1201 if (timestamp
== 0) {
1202 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1206 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1207 TALLOC_FREE(stripped
);
1209 return map_nt_error_from_unix(errno
);
1211 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1217 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1219 uint32 security_info
,
1220 TALLOC_CTX
*mem_ctx
,
1221 struct security_descriptor
**ppdesc
)
1228 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1229 ×tamp
, &stripped
)) {
1230 return map_nt_error_from_unix(errno
);
1232 if (timestamp
== 0) {
1233 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1236 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1237 TALLOC_FREE(stripped
);
1239 return map_nt_error_from_unix(errno
);
1241 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1247 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1248 const char *fname
, mode_t mode
)
1252 int ret
, saved_errno
;
1255 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1256 ×tamp
, &stripped
)) {
1259 if (timestamp
== 0) {
1260 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1262 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1263 TALLOC_FREE(stripped
);
1267 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1268 saved_errno
= errno
;
1270 errno
= saved_errno
;
1274 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1278 int ret
, saved_errno
;
1281 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1282 ×tamp
, &stripped
)) {
1285 if (timestamp
== 0) {
1286 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1288 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1289 TALLOC_FREE(stripped
);
1293 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1294 saved_errno
= errno
;
1296 errno
= saved_errno
;
1300 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1305 int ret
, saved_errno
;
1308 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1309 ×tamp
, &stripped
)) {
1312 if (timestamp
== 0) {
1313 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1315 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1316 TALLOC_FREE(stripped
);
1320 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1321 saved_errno
= errno
;
1323 errno
= saved_errno
;
1327 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1328 const char *fname
, const char *aname
,
1329 void *value
, size_t size
)
1337 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1338 ×tamp
, &stripped
)) {
1341 if (timestamp
== 0) {
1342 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1345 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1346 TALLOC_FREE(stripped
);
1350 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1351 saved_errno
= errno
;
1353 errno
= saved_errno
;
1357 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1359 char *list
, size_t size
)
1367 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1368 ×tamp
, &stripped
)) {
1371 if (timestamp
== 0) {
1372 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1374 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1375 TALLOC_FREE(stripped
);
1379 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1380 saved_errno
= errno
;
1382 errno
= saved_errno
;
1386 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1387 const char *fname
, const char *aname
)
1391 int ret
, saved_errno
;
1394 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1395 ×tamp
, &stripped
)) {
1398 if (timestamp
== 0) {
1399 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1401 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1402 TALLOC_FREE(stripped
);
1406 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1407 saved_errno
= errno
;
1409 errno
= saved_errno
;
1413 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1415 const char *aname
, const void *value
,
1416 size_t size
, int flags
)
1424 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1425 ×tamp
, &stripped
)) {
1428 if (timestamp
== 0) {
1429 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1432 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1433 TALLOC_FREE(stripped
);
1437 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1438 saved_errno
= errno
;
1440 errno
= saved_errno
;
1444 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1445 const char *fname
, mode_t mode
)
1453 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1454 ×tamp
, &stripped
)) {
1457 if (timestamp
== 0) {
1458 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1460 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1461 TALLOC_FREE(stripped
);
1465 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1466 saved_errno
= errno
;
1468 errno
= saved_errno
;
1472 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1475 TALLOC_CTX
*mem_ctx
,
1484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1485 ×tamp
, &stripped
)) {
1488 if (timestamp
== 0) {
1489 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1490 mem_ctx
, found_name
);
1492 if (stripped
[0] == '\0') {
1493 *found_name
= talloc_strdup(mem_ctx
, name
);
1494 if (*found_name
== NULL
) {
1500 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1501 TALLOC_FREE(stripped
);
1505 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1506 mem_ctx
, found_name
);
1507 saved_errno
= errno
;
1509 errno
= saved_errno
;
1514 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1515 .opendir_fn
= shadow_copy2_opendir
,
1516 .rename_fn
= shadow_copy2_rename
,
1517 .link_fn
= shadow_copy2_link
,
1518 .symlink_fn
= shadow_copy2_symlink
,
1519 .stat_fn
= shadow_copy2_stat
,
1520 .lstat_fn
= shadow_copy2_lstat
,
1521 .fstat_fn
= shadow_copy2_fstat
,
1522 .open_fn
= shadow_copy2_open
,
1523 .unlink_fn
= shadow_copy2_unlink
,
1524 .chmod_fn
= shadow_copy2_chmod
,
1525 .chown_fn
= shadow_copy2_chown
,
1526 .chdir_fn
= shadow_copy2_chdir
,
1527 .ntimes_fn
= shadow_copy2_ntimes
,
1528 .readlink_fn
= shadow_copy2_readlink
,
1529 .mknod_fn
= shadow_copy2_mknod
,
1530 .realpath_fn
= shadow_copy2_realpath
,
1531 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1532 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1533 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1534 .mkdir_fn
= shadow_copy2_mkdir
,
1535 .rmdir_fn
= shadow_copy2_rmdir
,
1536 .getxattr_fn
= shadow_copy2_getxattr
,
1537 .listxattr_fn
= shadow_copy2_listxattr
,
1538 .removexattr_fn
= shadow_copy2_removexattr
,
1539 .setxattr_fn
= shadow_copy2_setxattr
,
1540 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1541 .chflags_fn
= shadow_copy2_chflags
,
1542 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1545 NTSTATUS
vfs_shadow_copy2_init(void);
1546 NTSTATUS
vfs_shadow_copy2_init(void)
1548 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1549 "shadow_copy2", &vfs_shadow_copy2_fns
);