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 posix level GTM-tag string
162 * based on the configurable format.
164 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct
*handle
,
166 char *snaptime_string
,
171 struct shadow_copy2_config
*config
;
173 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
176 if (config
->use_sscanf
) {
177 snaptime_len
= snprintf(snaptime_string
,
180 (unsigned long)snapshot
);
181 if (snaptime_len
<= 0) {
182 DEBUG(10, ("snprintf failed\n"));
186 if (config
->use_localtime
) {
187 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
188 DEBUG(10, ("gmtime_r failed\n"));
192 if (gmtime_r(&snapshot
, &snap_tm
) == 0) {
193 DEBUG(10, ("gmtime_r failed\n"));
197 snaptime_len
= strftime(snaptime_string
,
201 if (snaptime_len
== 0) {
202 DEBUG(10, ("strftime failed\n"));
211 * Given a timstamp, build the string to insert into a path
212 * as a path component for creating the local path to the
213 * snapshot at the given timestamp of the input path.
215 * In the case of a parallel snapdir (specified with an
216 * absolute path), this is the inital portion of the
217 * local path of any snapshot file. The complete path is
218 * obtained by appending the portion of the file's path
219 * below the share root's mountpoint.
221 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
222 struct vfs_handle_struct
*handle
,
225 fstring snaptime_string
;
226 size_t snaptime_len
= 0;
228 struct shadow_copy2_config
*config
;
230 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
233 snaptime_len
= shadow_copy2_posix_gmt_string(handle
,
236 sizeof(snaptime_string
));
237 if (snaptime_len
<= 0) {
241 if (config
->snapdir_absolute
) {
242 result
= talloc_asprintf(mem_ctx
, "%s/%s",
243 config
->snapdir
, snaptime_string
);
245 result
= talloc_asprintf(mem_ctx
, "/%s/%s",
246 config
->snapdir
, snaptime_string
);
248 if (result
== NULL
) {
249 DEBUG(1, (__location__
" talloc_asprintf failed\n"));
256 * Build the posix snapshot path for the connection
257 * at the given timestamp, i.e. the absolute posix path
258 * that contains the snapshot for this file system.
260 * This only applies to classical case, i.e. not
261 * to the "snapdirseverywhere" mode.
263 static char *shadow_copy2_snapshot_path(TALLOC_CTX
*mem_ctx
,
264 struct vfs_handle_struct
*handle
,
267 fstring snaptime_string
;
268 size_t snaptime_len
= 0;
270 struct shadow_copy2_config
*config
;
272 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
275 snaptime_len
= shadow_copy2_posix_gmt_string(handle
,
278 sizeof(snaptime_string
));
279 if (snaptime_len
<= 0) {
283 result
= talloc_asprintf(mem_ctx
, "%s/%s",
284 config
->snapshot_basepath
, snaptime_string
);
285 if (result
== NULL
) {
286 DEBUG(1, (__location__
" talloc_asprintf failed\n"));
293 * Strip a snapshot component from an filename as
294 * handed in via the smb layer.
295 * Returns the parsed timestamp and the stripped filename.
297 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
298 struct vfs_handle_struct
*handle
,
308 size_t rest_len
, dst_len
;
309 struct shadow_copy2_config
*config
;
311 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
314 DEBUG(10, (__location__
": enter path '%s'\n", name
));
316 p
= strstr_m(name
, "@GMT-");
320 if ((p
> name
) && (p
[-1] != '/')) {
321 /* the GMT-token does not start a path-component */
324 q
= strptime(p
, GMT_FORMAT
, &tm
);
329 timestamp
= timegm(&tm
);
330 if (timestamp
== (time_t)-1) {
333 if ((p
== name
) && (q
[0] == '\0')) {
334 /* the name consists of only the GMT token */
335 if (pstripped
!= NULL
) {
336 stripped
= talloc_strdup(mem_ctx
, "");
337 if (stripped
== NULL
) {
340 *pstripped
= stripped
;
342 *ptimestamp
= timestamp
;
347 * The GMT token is either at the end of the path
348 * or it is not a complete path component, i.e. the
349 * path component continues after the gmt-token.
351 * TODO: Is this correct? Or would the GMT tag as the
352 * last component be a valid input?
358 rest_len
= strlen(q
);
359 dst_len
= (p
-name
) + rest_len
;
361 if (config
->snapdirseverywhere
) {
364 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
366 if (insert
== NULL
) {
371 DEBUG(10, (__location__
": snapdirseverywhere mode.\n"
373 "insert string '%s'\n", name
, insert
));
375 have_insert
= (strstr(name
, insert
+1) != NULL
);
377 DEBUG(10, (__location__
": insert string '%s' found in "
378 "path '%s' found in snapdirseverywhere mode "
379 "==> already converted\n", insert
, name
));
386 if (pstripped
!= NULL
) {
387 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
388 if (stripped
== NULL
) {
393 memcpy(stripped
, name
, p
-name
);
396 memcpy(stripped
+ (p
-name
), q
, rest_len
);
398 stripped
[dst_len
] = '\0';
399 *pstripped
= stripped
;
401 *ptimestamp
= timestamp
;
408 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
409 vfs_handle_struct
*handle
)
411 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
416 if (stat(path
, &st
) != 0) {
423 while ((p
= strrchr(path
, '/')) && p
> path
) {
425 if (stat(path
, &st
) != 0) {
429 if (st
.st_dev
!= dev
) {
439 * Convert from a name as handed in via the SMB layer
440 * and a timestamp into the local path of the snapshot
441 * of the provided file at the provided time.
443 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
444 struct vfs_handle_struct
*handle
,
445 const char *name
, time_t timestamp
)
447 struct smb_filename converted_fname
;
449 size_t *slashes
= NULL
;
450 unsigned num_slashes
;
454 char *converted
= NULL
;
458 struct shadow_copy2_config
*config
;
460 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
463 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
469 pathlen
= talloc_get_size(path
)-1;
471 DEBUG(10, ("converting %s\n", path
));
473 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
474 &slashes
, &num_slashes
)) {
477 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
478 if (insert
== NULL
) {
481 insertlen
= talloc_get_size(insert
)-1;
482 converted
= talloc_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
483 if (converted
== NULL
) {
487 if (path
[pathlen
-1] != '/') {
489 * Append a fake slash to find the snapshot root
492 tmp
= talloc_realloc(talloc_tos(), slashes
,
493 size_t, num_slashes
+1);
498 slashes
[num_slashes
] = pathlen
;
504 if (!config
->crossmountpoints
) {
507 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
509 if (mount_point
== NULL
) {
512 min_offset
= strlen(mount_point
);
513 TALLOC_FREE(mount_point
);
516 memcpy(converted
, path
, pathlen
+1);
517 converted
[pathlen
+insertlen
] = '\0';
519 ZERO_STRUCT(converted_fname
);
520 converted_fname
.base_name
= converted
;
522 for (i
= num_slashes
-1; i
>=0; i
--) {
528 if (offset
< min_offset
) {
533 memcpy(converted
+offset
, insert
, insertlen
);
536 memcpy(converted
+offset
, path
+ slashes
[i
],
537 pathlen
- slashes
[i
]);
539 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
541 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
542 ret
, ret
== 0 ? "ok" : strerror(errno
)));
547 if (errno
== ENOTDIR
) {
549 * This is a valid condition: We appended the
550 * .snaphots/@GMT.. to a file name. Just try
551 * with the upper levels.
555 if (errno
!= ENOENT
) {
556 /* Other problem than "not found" */
565 DEBUG(10, ("Found %s\n", converted
));
573 TALLOC_FREE(converted
);
575 TALLOC_FREE(slashes
);
582 modify a sbuf return to ensure that inodes in the shadow directory
583 are different from those in the main directory
585 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
586 SMB_STRUCT_STAT
*sbuf
)
588 struct shadow_copy2_config
*config
;
590 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
593 if (config
->fixinodes
) {
594 /* some snapshot systems, like GPFS, return the name
595 device:inode for the snapshot files as the current
596 files. That breaks the 'restore' button in the shadow copy
597 GUI, as the client gets a sharing violation.
599 This is a crude way of allowing both files to be
600 open at once. It has a slight chance of inode
601 number collision, but I can't see a better approach
602 without significant VFS changes
606 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
610 sbuf
->st_ex_ino
^= shash
;
614 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
625 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
626 ×tamp
, &stripped
)) {
629 if (timestamp
== 0) {
630 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
632 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
633 TALLOC_FREE(stripped
);
637 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
644 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
645 const struct smb_filename
*smb_fname_src
,
646 const struct smb_filename
*smb_fname_dst
)
648 time_t timestamp_src
, timestamp_dst
;
650 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
651 smb_fname_src
->base_name
,
652 ×tamp_src
, NULL
)) {
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
656 smb_fname_dst
->base_name
,
657 ×tamp_dst
, NULL
)) {
660 if (timestamp_src
!= 0) {
664 if (timestamp_dst
!= 0) {
668 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
671 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
672 const char *oldname
, const char *newname
)
674 time_t timestamp_old
, timestamp_new
;
676 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
677 ×tamp_old
, NULL
)) {
680 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
681 ×tamp_new
, NULL
)) {
684 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
688 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
691 static int shadow_copy2_link(vfs_handle_struct
*handle
,
692 const char *oldname
, const char *newname
)
694 time_t timestamp_old
, timestamp_new
;
696 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
697 ×tamp_old
, NULL
)) {
700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
701 ×tamp_new
, NULL
)) {
704 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
708 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
711 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
712 struct smb_filename
*smb_fname
)
715 char *stripped
, *tmp
;
716 int ret
, saved_errno
;
718 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
719 smb_fname
->base_name
,
720 ×tamp
, &stripped
)) {
723 if (timestamp
== 0) {
724 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
727 tmp
= smb_fname
->base_name
;
728 smb_fname
->base_name
= shadow_copy2_convert(
729 talloc_tos(), handle
, stripped
, timestamp
);
730 TALLOC_FREE(stripped
);
732 if (smb_fname
->base_name
== NULL
) {
733 smb_fname
->base_name
= tmp
;
737 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
740 TALLOC_FREE(smb_fname
->base_name
);
741 smb_fname
->base_name
= tmp
;
744 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
750 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
751 struct smb_filename
*smb_fname
)
754 char *stripped
, *tmp
;
755 int ret
, saved_errno
;
757 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
758 smb_fname
->base_name
,
759 ×tamp
, &stripped
)) {
762 if (timestamp
== 0) {
763 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
766 tmp
= smb_fname
->base_name
;
767 smb_fname
->base_name
= shadow_copy2_convert(
768 talloc_tos(), handle
, stripped
, timestamp
);
769 TALLOC_FREE(stripped
);
771 if (smb_fname
->base_name
== NULL
) {
772 smb_fname
->base_name
= tmp
;
776 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
779 TALLOC_FREE(smb_fname
->base_name
);
780 smb_fname
->base_name
= tmp
;
783 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
789 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
790 SMB_STRUCT_STAT
*sbuf
)
795 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
799 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
800 fsp
->fsp_name
->base_name
,
804 if (timestamp
!= 0) {
805 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
810 static int shadow_copy2_open(vfs_handle_struct
*handle
,
811 struct smb_filename
*smb_fname
, files_struct
*fsp
,
812 int flags
, mode_t mode
)
815 char *stripped
, *tmp
;
816 int ret
, saved_errno
;
818 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
819 smb_fname
->base_name
,
820 ×tamp
, &stripped
)) {
823 if (timestamp
== 0) {
824 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
827 tmp
= smb_fname
->base_name
;
828 smb_fname
->base_name
= shadow_copy2_convert(
829 talloc_tos(), handle
, stripped
, timestamp
);
830 TALLOC_FREE(stripped
);
832 if (smb_fname
->base_name
== NULL
) {
833 smb_fname
->base_name
= tmp
;
837 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
840 TALLOC_FREE(smb_fname
->base_name
);
841 smb_fname
->base_name
= tmp
;
847 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
848 const struct smb_filename
*smb_fname
)
852 int ret
, saved_errno
;
853 struct smb_filename
*conv
;
856 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
857 smb_fname
->base_name
,
858 ×tamp
, &stripped
)) {
861 if (timestamp
== 0) {
862 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
864 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
865 if (!NT_STATUS_IS_OK(status
)) {
869 conv
->base_name
= shadow_copy2_convert(
870 conv
, handle
, stripped
, timestamp
);
871 TALLOC_FREE(stripped
);
872 if (conv
->base_name
== NULL
) {
875 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
882 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
887 int ret
, saved_errno
;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
891 ×tamp
, &stripped
)) {
894 if (timestamp
== 0) {
895 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
897 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
898 TALLOC_FREE(stripped
);
902 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
909 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
910 uid_t uid
, gid_t gid
)
914 int ret
, saved_errno
;
917 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
918 ×tamp
, &stripped
)) {
921 if (timestamp
== 0) {
922 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
924 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
925 TALLOC_FREE(stripped
);
929 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
936 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
941 int ret
, saved_errno
;
944 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
945 ×tamp
, &stripped
)) {
948 if (timestamp
== 0) {
949 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
951 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
952 TALLOC_FREE(stripped
);
956 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
963 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
964 const struct smb_filename
*smb_fname
,
965 struct smb_file_time
*ft
)
969 int ret
, saved_errno
;
970 struct smb_filename
*conv
;
973 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
974 smb_fname
->base_name
,
975 ×tamp
, &stripped
)) {
978 if (timestamp
== 0) {
979 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
981 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
982 if (!NT_STATUS_IS_OK(status
)) {
986 conv
->base_name
= shadow_copy2_convert(
987 conv
, handle
, stripped
, timestamp
);
988 TALLOC_FREE(stripped
);
989 if (conv
->base_name
== NULL
) {
992 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
999 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
1000 const char *fname
, char *buf
, size_t bufsiz
)
1004 int ret
, saved_errno
;
1007 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1008 ×tamp
, &stripped
)) {
1011 if (timestamp
== 0) {
1012 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1014 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1015 TALLOC_FREE(stripped
);
1019 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1020 saved_errno
= errno
;
1022 errno
= saved_errno
;
1026 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
1027 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1031 int ret
, saved_errno
;
1034 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1035 ×tamp
, &stripped
)) {
1038 if (timestamp
== 0) {
1039 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1041 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1042 TALLOC_FREE(stripped
);
1046 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1047 saved_errno
= errno
;
1049 errno
= saved_errno
;
1053 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
1057 char *stripped
= NULL
;
1059 char *result
= NULL
;
1060 char *inserted
= NULL
;
1061 char *inserted_to
, *inserted_end
;
1064 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1065 ×tamp
, &stripped
)) {
1068 if (timestamp
== 0) {
1069 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1072 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1077 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1078 if (result
== NULL
) {
1083 * Take away what we've inserted. This removes the @GMT-thingy
1084 * completely, but will give a path under the share root.
1086 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
1087 if (inserted
== NULL
) {
1090 inserted_to
= strstr_m(result
, inserted
);
1091 if (inserted_to
== NULL
) {
1092 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
1095 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
1096 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
1099 saved_errno
= errno
;
1100 TALLOC_FREE(inserted
);
1102 TALLOC_FREE(stripped
);
1103 errno
= saved_errno
;
1108 * Check whether a given directory contains a
1109 * snapshot directory as direct subdirectory.
1110 * If yes, return the path of the snapshot-subdir,
1111 * otherwise return NULL.
1113 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1116 struct smb_filename smb_fname
;
1118 struct shadow_copy2_config
*config
;
1120 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1123 ZERO_STRUCT(smb_fname
);
1124 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1125 path
, config
->snapdir
);
1126 if (smb_fname
.base_name
== NULL
) {
1130 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1131 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1132 return smb_fname
.base_name
;
1134 TALLOC_FREE(smb_fname
.base_name
);
1139 * Find the snapshot directory (if any) for the given
1140 * filename (which is relative to the share).
1142 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1143 struct vfs_handle_struct
*handle
,
1144 struct smb_filename
*smb_fname
)
1147 const char *snapdir
;
1148 struct shadow_copy2_config
*config
;
1150 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1154 * If the non-snapdisrseverywhere mode, we should not search!
1156 if (!config
->snapdirseverywhere
) {
1157 return config
->snapshot_basepath
;
1160 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1161 handle
->conn
->connectpath
,
1162 smb_fname
->base_name
);
1167 snapdir
= have_snapdir(handle
, path
);
1168 if (snapdir
!= NULL
) {
1173 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1177 snapdir
= have_snapdir(handle
, path
);
1178 if (snapdir
!= NULL
) {
1187 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1189 char *gmt
, size_t gmt_len
)
1191 struct tm timestamp
;
1193 unsigned long int timestamp_long
;
1195 struct shadow_copy2_config
*config
;
1197 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1200 fmt
= config
->gmt_format
;
1202 ZERO_STRUCT(timestamp
);
1203 if (config
->use_sscanf
) {
1204 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1205 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1206 "no sscanf match %s: %s\n",
1210 timestamp_t
= timestamp_long
;
1211 gmtime_r(×tamp_t
, ×tamp
);
1213 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1214 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1215 "no match %s: %s\n",
1219 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1222 if (config
->use_localtime
) {
1223 timestamp
.tm_isdst
= -1;
1224 timestamp_t
= mktime(×tamp
);
1225 gmtime_r(×tamp_t
, ×tamp
);
1229 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1233 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1235 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1238 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1240 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1244 sort the shadow copy data in ascending or descending order
1246 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1247 struct shadow_copy_data
*shadow_copy2_data
)
1249 int (*cmpfunc
)(const void *, const void *);
1251 struct shadow_copy2_config
*config
;
1253 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1256 sort
= config
->sort_order
;
1261 if (strcmp(sort
, "asc") == 0) {
1262 cmpfunc
= shadow_copy2_label_cmp_asc
;
1263 } else if (strcmp(sort
, "desc") == 0) {
1264 cmpfunc
= shadow_copy2_label_cmp_desc
;
1269 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1270 shadow_copy2_data
->labels
)
1272 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1273 shadow_copy2_data
->num_volumes
,
1278 static int shadow_copy2_get_shadow_copy_data(
1279 vfs_handle_struct
*handle
, files_struct
*fsp
,
1280 struct shadow_copy_data
*shadow_copy2_data
,
1284 const char *snapdir
;
1286 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1288 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1289 if (snapdir
== NULL
) {
1290 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1291 handle
->conn
->connectpath
));
1293 talloc_free(tmp_ctx
);
1297 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1300 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1301 " - %s\n", snapdir
, strerror(errno
)));
1302 talloc_free(tmp_ctx
);
1307 shadow_copy2_data
->num_volumes
= 0;
1308 shadow_copy2_data
->labels
= NULL
;
1310 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1311 char snapshot
[GMT_NAME_LEN
+1];
1312 SHADOW_COPY_LABEL
*tlabels
;
1315 * ignore names not of the right form in the snapshot
1318 if (!shadow_copy2_snapshot_to_gmt(
1320 snapshot
, sizeof(snapshot
))) {
1322 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1323 "ignoring %s\n", d
->d_name
));
1326 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1327 d
->d_name
, snapshot
));
1330 /* the caller doesn't want the labels */
1331 shadow_copy2_data
->num_volumes
++;
1335 tlabels
= talloc_realloc(shadow_copy2_data
,
1336 shadow_copy2_data
->labels
,
1338 shadow_copy2_data
->num_volumes
+1);
1339 if (tlabels
== NULL
) {
1340 DEBUG(0,("shadow_copy2: out of memory\n"));
1341 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1342 talloc_free(tmp_ctx
);
1346 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1349 shadow_copy2_data
->num_volumes
++;
1350 shadow_copy2_data
->labels
= tlabels
;
1353 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1355 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1357 talloc_free(tmp_ctx
);
1361 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1362 struct files_struct
*fsp
,
1363 uint32 security_info
,
1364 TALLOC_CTX
*mem_ctx
,
1365 struct security_descriptor
**ppdesc
)
1372 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1373 fsp
->fsp_name
->base_name
,
1374 ×tamp
, &stripped
)) {
1375 return map_nt_error_from_unix(errno
);
1377 if (timestamp
== 0) {
1378 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1382 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1383 TALLOC_FREE(stripped
);
1385 return map_nt_error_from_unix(errno
);
1387 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1393 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1395 uint32 security_info
,
1396 TALLOC_CTX
*mem_ctx
,
1397 struct security_descriptor
**ppdesc
)
1404 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1405 ×tamp
, &stripped
)) {
1406 return map_nt_error_from_unix(errno
);
1408 if (timestamp
== 0) {
1409 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1412 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1413 TALLOC_FREE(stripped
);
1415 return map_nt_error_from_unix(errno
);
1417 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1423 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1424 const char *fname
, mode_t mode
)
1428 int ret
, saved_errno
;
1431 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1432 ×tamp
, &stripped
)) {
1435 if (timestamp
== 0) {
1436 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1438 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1439 TALLOC_FREE(stripped
);
1443 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1444 saved_errno
= errno
;
1446 errno
= saved_errno
;
1450 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1454 int ret
, saved_errno
;
1457 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1458 ×tamp
, &stripped
)) {
1461 if (timestamp
== 0) {
1462 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1464 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1465 TALLOC_FREE(stripped
);
1469 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1470 saved_errno
= errno
;
1472 errno
= saved_errno
;
1476 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1481 int ret
, saved_errno
;
1484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1485 ×tamp
, &stripped
)) {
1488 if (timestamp
== 0) {
1489 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1491 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1492 TALLOC_FREE(stripped
);
1496 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1497 saved_errno
= errno
;
1499 errno
= saved_errno
;
1503 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1504 const char *fname
, const char *aname
,
1505 void *value
, size_t size
)
1513 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1514 ×tamp
, &stripped
)) {
1517 if (timestamp
== 0) {
1518 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1521 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1522 TALLOC_FREE(stripped
);
1526 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1527 saved_errno
= errno
;
1529 errno
= saved_errno
;
1533 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1535 char *list
, size_t size
)
1543 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1544 ×tamp
, &stripped
)) {
1547 if (timestamp
== 0) {
1548 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1550 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1551 TALLOC_FREE(stripped
);
1555 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1556 saved_errno
= errno
;
1558 errno
= saved_errno
;
1562 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1563 const char *fname
, const char *aname
)
1567 int ret
, saved_errno
;
1570 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1571 ×tamp
, &stripped
)) {
1574 if (timestamp
== 0) {
1575 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1577 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1578 TALLOC_FREE(stripped
);
1582 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1583 saved_errno
= errno
;
1585 errno
= saved_errno
;
1589 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1591 const char *aname
, const void *value
,
1592 size_t size
, int flags
)
1600 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1601 ×tamp
, &stripped
)) {
1604 if (timestamp
== 0) {
1605 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1608 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1609 TALLOC_FREE(stripped
);
1613 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1614 saved_errno
= errno
;
1616 errno
= saved_errno
;
1620 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1621 const char *fname
, mode_t mode
)
1629 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1630 ×tamp
, &stripped
)) {
1633 if (timestamp
== 0) {
1634 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1636 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1637 TALLOC_FREE(stripped
);
1641 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1642 saved_errno
= errno
;
1644 errno
= saved_errno
;
1648 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1651 TALLOC_CTX
*mem_ctx
,
1660 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1661 ×tamp
, &stripped
)) {
1664 if (timestamp
== 0) {
1665 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1666 mem_ctx
, found_name
);
1668 if (stripped
[0] == '\0') {
1669 *found_name
= talloc_strdup(mem_ctx
, name
);
1670 if (*found_name
== NULL
) {
1676 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1677 TALLOC_FREE(stripped
);
1681 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1682 mem_ctx
, found_name
);
1683 saved_errno
= errno
;
1685 errno
= saved_errno
;
1689 static uint64_t shadow_copy2_disk_free(vfs_handle_struct
*handle
,
1690 const char *path
, bool small_query
,
1691 uint64_t *bsize
, uint64_t *dfree
,
1700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1701 ×tamp
, &stripped
)) {
1704 if (timestamp
== 0) {
1705 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1706 bsize
, dfree
, dsize
);
1709 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1710 TALLOC_FREE(stripped
);
1715 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1718 saved_errno
= errno
;
1720 errno
= saved_errno
;
1725 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1726 const char *service
, const char *user
)
1728 struct shadow_copy2_config
*config
;
1730 const char *snapdir
;
1731 const char *gmt_format
;
1732 const char *sort_order
;
1733 const char *basedir
;
1734 const char *mount_point
;
1736 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1737 (unsigned)handle
->conn
->cnum
,
1738 handle
->conn
->connectpath
));
1740 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1745 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1746 if (config
== NULL
) {
1747 DEBUG(0, ("talloc_zero() failed\n"));
1752 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1755 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1756 if (config
->gmt_format
== NULL
) {
1757 DEBUG(0, ("talloc_strdup() failed\n"));
1762 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1763 "shadow", "sscanf", false);
1765 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1766 "shadow", "localtime",
1769 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1770 "shadow", "snapdir",
1772 config
->snapdir
= talloc_strdup(config
, snapdir
);
1773 if (config
->snapdir
== NULL
) {
1774 DEBUG(0, ("talloc_strdup() failed\n"));
1779 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1781 "snapdirseverywhere",
1784 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1785 "shadow", "crossmountpoints",
1788 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1789 "shadow", "fixinodes",
1792 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1793 "shadow", "sort", "desc");
1794 config
->sort_order
= talloc_strdup(config
, sort_order
);
1795 if (config
->sort_order
== NULL
) {
1796 DEBUG(0, ("talloc_strdup() failed\n"));
1801 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1802 "shadow", "mountpoint", NULL
);
1803 if (mount_point
!= NULL
) {
1804 if (mount_point
[0] != '/') {
1805 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1806 "relative ('%s'), but it has to be an "
1807 "absolute path. Ignoring provided value.\n",
1812 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1813 if (p
!= handle
->conn
->connectpath
) {
1814 DEBUG(1, ("Warning: mount_point (%s) is not a "
1815 "subdirectory of the share root "
1816 "(%s). Ignoring provided value.\n",
1818 handle
->conn
->connectpath
));
1824 if (mount_point
!= NULL
) {
1825 config
->mount_point
= talloc_strdup(config
, mount_point
);
1826 if (config
->mount_point
== NULL
) {
1827 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1831 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1833 if (config
->mount_point
== NULL
) {
1834 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1835 " failed: %s\n", strerror(errno
)));
1840 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1841 "shadow", "basedir", NULL
);
1843 if (basedir
!= NULL
) {
1844 if (basedir
[0] != '/') {
1845 DEBUG(1, (__location__
" Warning: 'basedir' is "
1846 "relative ('%s'), but it has to be an "
1847 "absolute path. Disabling basedir.\n",
1851 p
= strstr(basedir
, config
->mount_point
);
1853 DEBUG(1, ("Warning: basedir (%s) is not a "
1854 "subdirectory of the share root's "
1855 "mount point (%s). "
1856 "Disabling basedir\n",
1857 basedir
, config
->mount_point
));
1859 config
->basedir
= talloc_strdup(config
,
1861 if (config
->basedir
== NULL
) {
1862 DEBUG(0, ("talloc_strdup() failed\n"));
1870 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1871 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1872 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1873 TALLOC_FREE(config
->basedir
);
1876 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1877 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1878 "with 'crossmountpoints'. Disabling basedir.\n"));
1879 TALLOC_FREE(config
->basedir
);
1882 if (config
->basedir
== NULL
) {
1883 config
->basedir
= config
->mount_point
;
1886 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1887 config
->rel_connectpath
= talloc_strdup(config
,
1888 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1889 if (config
->rel_connectpath
== NULL
) {
1890 DEBUG(0, ("talloc_strdup() failed\n"));
1896 if (config
->snapdir
[0] == '/') {
1897 config
->snapdir_absolute
= true;
1899 if (config
->snapdirseverywhere
== true) {
1900 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1901 "is incompatible with 'snapdirseverywhere', "
1902 "setting 'snapdirseverywhere' to false.\n"));
1903 config
->snapdirseverywhere
= false;
1906 if (config
->crossmountpoints
== true) {
1907 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1908 "is not supported with an absolute snapdir. "
1909 "Disabling it.\n"));
1910 config
->crossmountpoints
= false;
1913 config
->snapshot_basepath
= config
->snapdir
;
1915 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1916 config
->mount_point
, config
->snapdir
);
1917 if (config
->snapshot_basepath
== NULL
) {
1918 DEBUG(0, ("talloc_asprintf() failed\n"));
1924 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1925 " share root: '%s'\n"
1927 " mountpoint: '%s'\n"
1928 " rel share root: '%s'\n"
1930 " snapshot base path: '%s'\n"
1933 " snapdirs everywhere: %s\n"
1934 " cross mountpoints: %s\n"
1938 handle
->conn
->connectpath
,
1940 config
->mount_point
,
1941 config
->rel_connectpath
,
1943 config
->snapshot_basepath
,
1945 config
->use_sscanf
? "yes" : "no",
1946 config
->snapdirseverywhere
? "yes" : "no",
1947 config
->crossmountpoints
? "yes" : "no",
1948 config
->fixinodes
? "yes" : "no",
1953 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1954 NULL
, struct shadow_copy2_config
,
1960 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1961 .connect_fn
= shadow_copy2_connect
,
1962 .opendir_fn
= shadow_copy2_opendir
,
1963 .disk_free_fn
= shadow_copy2_disk_free
,
1964 .rename_fn
= shadow_copy2_rename
,
1965 .link_fn
= shadow_copy2_link
,
1966 .symlink_fn
= shadow_copy2_symlink
,
1967 .stat_fn
= shadow_copy2_stat
,
1968 .lstat_fn
= shadow_copy2_lstat
,
1969 .fstat_fn
= shadow_copy2_fstat
,
1970 .open_fn
= shadow_copy2_open
,
1971 .unlink_fn
= shadow_copy2_unlink
,
1972 .chmod_fn
= shadow_copy2_chmod
,
1973 .chown_fn
= shadow_copy2_chown
,
1974 .chdir_fn
= shadow_copy2_chdir
,
1975 .ntimes_fn
= shadow_copy2_ntimes
,
1976 .readlink_fn
= shadow_copy2_readlink
,
1977 .mknod_fn
= shadow_copy2_mknod
,
1978 .realpath_fn
= shadow_copy2_realpath
,
1979 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
1980 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
1981 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
1982 .mkdir_fn
= shadow_copy2_mkdir
,
1983 .rmdir_fn
= shadow_copy2_rmdir
,
1984 .getxattr_fn
= shadow_copy2_getxattr
,
1985 .listxattr_fn
= shadow_copy2_listxattr
,
1986 .removexattr_fn
= shadow_copy2_removexattr
,
1987 .setxattr_fn
= shadow_copy2_setxattr
,
1988 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
1989 .chflags_fn
= shadow_copy2_chflags
,
1990 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
1993 NTSTATUS
vfs_shadow_copy2_init(void);
1994 NTSTATUS
vfs_shadow_copy2_init(void)
1996 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1997 "shadow_copy2", &vfs_shadow_copy2_fns
);