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 <ccan/hash/hash.h>
102 #include "util_tdb.h"
104 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
105 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
107 static bool shadow_copy2_find_slashes(TALLOC_CTX
*mem_ctx
, const char *str
,
109 unsigned *pnum_offsets
)
111 unsigned num_offsets
;
118 while ((p
= strchr(p
, '/')) != NULL
) {
123 offsets
= talloc_array(mem_ctx
, size_t, num_offsets
);
124 if (offsets
== NULL
) {
130 while ((p
= strchr(p
, '/')) != NULL
) {
131 offsets
[num_offsets
] = p
-str
;
137 *pnum_offsets
= num_offsets
;
141 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
142 struct vfs_handle_struct
*handle
,
149 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
150 DEBUG(10, ("gmtime_r failed\n"));
153 gmt_len
= strftime(gmt
, sizeof(gmt
),
154 lp_parm_const_string(SNUM(handle
->conn
), "shadow",
155 "format", GMT_FORMAT
),
158 DEBUG(10, ("strftime failed\n"));
161 return talloc_asprintf(mem_ctx
, "/%s/%s",
162 lp_parm_const_string(
163 SNUM(handle
->conn
), "shadow", "snapdir",
168 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
169 struct vfs_handle_struct
*handle
,
179 size_t rest_len
, dst_len
;
181 p
= strstr_m(name
, "@GMT-");
185 if ((p
> name
) && (p
[-1] != '/')) {
188 q
= strptime(p
, GMT_FORMAT
, &tm
);
193 timestamp
= mktime(&tm
);
194 if (timestamp
== (time_t)-1) {
197 if ((p
== name
) && (q
[0] == '\0')) {
198 if (pstripped
!= NULL
) {
199 stripped
= talloc_strdup(mem_ctx
, "");
200 if (stripped
== NULL
) {
203 *pstripped
= stripped
;
205 *ptimestamp
= timestamp
;
213 rest_len
= strlen(q
);
214 dst_len
= (p
-name
) + rest_len
;
216 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "snapdirseverywhere",
220 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
222 if (insert
== NULL
) {
227 have_insert
= (strstr(name
, insert
+1) != NULL
);
234 if (pstripped
!= NULL
) {
235 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
236 if (stripped
== NULL
) {
241 memcpy(stripped
, name
, p
-name
);
244 memcpy(stripped
+ (p
-name
), q
, rest_len
);
246 stripped
[dst_len
] = '\0';
247 *pstripped
= stripped
;
249 *ptimestamp
= timestamp
;
256 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
257 vfs_handle_struct
*handle
)
259 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
264 if (stat(path
, &st
) != 0) {
271 while ((p
= strrchr(path
, '/')) && p
> path
) {
273 if (stat(path
, &st
) != 0) {
277 if (st
.st_dev
!= dev
) {
286 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
287 struct vfs_handle_struct
*handle
,
288 const char *name
, time_t timestamp
)
290 struct smb_filename converted_fname
;
292 size_t *slashes
= NULL
;
293 unsigned num_slashes
;
297 char *converted
= NULL
;
302 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
308 pathlen
= talloc_get_size(path
)-1;
310 DEBUG(10, ("converting %s\n", path
));
312 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
313 &slashes
, &num_slashes
)) {
316 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
317 if (insert
== NULL
) {
320 insertlen
= talloc_get_size(insert
)-1;
321 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
322 if (converted
== NULL
) {
326 if (path
[pathlen
-1] != '/') {
328 * Append a fake slash to find the snapshot root
331 tmp
= talloc_realloc(talloc_tos(), slashes
,
332 size_t, num_slashes
+1);
337 slashes
[num_slashes
] = pathlen
;
343 if (!lp_parm_bool(SNUM(handle
->conn
), "shadow", "crossmountpoints",
347 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
349 if (mount_point
== NULL
) {
352 min_offset
= strlen(mount_point
);
353 TALLOC_FREE(mount_point
);
356 memcpy(converted
, path
, pathlen
+1);
357 converted
[pathlen
+insertlen
] = '\0';
359 ZERO_STRUCT(converted_fname
);
360 converted_fname
.base_name
= converted
;
362 for (i
= num_slashes
-1; i
>=0; i
--) {
368 if (offset
< min_offset
) {
373 memcpy(converted
+offset
, insert
, insertlen
);
376 memcpy(converted
+offset
, path
+ slashes
[i
],
377 pathlen
- slashes
[i
]);
379 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
381 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
382 ret
, ret
== 0 ? "ok" : strerror(errno
)));
387 if (errno
== ENOTDIR
) {
389 * This is a valid condition: We appended the
390 * .snaphots/@GMT.. to a file name. Just try
391 * with the upper levels.
395 if (errno
!= ENOENT
) {
396 /* Other problem than "not found" */
405 DEBUG(10, ("Found %s\n", converted
));
413 TALLOC_FREE(converted
);
415 TALLOC_FREE(slashes
);
422 modify a sbuf return to ensure that inodes in the shadow directory
423 are different from those in the main directory
425 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
426 SMB_STRUCT_STAT
*sbuf
)
428 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
429 /* some snapshot systems, like GPFS, return the name
430 device:inode for the snapshot files as the current
431 files. That breaks the 'restore' button in the shadow copy
432 GUI, as the client gets a sharing violation.
434 This is a crude way of allowing both files to be
435 open at once. It has a slight chance of inode
436 number collision, but I can't see a better approach
437 without significant VFS changes
441 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
445 sbuf
->st_ex_ino
^= shash
;
449 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
460 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
461 ×tamp
, &stripped
)) {
464 if (timestamp
== 0) {
465 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
467 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
468 TALLOC_FREE(stripped
);
472 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
479 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
480 const struct smb_filename
*smb_fname_src
,
481 const struct smb_filename
*smb_fname_dst
)
483 time_t timestamp_src
, timestamp_dst
;
485 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
486 smb_fname_src
->base_name
,
487 ×tamp_src
, NULL
)) {
490 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
491 smb_fname_dst
->base_name
,
492 ×tamp_dst
, NULL
)) {
495 if (timestamp_src
!= 0) {
499 if (timestamp_dst
!= 0) {
503 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
506 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
507 const char *oldname
, const char *newname
)
509 time_t timestamp_old
, timestamp_new
;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
512 ×tamp_old
, NULL
)) {
515 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
516 ×tamp_new
, NULL
)) {
519 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
523 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
526 static int shadow_copy2_link(vfs_handle_struct
*handle
,
527 const char *oldname
, const char *newname
)
529 time_t timestamp_old
, timestamp_new
;
531 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
532 ×tamp_old
, NULL
)) {
535 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
536 ×tamp_new
, NULL
)) {
539 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
543 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
546 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
547 struct smb_filename
*smb_fname
)
550 char *stripped
, *tmp
;
551 int ret
, saved_errno
;
553 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
554 smb_fname
->base_name
,
555 ×tamp
, &stripped
)) {
558 if (timestamp
== 0) {
559 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
562 tmp
= smb_fname
->base_name
;
563 smb_fname
->base_name
= shadow_copy2_convert(
564 talloc_tos(), handle
, stripped
, timestamp
);
565 TALLOC_FREE(stripped
);
567 if (smb_fname
->base_name
== NULL
) {
568 smb_fname
->base_name
= tmp
;
572 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
575 TALLOC_FREE(smb_fname
->base_name
);
576 smb_fname
->base_name
= tmp
;
579 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
585 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
586 struct smb_filename
*smb_fname
)
589 char *stripped
, *tmp
;
590 int ret
, saved_errno
;
592 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
593 smb_fname
->base_name
,
594 ×tamp
, &stripped
)) {
597 if (timestamp
== 0) {
598 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
601 tmp
= smb_fname
->base_name
;
602 smb_fname
->base_name
= shadow_copy2_convert(
603 talloc_tos(), handle
, stripped
, timestamp
);
604 TALLOC_FREE(stripped
);
606 if (smb_fname
->base_name
== NULL
) {
607 smb_fname
->base_name
= tmp
;
611 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
614 TALLOC_FREE(smb_fname
->base_name
);
615 smb_fname
->base_name
= tmp
;
618 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
624 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
625 SMB_STRUCT_STAT
*sbuf
)
630 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
634 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
635 fsp
->fsp_name
->base_name
,
639 if (timestamp
!= 0) {
640 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
645 static int shadow_copy2_open(vfs_handle_struct
*handle
,
646 struct smb_filename
*smb_fname
, files_struct
*fsp
,
647 int flags
, mode_t mode
)
650 char *stripped
, *tmp
;
651 int ret
, saved_errno
;
653 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
654 smb_fname
->base_name
,
655 ×tamp
, &stripped
)) {
658 if (timestamp
== 0) {
659 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
662 tmp
= smb_fname
->base_name
;
663 smb_fname
->base_name
= shadow_copy2_convert(
664 talloc_tos(), handle
, stripped
, timestamp
);
665 TALLOC_FREE(stripped
);
667 if (smb_fname
->base_name
== NULL
) {
668 smb_fname
->base_name
= tmp
;
672 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
675 TALLOC_FREE(smb_fname
->base_name
);
676 smb_fname
->base_name
= tmp
;
682 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
683 const struct smb_filename
*smb_fname
)
687 int ret
, saved_errno
;
688 struct smb_filename
*conv
;
691 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
692 smb_fname
->base_name
,
693 ×tamp
, &stripped
)) {
696 if (timestamp
== 0) {
697 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
699 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
700 if (!NT_STATUS_IS_OK(status
)) {
704 conv
->base_name
= shadow_copy2_convert(
705 conv
, handle
, stripped
, timestamp
);
706 TALLOC_FREE(stripped
);
707 if (conv
->base_name
== NULL
) {
710 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
717 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
722 int ret
, saved_errno
;
725 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
726 ×tamp
, &stripped
)) {
729 if (timestamp
== 0) {
730 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
732 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
733 TALLOC_FREE(stripped
);
737 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
744 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
745 uid_t uid
, gid_t gid
)
749 int ret
, saved_errno
;
752 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
753 ×tamp
, &stripped
)) {
756 if (timestamp
== 0) {
757 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
759 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
760 TALLOC_FREE(stripped
);
764 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
771 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
776 int ret
, saved_errno
;
779 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
780 ×tamp
, &stripped
)) {
783 if (timestamp
== 0) {
784 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
786 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
787 TALLOC_FREE(stripped
);
791 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
798 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
799 const struct smb_filename
*smb_fname
,
800 struct smb_file_time
*ft
)
804 int ret
, saved_errno
;
805 struct smb_filename
*conv
;
808 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
809 smb_fname
->base_name
,
810 ×tamp
, &stripped
)) {
813 if (timestamp
== 0) {
814 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
816 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
817 if (!NT_STATUS_IS_OK(status
)) {
821 conv
->base_name
= shadow_copy2_convert(
822 conv
, handle
, stripped
, timestamp
);
823 TALLOC_FREE(stripped
);
824 if (conv
->base_name
== NULL
) {
827 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
834 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
835 const char *fname
, char *buf
, size_t bufsiz
)
839 int ret
, saved_errno
;
842 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
843 ×tamp
, &stripped
)) {
846 if (timestamp
== 0) {
847 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
849 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
850 TALLOC_FREE(stripped
);
854 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
861 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
862 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
866 int ret
, saved_errno
;
869 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
870 ×tamp
, &stripped
)) {
873 if (timestamp
== 0) {
874 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
876 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
877 TALLOC_FREE(stripped
);
881 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
888 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
892 char *stripped
= NULL
;
895 char *inserted
= NULL
;
896 char *inserted_to
, *inserted_end
;
899 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
900 ×tamp
, &stripped
)) {
903 if (timestamp
== 0) {
904 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
907 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
912 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
913 if (result
== NULL
) {
918 * Take away what we've inserted. This removes the @GMT-thingy
919 * completely, but will give a path under the share root.
921 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
922 if (inserted
== NULL
) {
925 inserted_to
= strstr_m(result
, inserted
);
926 if (inserted_to
== NULL
) {
927 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
930 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
931 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
935 TALLOC_FREE(inserted
);
937 TALLOC_FREE(stripped
);
942 static char *have_snapdir(struct vfs_handle_struct
*handle
,
945 struct smb_filename smb_fname
;
948 ZERO_STRUCT(smb_fname
);
949 smb_fname
.base_name
= talloc_asprintf(
950 talloc_tos(), "%s/%s", path
,
951 lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir",
953 if (smb_fname
.base_name
== NULL
) {
957 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
958 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
959 return smb_fname
.base_name
;
961 TALLOC_FREE(smb_fname
.base_name
);
965 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
966 struct vfs_handle_struct
*handle
,
967 struct smb_filename
*smb_fname
)
972 path
= talloc_asprintf(mem_ctx
, "%s/%s",
973 handle
->conn
->connectpath
,
974 smb_fname
->base_name
);
979 snapdir
= have_snapdir(handle
, path
);
980 if (snapdir
!= NULL
) {
985 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
989 snapdir
= have_snapdir(handle
, path
);
990 if (snapdir
!= NULL
) {
999 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1001 char *gmt
, size_t gmt_len
)
1003 struct tm timestamp
;
1007 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1008 "format", GMT_FORMAT
);
1010 ZERO_STRUCT(timestamp
);
1011 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1012 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1017 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
1019 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1020 timestamp
.tm_isdst
= -1;
1021 timestamp_t
= mktime(×tamp
);
1022 gmtime_r(×tamp_t
, ×tamp
);
1024 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1028 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1030 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1033 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1035 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1039 sort the shadow copy data in ascending or descending order
1041 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1042 struct shadow_copy_data
*shadow_copy2_data
)
1044 int (*cmpfunc
)(const void *, const void *);
1047 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1053 if (strcmp(sort
, "asc") == 0) {
1054 cmpfunc
= shadow_copy2_label_cmp_asc
;
1055 } else if (strcmp(sort
, "desc") == 0) {
1056 cmpfunc
= shadow_copy2_label_cmp_desc
;
1061 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1062 shadow_copy2_data
->labels
)
1064 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1065 shadow_copy2_data
->num_volumes
,
1072 static int shadow_copy2_get_shadow_copy_data(
1073 vfs_handle_struct
*handle
, files_struct
*fsp
,
1074 struct shadow_copy_data
*shadow_copy2_data
,
1078 const char *snapdir
;
1080 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1082 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1083 if (snapdir
== NULL
) {
1084 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1085 handle
->conn
->connectpath
));
1087 talloc_free(tmp_ctx
);
1091 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1094 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1095 " - %s\n", snapdir
, strerror(errno
)));
1096 talloc_free(tmp_ctx
);
1101 shadow_copy2_data
->num_volumes
= 0;
1102 shadow_copy2_data
->labels
= NULL
;
1104 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1105 char snapshot
[GMT_NAME_LEN
+1];
1106 SHADOW_COPY_LABEL
*tlabels
;
1109 * ignore names not of the right form in the snapshot
1112 if (!shadow_copy2_snapshot_to_gmt(
1114 snapshot
, sizeof(snapshot
))) {
1116 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1117 "ignoring %s\n", d
->d_name
));
1120 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1121 d
->d_name
, snapshot
));
1124 /* the caller doesn't want the labels */
1125 shadow_copy2_data
->num_volumes
++;
1129 tlabels
= talloc_realloc(shadow_copy2_data
,
1130 shadow_copy2_data
->labels
,
1132 shadow_copy2_data
->num_volumes
+1);
1133 if (tlabels
== NULL
) {
1134 DEBUG(0,("shadow_copy2: out of memory\n"));
1135 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1136 talloc_free(tmp_ctx
);
1140 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1143 shadow_copy2_data
->num_volumes
++;
1144 shadow_copy2_data
->labels
= tlabels
;
1147 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1149 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1151 talloc_free(tmp_ctx
);
1155 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1156 struct files_struct
*fsp
,
1157 uint32 security_info
,
1158 struct security_descriptor
**ppdesc
)
1165 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1166 fsp
->fsp_name
->base_name
,
1167 ×tamp
, &stripped
)) {
1168 return map_nt_error_from_unix(errno
);
1170 if (timestamp
== 0) {
1171 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1174 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1175 TALLOC_FREE(stripped
);
1177 return map_nt_error_from_unix(errno
);
1179 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1184 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1186 uint32 security_info
,
1187 struct security_descriptor
**ppdesc
)
1194 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1195 ×tamp
, &stripped
)) {
1196 return map_nt_error_from_unix(errno
);
1198 if (timestamp
== 0) {
1199 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1202 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1203 TALLOC_FREE(stripped
);
1205 return map_nt_error_from_unix(errno
);
1207 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1212 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1213 const char *fname
, mode_t mode
)
1217 int ret
, saved_errno
;
1220 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1221 ×tamp
, &stripped
)) {
1224 if (timestamp
== 0) {
1225 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1227 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1228 TALLOC_FREE(stripped
);
1232 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1233 saved_errno
= errno
;
1235 errno
= saved_errno
;
1239 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1243 int ret
, saved_errno
;
1246 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1247 ×tamp
, &stripped
)) {
1250 if (timestamp
== 0) {
1251 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1253 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1254 TALLOC_FREE(stripped
);
1258 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1259 saved_errno
= errno
;
1261 errno
= saved_errno
;
1265 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1270 int ret
, saved_errno
;
1273 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1274 ×tamp
, &stripped
)) {
1277 if (timestamp
== 0) {
1278 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1280 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1281 TALLOC_FREE(stripped
);
1285 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1286 saved_errno
= errno
;
1288 errno
= saved_errno
;
1292 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1293 const char *fname
, const char *aname
,
1294 void *value
, size_t size
)
1302 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1303 ×tamp
, &stripped
)) {
1306 if (timestamp
== 0) {
1307 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1310 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1311 TALLOC_FREE(stripped
);
1315 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1316 saved_errno
= errno
;
1318 errno
= saved_errno
;
1322 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1324 char *list
, size_t size
)
1332 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1333 ×tamp
, &stripped
)) {
1336 if (timestamp
== 0) {
1337 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1339 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1340 TALLOC_FREE(stripped
);
1344 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1345 saved_errno
= errno
;
1347 errno
= saved_errno
;
1351 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1352 const char *fname
, const char *aname
)
1356 int ret
, saved_errno
;
1359 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1360 ×tamp
, &stripped
)) {
1363 if (timestamp
== 0) {
1364 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1366 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1367 TALLOC_FREE(stripped
);
1371 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1372 saved_errno
= errno
;
1374 errno
= saved_errno
;
1378 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1380 const char *aname
, const void *value
,
1381 size_t size
, int flags
)
1389 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1390 ×tamp
, &stripped
)) {
1393 if (timestamp
== 0) {
1394 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1397 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1398 TALLOC_FREE(stripped
);
1402 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1403 saved_errno
= errno
;
1405 errno
= saved_errno
;
1409 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1410 const char *fname
, mode_t mode
)
1418 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1419 ×tamp
, &stripped
)) {
1422 if (timestamp
== 0) {
1423 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1425 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1426 TALLOC_FREE(stripped
);
1430 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1431 saved_errno
= errno
;
1433 errno
= saved_errno
;
1437 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1440 TALLOC_CTX
*mem_ctx
,
1449 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1450 ×tamp
, &stripped
)) {
1453 if (timestamp
== 0) {
1454 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1455 mem_ctx
, found_name
);
1457 if (stripped
[0] == '\0') {
1458 *found_name
= talloc_strdup(mem_ctx
, name
);
1459 if (*found_name
== NULL
) {
1465 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1466 TALLOC_FREE(stripped
);
1470 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1471 mem_ctx
, found_name
);
1472 saved_errno
= errno
;
1474 errno
= saved_errno
;
1479 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1480 .opendir_fn
= shadow_copy2_opendir
,
1481 .rename_fn
= shadow_copy2_rename
,
1482 .link_fn
= shadow_copy2_link
,
1483 .symlink_fn
= shadow_copy2_symlink
,
1484 .stat_fn
= shadow_copy2_stat
,
1485 .lstat_fn
= shadow_copy2_lstat
,
1486 .fstat_fn
= shadow_copy2_fstat
,
1487 .open_fn
= shadow_copy2_open
,
1488 .unlink_fn
= shadow_copy2_unlink
,
1489 .chmod_fn
= shadow_copy2_chmod
,
1490 .chown_fn
= shadow_copy2_chown
,
1491 .chdir_fn
= shadow_copy2_chdir
,
1492 .ntimes_fn
= shadow_copy2_ntimes
,
1493 .readlink_fn
= shadow_copy2_readlink
,
1494 .mknod_fn
= shadow_copy2_mknod
,
1495 .realpath_fn
= shadow_copy2_realpath
,
1496 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1497 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1498 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1499 .mkdir_fn
= shadow_copy2_mkdir
,
1500 .rmdir_fn
= shadow_copy2_rmdir
,
1501 .getxattr_fn
= shadow_copy2_getxattr
,
1502 .listxattr_fn
= shadow_copy2_listxattr
,
1503 .removexattr_fn
= shadow_copy2_removexattr
,
1504 .setxattr_fn
= shadow_copy2_setxattr
,
1505 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1506 .chflags_fn
= shadow_copy2_chflags
,
1507 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1510 NTSTATUS
vfs_shadow_copy2_init(void);
1511 NTSTATUS
vfs_shadow_copy2_init(void)
1513 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1514 "shadow_copy2", &vfs_shadow_copy2_fns
);