2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 struct shadow_copy2_config
{
115 bool snapdirseverywhere
;
116 bool crossmountpoints
;
119 bool snapdir_absolute
;
122 char *rel_connectpath
; /* share root, relative to the basedir */
123 char *snapshot_basepath
; /* the absolute version of snapdir */
126 static bool shadow_copy2_find_slashes(TALLOC_CTX
*mem_ctx
, const char *str
,
128 unsigned *pnum_offsets
)
130 unsigned num_offsets
;
137 while ((p
= strchr(p
, '/')) != NULL
) {
142 offsets
= talloc_array(mem_ctx
, size_t, num_offsets
);
143 if (offsets
== NULL
) {
149 while ((p
= strchr(p
, '/')) != NULL
) {
150 offsets
[num_offsets
] = p
-str
;
156 *pnum_offsets
= num_offsets
;
161 * Given a timstamp, build the string to insert into a path
162 * as a path component for creating the local path to the
163 * snapshot at the given timestamp of the input path.
165 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
166 struct vfs_handle_struct
*handle
,
170 fstring snaptime_string
;
172 struct shadow_copy2_config
*config
;
174 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
177 if (config
->use_sscanf
) {
178 snaptime_len
= snprintf(snaptime_string
,
179 sizeof(snaptime_string
),
181 (unsigned long)snapshot
);
182 if (snaptime_len
<= 0) {
183 DEBUG(10, ("snprintf failed\n"));
187 if (config
->use_localtime
) {
188 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
189 DEBUG(10, ("gmtime_r failed\n"));
193 if (gmtime_r(&snapshot
, &snap_tm
) == 0) {
194 DEBUG(10, ("gmtime_r failed\n"));
198 snaptime_len
= strftime(snaptime_string
,
199 sizeof(snaptime_string
),
202 if (snaptime_len
== 0) {
203 DEBUG(10, ("strftime failed\n"));
207 return talloc_asprintf(mem_ctx
, "/%s/%s",
208 config
->snapdir
, snaptime_string
);
212 * Strip a snapshot component from an filename as
213 * handed in via the smb layer.
214 * Returns the parsed timestamp and the stripped filename.
216 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
217 struct vfs_handle_struct
*handle
,
227 size_t rest_len
, dst_len
;
228 struct shadow_copy2_config
*config
;
230 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
233 p
= strstr_m(name
, "@GMT-");
237 if ((p
> name
) && (p
[-1] != '/')) {
240 q
= strptime(p
, GMT_FORMAT
, &tm
);
245 timestamp
= timegm(&tm
);
246 if (timestamp
== (time_t)-1) {
249 if ((p
== name
) && (q
[0] == '\0')) {
250 if (pstripped
!= NULL
) {
251 stripped
= talloc_strdup(mem_ctx
, "");
252 if (stripped
== NULL
) {
255 *pstripped
= stripped
;
257 *ptimestamp
= timestamp
;
265 rest_len
= strlen(q
);
266 dst_len
= (p
-name
) + rest_len
;
268 if (config
->snapdirseverywhere
) {
271 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
273 if (insert
== NULL
) {
278 have_insert
= (strstr(name
, insert
+1) != NULL
);
285 if (pstripped
!= NULL
) {
286 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
287 if (stripped
== NULL
) {
292 memcpy(stripped
, name
, p
-name
);
295 memcpy(stripped
+ (p
-name
), q
, rest_len
);
297 stripped
[dst_len
] = '\0';
298 *pstripped
= stripped
;
300 *ptimestamp
= timestamp
;
307 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
308 vfs_handle_struct
*handle
)
310 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
315 if (stat(path
, &st
) != 0) {
322 while ((p
= strrchr(path
, '/')) && p
> path
) {
324 if (stat(path
, &st
) != 0) {
328 if (st
.st_dev
!= dev
) {
338 * Convert from a name as handed in via the SMB layer
339 * and a timestamp into the local path of the snapshot
340 * of the provided file at the provided time.
342 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
343 struct vfs_handle_struct
*handle
,
344 const char *name
, time_t timestamp
)
346 struct smb_filename converted_fname
;
348 size_t *slashes
= NULL
;
349 unsigned num_slashes
;
353 char *converted
= NULL
;
357 struct shadow_copy2_config
*config
;
359 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
362 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
368 pathlen
= talloc_get_size(path
)-1;
370 DEBUG(10, ("converting %s\n", path
));
372 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
373 &slashes
, &num_slashes
)) {
376 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
377 if (insert
== NULL
) {
380 insertlen
= talloc_get_size(insert
)-1;
381 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
382 if (converted
== NULL
) {
386 if (path
[pathlen
-1] != '/') {
388 * Append a fake slash to find the snapshot root
391 tmp
= talloc_realloc(talloc_tos(), slashes
,
392 size_t, num_slashes
+1);
397 slashes
[num_slashes
] = pathlen
;
403 if (!config
->crossmountpoints
) {
406 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
408 if (mount_point
== NULL
) {
411 min_offset
= strlen(mount_point
);
412 TALLOC_FREE(mount_point
);
415 memcpy(converted
, path
, pathlen
+1);
416 converted
[pathlen
+insertlen
] = '\0';
418 ZERO_STRUCT(converted_fname
);
419 converted_fname
.base_name
= converted
;
421 for (i
= num_slashes
-1; i
>=0; i
--) {
427 if (offset
< min_offset
) {
432 memcpy(converted
+offset
, insert
, insertlen
);
435 memcpy(converted
+offset
, path
+ slashes
[i
],
436 pathlen
- slashes
[i
]);
438 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
440 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
441 ret
, ret
== 0 ? "ok" : strerror(errno
)));
446 if (errno
== ENOTDIR
) {
448 * This is a valid condition: We appended the
449 * .snaphots/@GMT.. to a file name. Just try
450 * with the upper levels.
454 if (errno
!= ENOENT
) {
455 /* Other problem than "not found" */
464 DEBUG(10, ("Found %s\n", converted
));
472 TALLOC_FREE(converted
);
474 TALLOC_FREE(slashes
);
481 modify a sbuf return to ensure that inodes in the shadow directory
482 are different from those in the main directory
484 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
485 SMB_STRUCT_STAT
*sbuf
)
487 struct shadow_copy2_config
*config
;
489 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
492 if (config
->fixinodes
) {
493 /* some snapshot systems, like GPFS, return the name
494 device:inode for the snapshot files as the current
495 files. That breaks the 'restore' button in the shadow copy
496 GUI, as the client gets a sharing violation.
498 This is a crude way of allowing both files to be
499 open at once. It has a slight chance of inode
500 number collision, but I can't see a better approach
501 without significant VFS changes
505 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
509 sbuf
->st_ex_ino
^= shash
;
513 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
524 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
525 ×tamp
, &stripped
)) {
528 if (timestamp
== 0) {
529 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
531 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
532 TALLOC_FREE(stripped
);
536 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
543 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
544 const struct smb_filename
*smb_fname_src
,
545 const struct smb_filename
*smb_fname_dst
)
547 time_t timestamp_src
, timestamp_dst
;
549 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
550 smb_fname_src
->base_name
,
551 ×tamp_src
, NULL
)) {
554 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
555 smb_fname_dst
->base_name
,
556 ×tamp_dst
, NULL
)) {
559 if (timestamp_src
!= 0) {
563 if (timestamp_dst
!= 0) {
567 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
570 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
571 const char *oldname
, const char *newname
)
573 time_t timestamp_old
, timestamp_new
;
575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
576 ×tamp_old
, NULL
)) {
579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
580 ×tamp_new
, NULL
)) {
583 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
587 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
590 static int shadow_copy2_link(vfs_handle_struct
*handle
,
591 const char *oldname
, const char *newname
)
593 time_t timestamp_old
, timestamp_new
;
595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
596 ×tamp_old
, NULL
)) {
599 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
600 ×tamp_new
, NULL
)) {
603 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
607 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
610 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
611 struct smb_filename
*smb_fname
)
614 char *stripped
, *tmp
;
615 int ret
, saved_errno
;
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
618 smb_fname
->base_name
,
619 ×tamp
, &stripped
)) {
622 if (timestamp
== 0) {
623 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
626 tmp
= smb_fname
->base_name
;
627 smb_fname
->base_name
= shadow_copy2_convert(
628 talloc_tos(), handle
, stripped
, timestamp
);
629 TALLOC_FREE(stripped
);
631 if (smb_fname
->base_name
== NULL
) {
632 smb_fname
->base_name
= tmp
;
636 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
639 TALLOC_FREE(smb_fname
->base_name
);
640 smb_fname
->base_name
= tmp
;
643 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
649 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
650 struct smb_filename
*smb_fname
)
653 char *stripped
, *tmp
;
654 int ret
, saved_errno
;
656 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
657 smb_fname
->base_name
,
658 ×tamp
, &stripped
)) {
661 if (timestamp
== 0) {
662 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
665 tmp
= smb_fname
->base_name
;
666 smb_fname
->base_name
= shadow_copy2_convert(
667 talloc_tos(), handle
, stripped
, timestamp
);
668 TALLOC_FREE(stripped
);
670 if (smb_fname
->base_name
== NULL
) {
671 smb_fname
->base_name
= tmp
;
675 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
678 TALLOC_FREE(smb_fname
->base_name
);
679 smb_fname
->base_name
= tmp
;
682 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
688 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
689 SMB_STRUCT_STAT
*sbuf
)
694 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
698 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
699 fsp
->fsp_name
->base_name
,
703 if (timestamp
!= 0) {
704 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
709 static int shadow_copy2_open(vfs_handle_struct
*handle
,
710 struct smb_filename
*smb_fname
, files_struct
*fsp
,
711 int flags
, mode_t mode
)
714 char *stripped
, *tmp
;
715 int ret
, saved_errno
;
717 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
718 smb_fname
->base_name
,
719 ×tamp
, &stripped
)) {
722 if (timestamp
== 0) {
723 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
726 tmp
= smb_fname
->base_name
;
727 smb_fname
->base_name
= shadow_copy2_convert(
728 talloc_tos(), handle
, stripped
, timestamp
);
729 TALLOC_FREE(stripped
);
731 if (smb_fname
->base_name
== NULL
) {
732 smb_fname
->base_name
= tmp
;
736 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
739 TALLOC_FREE(smb_fname
->base_name
);
740 smb_fname
->base_name
= tmp
;
746 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
747 const struct smb_filename
*smb_fname
)
751 int ret
, saved_errno
;
752 struct smb_filename
*conv
;
754 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
755 smb_fname
->base_name
,
756 ×tamp
, &stripped
)) {
759 if (timestamp
== 0) {
760 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
762 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
767 conv
->base_name
= shadow_copy2_convert(
768 conv
, handle
, stripped
, timestamp
);
769 TALLOC_FREE(stripped
);
770 if (conv
->base_name
== NULL
) {
773 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
780 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
785 int ret
, saved_errno
;
788 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
789 ×tamp
, &stripped
)) {
792 if (timestamp
== 0) {
793 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
795 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
796 TALLOC_FREE(stripped
);
800 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
807 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
808 uid_t uid
, gid_t gid
)
812 int ret
, saved_errno
;
815 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
816 ×tamp
, &stripped
)) {
819 if (timestamp
== 0) {
820 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
822 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
823 TALLOC_FREE(stripped
);
827 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
834 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
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_CHDIR(handle
, fname
);
849 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
850 TALLOC_FREE(stripped
);
854 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
861 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
862 const struct smb_filename
*smb_fname
,
863 struct smb_file_time
*ft
)
867 int ret
, saved_errno
;
868 struct smb_filename
*conv
;
870 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
871 smb_fname
->base_name
,
872 ×tamp
, &stripped
)) {
875 if (timestamp
== 0) {
876 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
878 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
883 conv
->base_name
= shadow_copy2_convert(
884 conv
, handle
, stripped
, timestamp
);
885 TALLOC_FREE(stripped
);
886 if (conv
->base_name
== NULL
) {
889 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
896 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
897 const char *fname
, char *buf
, size_t bufsiz
)
901 int ret
, saved_errno
;
904 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
905 ×tamp
, &stripped
)) {
908 if (timestamp
== 0) {
909 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
911 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
912 TALLOC_FREE(stripped
);
916 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
923 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
924 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
928 int ret
, saved_errno
;
931 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
932 ×tamp
, &stripped
)) {
935 if (timestamp
== 0) {
936 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
938 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
939 TALLOC_FREE(stripped
);
943 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
950 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
954 char *stripped
= NULL
;
957 char *inserted
= NULL
;
958 char *inserted_to
, *inserted_end
;
961 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
962 ×tamp
, &stripped
)) {
965 if (timestamp
== 0) {
966 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
969 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
974 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
975 if (result
== NULL
) {
980 * Take away what we've inserted. This removes the @GMT-thingy
981 * completely, but will give a path under the share root.
983 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
984 if (inserted
== NULL
) {
987 inserted_to
= strstr_m(result
, inserted
);
988 if (inserted_to
== NULL
) {
989 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
992 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
993 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
997 TALLOC_FREE(inserted
);
999 TALLOC_FREE(stripped
);
1000 errno
= saved_errno
;
1005 * Check whether a given directory contains a
1006 * snapshot directory as direct subdirectory.
1007 * If yes, return the path of the snapshot-subdir,
1008 * otherwise return NULL.
1010 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1013 struct smb_filename smb_fname
;
1015 struct shadow_copy2_config
*config
;
1017 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1020 ZERO_STRUCT(smb_fname
);
1021 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1022 path
, config
->snapdir
);
1023 if (smb_fname
.base_name
== NULL
) {
1027 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1028 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1029 return smb_fname
.base_name
;
1031 TALLOC_FREE(smb_fname
.base_name
);
1036 * Find the snapshot directory (if any) for the given
1037 * filename (which is relative to the share).
1039 static char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1040 struct vfs_handle_struct
*handle
,
1041 struct smb_filename
*smb_fname
)
1045 struct shadow_copy2_config
*config
;
1047 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1050 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1051 handle
->conn
->connectpath
,
1052 smb_fname
->base_name
);
1057 snapdir
= have_snapdir(handle
, path
);
1058 if (snapdir
!= NULL
) {
1063 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1067 snapdir
= have_snapdir(handle
, path
);
1068 if (snapdir
!= NULL
) {
1077 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1079 char *gmt
, size_t gmt_len
)
1081 struct tm timestamp
;
1083 unsigned long int timestamp_long
;
1085 struct shadow_copy2_config
*config
;
1087 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1090 fmt
= config
->gmt_format
;
1092 ZERO_STRUCT(timestamp
);
1093 if (config
->use_sscanf
) {
1094 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1095 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1096 "no sscanf match %s: %s\n",
1100 timestamp_t
= timestamp_long
;
1101 gmtime_r(×tamp_t
, ×tamp
);
1103 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1104 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1105 "no match %s: %s\n",
1109 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1112 if (config
->use_localtime
) {
1113 timestamp
.tm_isdst
= -1;
1114 timestamp_t
= mktime(×tamp
);
1115 gmtime_r(×tamp_t
, ×tamp
);
1119 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1123 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1125 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1128 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1130 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1134 sort the shadow copy data in ascending or descending order
1136 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1137 struct shadow_copy_data
*shadow_copy2_data
)
1139 int (*cmpfunc
)(const void *, const void *);
1141 struct shadow_copy2_config
*config
;
1143 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1146 sort
= config
->sort_order
;
1151 if (strcmp(sort
, "asc") == 0) {
1152 cmpfunc
= shadow_copy2_label_cmp_asc
;
1153 } else if (strcmp(sort
, "desc") == 0) {
1154 cmpfunc
= shadow_copy2_label_cmp_desc
;
1159 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1160 shadow_copy2_data
->labels
)
1162 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1163 shadow_copy2_data
->num_volumes
,
1168 static int shadow_copy2_get_shadow_copy_data(
1169 vfs_handle_struct
*handle
, files_struct
*fsp
,
1170 struct shadow_copy_data
*shadow_copy2_data
,
1174 const char *snapdir
;
1176 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1178 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1179 if (snapdir
== NULL
) {
1180 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1181 handle
->conn
->connectpath
));
1183 talloc_free(tmp_ctx
);
1187 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1190 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1191 " - %s\n", snapdir
, strerror(errno
)));
1192 talloc_free(tmp_ctx
);
1197 shadow_copy2_data
->num_volumes
= 0;
1198 shadow_copy2_data
->labels
= NULL
;
1200 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1201 char snapshot
[GMT_NAME_LEN
+1];
1202 SHADOW_COPY_LABEL
*tlabels
;
1205 * ignore names not of the right form in the snapshot
1208 if (!shadow_copy2_snapshot_to_gmt(
1210 snapshot
, sizeof(snapshot
))) {
1212 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1213 "ignoring %s\n", d
->d_name
));
1216 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1217 d
->d_name
, snapshot
));
1220 /* the caller doesn't want the labels */
1221 shadow_copy2_data
->num_volumes
++;
1225 tlabels
= talloc_realloc(shadow_copy2_data
,
1226 shadow_copy2_data
->labels
,
1228 shadow_copy2_data
->num_volumes
+1);
1229 if (tlabels
== NULL
) {
1230 DEBUG(0,("shadow_copy2: out of memory\n"));
1231 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1232 talloc_free(tmp_ctx
);
1236 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1239 shadow_copy2_data
->num_volumes
++;
1240 shadow_copy2_data
->labels
= tlabels
;
1243 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1245 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1247 talloc_free(tmp_ctx
);
1251 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1252 struct files_struct
*fsp
,
1253 uint32 security_info
,
1254 TALLOC_CTX
*mem_ctx
,
1255 struct security_descriptor
**ppdesc
)
1262 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1263 fsp
->fsp_name
->base_name
,
1264 ×tamp
, &stripped
)) {
1265 return map_nt_error_from_unix(errno
);
1267 if (timestamp
== 0) {
1268 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1272 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1273 TALLOC_FREE(stripped
);
1275 return map_nt_error_from_unix(errno
);
1277 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1283 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1285 uint32 security_info
,
1286 TALLOC_CTX
*mem_ctx
,
1287 struct security_descriptor
**ppdesc
)
1294 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1295 ×tamp
, &stripped
)) {
1296 return map_nt_error_from_unix(errno
);
1298 if (timestamp
== 0) {
1299 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1302 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1303 TALLOC_FREE(stripped
);
1305 return map_nt_error_from_unix(errno
);
1307 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1313 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1314 const char *fname
, mode_t mode
)
1318 int ret
, saved_errno
;
1321 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1322 ×tamp
, &stripped
)) {
1325 if (timestamp
== 0) {
1326 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1328 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1329 TALLOC_FREE(stripped
);
1333 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1334 saved_errno
= errno
;
1336 errno
= saved_errno
;
1340 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1344 int ret
, saved_errno
;
1347 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1348 ×tamp
, &stripped
)) {
1351 if (timestamp
== 0) {
1352 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1354 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1355 TALLOC_FREE(stripped
);
1359 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1360 saved_errno
= errno
;
1362 errno
= saved_errno
;
1366 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1371 int ret
, saved_errno
;
1374 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1375 ×tamp
, &stripped
)) {
1378 if (timestamp
== 0) {
1379 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1381 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1382 TALLOC_FREE(stripped
);
1386 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1387 saved_errno
= errno
;
1389 errno
= saved_errno
;
1393 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1394 const char *fname
, const char *aname
,
1395 void *value
, size_t size
)
1403 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1404 ×tamp
, &stripped
)) {
1407 if (timestamp
== 0) {
1408 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1411 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1412 TALLOC_FREE(stripped
);
1416 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1417 saved_errno
= errno
;
1419 errno
= saved_errno
;
1423 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1425 char *list
, size_t size
)
1433 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1434 ×tamp
, &stripped
)) {
1437 if (timestamp
== 0) {
1438 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1440 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1441 TALLOC_FREE(stripped
);
1445 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1446 saved_errno
= errno
;
1448 errno
= saved_errno
;
1452 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1453 const char *fname
, const char *aname
)
1457 int ret
, saved_errno
;
1460 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1461 ×tamp
, &stripped
)) {
1464 if (timestamp
== 0) {
1465 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1467 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1468 TALLOC_FREE(stripped
);
1472 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1473 saved_errno
= errno
;
1475 errno
= saved_errno
;
1479 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1481 const char *aname
, const void *value
,
1482 size_t size
, int flags
)
1490 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1491 ×tamp
, &stripped
)) {
1494 if (timestamp
== 0) {
1495 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1498 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1499 TALLOC_FREE(stripped
);
1503 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1504 saved_errno
= errno
;
1506 errno
= saved_errno
;
1510 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1511 const char *fname
, mode_t mode
)
1519 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1520 ×tamp
, &stripped
)) {
1523 if (timestamp
== 0) {
1524 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1526 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1527 TALLOC_FREE(stripped
);
1531 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1532 saved_errno
= errno
;
1534 errno
= saved_errno
;
1538 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1541 TALLOC_CTX
*mem_ctx
,
1550 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1551 ×tamp
, &stripped
)) {
1554 if (timestamp
== 0) {
1555 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1556 mem_ctx
, found_name
);
1558 if (stripped
[0] == '\0') {
1559 *found_name
= talloc_strdup(mem_ctx
, name
);
1560 if (*found_name
== NULL
) {
1566 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1567 TALLOC_FREE(stripped
);
1571 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1572 mem_ctx
, found_name
);
1573 saved_errno
= errno
;
1575 errno
= saved_errno
;
1579 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1580 const char *service
, const char *user
)
1582 struct shadow_copy2_config
*config
;
1584 const char *snapdir
;
1585 const char *gmt_format
;
1586 const char *sort_order
;
1587 const char *basedir
;
1588 const char *mount_point
;
1590 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1591 (unsigned)handle
->conn
->cnum
,
1592 handle
->conn
->connectpath
));
1594 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1599 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1600 if (config
== NULL
) {
1601 DEBUG(0, ("talloc_zero() failed\n"));
1606 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1609 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1610 if (config
->gmt_format
== NULL
) {
1611 DEBUG(0, ("talloc_strdup() failed\n"));
1616 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1617 "shadow", "sscanf", false);
1619 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1620 "shadow", "localtime",
1623 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1624 "shadow", "snapdir",
1626 config
->snapdir
= talloc_strdup(config
, snapdir
);
1627 if (config
->snapdir
== NULL
) {
1628 DEBUG(0, ("talloc_strdup() failed\n"));
1633 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1635 "snapdirseverywhere",
1638 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1639 "shadow", "crossmountpoints",
1642 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1643 "shadow", "fixinodes",
1646 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1647 "shadow", "sort", "desc");
1648 config
->sort_order
= talloc_strdup(config
, sort_order
);
1649 if (config
->sort_order
== NULL
) {
1650 DEBUG(0, ("talloc_strdup() failed\n"));
1655 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1656 "shadow", "mountpoint", NULL
);
1657 if (mount_point
!= NULL
) {
1658 if (mount_point
[0] != '/') {
1659 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1660 "relative ('%s'), but it has to be an "
1661 "absolute path. Ignoring provided value.\n",
1666 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1667 if (p
!= handle
->conn
->connectpath
) {
1668 DEBUG(1, ("Warning: mount_point (%s) is not a "
1669 "subdirectory of the share root "
1670 "(%s). Ignoring provided value.\n",
1672 handle
->conn
->connectpath
));
1678 if (mount_point
!= NULL
) {
1679 config
->mount_point
= talloc_strdup(config
, mount_point
);
1680 if (config
->mount_point
== NULL
) {
1681 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1685 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1687 if (config
->mount_point
== NULL
) {
1688 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1689 " failed: %s\n", strerror(errno
)));
1694 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1695 "shadow", "basedir", NULL
);
1697 if (basedir
!= NULL
) {
1698 if (basedir
[0] != '/') {
1699 DEBUG(1, (__location__
" Warning: 'basedir' is "
1700 "relative ('%s'), but it has to be an "
1701 "absolute path. Disabling basedir.\n",
1705 p
= strstr(basedir
, config
->mount_point
);
1707 DEBUG(1, ("Warning: basedir (%s) is not a "
1708 "subdirectory of the share root's "
1709 "mount point (%s). "
1710 "Disabling basedir\n",
1711 basedir
, config
->mount_point
));
1713 config
->basedir
= talloc_strdup(config
,
1715 if (config
->basedir
== NULL
) {
1716 DEBUG(0, ("talloc_strdup() failed\n"));
1724 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1725 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1726 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1727 TALLOC_FREE(config
->basedir
);
1730 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1731 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1732 "with 'crossmountpoints'. Disabling basedir.\n"));
1733 TALLOC_FREE(config
->basedir
);
1736 if (config
->basedir
== NULL
) {
1737 config
->basedir
= config
->mount_point
;
1740 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1741 config
->rel_connectpath
= talloc_strdup(config
,
1742 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1743 if (config
->rel_connectpath
== NULL
) {
1744 DEBUG(0, ("talloc_strdup() failed\n"));
1750 if (config
->snapdir
[0] == '/') {
1751 config
->snapdir_absolute
= true;
1753 if (config
->snapdirseverywhere
== true) {
1754 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1755 "is incompatible with 'snapdirseverywhere', "
1756 "setting 'snapdirseverywhere' to false.\n"));
1757 config
->snapdirseverywhere
= false;
1760 if (config
->crossmountpoints
== true) {
1761 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1762 "is not supported with an absolute snapdir. "
1763 "Disabling it.\n"));
1764 config
->crossmountpoints
= false;
1767 config
->snapshot_basepath
= config
->snapdir
;
1769 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1770 config
->mount_point
, config
->snapdir
);
1771 if (config
->snapshot_basepath
== NULL
) {
1772 DEBUG(0, ("talloc_asprintf() failed\n"));
1778 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1779 NULL
, struct shadow_copy2_config
,
1785 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1786 .connect_fn
= shadow_copy2_connect
,
1787 .opendir_fn
= shadow_copy2_opendir
,
1788 .rename_fn
= shadow_copy2_rename
,
1789 .link_fn
= shadow_copy2_link
,
1790 .symlink_fn
= shadow_copy2_symlink
,
1791 .stat_fn
= shadow_copy2_stat
,
1792 .lstat_fn
= shadow_copy2_lstat
,
1793 .fstat_fn
= shadow_copy2_fstat
,
1794 .open_fn
= shadow_copy2_open
,
1795 .unlink_fn
= shadow_copy2_unlink
,
1796 .chmod_fn
= shadow_copy2_chmod
,
1797 .chown_fn
= shadow_copy2_chown
,
1798 .chdir_fn
= shadow_copy2_chdir
,
1799 .ntimes_fn
= shadow_copy2_ntimes
,
1800 .readlink_fn
= shadow_copy2_readlink
,
1801 .mknod_fn
= shadow_copy2_mknod
,
1802 .realpath_fn
= shadow_copy2_realpath
,
1803 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1804 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1805 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1806 .mkdir_fn
= shadow_copy2_mkdir
,
1807 .rmdir_fn
= shadow_copy2_rmdir
,
1808 .getxattr_fn
= shadow_copy2_getxattr
,
1809 .listxattr_fn
= shadow_copy2_listxattr
,
1810 .removexattr_fn
= shadow_copy2_removexattr
,
1811 .setxattr_fn
= shadow_copy2_setxattr
,
1812 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1813 .chflags_fn
= shadow_copy2_chflags
,
1814 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1817 NTSTATUS
vfs_shadow_copy2_init(void);
1818 NTSTATUS
vfs_shadow_copy2_init(void)
1820 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1821 "shadow_copy2", &vfs_shadow_copy2_fns
);