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
));
388 snapshot_path
= shadow_copy2_snapshot_path(talloc_tos(),
391 if (snapshot_path
== NULL
) {
396 DEBUG(10, (__location__
" path: '%s'.\n"
397 "snapshot path: '%s'\n", name
, snapshot_path
));
399 s
= strstr(name
, snapshot_path
);
402 * this starts with "snapshot_basepath/GMT-Token"
403 * so it is already a converted absolute
404 * path. Don't process further.
406 DEBUG(10, (__location__
": path '%s' starts with "
407 "snapshot path '%s' (not in "
408 "snapdirseverywhere mode) ==> "
409 "already converted\n", name
, snapshot_path
));
410 talloc_free(snapshot_path
);
413 talloc_free(snapshot_path
);
416 if (pstripped
!= NULL
) {
417 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
418 if (stripped
== NULL
) {
423 memcpy(stripped
, name
, p
-name
);
426 memcpy(stripped
+ (p
-name
), q
, rest_len
);
428 stripped
[dst_len
] = '\0';
429 *pstripped
= stripped
;
431 *ptimestamp
= timestamp
;
438 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
439 vfs_handle_struct
*handle
)
441 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
446 if (stat(path
, &st
) != 0) {
453 while ((p
= strrchr(path
, '/')) && p
> path
) {
455 if (stat(path
, &st
) != 0) {
459 if (st
.st_dev
!= dev
) {
469 * Convert from a name as handed in via the SMB layer
470 * and a timestamp into the local path of the snapshot
471 * of the provided file at the provided time.
473 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
474 struct vfs_handle_struct
*handle
,
475 const char *name
, time_t timestamp
)
477 struct smb_filename converted_fname
;
479 size_t *slashes
= NULL
;
480 unsigned num_slashes
;
484 char *converted
= NULL
;
488 struct shadow_copy2_config
*config
;
490 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
493 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
499 pathlen
= talloc_get_size(path
)-1;
501 DEBUG(10, ("converting %s\n", path
));
503 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
504 &slashes
, &num_slashes
)) {
508 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
509 if (insert
== NULL
) {
512 insertlen
= talloc_get_size(insert
)-1;
514 converted
= talloc_zero_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
515 if (converted
== NULL
) {
519 if (path
[pathlen
-1] != '/') {
521 * Append a fake slash to find the snapshot root
524 tmp
= talloc_realloc(talloc_tos(), slashes
,
525 size_t, num_slashes
+1);
530 slashes
[num_slashes
] = pathlen
;
536 if (!config
->crossmountpoints
) {
539 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
541 if (mount_point
== NULL
) {
544 min_offset
= strlen(mount_point
);
545 TALLOC_FREE(mount_point
);
548 memcpy(converted
, path
, pathlen
+1);
549 converted
[pathlen
+insertlen
] = '\0';
551 ZERO_STRUCT(converted_fname
);
552 converted_fname
.base_name
= converted
;
554 for (i
= num_slashes
-1; i
>=0; i
--) {
560 if (offset
< min_offset
) {
565 memcpy(converted
+offset
, insert
, insertlen
);
568 memcpy(converted
+offset
, path
+ slashes
[i
],
569 pathlen
- slashes
[i
]);
571 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
573 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
574 ret
, ret
== 0 ? "ok" : strerror(errno
)));
579 if (errno
== ENOTDIR
) {
581 * This is a valid condition: We appended the
582 * .snaphots/@GMT.. to a file name. Just try
583 * with the upper levels.
587 if (errno
!= ENOENT
) {
588 /* Other problem than "not found" */
597 DEBUG(10, ("Found %s\n", converted
));
605 TALLOC_FREE(converted
);
607 TALLOC_FREE(slashes
);
614 modify a sbuf return to ensure that inodes in the shadow directory
615 are different from those in the main directory
617 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
618 SMB_STRUCT_STAT
*sbuf
)
620 struct shadow_copy2_config
*config
;
622 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
625 if (config
->fixinodes
) {
626 /* some snapshot systems, like GPFS, return the name
627 device:inode for the snapshot files as the current
628 files. That breaks the 'restore' button in the shadow copy
629 GUI, as the client gets a sharing violation.
631 This is a crude way of allowing both files to be
632 open at once. It has a slight chance of inode
633 number collision, but I can't see a better approach
634 without significant VFS changes
638 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
642 sbuf
->st_ex_ino
^= shash
;
646 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
657 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
658 ×tamp
, &stripped
)) {
661 if (timestamp
== 0) {
662 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
664 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
665 TALLOC_FREE(stripped
);
669 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
676 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
677 const struct smb_filename
*smb_fname_src
,
678 const struct smb_filename
*smb_fname_dst
)
680 time_t timestamp_src
, timestamp_dst
;
682 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
683 smb_fname_src
->base_name
,
684 ×tamp_src
, NULL
)) {
687 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
688 smb_fname_dst
->base_name
,
689 ×tamp_dst
, NULL
)) {
692 if (timestamp_src
!= 0) {
696 if (timestamp_dst
!= 0) {
700 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
703 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
704 const char *oldname
, const char *newname
)
706 time_t timestamp_old
, timestamp_new
;
708 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
709 ×tamp_old
, NULL
)) {
712 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
713 ×tamp_new
, NULL
)) {
716 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
720 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
723 static int shadow_copy2_link(vfs_handle_struct
*handle
,
724 const char *oldname
, const char *newname
)
726 time_t timestamp_old
, timestamp_new
;
728 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
729 ×tamp_old
, NULL
)) {
732 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
733 ×tamp_new
, NULL
)) {
736 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
740 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
743 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
744 struct smb_filename
*smb_fname
)
747 char *stripped
, *tmp
;
748 int ret
, saved_errno
;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
751 smb_fname
->base_name
,
752 ×tamp
, &stripped
)) {
755 if (timestamp
== 0) {
756 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
759 tmp
= smb_fname
->base_name
;
760 smb_fname
->base_name
= shadow_copy2_convert(
761 talloc_tos(), handle
, stripped
, timestamp
);
762 TALLOC_FREE(stripped
);
764 if (smb_fname
->base_name
== NULL
) {
765 smb_fname
->base_name
= tmp
;
769 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
772 TALLOC_FREE(smb_fname
->base_name
);
773 smb_fname
->base_name
= tmp
;
776 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
782 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
783 struct smb_filename
*smb_fname
)
786 char *stripped
, *tmp
;
787 int ret
, saved_errno
;
789 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
790 smb_fname
->base_name
,
791 ×tamp
, &stripped
)) {
794 if (timestamp
== 0) {
795 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
798 tmp
= smb_fname
->base_name
;
799 smb_fname
->base_name
= shadow_copy2_convert(
800 talloc_tos(), handle
, stripped
, timestamp
);
801 TALLOC_FREE(stripped
);
803 if (smb_fname
->base_name
== NULL
) {
804 smb_fname
->base_name
= tmp
;
808 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
811 TALLOC_FREE(smb_fname
->base_name
);
812 smb_fname
->base_name
= tmp
;
815 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
821 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
822 SMB_STRUCT_STAT
*sbuf
)
827 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
831 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
832 fsp
->fsp_name
->base_name
,
836 if (timestamp
!= 0) {
837 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
842 static int shadow_copy2_open(vfs_handle_struct
*handle
,
843 struct smb_filename
*smb_fname
, files_struct
*fsp
,
844 int flags
, mode_t mode
)
847 char *stripped
, *tmp
;
848 int ret
, saved_errno
;
850 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
851 smb_fname
->base_name
,
852 ×tamp
, &stripped
)) {
855 if (timestamp
== 0) {
856 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
859 tmp
= smb_fname
->base_name
;
860 smb_fname
->base_name
= shadow_copy2_convert(
861 talloc_tos(), handle
, stripped
, timestamp
);
862 TALLOC_FREE(stripped
);
864 if (smb_fname
->base_name
== NULL
) {
865 smb_fname
->base_name
= tmp
;
869 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
872 TALLOC_FREE(smb_fname
->base_name
);
873 smb_fname
->base_name
= tmp
;
879 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
880 const struct smb_filename
*smb_fname
)
884 int ret
, saved_errno
;
885 struct smb_filename
*conv
;
888 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
889 smb_fname
->base_name
,
890 ×tamp
, &stripped
)) {
893 if (timestamp
== 0) {
894 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
896 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
897 if (!NT_STATUS_IS_OK(status
)) {
901 conv
->base_name
= shadow_copy2_convert(
902 conv
, handle
, stripped
, timestamp
);
903 TALLOC_FREE(stripped
);
904 if (conv
->base_name
== NULL
) {
907 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
914 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
919 int ret
, saved_errno
;
922 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
923 ×tamp
, &stripped
)) {
926 if (timestamp
== 0) {
927 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
929 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
930 TALLOC_FREE(stripped
);
934 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
941 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
942 uid_t uid
, gid_t gid
)
946 int ret
, saved_errno
;
949 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
950 ×tamp
, &stripped
)) {
953 if (timestamp
== 0) {
954 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
956 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
957 TALLOC_FREE(stripped
);
961 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
968 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
973 int ret
, saved_errno
;
976 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
977 ×tamp
, &stripped
)) {
980 if (timestamp
== 0) {
981 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
983 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
984 TALLOC_FREE(stripped
);
988 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
995 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
996 const struct smb_filename
*smb_fname
,
997 struct smb_file_time
*ft
)
1001 int ret
, saved_errno
;
1002 struct smb_filename
*conv
;
1005 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1006 smb_fname
->base_name
,
1007 ×tamp
, &stripped
)) {
1010 if (timestamp
== 0) {
1011 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1013 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
1014 if (!NT_STATUS_IS_OK(status
)) {
1018 conv
->base_name
= shadow_copy2_convert(
1019 conv
, handle
, stripped
, timestamp
);
1020 TALLOC_FREE(stripped
);
1021 if (conv
->base_name
== NULL
) {
1024 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1025 saved_errno
= errno
;
1027 errno
= saved_errno
;
1031 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
1032 const char *fname
, char *buf
, size_t bufsiz
)
1036 int ret
, saved_errno
;
1039 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1040 ×tamp
, &stripped
)) {
1043 if (timestamp
== 0) {
1044 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1046 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1047 TALLOC_FREE(stripped
);
1051 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1052 saved_errno
= errno
;
1054 errno
= saved_errno
;
1058 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
1059 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1063 int ret
, saved_errno
;
1066 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1067 ×tamp
, &stripped
)) {
1070 if (timestamp
== 0) {
1071 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1073 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1074 TALLOC_FREE(stripped
);
1078 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1079 saved_errno
= errno
;
1081 errno
= saved_errno
;
1085 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
1089 char *stripped
= NULL
;
1091 char *result
= NULL
;
1092 char *inserted
= NULL
;
1093 char *inserted_to
, *inserted_end
;
1096 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1097 ×tamp
, &stripped
)) {
1100 if (timestamp
== 0) {
1101 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1104 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1109 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1110 if (result
== NULL
) {
1115 * Take away what we've inserted. This removes the @GMT-thingy
1116 * completely, but will give a path under the share root.
1118 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
1119 if (inserted
== NULL
) {
1122 inserted_to
= strstr_m(result
, inserted
);
1123 if (inserted_to
== NULL
) {
1124 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
1127 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
1128 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
1131 saved_errno
= errno
;
1132 TALLOC_FREE(inserted
);
1134 TALLOC_FREE(stripped
);
1135 errno
= saved_errno
;
1140 * Check whether a given directory contains a
1141 * snapshot directory as direct subdirectory.
1142 * If yes, return the path of the snapshot-subdir,
1143 * otherwise return NULL.
1145 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1148 struct smb_filename smb_fname
;
1150 struct shadow_copy2_config
*config
;
1152 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1155 ZERO_STRUCT(smb_fname
);
1156 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1157 path
, config
->snapdir
);
1158 if (smb_fname
.base_name
== NULL
) {
1162 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1163 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1164 return smb_fname
.base_name
;
1166 TALLOC_FREE(smb_fname
.base_name
);
1171 * Find the snapshot directory (if any) for the given
1172 * filename (which is relative to the share).
1174 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1175 struct vfs_handle_struct
*handle
,
1176 struct smb_filename
*smb_fname
)
1179 const char *snapdir
;
1180 struct shadow_copy2_config
*config
;
1182 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1186 * If the non-snapdisrseverywhere mode, we should not search!
1188 if (!config
->snapdirseverywhere
) {
1189 return config
->snapshot_basepath
;
1192 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1193 handle
->conn
->connectpath
,
1194 smb_fname
->base_name
);
1199 snapdir
= have_snapdir(handle
, path
);
1200 if (snapdir
!= NULL
) {
1205 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1209 snapdir
= have_snapdir(handle
, path
);
1210 if (snapdir
!= NULL
) {
1219 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1221 char *gmt
, size_t gmt_len
)
1223 struct tm timestamp
;
1225 unsigned long int timestamp_long
;
1227 struct shadow_copy2_config
*config
;
1229 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1232 fmt
= config
->gmt_format
;
1234 ZERO_STRUCT(timestamp
);
1235 if (config
->use_sscanf
) {
1236 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1237 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1238 "no sscanf match %s: %s\n",
1242 timestamp_t
= timestamp_long
;
1243 gmtime_r(×tamp_t
, ×tamp
);
1245 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1246 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1247 "no match %s: %s\n",
1251 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1254 if (config
->use_localtime
) {
1255 timestamp
.tm_isdst
= -1;
1256 timestamp_t
= mktime(×tamp
);
1257 gmtime_r(×tamp_t
, ×tamp
);
1261 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1265 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1267 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1270 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1272 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1276 sort the shadow copy data in ascending or descending order
1278 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1279 struct shadow_copy_data
*shadow_copy2_data
)
1281 int (*cmpfunc
)(const void *, const void *);
1283 struct shadow_copy2_config
*config
;
1285 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1288 sort
= config
->sort_order
;
1293 if (strcmp(sort
, "asc") == 0) {
1294 cmpfunc
= shadow_copy2_label_cmp_asc
;
1295 } else if (strcmp(sort
, "desc") == 0) {
1296 cmpfunc
= shadow_copy2_label_cmp_desc
;
1301 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1302 shadow_copy2_data
->labels
)
1304 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1305 shadow_copy2_data
->num_volumes
,
1310 static int shadow_copy2_get_shadow_copy_data(
1311 vfs_handle_struct
*handle
, files_struct
*fsp
,
1312 struct shadow_copy_data
*shadow_copy2_data
,
1316 const char *snapdir
;
1318 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1320 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1321 if (snapdir
== NULL
) {
1322 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1323 handle
->conn
->connectpath
));
1325 talloc_free(tmp_ctx
);
1329 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1332 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1333 " - %s\n", snapdir
, strerror(errno
)));
1334 talloc_free(tmp_ctx
);
1339 shadow_copy2_data
->num_volumes
= 0;
1340 shadow_copy2_data
->labels
= NULL
;
1342 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1343 char snapshot
[GMT_NAME_LEN
+1];
1344 SHADOW_COPY_LABEL
*tlabels
;
1347 * ignore names not of the right form in the snapshot
1350 if (!shadow_copy2_snapshot_to_gmt(
1352 snapshot
, sizeof(snapshot
))) {
1354 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1355 "ignoring %s\n", d
->d_name
));
1358 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1359 d
->d_name
, snapshot
));
1362 /* the caller doesn't want the labels */
1363 shadow_copy2_data
->num_volumes
++;
1367 tlabels
= talloc_realloc(shadow_copy2_data
,
1368 shadow_copy2_data
->labels
,
1370 shadow_copy2_data
->num_volumes
+1);
1371 if (tlabels
== NULL
) {
1372 DEBUG(0,("shadow_copy2: out of memory\n"));
1373 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1374 talloc_free(tmp_ctx
);
1378 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1381 shadow_copy2_data
->num_volumes
++;
1382 shadow_copy2_data
->labels
= tlabels
;
1385 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1387 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1389 talloc_free(tmp_ctx
);
1393 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1394 struct files_struct
*fsp
,
1395 uint32 security_info
,
1396 TALLOC_CTX
*mem_ctx
,
1397 struct security_descriptor
**ppdesc
)
1404 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1405 fsp
->fsp_name
->base_name
,
1406 ×tamp
, &stripped
)) {
1407 return map_nt_error_from_unix(errno
);
1409 if (timestamp
== 0) {
1410 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1414 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1415 TALLOC_FREE(stripped
);
1417 return map_nt_error_from_unix(errno
);
1419 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1425 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1427 uint32 security_info
,
1428 TALLOC_CTX
*mem_ctx
,
1429 struct security_descriptor
**ppdesc
)
1436 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1437 ×tamp
, &stripped
)) {
1438 return map_nt_error_from_unix(errno
);
1440 if (timestamp
== 0) {
1441 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1444 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1445 TALLOC_FREE(stripped
);
1447 return map_nt_error_from_unix(errno
);
1449 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1455 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1456 const char *fname
, mode_t mode
)
1460 int ret
, saved_errno
;
1463 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1464 ×tamp
, &stripped
)) {
1467 if (timestamp
== 0) {
1468 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1470 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1471 TALLOC_FREE(stripped
);
1475 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1476 saved_errno
= errno
;
1478 errno
= saved_errno
;
1482 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1486 int ret
, saved_errno
;
1489 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1490 ×tamp
, &stripped
)) {
1493 if (timestamp
== 0) {
1494 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1496 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1497 TALLOC_FREE(stripped
);
1501 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1502 saved_errno
= errno
;
1504 errno
= saved_errno
;
1508 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1513 int ret
, saved_errno
;
1516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1517 ×tamp
, &stripped
)) {
1520 if (timestamp
== 0) {
1521 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1523 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1524 TALLOC_FREE(stripped
);
1528 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1529 saved_errno
= errno
;
1531 errno
= saved_errno
;
1535 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1536 const char *fname
, const char *aname
,
1537 void *value
, size_t size
)
1545 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1546 ×tamp
, &stripped
)) {
1549 if (timestamp
== 0) {
1550 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1553 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1554 TALLOC_FREE(stripped
);
1558 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1559 saved_errno
= errno
;
1561 errno
= saved_errno
;
1565 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1567 char *list
, size_t size
)
1575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1576 ×tamp
, &stripped
)) {
1579 if (timestamp
== 0) {
1580 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1582 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1583 TALLOC_FREE(stripped
);
1587 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1588 saved_errno
= errno
;
1590 errno
= saved_errno
;
1594 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1595 const char *fname
, const char *aname
)
1599 int ret
, saved_errno
;
1602 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1603 ×tamp
, &stripped
)) {
1606 if (timestamp
== 0) {
1607 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1609 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1610 TALLOC_FREE(stripped
);
1614 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1615 saved_errno
= errno
;
1617 errno
= saved_errno
;
1621 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1623 const char *aname
, const void *value
,
1624 size_t size
, int flags
)
1632 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1633 ×tamp
, &stripped
)) {
1636 if (timestamp
== 0) {
1637 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1640 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1641 TALLOC_FREE(stripped
);
1645 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1646 saved_errno
= errno
;
1648 errno
= saved_errno
;
1652 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1653 const char *fname
, mode_t mode
)
1661 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1662 ×tamp
, &stripped
)) {
1665 if (timestamp
== 0) {
1666 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1668 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1669 TALLOC_FREE(stripped
);
1673 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1674 saved_errno
= errno
;
1676 errno
= saved_errno
;
1680 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1683 TALLOC_CTX
*mem_ctx
,
1692 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1693 ×tamp
, &stripped
)) {
1696 if (timestamp
== 0) {
1697 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1698 mem_ctx
, found_name
);
1700 if (stripped
[0] == '\0') {
1701 *found_name
= talloc_strdup(mem_ctx
, name
);
1702 if (*found_name
== NULL
) {
1708 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1709 TALLOC_FREE(stripped
);
1713 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1714 mem_ctx
, found_name
);
1715 saved_errno
= errno
;
1717 errno
= saved_errno
;
1721 static uint64_t shadow_copy2_disk_free(vfs_handle_struct
*handle
,
1722 const char *path
, bool small_query
,
1723 uint64_t *bsize
, uint64_t *dfree
,
1732 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1733 ×tamp
, &stripped
)) {
1736 if (timestamp
== 0) {
1737 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1738 bsize
, dfree
, dsize
);
1741 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1742 TALLOC_FREE(stripped
);
1747 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1750 saved_errno
= errno
;
1752 errno
= saved_errno
;
1757 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1758 const char *service
, const char *user
)
1760 struct shadow_copy2_config
*config
;
1762 const char *snapdir
;
1763 const char *gmt_format
;
1764 const char *sort_order
;
1765 const char *basedir
;
1766 const char *mount_point
;
1768 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1769 (unsigned)handle
->conn
->cnum
,
1770 handle
->conn
->connectpath
));
1772 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1777 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1778 if (config
== NULL
) {
1779 DEBUG(0, ("talloc_zero() failed\n"));
1784 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1787 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1788 if (config
->gmt_format
== NULL
) {
1789 DEBUG(0, ("talloc_strdup() failed\n"));
1794 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1795 "shadow", "sscanf", false);
1797 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1798 "shadow", "localtime",
1801 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1802 "shadow", "snapdir",
1804 config
->snapdir
= talloc_strdup(config
, snapdir
);
1805 if (config
->snapdir
== NULL
) {
1806 DEBUG(0, ("talloc_strdup() failed\n"));
1811 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1813 "snapdirseverywhere",
1816 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1817 "shadow", "crossmountpoints",
1820 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1821 "shadow", "fixinodes",
1824 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1825 "shadow", "sort", "desc");
1826 config
->sort_order
= talloc_strdup(config
, sort_order
);
1827 if (config
->sort_order
== NULL
) {
1828 DEBUG(0, ("talloc_strdup() failed\n"));
1833 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1834 "shadow", "mountpoint", NULL
);
1835 if (mount_point
!= NULL
) {
1836 if (mount_point
[0] != '/') {
1837 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1838 "relative ('%s'), but it has to be an "
1839 "absolute path. Ignoring provided value.\n",
1844 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1845 if (p
!= handle
->conn
->connectpath
) {
1846 DEBUG(1, ("Warning: mount_point (%s) is not a "
1847 "subdirectory of the share root "
1848 "(%s). Ignoring provided value.\n",
1850 handle
->conn
->connectpath
));
1856 if (mount_point
!= NULL
) {
1857 config
->mount_point
= talloc_strdup(config
, mount_point
);
1858 if (config
->mount_point
== NULL
) {
1859 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1863 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1865 if (config
->mount_point
== NULL
) {
1866 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1867 " failed: %s\n", strerror(errno
)));
1872 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1873 "shadow", "basedir", NULL
);
1875 if (basedir
!= NULL
) {
1876 if (basedir
[0] != '/') {
1877 DEBUG(1, (__location__
" Warning: 'basedir' is "
1878 "relative ('%s'), but it has to be an "
1879 "absolute path. Disabling basedir.\n",
1883 p
= strstr(basedir
, config
->mount_point
);
1885 DEBUG(1, ("Warning: basedir (%s) is not a "
1886 "subdirectory of the share root's "
1887 "mount point (%s). "
1888 "Disabling basedir\n",
1889 basedir
, config
->mount_point
));
1891 config
->basedir
= talloc_strdup(config
,
1893 if (config
->basedir
== NULL
) {
1894 DEBUG(0, ("talloc_strdup() failed\n"));
1902 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1903 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1904 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1905 TALLOC_FREE(config
->basedir
);
1908 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1909 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1910 "with 'crossmountpoints'. Disabling basedir.\n"));
1911 TALLOC_FREE(config
->basedir
);
1914 if (config
->basedir
== NULL
) {
1915 config
->basedir
= config
->mount_point
;
1918 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1919 config
->rel_connectpath
= talloc_strdup(config
,
1920 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1921 if (config
->rel_connectpath
== NULL
) {
1922 DEBUG(0, ("talloc_strdup() failed\n"));
1928 if (config
->snapdir
[0] == '/') {
1929 config
->snapdir_absolute
= true;
1931 if (config
->snapdirseverywhere
== true) {
1932 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1933 "is incompatible with 'snapdirseverywhere', "
1934 "setting 'snapdirseverywhere' to false.\n"));
1935 config
->snapdirseverywhere
= false;
1938 if (config
->crossmountpoints
== true) {
1939 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1940 "is not supported with an absolute snapdir. "
1941 "Disabling it.\n"));
1942 config
->crossmountpoints
= false;
1945 config
->snapshot_basepath
= config
->snapdir
;
1947 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1948 config
->mount_point
, config
->snapdir
);
1949 if (config
->snapshot_basepath
== NULL
) {
1950 DEBUG(0, ("talloc_asprintf() failed\n"));
1956 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1957 " share root: '%s'\n"
1959 " mountpoint: '%s'\n"
1960 " rel share root: '%s'\n"
1962 " snapshot base path: '%s'\n"
1965 " snapdirs everywhere: %s\n"
1966 " cross mountpoints: %s\n"
1970 handle
->conn
->connectpath
,
1972 config
->mount_point
,
1973 config
->rel_connectpath
,
1975 config
->snapshot_basepath
,
1977 config
->use_sscanf
? "yes" : "no",
1978 config
->snapdirseverywhere
? "yes" : "no",
1979 config
->crossmountpoints
? "yes" : "no",
1980 config
->fixinodes
? "yes" : "no",
1985 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
1986 NULL
, struct shadow_copy2_config
,
1992 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
1993 .connect_fn
= shadow_copy2_connect
,
1994 .opendir_fn
= shadow_copy2_opendir
,
1995 .disk_free_fn
= shadow_copy2_disk_free
,
1996 .rename_fn
= shadow_copy2_rename
,
1997 .link_fn
= shadow_copy2_link
,
1998 .symlink_fn
= shadow_copy2_symlink
,
1999 .stat_fn
= shadow_copy2_stat
,
2000 .lstat_fn
= shadow_copy2_lstat
,
2001 .fstat_fn
= shadow_copy2_fstat
,
2002 .open_fn
= shadow_copy2_open
,
2003 .unlink_fn
= shadow_copy2_unlink
,
2004 .chmod_fn
= shadow_copy2_chmod
,
2005 .chown_fn
= shadow_copy2_chown
,
2006 .chdir_fn
= shadow_copy2_chdir
,
2007 .ntimes_fn
= shadow_copy2_ntimes
,
2008 .readlink_fn
= shadow_copy2_readlink
,
2009 .mknod_fn
= shadow_copy2_mknod
,
2010 .realpath_fn
= shadow_copy2_realpath
,
2011 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
2012 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
2013 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
2014 .mkdir_fn
= shadow_copy2_mkdir
,
2015 .rmdir_fn
= shadow_copy2_rmdir
,
2016 .getxattr_fn
= shadow_copy2_getxattr
,
2017 .listxattr_fn
= shadow_copy2_listxattr
,
2018 .removexattr_fn
= shadow_copy2_removexattr
,
2019 .setxattr_fn
= shadow_copy2_setxattr
,
2020 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
2021 .chflags_fn
= shadow_copy2_chflags
,
2022 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
2025 NTSTATUS
vfs_shadow_copy2_init(void);
2026 NTSTATUS
vfs_shadow_copy2_init(void)
2028 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2029 "shadow_copy2", &vfs_shadow_copy2_fns
);