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(mem_ctx
, "/%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(vfs_handle_struct
*handle
,
1002 char *gmt
, size_t gmt_len
)
1004 struct tm timestamp
;
1008 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1009 "format", GMT_FORMAT
);
1011 ZERO_STRUCT(timestamp
);
1012 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1013 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1018 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
1020 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime", false)) {
1021 timestamp
.tm_isdst
= -1;
1022 timestamp_t
= mktime(×tamp
);
1023 gmtime_r(×tamp_t
, ×tamp
);
1025 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1029 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1031 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1034 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1036 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1040 sort the shadow copy data in ascending or descending order
1042 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1043 struct shadow_copy_data
*shadow_copy2_data
)
1045 int (*cmpfunc
)(const void *, const void *);
1048 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
1054 if (strcmp(sort
, "asc") == 0) {
1055 cmpfunc
= shadow_copy2_label_cmp_asc
;
1056 } else if (strcmp(sort
, "desc") == 0) {
1057 cmpfunc
= shadow_copy2_label_cmp_desc
;
1062 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1063 shadow_copy2_data
->labels
)
1065 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1066 shadow_copy2_data
->num_volumes
,
1073 static int shadow_copy2_get_shadow_copy_data(
1074 vfs_handle_struct
*handle
, files_struct
*fsp
,
1075 struct shadow_copy_data
*shadow_copy2_data
,
1079 const char *snapdir
;
1081 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1083 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1084 if (snapdir
== NULL
) {
1085 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1086 handle
->conn
->connectpath
));
1088 talloc_free(tmp_ctx
);
1092 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1095 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1096 " - %s\n", snapdir
, strerror(errno
)));
1097 talloc_free(tmp_ctx
);
1102 shadow_copy2_data
->num_volumes
= 0;
1103 shadow_copy2_data
->labels
= NULL
;
1105 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1106 char snapshot
[GMT_NAME_LEN
+1];
1107 SHADOW_COPY_LABEL
*tlabels
;
1110 * ignore names not of the right form in the snapshot
1113 if (!shadow_copy2_snapshot_to_gmt(
1115 snapshot
, sizeof(snapshot
))) {
1117 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1118 "ignoring %s\n", d
->d_name
));
1121 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1122 d
->d_name
, snapshot
));
1125 /* the caller doesn't want the labels */
1126 shadow_copy2_data
->num_volumes
++;
1130 tlabels
= talloc_realloc(shadow_copy2_data
,
1131 shadow_copy2_data
->labels
,
1133 shadow_copy2_data
->num_volumes
+1);
1134 if (tlabels
== NULL
) {
1135 DEBUG(0,("shadow_copy2: out of memory\n"));
1136 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1137 talloc_free(tmp_ctx
);
1141 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1144 shadow_copy2_data
->num_volumes
++;
1145 shadow_copy2_data
->labels
= tlabels
;
1148 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1150 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1152 talloc_free(tmp_ctx
);
1156 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1157 struct files_struct
*fsp
,
1158 uint32 security_info
,
1159 struct security_descriptor
**ppdesc
)
1166 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1167 fsp
->fsp_name
->base_name
,
1168 ×tamp
, &stripped
)) {
1169 return map_nt_error_from_unix(errno
);
1171 if (timestamp
== 0) {
1172 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1175 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1176 TALLOC_FREE(stripped
);
1178 return map_nt_error_from_unix(errno
);
1180 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1185 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1187 uint32 security_info
,
1188 struct security_descriptor
**ppdesc
)
1195 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1196 ×tamp
, &stripped
)) {
1197 return map_nt_error_from_unix(errno
);
1199 if (timestamp
== 0) {
1200 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1203 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1204 TALLOC_FREE(stripped
);
1206 return map_nt_error_from_unix(errno
);
1208 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
, ppdesc
);
1213 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1214 const char *fname
, mode_t mode
)
1218 int ret
, saved_errno
;
1221 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1222 ×tamp
, &stripped
)) {
1225 if (timestamp
== 0) {
1226 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1228 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1229 TALLOC_FREE(stripped
);
1233 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1234 saved_errno
= errno
;
1236 errno
= saved_errno
;
1240 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1244 int ret
, saved_errno
;
1247 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1248 ×tamp
, &stripped
)) {
1251 if (timestamp
== 0) {
1252 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1254 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1255 TALLOC_FREE(stripped
);
1259 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1260 saved_errno
= errno
;
1262 errno
= saved_errno
;
1266 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1271 int ret
, saved_errno
;
1274 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1275 ×tamp
, &stripped
)) {
1278 if (timestamp
== 0) {
1279 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1281 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1282 TALLOC_FREE(stripped
);
1286 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1287 saved_errno
= errno
;
1289 errno
= saved_errno
;
1293 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1294 const char *fname
, const char *aname
,
1295 void *value
, size_t size
)
1303 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1304 ×tamp
, &stripped
)) {
1307 if (timestamp
== 0) {
1308 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1311 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1312 TALLOC_FREE(stripped
);
1316 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1317 saved_errno
= errno
;
1319 errno
= saved_errno
;
1323 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1325 char *list
, size_t size
)
1333 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1334 ×tamp
, &stripped
)) {
1337 if (timestamp
== 0) {
1338 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1340 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1341 TALLOC_FREE(stripped
);
1345 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1346 saved_errno
= errno
;
1348 errno
= saved_errno
;
1352 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1353 const char *fname
, const char *aname
)
1357 int ret
, saved_errno
;
1360 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1361 ×tamp
, &stripped
)) {
1364 if (timestamp
== 0) {
1365 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1367 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1368 TALLOC_FREE(stripped
);
1372 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1373 saved_errno
= errno
;
1375 errno
= saved_errno
;
1379 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1381 const char *aname
, const void *value
,
1382 size_t size
, int flags
)
1390 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1391 ×tamp
, &stripped
)) {
1394 if (timestamp
== 0) {
1395 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1398 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1399 TALLOC_FREE(stripped
);
1403 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1404 saved_errno
= errno
;
1406 errno
= saved_errno
;
1410 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1411 const char *fname
, mode_t mode
)
1419 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1420 ×tamp
, &stripped
)) {
1423 if (timestamp
== 0) {
1424 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1426 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1427 TALLOC_FREE(stripped
);
1431 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1432 saved_errno
= errno
;
1434 errno
= saved_errno
;
1438 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1441 TALLOC_CTX
*mem_ctx
,
1450 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1451 ×tamp
, &stripped
)) {
1454 if (timestamp
== 0) {
1455 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1456 mem_ctx
, found_name
);
1458 if (stripped
[0] == '\0') {
1459 *found_name
= talloc_strdup(mem_ctx
, name
);
1460 if (*found_name
== NULL
) {
1466 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1467 TALLOC_FREE(stripped
);
1471 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1472 mem_ctx
, found_name
);
1473 saved_errno
= errno
;
1475 errno
= saved_errno
;
1480 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1481 .opendir_fn
= shadow_copy2_opendir
,
1482 .rename_fn
= shadow_copy2_rename
,
1483 .link_fn
= shadow_copy2_link
,
1484 .symlink_fn
= shadow_copy2_symlink
,
1485 .stat_fn
= shadow_copy2_stat
,
1486 .lstat_fn
= shadow_copy2_lstat
,
1487 .fstat_fn
= shadow_copy2_fstat
,
1488 .open_fn
= shadow_copy2_open
,
1489 .unlink_fn
= shadow_copy2_unlink
,
1490 .chmod_fn
= shadow_copy2_chmod
,
1491 .chown_fn
= shadow_copy2_chown
,
1492 .chdir_fn
= shadow_copy2_chdir
,
1493 .ntimes_fn
= shadow_copy2_ntimes
,
1494 .readlink_fn
= shadow_copy2_readlink
,
1495 .mknod_fn
= shadow_copy2_mknod
,
1496 .realpath_fn
= shadow_copy2_realpath
,
1497 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1498 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1499 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1500 .mkdir_fn
= shadow_copy2_mkdir
,
1501 .rmdir_fn
= shadow_copy2_rmdir
,
1502 .getxattr_fn
= shadow_copy2_getxattr
,
1503 .listxattr_fn
= shadow_copy2_listxattr
,
1504 .removexattr_fn
= shadow_copy2_removexattr
,
1505 .setxattr_fn
= shadow_copy2_setxattr
,
1506 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1507 .chflags_fn
= shadow_copy2_chflags
,
1508 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1511 NTSTATUS
vfs_shadow_copy2_init(void);
1512 NTSTATUS
vfs_shadow_copy2_init(void)
1514 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1515 "shadow_copy2", &vfs_shadow_copy2_fns
);