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:localtime = yes/no (default is no)
89 This is an optional parameter that indicates whether the
90 snapshot names are in UTC/GMT or the local time.
93 The following command would generate a correctly formatted directory name
94 for use with the default parameters:
95 date -u +@GMT-%Y.%m.%d-%H.%M.%S
99 #include "system/filesys.h"
100 #include "include/ntioctl.h"
101 #include "smbd/proto.h"
103 #include "util_tdb.h"
105 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
106 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
108 static bool shadow_copy2_find_slashes(TALLOC_CTX
*mem_ctx
, const char *str
,
110 unsigned *pnum_offsets
)
112 unsigned num_offsets
;
119 while ((p
= strchr(p
, '/')) != NULL
) {
124 offsets
= talloc_array(mem_ctx
, size_t, num_offsets
);
125 if (offsets
== NULL
) {
131 while ((p
= strchr(p
, '/')) != NULL
) {
132 offsets
[num_offsets
] = p
-str
;
138 *pnum_offsets
= num_offsets
;
142 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
143 struct vfs_handle_struct
*handle
,
150 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
151 DEBUG(10, ("gmtime_r failed\n"));
154 gmt_len
= strftime(gmt
, sizeof(gmt
),
155 lp_parm_const_string(SNUM(handle
->conn
), "shadow",
156 "format", GMT_FORMAT
),
159 DEBUG(10, ("strftime failed\n"));
162 return talloc_asprintf(talloc_tos(), "/%s/%s",
163 lp_parm_const_string(
164 SNUM(handle
->conn
), "shadow", "snapdir",
169 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
170 struct vfs_handle_struct
*handle
,
180 size_t rest_len
, dst_len
;
182 p
= strstr_m(name
, "@GMT-");
186 if ((p
> name
) && (p
[-1] != '/')) {
189 q
= strptime(p
, GMT_FORMAT
, &tm
);
194 timestamp
= mktime(&tm
);
195 if (timestamp
== (time_t)-1) {
198 if ((p
== name
) && (q
[0] == '\0')) {
199 if (pstripped
!= NULL
) {
200 stripped
= talloc_strdup(mem_ctx
, "");
201 if (stripped
== NULL
) {
204 *pstripped
= stripped
;
206 *ptimestamp
= timestamp
;
214 rest_len
= strlen(q
);
215 dst_len
= (p
-name
) + rest_len
;
217 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "snapdirseverywhere",
221 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
223 if (insert
== NULL
) {
228 have_insert
= (strstr(name
, insert
+1) != NULL
);
235 if (pstripped
!= NULL
) {
236 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
237 if (stripped
== NULL
) {
242 memcpy(stripped
, name
, p
-name
);
245 memcpy(stripped
+ (p
-name
), q
, rest_len
);
247 stripped
[dst_len
] = '\0';
248 *pstripped
= stripped
;
250 *ptimestamp
= timestamp
;
257 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
258 vfs_handle_struct
*handle
)
260 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
265 if (stat(path
, &st
) != 0) {
272 while ((p
= strrchr(path
, '/')) && p
> path
) {
274 if (stat(path
, &st
) != 0) {
278 if (st
.st_dev
!= dev
) {
287 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
288 struct vfs_handle_struct
*handle
,
289 const char *name
, time_t timestamp
)
291 struct smb_filename converted_fname
;
293 size_t *slashes
= NULL
;
294 unsigned num_slashes
;
298 char *converted
= NULL
;
303 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
309 pathlen
= talloc_get_size(path
)-1;
311 DEBUG(10, ("converting %s\n", path
));
313 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
314 &slashes
, &num_slashes
)) {
317 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
318 if (insert
== NULL
) {
321 insertlen
= talloc_get_size(insert
)-1;
322 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
323 if (converted
== NULL
) {
327 if (path
[pathlen
-1] != '/') {
329 * Append a fake slash to find the snapshot root
332 tmp
= talloc_realloc(talloc_tos(), slashes
,
333 size_t, num_slashes
+1);
338 slashes
[num_slashes
] = pathlen
;
344 if (!lp_parm_bool(SNUM(handle
->conn
), "shadow", "crossmountpoints",
348 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
350 if (mount_point
== NULL
) {
353 min_offset
= strlen(mount_point
);
354 TALLOC_FREE(mount_point
);
357 memcpy(converted
, path
, pathlen
+1);
358 converted
[pathlen
+insertlen
] = '\0';
360 ZERO_STRUCT(converted_fname
);
361 converted_fname
.base_name
= converted
;
363 for (i
= num_slashes
-1; i
>=0; i
--) {
369 if (offset
< min_offset
) {
374 memcpy(converted
+offset
, insert
, insertlen
);
377 memcpy(converted
+offset
, path
+ slashes
[i
],
378 pathlen
- slashes
[i
]);
380 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
382 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
383 ret
, ret
== 0 ? "ok" : strerror(errno
)));
388 if (errno
== ENOTDIR
) {
390 * This is a valid condition: We appended the
391 * .snaphots/@GMT.. to a file name. Just try
392 * with the upper levels.
396 if (errno
!= ENOENT
) {
397 /* Other problem than "not found" */
406 DEBUG(10, ("Found %s\n", converted
));
414 TALLOC_FREE(converted
);
416 TALLOC_FREE(slashes
);
423 modify a sbuf return to ensure that inodes in the shadow directory
424 are different from those in the main directory
426 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
427 SMB_STRUCT_STAT
*sbuf
)
429 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
430 /* some snapshot systems, like GPFS, return the name
431 device:inode for the snapshot files as the current
432 files. That breaks the 'restore' button in the shadow copy
433 GUI, as the client gets a sharing violation.
435 This is a crude way of allowing both files to be
436 open at once. It has a slight chance of inode
437 number collision, but I can't see a better approach
438 without significant VFS changes
441 TDB_DATA data
= string_tdb_data(fname
);
443 shash
= tdb_jenkins_hash(&data
) & 0xFF000000;
447 sbuf
->st_ex_ino
^= shash
;
451 static SMB_STRUCT_DIR
*shadow_copy2_opendir(vfs_handle_struct
*handle
,
462 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
463 ×tamp
, &stripped
)) {
466 if (timestamp
== 0) {
467 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
469 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
470 TALLOC_FREE(stripped
);
474 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
481 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
482 const struct smb_filename
*smb_fname_src
,
483 const struct smb_filename
*smb_fname_dst
)
485 time_t timestamp_src
, timestamp_dst
;
487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
488 smb_fname_src
->base_name
,
489 ×tamp_src
, NULL
)) {
492 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
493 smb_fname_dst
->base_name
,
494 ×tamp_dst
, NULL
)) {
497 if (timestamp_src
!= 0) {
501 if (timestamp_dst
!= 0) {
505 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
508 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
509 const char *oldname
, const char *newname
)
511 time_t timestamp_old
, timestamp_new
;
513 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
514 ×tamp_old
, NULL
)) {
517 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
518 ×tamp_new
, NULL
)) {
521 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
525 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
528 static int shadow_copy2_link(vfs_handle_struct
*handle
,
529 const char *oldname
, const char *newname
)
531 time_t timestamp_old
, timestamp_new
;
533 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
534 ×tamp_old
, NULL
)) {
537 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
538 ×tamp_new
, NULL
)) {
541 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
545 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
548 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
549 struct smb_filename
*smb_fname
)
552 char *stripped
, *tmp
;
553 int ret
, saved_errno
;
555 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
556 smb_fname
->base_name
,
557 ×tamp
, &stripped
)) {
560 if (timestamp
== 0) {
561 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
564 tmp
= smb_fname
->base_name
;
565 smb_fname
->base_name
= shadow_copy2_convert(
566 talloc_tos(), handle
, stripped
, timestamp
);
567 TALLOC_FREE(stripped
);
569 if (smb_fname
->base_name
== NULL
) {
570 smb_fname
->base_name
= tmp
;
574 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
577 TALLOC_FREE(smb_fname
->base_name
);
578 smb_fname
->base_name
= tmp
;
581 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
587 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
588 struct smb_filename
*smb_fname
)
591 char *stripped
, *tmp
;
592 int ret
, saved_errno
;
594 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
595 smb_fname
->base_name
,
596 ×tamp
, &stripped
)) {
599 if (timestamp
== 0) {
600 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
603 tmp
= smb_fname
->base_name
;
604 smb_fname
->base_name
= shadow_copy2_convert(
605 talloc_tos(), handle
, stripped
, timestamp
);
606 TALLOC_FREE(stripped
);
608 if (smb_fname
->base_name
== NULL
) {
609 smb_fname
->base_name
= tmp
;
613 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
616 TALLOC_FREE(smb_fname
->base_name
);
617 smb_fname
->base_name
= tmp
;
620 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
626 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
627 SMB_STRUCT_STAT
*sbuf
)
632 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
636 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
637 fsp
->fsp_name
->base_name
,
641 if (timestamp
!= 0) {
642 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
647 static int shadow_copy2_open(vfs_handle_struct
*handle
,
648 struct smb_filename
*smb_fname
, files_struct
*fsp
,
649 int flags
, mode_t mode
)
652 char *stripped
, *tmp
;
653 int ret
, saved_errno
;
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
656 smb_fname
->base_name
,
657 ×tamp
, &stripped
)) {
660 if (timestamp
== 0) {
661 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
664 tmp
= smb_fname
->base_name
;
665 smb_fname
->base_name
= shadow_copy2_convert(
666 talloc_tos(), handle
, stripped
, timestamp
);
667 TALLOC_FREE(stripped
);
669 if (smb_fname
->base_name
== NULL
) {
670 smb_fname
->base_name
= tmp
;
674 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
677 TALLOC_FREE(smb_fname
->base_name
);
678 smb_fname
->base_name
= tmp
;
684 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
685 const struct smb_filename
*smb_fname
)
689 int ret
, saved_errno
;
690 struct smb_filename
*conv
;
693 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
694 smb_fname
->base_name
,
695 ×tamp
, &stripped
)) {
698 if (timestamp
== 0) {
699 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
701 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
702 if (!NT_STATUS_IS_OK(status
)) {
706 conv
->base_name
= shadow_copy2_convert(
707 conv
, handle
, stripped
, timestamp
);
708 TALLOC_FREE(stripped
);
709 if (conv
->base_name
== NULL
) {
712 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
719 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
724 int ret
, saved_errno
;
727 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
728 ×tamp
, &stripped
)) {
731 if (timestamp
== 0) {
732 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
734 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
735 TALLOC_FREE(stripped
);
739 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
746 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
747 uid_t uid
, gid_t gid
)
751 int ret
, saved_errno
;
754 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
755 ×tamp
, &stripped
)) {
758 if (timestamp
== 0) {
759 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
761 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
762 TALLOC_FREE(stripped
);
766 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
773 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
778 int ret
, saved_errno
;
781 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
782 ×tamp
, &stripped
)) {
785 if (timestamp
== 0) {
786 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
788 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
789 TALLOC_FREE(stripped
);
793 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
800 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
801 const struct smb_filename
*smb_fname
,
802 struct smb_file_time
*ft
)
806 int ret
, saved_errno
;
807 struct smb_filename
*conv
;
810 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
811 smb_fname
->base_name
,
812 ×tamp
, &stripped
)) {
815 if (timestamp
== 0) {
816 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
818 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
819 if (!NT_STATUS_IS_OK(status
)) {
823 conv
->base_name
= shadow_copy2_convert(
824 conv
, handle
, stripped
, timestamp
);
825 TALLOC_FREE(stripped
);
826 if (conv
->base_name
== NULL
) {
829 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
836 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
837 const char *fname
, char *buf
, size_t bufsiz
)
841 int ret
, saved_errno
;
844 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
845 ×tamp
, &stripped
)) {
848 if (timestamp
== 0) {
849 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
851 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
852 TALLOC_FREE(stripped
);
856 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
863 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
864 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
868 int ret
, saved_errno
;
871 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
872 ×tamp
, &stripped
)) {
875 if (timestamp
== 0) {
876 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
878 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
879 TALLOC_FREE(stripped
);
883 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
890 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
894 char *stripped
= NULL
;
897 char *inserted
= NULL
;
898 char *inserted_to
, *inserted_end
;
901 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
902 ×tamp
, &stripped
)) {
905 if (timestamp
== 0) {
906 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
909 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
914 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
915 if (result
== NULL
) {
920 * Take away what we've inserted. This removes the @GMT-thingy
921 * completely, but will give a path under the share root.
923 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
924 if (inserted
== NULL
) {
927 inserted_to
= strstr_m(result
, inserted
);
928 if (inserted_to
== NULL
) {
929 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
932 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
933 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
937 TALLOC_FREE(inserted
);
939 TALLOC_FREE(stripped
);
944 static char *have_snapdir(struct vfs_handle_struct
*handle
,
947 struct smb_filename smb_fname
;
950 ZERO_STRUCT(smb_fname
);
951 smb_fname
.base_name
= talloc_asprintf(
952 talloc_tos(), "%s/%s", path
,
953 lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir",
955 if (smb_fname
.base_name
== NULL
) {
959 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
960 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
961 return smb_fname
.base_name
;
963 TALLOC_FREE(smb_fname
.base_name
);
967 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
968 struct vfs_handle_struct
*handle
,
969 struct smb_filename
*smb_fname
)
974 path
= talloc_asprintf(mem_ctx
, "%s/%s",
975 handle
->conn
->connectpath
,
976 smb_fname
->base_name
);
981 snapdir
= have_snapdir(handle
, path
);
982 if (snapdir
!= NULL
) {
987 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
991 snapdir
= have_snapdir(handle
, path
);
992 if (snapdir
!= NULL
) {
1001 static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX
*mem_ctx
,
1002 vfs_handle_struct
*handle
,
1004 char *gmt
, size_t gmt_len
)
1006 struct tm timestamp
;
1010 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1011 "format", GMT_FORMAT
);
1013 ZERO_STRUCT(timestamp
);
1014 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1015 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1020 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
1022 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1023 timestamp
.tm_isdst
= -1;
1024 timestamp_t
= mktime(×tamp
);
1025 gmtime_r(×tamp_t
, ×tamp
);
1027 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1031 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1033 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1036 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1038 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1042 sort the shadow copy data in ascending or descending order
1044 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1045 struct shadow_copy_data
*shadow_copy2_data
)
1047 int (*cmpfunc
)(const void *, const void *);
1050 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1056 if (strcmp(sort
, "asc") == 0) {
1057 cmpfunc
= shadow_copy2_label_cmp_asc
;
1058 } else if (strcmp(sort
, "desc") == 0) {
1059 cmpfunc
= shadow_copy2_label_cmp_desc
;
1064 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1065 shadow_copy2_data
->labels
)
1067 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1068 shadow_copy2_data
->num_volumes
,
1075 static int shadow_copy2_get_shadow_copy_data(
1076 vfs_handle_struct
*handle
, files_struct
*fsp
,
1077 struct shadow_copy_data
*shadow_copy2_data
,
1081 const char *snapdir
;
1082 SMB_STRUCT_DIRENT
*d
;
1083 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1085 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1086 if (snapdir
== NULL
) {
1087 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1088 handle
->conn
->connectpath
));
1090 talloc_free(tmp_ctx
);
1094 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1097 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1098 " - %s\n", snapdir
, strerror(errno
)));
1099 talloc_free(tmp_ctx
);
1104 shadow_copy2_data
->num_volumes
= 0;
1105 shadow_copy2_data
->labels
= NULL
;
1107 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1108 char snapshot
[GMT_NAME_LEN
+1];
1109 SHADOW_COPY_LABEL
*tlabels
;
1112 * ignore names not of the right form in the snapshot
1115 if (!shadow_copy2_snapshot_to_gmt(
1116 tmp_ctx
, handle
, d
->d_name
,
1117 snapshot
, sizeof(snapshot
))) {
1119 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1120 "ignoring %s\n", d
->d_name
));
1123 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1124 d
->d_name
, snapshot
));
1127 /* the caller doesn't want the labels */
1128 shadow_copy2_data
->num_volumes
++;
1132 tlabels
= talloc_realloc(shadow_copy2_data
,
1133 shadow_copy2_data
->labels
,
1135 shadow_copy2_data
->num_volumes
+1);
1136 if (tlabels
== NULL
) {
1137 DEBUG(0,("shadow_copy2: out of memory\n"));
1138 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1139 talloc_free(tmp_ctx
);
1143 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1146 shadow_copy2_data
->num_volumes
++;
1147 shadow_copy2_data
->labels
= tlabels
;
1150 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1152 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1154 talloc_free(tmp_ctx
);
1158 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1159 struct files_struct
*fsp
,
1160 uint32 security_info
,
1161 struct security_descriptor
**ppdesc
)
1168 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1169 fsp
->fsp_name
->base_name
,
1170 ×tamp
, &stripped
)) {
1171 return map_nt_error_from_unix(errno
);
1173 if (timestamp
== 0) {
1174 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1177 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1178 TALLOC_FREE(stripped
);
1180 return map_nt_error_from_unix(errno
);
1182 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1187 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1189 uint32 security_info
,
1190 struct security_descriptor
**ppdesc
)
1197 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1198 ×tamp
, &stripped
)) {
1199 return map_nt_error_from_unix(errno
);
1201 if (timestamp
== 0) {
1202 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1205 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1206 TALLOC_FREE(stripped
);
1208 return map_nt_error_from_unix(errno
);
1210 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1215 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1216 const char *fname
, mode_t mode
)
1220 int ret
, saved_errno
;
1223 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1224 ×tamp
, &stripped
)) {
1227 if (timestamp
== 0) {
1228 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1230 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1231 TALLOC_FREE(stripped
);
1235 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1236 saved_errno
= errno
;
1238 errno
= saved_errno
;
1242 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1246 int ret
, saved_errno
;
1249 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1250 ×tamp
, &stripped
)) {
1253 if (timestamp
== 0) {
1254 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1256 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1257 TALLOC_FREE(stripped
);
1261 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1262 saved_errno
= errno
;
1264 errno
= saved_errno
;
1268 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1273 int ret
, saved_errno
;
1276 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1277 ×tamp
, &stripped
)) {
1280 if (timestamp
== 0) {
1281 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1283 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1284 TALLOC_FREE(stripped
);
1288 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1289 saved_errno
= errno
;
1291 errno
= saved_errno
;
1295 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1296 const char *fname
, const char *aname
,
1297 void *value
, size_t size
)
1305 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1306 ×tamp
, &stripped
)) {
1309 if (timestamp
== 0) {
1310 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1313 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1314 TALLOC_FREE(stripped
);
1318 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1319 saved_errno
= errno
;
1321 errno
= saved_errno
;
1325 static ssize_t
shadow_copy2_lgetxattr(vfs_handle_struct
*handle
,
1326 const char *fname
, const char *aname
,
1327 void *value
, size_t size
)
1335 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1336 ×tamp
, &stripped
)) {
1339 if (timestamp
== 0) {
1340 return SMB_VFS_NEXT_LGETXATTR(handle
, fname
, aname
, value
,
1343 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1344 TALLOC_FREE(stripped
);
1348 ret
= SMB_VFS_NEXT_LGETXATTR(handle
, conv
, aname
, value
, size
);
1349 saved_errno
= errno
;
1351 errno
= saved_errno
;
1355 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1357 char *list
, 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_LISTXATTR(handle
, fname
, list
, size
);
1372 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1373 TALLOC_FREE(stripped
);
1377 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1378 saved_errno
= errno
;
1380 errno
= saved_errno
;
1384 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1385 const char *fname
, const char *aname
)
1389 int ret
, saved_errno
;
1392 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1393 ×tamp
, &stripped
)) {
1396 if (timestamp
== 0) {
1397 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1399 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1400 TALLOC_FREE(stripped
);
1404 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1405 saved_errno
= errno
;
1407 errno
= saved_errno
;
1411 static int shadow_copy2_lremovexattr(vfs_handle_struct
*handle
,
1412 const char *fname
, const char *aname
)
1416 int ret
, saved_errno
;
1419 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1420 ×tamp
, &stripped
)) {
1423 if (timestamp
== 0) {
1424 return SMB_VFS_NEXT_LREMOVEXATTR(handle
, fname
, aname
);
1426 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1427 TALLOC_FREE(stripped
);
1431 ret
= SMB_VFS_NEXT_LREMOVEXATTR(handle
, conv
, aname
);
1432 saved_errno
= errno
;
1434 errno
= saved_errno
;
1438 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1440 const char *aname
, const void *value
,
1441 size_t size
, int flags
)
1449 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1450 ×tamp
, &stripped
)) {
1453 if (timestamp
== 0) {
1454 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1457 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1458 TALLOC_FREE(stripped
);
1462 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1463 saved_errno
= errno
;
1465 errno
= saved_errno
;
1469 static int shadow_copy2_lsetxattr(struct vfs_handle_struct
*handle
,
1471 const char *aname
, const void *value
,
1472 size_t size
, int flags
)
1480 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1481 ×tamp
, &stripped
)) {
1484 if (timestamp
== 0) {
1485 return SMB_VFS_NEXT_LSETXATTR(handle
, fname
, aname
, value
,
1488 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1489 TALLOC_FREE(stripped
);
1493 ret
= SMB_VFS_NEXT_LSETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1494 saved_errno
= errno
;
1496 errno
= saved_errno
;
1500 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1501 const char *fname
, mode_t mode
)
1509 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1510 ×tamp
, &stripped
)) {
1513 if (timestamp
== 0) {
1514 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1516 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1517 TALLOC_FREE(stripped
);
1521 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1522 saved_errno
= errno
;
1524 errno
= saved_errno
;
1528 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1531 TALLOC_CTX
*mem_ctx
,
1540 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1541 ×tamp
, &stripped
)) {
1544 if (timestamp
== 0) {
1545 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1546 mem_ctx
, found_name
);
1548 if (stripped
[0] == '\0') {
1549 *found_name
= talloc_strdup(mem_ctx
, name
);
1550 if (*found_name
== NULL
) {
1556 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1557 TALLOC_FREE(stripped
);
1561 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1562 mem_ctx
, found_name
);
1563 saved_errno
= errno
;
1565 errno
= saved_errno
;
1570 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1571 .opendir
= shadow_copy2_opendir
,
1572 .rename
= shadow_copy2_rename
,
1573 .link
= shadow_copy2_link
,
1574 .symlink
= shadow_copy2_symlink
,
1575 .stat
= shadow_copy2_stat
,
1576 .lstat
= shadow_copy2_lstat
,
1577 .fstat
= shadow_copy2_fstat
,
1578 .open_fn
= shadow_copy2_open
,
1579 .unlink
= shadow_copy2_unlink
,
1580 .chmod
= shadow_copy2_chmod
,
1581 .chown
= shadow_copy2_chown
,
1582 .chdir
= shadow_copy2_chdir
,
1583 .ntimes
= shadow_copy2_ntimes
,
1584 .vfs_readlink
= shadow_copy2_readlink
,
1585 .mknod
= shadow_copy2_mknod
,
1586 .realpath
= shadow_copy2_realpath
,
1587 .get_nt_acl
= shadow_copy2_get_nt_acl
,
1588 .fget_nt_acl
= shadow_copy2_fget_nt_acl
,
1589 .get_shadow_copy_data
= shadow_copy2_get_shadow_copy_data
,
1590 .mkdir
= shadow_copy2_mkdir
,
1591 .rmdir
= shadow_copy2_rmdir
,
1592 .getxattr
= shadow_copy2_getxattr
,
1593 .lgetxattr
= shadow_copy2_lgetxattr
,
1594 .listxattr
= shadow_copy2_listxattr
,
1595 .removexattr
= shadow_copy2_removexattr
,
1596 .lremovexattr
= shadow_copy2_lremovexattr
,
1597 .setxattr
= shadow_copy2_setxattr
,
1598 .lsetxattr
= shadow_copy2_lsetxattr
,
1599 .chmod_acl
= shadow_copy2_chmod_acl
,
1600 .chflags
= shadow_copy2_chflags
,
1601 .get_real_filename
= shadow_copy2_get_real_filename
,
1604 NTSTATUS
vfs_shadow_copy2_init(void);
1605 NTSTATUS
vfs_shadow_copy2_init(void)
1607 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1608 "shadow_copy2", &vfs_shadow_copy2_fns
);