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"
102 #include <ccan/hash/hash.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
442 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
446 sbuf
->st_ex_ino
^= shash
;
450 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
461 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
462 ×tamp
, &stripped
)) {
465 if (timestamp
== 0) {
466 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
468 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
469 TALLOC_FREE(stripped
);
473 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
480 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
481 const struct smb_filename
*smb_fname_src
,
482 const struct smb_filename
*smb_fname_dst
)
484 time_t timestamp_src
, timestamp_dst
;
486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
487 smb_fname_src
->base_name
,
488 ×tamp_src
, NULL
)) {
491 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
492 smb_fname_dst
->base_name
,
493 ×tamp_dst
, NULL
)) {
496 if (timestamp_src
!= 0) {
500 if (timestamp_dst
!= 0) {
504 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
507 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
508 const char *oldname
, const char *newname
)
510 time_t timestamp_old
, timestamp_new
;
512 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
513 ×tamp_old
, NULL
)) {
516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
517 ×tamp_new
, NULL
)) {
520 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
524 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
527 static int shadow_copy2_link(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_LINK(handle
, oldname
, newname
);
547 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
548 struct smb_filename
*smb_fname
)
551 char *stripped
, *tmp
;
552 int ret
, saved_errno
;
554 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
555 smb_fname
->base_name
,
556 ×tamp
, &stripped
)) {
559 if (timestamp
== 0) {
560 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
563 tmp
= smb_fname
->base_name
;
564 smb_fname
->base_name
= shadow_copy2_convert(
565 talloc_tos(), handle
, stripped
, timestamp
);
566 TALLOC_FREE(stripped
);
568 if (smb_fname
->base_name
== NULL
) {
569 smb_fname
->base_name
= tmp
;
573 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
576 TALLOC_FREE(smb_fname
->base_name
);
577 smb_fname
->base_name
= tmp
;
580 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
586 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
587 struct smb_filename
*smb_fname
)
590 char *stripped
, *tmp
;
591 int ret
, saved_errno
;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
594 smb_fname
->base_name
,
595 ×tamp
, &stripped
)) {
598 if (timestamp
== 0) {
599 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
602 tmp
= smb_fname
->base_name
;
603 smb_fname
->base_name
= shadow_copy2_convert(
604 talloc_tos(), handle
, stripped
, timestamp
);
605 TALLOC_FREE(stripped
);
607 if (smb_fname
->base_name
== NULL
) {
608 smb_fname
->base_name
= tmp
;
612 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
615 TALLOC_FREE(smb_fname
->base_name
);
616 smb_fname
->base_name
= tmp
;
619 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
625 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
626 SMB_STRUCT_STAT
*sbuf
)
631 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
635 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
636 fsp
->fsp_name
->base_name
,
640 if (timestamp
!= 0) {
641 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
646 static int shadow_copy2_open(vfs_handle_struct
*handle
,
647 struct smb_filename
*smb_fname
, files_struct
*fsp
,
648 int flags
, mode_t mode
)
651 char *stripped
, *tmp
;
652 int ret
, saved_errno
;
654 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
655 smb_fname
->base_name
,
656 ×tamp
, &stripped
)) {
659 if (timestamp
== 0) {
660 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
663 tmp
= smb_fname
->base_name
;
664 smb_fname
->base_name
= shadow_copy2_convert(
665 talloc_tos(), handle
, stripped
, timestamp
);
666 TALLOC_FREE(stripped
);
668 if (smb_fname
->base_name
== NULL
) {
669 smb_fname
->base_name
= tmp
;
673 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
676 TALLOC_FREE(smb_fname
->base_name
);
677 smb_fname
->base_name
= tmp
;
683 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
684 const struct smb_filename
*smb_fname
)
688 int ret
, saved_errno
;
689 struct smb_filename
*conv
;
692 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
693 smb_fname
->base_name
,
694 ×tamp
, &stripped
)) {
697 if (timestamp
== 0) {
698 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
700 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
701 if (!NT_STATUS_IS_OK(status
)) {
705 conv
->base_name
= shadow_copy2_convert(
706 conv
, handle
, stripped
, timestamp
);
707 TALLOC_FREE(stripped
);
708 if (conv
->base_name
== NULL
) {
711 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
718 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
723 int ret
, saved_errno
;
726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
727 ×tamp
, &stripped
)) {
730 if (timestamp
== 0) {
731 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
733 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
734 TALLOC_FREE(stripped
);
738 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
745 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
746 uid_t uid
, gid_t gid
)
750 int ret
, saved_errno
;
753 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
754 ×tamp
, &stripped
)) {
757 if (timestamp
== 0) {
758 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
760 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
761 TALLOC_FREE(stripped
);
765 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
772 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
777 int ret
, saved_errno
;
780 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
781 ×tamp
, &stripped
)) {
784 if (timestamp
== 0) {
785 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
787 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
788 TALLOC_FREE(stripped
);
792 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
799 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
800 const struct smb_filename
*smb_fname
,
801 struct smb_file_time
*ft
)
805 int ret
, saved_errno
;
806 struct smb_filename
*conv
;
809 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
810 smb_fname
->base_name
,
811 ×tamp
, &stripped
)) {
814 if (timestamp
== 0) {
815 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
817 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
818 if (!NT_STATUS_IS_OK(status
)) {
822 conv
->base_name
= shadow_copy2_convert(
823 conv
, handle
, stripped
, timestamp
);
824 TALLOC_FREE(stripped
);
825 if (conv
->base_name
== NULL
) {
828 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
835 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
836 const char *fname
, char *buf
, size_t bufsiz
)
840 int ret
, saved_errno
;
843 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
844 ×tamp
, &stripped
)) {
847 if (timestamp
== 0) {
848 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
850 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
851 TALLOC_FREE(stripped
);
855 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
862 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
863 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
867 int ret
, saved_errno
;
870 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
871 ×tamp
, &stripped
)) {
874 if (timestamp
== 0) {
875 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
877 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
878 TALLOC_FREE(stripped
);
882 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
889 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
893 char *stripped
= NULL
;
896 char *inserted
= NULL
;
897 char *inserted_to
, *inserted_end
;
900 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
901 ×tamp
, &stripped
)) {
904 if (timestamp
== 0) {
905 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
908 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
913 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
914 if (result
== NULL
) {
919 * Take away what we've inserted. This removes the @GMT-thingy
920 * completely, but will give a path under the share root.
922 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
923 if (inserted
== NULL
) {
926 inserted_to
= strstr_m(result
, inserted
);
927 if (inserted_to
== NULL
) {
928 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
931 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
932 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
936 TALLOC_FREE(inserted
);
938 TALLOC_FREE(stripped
);
943 static char *have_snapdir(struct vfs_handle_struct
*handle
,
946 struct smb_filename smb_fname
;
949 ZERO_STRUCT(smb_fname
);
950 smb_fname
.base_name
= talloc_asprintf(
951 talloc_tos(), "%s/%s", path
,
952 lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir",
954 if (smb_fname
.base_name
== NULL
) {
958 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
959 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
960 return smb_fname
.base_name
;
962 TALLOC_FREE(smb_fname
.base_name
);
966 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
967 struct vfs_handle_struct
*handle
,
968 struct smb_filename
*smb_fname
)
973 path
= talloc_asprintf(mem_ctx
, "%s/%s",
974 handle
->conn
->connectpath
,
975 smb_fname
->base_name
);
980 snapdir
= have_snapdir(handle
, path
);
981 if (snapdir
!= NULL
) {
986 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
990 snapdir
= have_snapdir(handle
, path
);
991 if (snapdir
!= NULL
) {
1000 static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX
*mem_ctx
,
1001 vfs_handle_struct
*handle
,
1003 char *gmt
, size_t gmt_len
)
1005 struct tm timestamp
;
1009 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1010 "format", GMT_FORMAT
);
1012 ZERO_STRUCT(timestamp
);
1013 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1014 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1019 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
1021 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1022 timestamp
.tm_isdst
= -1;
1023 timestamp_t
= mktime(×tamp
);
1024 gmtime_r(×tamp_t
, ×tamp
);
1026 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1030 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1032 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1035 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1037 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1041 sort the shadow copy data in ascending or descending order
1043 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1044 struct shadow_copy_data
*shadow_copy2_data
)
1046 int (*cmpfunc
)(const void *, const void *);
1049 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1055 if (strcmp(sort
, "asc") == 0) {
1056 cmpfunc
= shadow_copy2_label_cmp_asc
;
1057 } else if (strcmp(sort
, "desc") == 0) {
1058 cmpfunc
= shadow_copy2_label_cmp_desc
;
1063 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1064 shadow_copy2_data
->labels
)
1066 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1067 shadow_copy2_data
->num_volumes
,
1074 static int shadow_copy2_get_shadow_copy_data(
1075 vfs_handle_struct
*handle
, files_struct
*fsp
,
1076 struct shadow_copy_data
*shadow_copy2_data
,
1080 const char *snapdir
;
1082 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1084 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1085 if (snapdir
== NULL
) {
1086 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1087 handle
->conn
->connectpath
));
1089 talloc_free(tmp_ctx
);
1093 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1096 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1097 " - %s\n", snapdir
, strerror(errno
)));
1098 talloc_free(tmp_ctx
);
1103 shadow_copy2_data
->num_volumes
= 0;
1104 shadow_copy2_data
->labels
= NULL
;
1106 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1107 char snapshot
[GMT_NAME_LEN
+1];
1108 SHADOW_COPY_LABEL
*tlabels
;
1111 * ignore names not of the right form in the snapshot
1114 if (!shadow_copy2_snapshot_to_gmt(
1115 tmp_ctx
, handle
, d
->d_name
,
1116 snapshot
, sizeof(snapshot
))) {
1118 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1119 "ignoring %s\n", d
->d_name
));
1122 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1123 d
->d_name
, snapshot
));
1126 /* the caller doesn't want the labels */
1127 shadow_copy2_data
->num_volumes
++;
1131 tlabels
= talloc_realloc(shadow_copy2_data
,
1132 shadow_copy2_data
->labels
,
1134 shadow_copy2_data
->num_volumes
+1);
1135 if (tlabels
== NULL
) {
1136 DEBUG(0,("shadow_copy2: out of memory\n"));
1137 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1138 talloc_free(tmp_ctx
);
1142 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1145 shadow_copy2_data
->num_volumes
++;
1146 shadow_copy2_data
->labels
= tlabels
;
1149 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1151 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1153 talloc_free(tmp_ctx
);
1157 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1158 struct files_struct
*fsp
,
1159 uint32 security_info
,
1160 struct security_descriptor
**ppdesc
)
1167 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1168 fsp
->fsp_name
->base_name
,
1169 ×tamp
, &stripped
)) {
1170 return map_nt_error_from_unix(errno
);
1172 if (timestamp
== 0) {
1173 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1176 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1177 TALLOC_FREE(stripped
);
1179 return map_nt_error_from_unix(errno
);
1181 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1186 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1188 uint32 security_info
,
1189 struct security_descriptor
**ppdesc
)
1196 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1197 ×tamp
, &stripped
)) {
1198 return map_nt_error_from_unix(errno
);
1200 if (timestamp
== 0) {
1201 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1204 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1205 TALLOC_FREE(stripped
);
1207 return map_nt_error_from_unix(errno
);
1209 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1214 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1215 const char *fname
, mode_t mode
)
1219 int ret
, saved_errno
;
1222 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1223 ×tamp
, &stripped
)) {
1226 if (timestamp
== 0) {
1227 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1229 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1230 TALLOC_FREE(stripped
);
1234 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1235 saved_errno
= errno
;
1237 errno
= saved_errno
;
1241 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1245 int ret
, saved_errno
;
1248 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1249 ×tamp
, &stripped
)) {
1252 if (timestamp
== 0) {
1253 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1255 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1256 TALLOC_FREE(stripped
);
1260 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1261 saved_errno
= errno
;
1263 errno
= saved_errno
;
1267 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1272 int ret
, saved_errno
;
1275 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1276 ×tamp
, &stripped
)) {
1279 if (timestamp
== 0) {
1280 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1282 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1283 TALLOC_FREE(stripped
);
1287 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1288 saved_errno
= errno
;
1290 errno
= saved_errno
;
1294 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1295 const char *fname
, const char *aname
,
1296 void *value
, size_t size
)
1304 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1305 ×tamp
, &stripped
)) {
1308 if (timestamp
== 0) {
1309 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1312 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1313 TALLOC_FREE(stripped
);
1317 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1318 saved_errno
= errno
;
1320 errno
= saved_errno
;
1324 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1326 char *list
, size_t size
)
1334 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1335 ×tamp
, &stripped
)) {
1338 if (timestamp
== 0) {
1339 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1341 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1342 TALLOC_FREE(stripped
);
1346 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1347 saved_errno
= errno
;
1349 errno
= saved_errno
;
1353 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1354 const char *fname
, const char *aname
)
1358 int ret
, saved_errno
;
1361 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1362 ×tamp
, &stripped
)) {
1365 if (timestamp
== 0) {
1366 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1368 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1369 TALLOC_FREE(stripped
);
1373 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1374 saved_errno
= errno
;
1376 errno
= saved_errno
;
1380 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1382 const char *aname
, const void *value
,
1383 size_t size
, int flags
)
1391 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1392 ×tamp
, &stripped
)) {
1395 if (timestamp
== 0) {
1396 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1399 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1400 TALLOC_FREE(stripped
);
1404 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1405 saved_errno
= errno
;
1407 errno
= saved_errno
;
1411 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1412 const char *fname
, mode_t mode
)
1420 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1421 ×tamp
, &stripped
)) {
1424 if (timestamp
== 0) {
1425 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1427 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1428 TALLOC_FREE(stripped
);
1432 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1433 saved_errno
= errno
;
1435 errno
= saved_errno
;
1439 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1442 TALLOC_CTX
*mem_ctx
,
1451 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1452 ×tamp
, &stripped
)) {
1455 if (timestamp
== 0) {
1456 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1457 mem_ctx
, found_name
);
1459 if (stripped
[0] == '\0') {
1460 *found_name
= talloc_strdup(mem_ctx
, name
);
1461 if (*found_name
== NULL
) {
1467 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1468 TALLOC_FREE(stripped
);
1472 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1473 mem_ctx
, found_name
);
1474 saved_errno
= errno
;
1476 errno
= saved_errno
;
1481 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1482 .opendir_fn
= shadow_copy2_opendir
,
1483 .rename_fn
= shadow_copy2_rename
,
1484 .link_fn
= shadow_copy2_link
,
1485 .symlink_fn
= shadow_copy2_symlink
,
1486 .stat_fn
= shadow_copy2_stat
,
1487 .lstat_fn
= shadow_copy2_lstat
,
1488 .fstat_fn
= shadow_copy2_fstat
,
1489 .open_fn
= shadow_copy2_open
,
1490 .unlink_fn
= shadow_copy2_unlink
,
1491 .chmod_fn
= shadow_copy2_chmod
,
1492 .chown_fn
= shadow_copy2_chown
,
1493 .chdir_fn
= shadow_copy2_chdir
,
1494 .ntimes_fn
= shadow_copy2_ntimes
,
1495 .readlink_fn
= shadow_copy2_readlink
,
1496 .mknod_fn
= shadow_copy2_mknod
,
1497 .realpath_fn
= shadow_copy2_realpath
,
1498 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1499 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1500 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1501 .mkdir_fn
= shadow_copy2_mkdir
,
1502 .rmdir_fn
= shadow_copy2_rmdir
,
1503 .getxattr_fn
= shadow_copy2_getxattr
,
1504 .listxattr_fn
= shadow_copy2_listxattr
,
1505 .removexattr_fn
= shadow_copy2_removexattr
,
1506 .setxattr_fn
= shadow_copy2_setxattr
,
1507 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1508 .chflags_fn
= shadow_copy2_chflags
,
1509 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1512 NTSTATUS
vfs_shadow_copy2_init(void);
1513 NTSTATUS
vfs_shadow_copy2_init(void)
1515 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1516 "shadow_copy2", &vfs_shadow_copy2_fns
);