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 * In the case of a parallel snapdir (specified with an
166 * absolute path), this is the inital portion of the
167 * local path of any snapshot file. The complete path is
168 * obtained by appending the portion of the file's path
169 * below the share root's mountpoint.
171 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
172 struct vfs_handle_struct
*handle
,
176 fstring snaptime_string
;
178 struct shadow_copy2_config
*config
;
181 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
184 if (config
->use_sscanf
) {
185 snaptime_len
= snprintf(snaptime_string
,
186 sizeof(snaptime_string
),
188 (unsigned long)snapshot
);
189 if (snaptime_len
<= 0) {
190 DEBUG(10, ("snprintf failed\n"));
194 if (config
->use_localtime
) {
195 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
196 DEBUG(10, ("gmtime_r failed\n"));
200 if (gmtime_r(&snapshot
, &snap_tm
) == 0) {
201 DEBUG(10, ("gmtime_r failed\n"));
205 snaptime_len
= strftime(snaptime_string
,
206 sizeof(snaptime_string
),
209 if (snaptime_len
== 0) {
210 DEBUG(10, ("strftime failed\n"));
215 if (config
->snapdir_absolute
) {
216 result
= talloc_asprintf(mem_ctx
, "%s/%s",
217 config
->snapdir
, snaptime_string
);
219 result
= talloc_asprintf(mem_ctx
, "/%s/%s",
220 config
->snapdir
, snaptime_string
);
222 if (result
== NULL
) {
223 DEBUG(1, (__location__
" talloc_asprintf failed\n"));
230 * Strip a snapshot component from an filename as
231 * handed in via the smb layer.
232 * Returns the parsed timestamp and the stripped filename.
234 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
235 struct vfs_handle_struct
*handle
,
245 size_t rest_len
, dst_len
;
246 struct shadow_copy2_config
*config
;
248 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
251 p
= strstr_m(name
, "@GMT-");
255 if ((p
> name
) && (p
[-1] != '/')) {
258 q
= strptime(p
, GMT_FORMAT
, &tm
);
263 timestamp
= timegm(&tm
);
264 if (timestamp
== (time_t)-1) {
267 if ((p
== name
) && (q
[0] == '\0')) {
268 if (pstripped
!= NULL
) {
269 stripped
= talloc_strdup(mem_ctx
, "");
270 if (stripped
== NULL
) {
273 *pstripped
= stripped
;
275 *ptimestamp
= timestamp
;
283 rest_len
= strlen(q
);
284 dst_len
= (p
-name
) + rest_len
;
286 if (config
->snapdirseverywhere
) {
289 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
291 if (insert
== NULL
) {
296 have_insert
= (strstr(name
, insert
+1) != NULL
);
303 if (pstripped
!= NULL
) {
304 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
305 if (stripped
== NULL
) {
310 memcpy(stripped
, name
, p
-name
);
313 memcpy(stripped
+ (p
-name
), q
, rest_len
);
315 stripped
[dst_len
] = '\0';
316 *pstripped
= stripped
;
318 *ptimestamp
= timestamp
;
325 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
326 vfs_handle_struct
*handle
)
328 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
333 if (stat(path
, &st
) != 0) {
340 while ((p
= strrchr(path
, '/')) && p
> path
) {
342 if (stat(path
, &st
) != 0) {
346 if (st
.st_dev
!= dev
) {
356 * Convert from a name as handed in via the SMB layer
357 * and a timestamp into the local path of the snapshot
358 * of the provided file at the provided time.
360 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
361 struct vfs_handle_struct
*handle
,
362 const char *name
, time_t timestamp
)
364 struct smb_filename converted_fname
;
366 size_t *slashes
= NULL
;
367 unsigned num_slashes
;
371 char *converted
= NULL
;
375 struct shadow_copy2_config
*config
;
377 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
380 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
386 pathlen
= talloc_get_size(path
)-1;
388 DEBUG(10, ("converting %s\n", path
));
390 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
391 &slashes
, &num_slashes
)) {
394 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
395 if (insert
== NULL
) {
398 insertlen
= talloc_get_size(insert
)-1;
399 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
400 if (converted
== NULL
) {
404 if (path
[pathlen
-1] != '/') {
406 * Append a fake slash to find the snapshot root
409 tmp
= talloc_realloc(talloc_tos(), slashes
,
410 size_t, num_slashes
+1);
415 slashes
[num_slashes
] = pathlen
;
421 if (!config
->crossmountpoints
) {
424 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
426 if (mount_point
== NULL
) {
429 min_offset
= strlen(mount_point
);
430 TALLOC_FREE(mount_point
);
433 memcpy(converted
, path
, pathlen
+1);
434 converted
[pathlen
+insertlen
] = '\0';
436 ZERO_STRUCT(converted_fname
);
437 converted_fname
.base_name
= converted
;
439 for (i
= num_slashes
-1; i
>=0; i
--) {
445 if (offset
< min_offset
) {
450 memcpy(converted
+offset
, insert
, insertlen
);
453 memcpy(converted
+offset
, path
+ slashes
[i
],
454 pathlen
- slashes
[i
]);
456 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
458 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
459 ret
, ret
== 0 ? "ok" : strerror(errno
)));
464 if (errno
== ENOTDIR
) {
466 * This is a valid condition: We appended the
467 * .snaphots/@GMT.. to a file name. Just try
468 * with the upper levels.
472 if (errno
!= ENOENT
) {
473 /* Other problem than "not found" */
482 DEBUG(10, ("Found %s\n", converted
));
490 TALLOC_FREE(converted
);
492 TALLOC_FREE(slashes
);
499 modify a sbuf return to ensure that inodes in the shadow directory
500 are different from those in the main directory
502 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
503 SMB_STRUCT_STAT
*sbuf
)
505 struct shadow_copy2_config
*config
;
507 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
510 if (config
->fixinodes
) {
511 /* some snapshot systems, like GPFS, return the name
512 device:inode for the snapshot files as the current
513 files. That breaks the 'restore' button in the shadow copy
514 GUI, as the client gets a sharing violation.
516 This is a crude way of allowing both files to be
517 open at once. It has a slight chance of inode
518 number collision, but I can't see a better approach
519 without significant VFS changes
523 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
527 sbuf
->st_ex_ino
^= shash
;
531 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
542 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
543 ×tamp
, &stripped
)) {
546 if (timestamp
== 0) {
547 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
549 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
550 TALLOC_FREE(stripped
);
554 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
561 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
562 const struct smb_filename
*smb_fname_src
,
563 const struct smb_filename
*smb_fname_dst
)
565 time_t timestamp_src
, timestamp_dst
;
567 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
568 smb_fname_src
->base_name
,
569 ×tamp_src
, NULL
)) {
572 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
573 smb_fname_dst
->base_name
,
574 ×tamp_dst
, NULL
)) {
577 if (timestamp_src
!= 0) {
581 if (timestamp_dst
!= 0) {
585 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
588 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
589 const char *oldname
, const char *newname
)
591 time_t timestamp_old
, timestamp_new
;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
594 ×tamp_old
, NULL
)) {
597 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
598 ×tamp_new
, NULL
)) {
601 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
605 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
608 static int shadow_copy2_link(vfs_handle_struct
*handle
,
609 const char *oldname
, const char *newname
)
611 time_t timestamp_old
, timestamp_new
;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
614 ×tamp_old
, NULL
)) {
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
618 ×tamp_new
, NULL
)) {
621 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
625 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
628 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
629 struct smb_filename
*smb_fname
)
632 char *stripped
, *tmp
;
633 int ret
, saved_errno
;
635 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
636 smb_fname
->base_name
,
637 ×tamp
, &stripped
)) {
640 if (timestamp
== 0) {
641 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
644 tmp
= smb_fname
->base_name
;
645 smb_fname
->base_name
= shadow_copy2_convert(
646 talloc_tos(), handle
, stripped
, timestamp
);
647 TALLOC_FREE(stripped
);
649 if (smb_fname
->base_name
== NULL
) {
650 smb_fname
->base_name
= tmp
;
654 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
657 TALLOC_FREE(smb_fname
->base_name
);
658 smb_fname
->base_name
= tmp
;
661 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
667 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
668 struct smb_filename
*smb_fname
)
671 char *stripped
, *tmp
;
672 int ret
, saved_errno
;
674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
675 smb_fname
->base_name
,
676 ×tamp
, &stripped
)) {
679 if (timestamp
== 0) {
680 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
683 tmp
= smb_fname
->base_name
;
684 smb_fname
->base_name
= shadow_copy2_convert(
685 talloc_tos(), handle
, stripped
, timestamp
);
686 TALLOC_FREE(stripped
);
688 if (smb_fname
->base_name
== NULL
) {
689 smb_fname
->base_name
= tmp
;
693 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
696 TALLOC_FREE(smb_fname
->base_name
);
697 smb_fname
->base_name
= tmp
;
700 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
706 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
707 SMB_STRUCT_STAT
*sbuf
)
712 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
716 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
717 fsp
->fsp_name
->base_name
,
721 if (timestamp
!= 0) {
722 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
727 static int shadow_copy2_open(vfs_handle_struct
*handle
,
728 struct smb_filename
*smb_fname
, files_struct
*fsp
,
729 int flags
, mode_t mode
)
732 char *stripped
, *tmp
;
733 int ret
, saved_errno
;
735 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
736 smb_fname
->base_name
,
737 ×tamp
, &stripped
)) {
740 if (timestamp
== 0) {
741 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
744 tmp
= smb_fname
->base_name
;
745 smb_fname
->base_name
= shadow_copy2_convert(
746 talloc_tos(), handle
, stripped
, timestamp
);
747 TALLOC_FREE(stripped
);
749 if (smb_fname
->base_name
== NULL
) {
750 smb_fname
->base_name
= tmp
;
754 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
757 TALLOC_FREE(smb_fname
->base_name
);
758 smb_fname
->base_name
= tmp
;
764 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
765 const struct smb_filename
*smb_fname
)
769 int ret
, saved_errno
;
770 struct smb_filename
*conv
;
773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
774 smb_fname
->base_name
,
775 ×tamp
, &stripped
)) {
778 if (timestamp
== 0) {
779 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
781 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
782 if (!NT_STATUS_IS_OK(status
)) {
786 conv
->base_name
= shadow_copy2_convert(
787 conv
, handle
, stripped
, timestamp
);
788 TALLOC_FREE(stripped
);
789 if (conv
->base_name
== NULL
) {
792 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
799 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
804 int ret
, saved_errno
;
807 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
808 ×tamp
, &stripped
)) {
811 if (timestamp
== 0) {
812 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
814 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
815 TALLOC_FREE(stripped
);
819 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
826 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
827 uid_t uid
, gid_t gid
)
831 int ret
, saved_errno
;
834 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
835 ×tamp
, &stripped
)) {
838 if (timestamp
== 0) {
839 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
841 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
842 TALLOC_FREE(stripped
);
846 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
853 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
858 int ret
, saved_errno
;
861 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
862 ×tamp
, &stripped
)) {
865 if (timestamp
== 0) {
866 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
868 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
869 TALLOC_FREE(stripped
);
873 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
880 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
881 const struct smb_filename
*smb_fname
,
882 struct smb_file_time
*ft
)
886 int ret
, saved_errno
;
887 struct smb_filename
*conv
;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
891 smb_fname
->base_name
,
892 ×tamp
, &stripped
)) {
895 if (timestamp
== 0) {
896 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
898 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
899 if (!NT_STATUS_IS_OK(status
)) {
903 conv
->base_name
= shadow_copy2_convert(
904 conv
, handle
, stripped
, timestamp
);
905 TALLOC_FREE(stripped
);
906 if (conv
->base_name
== NULL
) {
909 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
916 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
917 const char *fname
, char *buf
, size_t bufsiz
)
921 int ret
, saved_errno
;
924 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
925 ×tamp
, &stripped
)) {
928 if (timestamp
== 0) {
929 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
931 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
932 TALLOC_FREE(stripped
);
936 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
943 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
944 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
948 int ret
, saved_errno
;
951 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
952 ×tamp
, &stripped
)) {
955 if (timestamp
== 0) {
956 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
958 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
959 TALLOC_FREE(stripped
);
963 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
970 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
974 char *stripped
= NULL
;
977 char *inserted
= NULL
;
978 char *inserted_to
, *inserted_end
;
981 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
982 ×tamp
, &stripped
)) {
985 if (timestamp
== 0) {
986 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
989 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
994 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
995 if (result
== NULL
) {
1000 * Take away what we've inserted. This removes the @GMT-thingy
1001 * completely, but will give a path under the share root.
1003 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
1004 if (inserted
== NULL
) {
1007 inserted_to
= strstr_m(result
, inserted
);
1008 if (inserted_to
== NULL
) {
1009 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
1012 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
1013 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
1016 saved_errno
= errno
;
1017 TALLOC_FREE(inserted
);
1019 TALLOC_FREE(stripped
);
1020 errno
= saved_errno
;
1025 * Check whether a given directory contains a
1026 * snapshot directory as direct subdirectory.
1027 * If yes, return the path of the snapshot-subdir,
1028 * otherwise return NULL.
1030 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1033 struct smb_filename smb_fname
;
1035 struct shadow_copy2_config
*config
;
1037 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1040 ZERO_STRUCT(smb_fname
);
1041 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1042 path
, config
->snapdir
);
1043 if (smb_fname
.base_name
== NULL
) {
1047 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1048 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1049 return smb_fname
.base_name
;
1051 TALLOC_FREE(smb_fname
.base_name
);
1056 * Find the snapshot directory (if any) for the given
1057 * filename (which is relative to the share).
1059 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1060 struct vfs_handle_struct
*handle
,
1061 struct smb_filename
*smb_fname
)
1064 const char *snapdir
;
1065 struct shadow_copy2_config
*config
;
1067 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1071 * If the non-snapdisrseverywhere mode, we should not search!
1073 if (!config
->snapdirseverywhere
) {
1074 return config
->snapshot_basepath
;
1077 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1078 handle
->conn
->connectpath
,
1079 smb_fname
->base_name
);
1084 snapdir
= have_snapdir(handle
, path
);
1085 if (snapdir
!= NULL
) {
1090 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1094 snapdir
= have_snapdir(handle
, path
);
1095 if (snapdir
!= NULL
) {
1104 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1106 char *gmt
, size_t gmt_len
)
1108 struct tm timestamp
;
1110 unsigned long int timestamp_long
;
1112 struct shadow_copy2_config
*config
;
1114 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1117 fmt
= config
->gmt_format
;
1119 ZERO_STRUCT(timestamp
);
1120 if (config
->use_sscanf
) {
1121 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1122 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1123 "no sscanf match %s: %s\n",
1127 timestamp_t
= timestamp_long
;
1128 gmtime_r(×tamp_t
, ×tamp
);
1130 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1131 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1132 "no match %s: %s\n",
1136 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1139 if (config
->use_localtime
) {
1140 timestamp
.tm_isdst
= -1;
1141 timestamp_t
= mktime(×tamp
);
1142 gmtime_r(×tamp_t
, ×tamp
);
1146 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1150 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1152 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1155 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1157 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1161 sort the shadow copy data in ascending or descending order
1163 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1164 struct shadow_copy_data
*shadow_copy2_data
)
1166 int (*cmpfunc
)(const void *, const void *);
1168 struct shadow_copy2_config
*config
;
1170 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1173 sort
= config
->sort_order
;
1178 if (strcmp(sort
, "asc") == 0) {
1179 cmpfunc
= shadow_copy2_label_cmp_asc
;
1180 } else if (strcmp(sort
, "desc") == 0) {
1181 cmpfunc
= shadow_copy2_label_cmp_desc
;
1186 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1187 shadow_copy2_data
->labels
)
1189 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1190 shadow_copy2_data
->num_volumes
,
1195 static int shadow_copy2_get_shadow_copy_data(
1196 vfs_handle_struct
*handle
, files_struct
*fsp
,
1197 struct shadow_copy_data
*shadow_copy2_data
,
1201 const char *snapdir
;
1203 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1205 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1206 if (snapdir
== NULL
) {
1207 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1208 handle
->conn
->connectpath
));
1210 talloc_free(tmp_ctx
);
1214 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1217 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1218 " - %s\n", snapdir
, strerror(errno
)));
1219 talloc_free(tmp_ctx
);
1224 shadow_copy2_data
->num_volumes
= 0;
1225 shadow_copy2_data
->labels
= NULL
;
1227 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1228 char snapshot
[GMT_NAME_LEN
+1];
1229 SHADOW_COPY_LABEL
*tlabels
;
1232 * ignore names not of the right form in the snapshot
1235 if (!shadow_copy2_snapshot_to_gmt(
1237 snapshot
, sizeof(snapshot
))) {
1239 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1240 "ignoring %s\n", d
->d_name
));
1243 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1244 d
->d_name
, snapshot
));
1247 /* the caller doesn't want the labels */
1248 shadow_copy2_data
->num_volumes
++;
1252 tlabels
= talloc_realloc(shadow_copy2_data
,
1253 shadow_copy2_data
->labels
,
1255 shadow_copy2_data
->num_volumes
+1);
1256 if (tlabels
== NULL
) {
1257 DEBUG(0,("shadow_copy2: out of memory\n"));
1258 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1259 talloc_free(tmp_ctx
);
1263 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1266 shadow_copy2_data
->num_volumes
++;
1267 shadow_copy2_data
->labels
= tlabels
;
1270 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1272 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1274 talloc_free(tmp_ctx
);
1278 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1279 struct files_struct
*fsp
,
1280 uint32 security_info
,
1281 TALLOC_CTX
*mem_ctx
,
1282 struct security_descriptor
**ppdesc
)
1289 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1290 fsp
->fsp_name
->base_name
,
1291 ×tamp
, &stripped
)) {
1292 return map_nt_error_from_unix(errno
);
1294 if (timestamp
== 0) {
1295 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1299 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1300 TALLOC_FREE(stripped
);
1302 return map_nt_error_from_unix(errno
);
1304 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1310 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1312 uint32 security_info
,
1313 TALLOC_CTX
*mem_ctx
,
1314 struct security_descriptor
**ppdesc
)
1321 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1322 ×tamp
, &stripped
)) {
1323 return map_nt_error_from_unix(errno
);
1325 if (timestamp
== 0) {
1326 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1329 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1330 TALLOC_FREE(stripped
);
1332 return map_nt_error_from_unix(errno
);
1334 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1340 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1341 const char *fname
, mode_t mode
)
1345 int ret
, saved_errno
;
1348 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1349 ×tamp
, &stripped
)) {
1352 if (timestamp
== 0) {
1353 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1355 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1356 TALLOC_FREE(stripped
);
1360 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1361 saved_errno
= errno
;
1363 errno
= saved_errno
;
1367 static int shadow_copy2_rmdir(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_RMDIR(handle
, fname
);
1381 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1382 TALLOC_FREE(stripped
);
1386 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1387 saved_errno
= errno
;
1389 errno
= saved_errno
;
1393 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1398 int ret
, saved_errno
;
1401 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1402 ×tamp
, &stripped
)) {
1405 if (timestamp
== 0) {
1406 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1408 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1409 TALLOC_FREE(stripped
);
1413 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1414 saved_errno
= errno
;
1416 errno
= saved_errno
;
1420 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1421 const char *fname
, const char *aname
,
1422 void *value
, size_t size
)
1430 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1431 ×tamp
, &stripped
)) {
1434 if (timestamp
== 0) {
1435 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1438 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1439 TALLOC_FREE(stripped
);
1443 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1444 saved_errno
= errno
;
1446 errno
= saved_errno
;
1450 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1452 char *list
, size_t size
)
1460 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1461 ×tamp
, &stripped
)) {
1464 if (timestamp
== 0) {
1465 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1467 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1468 TALLOC_FREE(stripped
);
1472 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1473 saved_errno
= errno
;
1475 errno
= saved_errno
;
1479 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1480 const char *fname
, const char *aname
)
1484 int ret
, saved_errno
;
1487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1488 ×tamp
, &stripped
)) {
1491 if (timestamp
== 0) {
1492 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1494 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1495 TALLOC_FREE(stripped
);
1499 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1500 saved_errno
= errno
;
1502 errno
= saved_errno
;
1506 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1508 const char *aname
, const void *value
,
1509 size_t size
, int flags
)
1517 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1518 ×tamp
, &stripped
)) {
1521 if (timestamp
== 0) {
1522 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1525 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1526 TALLOC_FREE(stripped
);
1530 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1531 saved_errno
= errno
;
1533 errno
= saved_errno
;
1537 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1538 const char *fname
, mode_t mode
)
1546 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1547 ×tamp
, &stripped
)) {
1550 if (timestamp
== 0) {
1551 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1553 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1554 TALLOC_FREE(stripped
);
1558 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1559 saved_errno
= errno
;
1561 errno
= saved_errno
;
1565 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1568 TALLOC_CTX
*mem_ctx
,
1577 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1578 ×tamp
, &stripped
)) {
1581 if (timestamp
== 0) {
1582 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1583 mem_ctx
, found_name
);
1585 if (stripped
[0] == '\0') {
1586 *found_name
= talloc_strdup(mem_ctx
, name
);
1587 if (*found_name
== NULL
) {
1593 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1594 TALLOC_FREE(stripped
);
1598 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1599 mem_ctx
, found_name
);
1600 saved_errno
= errno
;
1602 errno
= saved_errno
;
1606 static uint64_t shadow_copy2_disk_free(vfs_handle_struct
*handle
,
1607 const char *path
, bool small_query
,
1608 uint64_t *bsize
, uint64_t *dfree
,
1617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1618 ×tamp
, &stripped
)) {
1621 if (timestamp
== 0) {
1622 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1623 bsize
, dfree
, dsize
);
1626 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1627 TALLOC_FREE(stripped
);
1632 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1635 saved_errno
= errno
;
1637 errno
= saved_errno
;
1642 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1643 const char *service
, const char *user
)
1645 struct shadow_copy2_config
*config
;
1647 const char *snapdir
;
1648 const char *gmt_format
;
1649 const char *sort_order
;
1650 const char *basedir
;
1651 const char *mount_point
;
1653 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1654 (unsigned)handle
->conn
->cnum
,
1655 handle
->conn
->connectpath
));
1657 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1662 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1663 if (config
== NULL
) {
1664 DEBUG(0, ("talloc_zero() failed\n"));
1669 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1672 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1673 if (config
->gmt_format
== NULL
) {
1674 DEBUG(0, ("talloc_strdup() failed\n"));
1679 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1680 "shadow", "sscanf", false);
1682 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1683 "shadow", "localtime",
1686 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1687 "shadow", "snapdir",
1689 config
->snapdir
= talloc_strdup(config
, snapdir
);
1690 if (config
->snapdir
== NULL
) {
1691 DEBUG(0, ("talloc_strdup() failed\n"));
1696 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1698 "snapdirseverywhere",
1701 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1702 "shadow", "crossmountpoints",
1705 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1706 "shadow", "fixinodes",
1709 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1710 "shadow", "sort", "desc");
1711 config
->sort_order
= talloc_strdup(config
, sort_order
);
1712 if (config
->sort_order
== NULL
) {
1713 DEBUG(0, ("talloc_strdup() failed\n"));
1718 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1719 "shadow", "mountpoint", NULL
);
1720 if (mount_point
!= NULL
) {
1721 if (mount_point
[0] != '/') {
1722 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1723 "relative ('%s'), but it has to be an "
1724 "absolute path. Ignoring provided value.\n",
1729 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1730 if (p
!= handle
->conn
->connectpath
) {
1731 DEBUG(1, ("Warning: mount_point (%s) is not a "
1732 "subdirectory of the share root "
1733 "(%s). Ignoring provided value.\n",
1735 handle
->conn
->connectpath
));
1741 if (mount_point
!= NULL
) {
1742 config
->mount_point
= talloc_strdup(config
, mount_point
);
1743 if (config
->mount_point
== NULL
) {
1744 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1748 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1750 if (config
->mount_point
== NULL
) {
1751 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1752 " failed: %s\n", strerror(errno
)));
1757 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1758 "shadow", "basedir", NULL
);
1760 if (basedir
!= NULL
) {
1761 if (basedir
[0] != '/') {
1762 DEBUG(1, (__location__
" Warning: 'basedir' is "
1763 "relative ('%s'), but it has to be an "
1764 "absolute path. Disabling basedir.\n",
1768 p
= strstr(basedir
, config
->mount_point
);
1770 DEBUG(1, ("Warning: basedir (%s) is not a "
1771 "subdirectory of the share root's "
1772 "mount point (%s). "
1773 "Disabling basedir\n",
1774 basedir
, config
->mount_point
));
1776 config
->basedir
= talloc_strdup(config
,
1778 if (config
->basedir
== NULL
) {
1779 DEBUG(0, ("talloc_strdup() failed\n"));
1787 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1788 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1789 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1790 TALLOC_FREE(config
->basedir
);
1793 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1794 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1795 "with 'crossmountpoints'. Disabling basedir.\n"));
1796 TALLOC_FREE(config
->basedir
);
1799 if (config
->basedir
== NULL
) {
1800 config
->basedir
= config
->mount_point
;
1803 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1804 config
->rel_connectpath
= talloc_strdup(config
,
1805 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1806 if (config
->rel_connectpath
== NULL
) {
1807 DEBUG(0, ("talloc_strdup() failed\n"));
1813 if (config
->snapdir
[0] == '/') {
1814 config
->snapdir_absolute
= true;
1816 if (config
->snapdirseverywhere
== true) {
1817 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1818 "is incompatible with 'snapdirseverywhere', "
1819 "setting 'snapdirseverywhere' to false.\n"));
1820 config
->snapdirseverywhere
= false;
1823 if (config
->crossmountpoints
== true) {
1824 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1825 "is not supported with an absolute snapdir. "
1826 "Disabling it.\n"));
1827 config
->crossmountpoints
= false;
1830 config
->snapshot_basepath
= config
->snapdir
;
1832 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1833 config
->mount_point
, config
->snapdir
);
1834 if (config
->snapshot_basepath
== NULL
) {
1835 DEBUG(0, ("talloc_asprintf() failed\n"));
1841 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1842 " share root: '%s'\n"
1844 " mountpoint: '%s'\n"
1845 " rel share root: '%s'\n"
1847 " snapshot base path: '%s'\n"
1850 " snapdirs everywhere: %s\n"
1851 " cross mountpoints: %s\n"
1855 handle
->conn
->connectpath
,
1857 config
->mount_point
,
1858 config
->rel_connectpath
,
1860 config
->snapshot_basepath
,
1862 config
->use_sscanf
? "yes" : "no",
1863 config
->snapdirseverywhere
? "yes" : "no",
1864 config
->crossmountpoints
? "yes" : "no",
1865 config
->fixinodes
? "yes" : "no",
1870 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1871 NULL
, struct shadow_copy2_config
,
1877 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1878 .connect_fn
= shadow_copy2_connect
,
1879 .opendir_fn
= shadow_copy2_opendir
,
1880 .disk_free_fn
= shadow_copy2_disk_free
,
1881 .rename_fn
= shadow_copy2_rename
,
1882 .link_fn
= shadow_copy2_link
,
1883 .symlink_fn
= shadow_copy2_symlink
,
1884 .stat_fn
= shadow_copy2_stat
,
1885 .lstat_fn
= shadow_copy2_lstat
,
1886 .fstat_fn
= shadow_copy2_fstat
,
1887 .open_fn
= shadow_copy2_open
,
1888 .unlink_fn
= shadow_copy2_unlink
,
1889 .chmod_fn
= shadow_copy2_chmod
,
1890 .chown_fn
= shadow_copy2_chown
,
1891 .chdir_fn
= shadow_copy2_chdir
,
1892 .ntimes_fn
= shadow_copy2_ntimes
,
1893 .readlink_fn
= shadow_copy2_readlink
,
1894 .mknod_fn
= shadow_copy2_mknod
,
1895 .realpath_fn
= shadow_copy2_realpath
,
1896 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1897 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1898 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1899 .mkdir_fn
= shadow_copy2_mkdir
,
1900 .rmdir_fn
= shadow_copy2_rmdir
,
1901 .getxattr_fn
= shadow_copy2_getxattr
,
1902 .listxattr_fn
= shadow_copy2_listxattr
,
1903 .removexattr_fn
= shadow_copy2_removexattr
,
1904 .setxattr_fn
= shadow_copy2_setxattr
,
1905 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1906 .chflags_fn
= shadow_copy2_chflags
,
1907 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1910 NTSTATUS
vfs_shadow_copy2_init(void);
1911 NTSTATUS
vfs_shadow_copy2_init(void)
1913 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1914 "shadow_copy2", &vfs_shadow_copy2_fns
);