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 DEBUG(10, ("converting '%s'\n", name
));
495 if (!config
->snapdirseverywhere
) {
499 snapshot_path
= shadow_copy2_snapshot_path(talloc_tos(),
502 if (snapshot_path
== NULL
) {
506 if (config
->rel_connectpath
== NULL
) {
507 converted
= talloc_asprintf(mem_ctx
, "%s/%s",
508 snapshot_path
, name
);
510 converted
= talloc_asprintf(mem_ctx
, "%s/%s/%s",
512 config
->rel_connectpath
,
515 if (converted
== NULL
) {
519 ZERO_STRUCT(converted_fname
);
520 converted_fname
.base_name
= converted
;
522 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
523 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
525 ret
, ret
== 0 ? "ok" : strerror(errno
)));
527 DEBUG(10, ("Found %s\n", converted
));
535 /* never reached ... */
538 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
544 pathlen
= talloc_get_size(path
)-1;
546 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
547 &slashes
, &num_slashes
)) {
551 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
552 if (insert
== NULL
) {
555 insertlen
= talloc_get_size(insert
)-1;
557 converted
= talloc_zero_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
558 if (converted
== NULL
) {
562 if (path
[pathlen
-1] != '/') {
564 * Append a fake slash to find the snapshot root
567 tmp
= talloc_realloc(talloc_tos(), slashes
,
568 size_t, num_slashes
+1);
573 slashes
[num_slashes
] = pathlen
;
579 if (!config
->crossmountpoints
) {
582 mount_point
= shadow_copy2_find_mount_point(talloc_tos(),
584 if (mount_point
== NULL
) {
587 min_offset
= strlen(mount_point
);
588 TALLOC_FREE(mount_point
);
591 memcpy(converted
, path
, pathlen
+1);
592 converted
[pathlen
+insertlen
] = '\0';
594 ZERO_STRUCT(converted_fname
);
595 converted_fname
.base_name
= converted
;
597 for (i
= num_slashes
-1; i
>=0; i
--) {
603 if (offset
< min_offset
) {
608 memcpy(converted
+offset
, insert
, insertlen
);
611 memcpy(converted
+offset
, path
+ slashes
[i
],
612 pathlen
- slashes
[i
]);
614 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
616 DEBUG(10, ("Trying %s: %d (%s)\n", converted
,
617 ret
, ret
== 0 ? "ok" : strerror(errno
)));
622 if (errno
== ENOTDIR
) {
624 * This is a valid condition: We appended the
625 * .snaphots/@GMT.. to a file name. Just try
626 * with the upper levels.
630 if (errno
!= ENOENT
) {
631 /* Other problem than "not found" */
640 DEBUG(10, ("Found %s\n", converted
));
648 TALLOC_FREE(converted
);
650 TALLOC_FREE(slashes
);
657 modify a sbuf return to ensure that inodes in the shadow directory
658 are different from those in the main directory
660 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
661 SMB_STRUCT_STAT
*sbuf
)
663 struct shadow_copy2_config
*config
;
665 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
668 if (config
->fixinodes
) {
669 /* some snapshot systems, like GPFS, return the name
670 device:inode for the snapshot files as the current
671 files. That breaks the 'restore' button in the shadow copy
672 GUI, as the client gets a sharing violation.
674 This is a crude way of allowing both files to be
675 open at once. It has a slight chance of inode
676 number collision, but I can't see a better approach
677 without significant VFS changes
681 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
685 sbuf
->st_ex_ino
^= shash
;
689 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
701 ×tamp
, &stripped
)) {
704 if (timestamp
== 0) {
705 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
707 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
708 TALLOC_FREE(stripped
);
712 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
719 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
720 const struct smb_filename
*smb_fname_src
,
721 const struct smb_filename
*smb_fname_dst
)
723 time_t timestamp_src
, timestamp_dst
;
725 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
726 smb_fname_src
->base_name
,
727 ×tamp_src
, NULL
)) {
730 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
731 smb_fname_dst
->base_name
,
732 ×tamp_dst
, NULL
)) {
735 if (timestamp_src
!= 0) {
739 if (timestamp_dst
!= 0) {
743 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
746 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
747 const char *oldname
, const char *newname
)
749 time_t timestamp_old
, timestamp_new
;
751 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
752 ×tamp_old
, NULL
)) {
755 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
756 ×tamp_new
, NULL
)) {
759 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
763 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
766 static int shadow_copy2_link(vfs_handle_struct
*handle
,
767 const char *oldname
, const char *newname
)
769 time_t timestamp_old
, timestamp_new
;
771 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
772 ×tamp_old
, NULL
)) {
775 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
776 ×tamp_new
, NULL
)) {
779 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
783 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
786 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
787 struct smb_filename
*smb_fname
)
790 char *stripped
, *tmp
;
791 int ret
, saved_errno
;
793 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
794 smb_fname
->base_name
,
795 ×tamp
, &stripped
)) {
798 if (timestamp
== 0) {
799 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
802 tmp
= smb_fname
->base_name
;
803 smb_fname
->base_name
= shadow_copy2_convert(
804 talloc_tos(), handle
, stripped
, timestamp
);
805 TALLOC_FREE(stripped
);
807 if (smb_fname
->base_name
== NULL
) {
808 smb_fname
->base_name
= tmp
;
812 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
815 TALLOC_FREE(smb_fname
->base_name
);
816 smb_fname
->base_name
= tmp
;
819 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
825 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
826 struct smb_filename
*smb_fname
)
829 char *stripped
, *tmp
;
830 int ret
, saved_errno
;
832 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
833 smb_fname
->base_name
,
834 ×tamp
, &stripped
)) {
837 if (timestamp
== 0) {
838 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
841 tmp
= smb_fname
->base_name
;
842 smb_fname
->base_name
= shadow_copy2_convert(
843 talloc_tos(), handle
, stripped
, timestamp
);
844 TALLOC_FREE(stripped
);
846 if (smb_fname
->base_name
== NULL
) {
847 smb_fname
->base_name
= tmp
;
851 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
854 TALLOC_FREE(smb_fname
->base_name
);
855 smb_fname
->base_name
= tmp
;
858 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
864 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
865 SMB_STRUCT_STAT
*sbuf
)
870 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
874 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
875 fsp
->fsp_name
->base_name
,
879 if (timestamp
!= 0) {
880 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
885 static int shadow_copy2_open(vfs_handle_struct
*handle
,
886 struct smb_filename
*smb_fname
, files_struct
*fsp
,
887 int flags
, mode_t mode
)
890 char *stripped
, *tmp
;
891 int ret
, saved_errno
;
893 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
894 smb_fname
->base_name
,
895 ×tamp
, &stripped
)) {
898 if (timestamp
== 0) {
899 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
902 tmp
= smb_fname
->base_name
;
903 smb_fname
->base_name
= shadow_copy2_convert(
904 talloc_tos(), handle
, stripped
, timestamp
);
905 TALLOC_FREE(stripped
);
907 if (smb_fname
->base_name
== NULL
) {
908 smb_fname
->base_name
= tmp
;
912 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
915 TALLOC_FREE(smb_fname
->base_name
);
916 smb_fname
->base_name
= tmp
;
922 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
923 const struct smb_filename
*smb_fname
)
927 int ret
, saved_errno
;
928 struct smb_filename
*conv
;
930 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
931 smb_fname
->base_name
,
932 ×tamp
, &stripped
)) {
935 if (timestamp
== 0) {
936 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
938 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
943 conv
->base_name
= shadow_copy2_convert(
944 conv
, handle
, stripped
, timestamp
);
945 TALLOC_FREE(stripped
);
946 if (conv
->base_name
== NULL
) {
949 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
956 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
961 int ret
, saved_errno
;
964 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
965 ×tamp
, &stripped
)) {
968 if (timestamp
== 0) {
969 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
971 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
972 TALLOC_FREE(stripped
);
976 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
983 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
984 uid_t uid
, gid_t gid
)
988 int ret
, saved_errno
;
991 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
992 ×tamp
, &stripped
)) {
995 if (timestamp
== 0) {
996 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
998 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
999 TALLOC_FREE(stripped
);
1003 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1004 saved_errno
= errno
;
1006 errno
= saved_errno
;
1010 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
1015 int ret
, saved_errno
;
1018 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1019 ×tamp
, &stripped
)) {
1022 if (timestamp
== 0) {
1023 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1025 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1026 TALLOC_FREE(stripped
);
1030 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1031 saved_errno
= errno
;
1033 errno
= saved_errno
;
1037 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
1038 const struct smb_filename
*smb_fname
,
1039 struct smb_file_time
*ft
)
1043 int ret
, saved_errno
;
1044 struct smb_filename
*conv
;
1046 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1047 smb_fname
->base_name
,
1048 ×tamp
, &stripped
)) {
1051 if (timestamp
== 0) {
1052 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1054 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1059 conv
->base_name
= shadow_copy2_convert(
1060 conv
, handle
, stripped
, timestamp
);
1061 TALLOC_FREE(stripped
);
1062 if (conv
->base_name
== NULL
) {
1065 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1066 saved_errno
= errno
;
1068 errno
= saved_errno
;
1072 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
1073 const char *fname
, char *buf
, size_t bufsiz
)
1077 int ret
, saved_errno
;
1080 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1081 ×tamp
, &stripped
)) {
1084 if (timestamp
== 0) {
1085 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1087 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1088 TALLOC_FREE(stripped
);
1092 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1093 saved_errno
= errno
;
1095 errno
= saved_errno
;
1099 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
1100 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1104 int ret
, saved_errno
;
1107 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1108 ×tamp
, &stripped
)) {
1111 if (timestamp
== 0) {
1112 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1114 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1115 TALLOC_FREE(stripped
);
1119 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1120 saved_errno
= errno
;
1122 errno
= saved_errno
;
1126 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
1130 char *stripped
= NULL
;
1132 char *result
= NULL
;
1133 char *inserted
= NULL
;
1134 char *inserted_to
, *inserted_end
;
1137 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1138 ×tamp
, &stripped
)) {
1141 if (timestamp
== 0) {
1142 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1145 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1150 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1151 if (result
== NULL
) {
1156 * Take away what we've inserted. This removes the @GMT-thingy
1157 * completely, but will give a path under the share root.
1159 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
1160 if (inserted
== NULL
) {
1163 inserted_to
= strstr_m(result
, inserted
);
1164 if (inserted_to
== NULL
) {
1165 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
1168 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
1169 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
1172 saved_errno
= errno
;
1173 TALLOC_FREE(inserted
);
1175 TALLOC_FREE(stripped
);
1176 errno
= saved_errno
;
1181 * Check whether a given directory contains a
1182 * snapshot directory as direct subdirectory.
1183 * If yes, return the path of the snapshot-subdir,
1184 * otherwise return NULL.
1186 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1189 struct smb_filename smb_fname
;
1191 struct shadow_copy2_config
*config
;
1193 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1196 ZERO_STRUCT(smb_fname
);
1197 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1198 path
, config
->snapdir
);
1199 if (smb_fname
.base_name
== NULL
) {
1203 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1204 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1205 return smb_fname
.base_name
;
1207 TALLOC_FREE(smb_fname
.base_name
);
1212 * Find the snapshot directory (if any) for the given
1213 * filename (which is relative to the share).
1215 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1216 struct vfs_handle_struct
*handle
,
1217 struct smb_filename
*smb_fname
)
1220 const char *snapdir
;
1221 struct shadow_copy2_config
*config
;
1223 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1227 * If the non-snapdisrseverywhere mode, we should not search!
1229 if (!config
->snapdirseverywhere
) {
1230 return config
->snapshot_basepath
;
1233 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1234 handle
->conn
->connectpath
,
1235 smb_fname
->base_name
);
1240 snapdir
= have_snapdir(handle
, path
);
1241 if (snapdir
!= NULL
) {
1246 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1250 snapdir
= have_snapdir(handle
, path
);
1251 if (snapdir
!= NULL
) {
1260 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1262 char *gmt
, size_t gmt_len
)
1264 struct tm timestamp
;
1266 unsigned long int timestamp_long
;
1268 struct shadow_copy2_config
*config
;
1270 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1273 fmt
= config
->gmt_format
;
1275 ZERO_STRUCT(timestamp
);
1276 if (config
->use_sscanf
) {
1277 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1278 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1279 "no sscanf match %s: %s\n",
1283 timestamp_t
= timestamp_long
;
1284 gmtime_r(×tamp_t
, ×tamp
);
1286 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1287 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1288 "no match %s: %s\n",
1292 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1295 if (config
->use_localtime
) {
1296 timestamp
.tm_isdst
= -1;
1297 timestamp_t
= mktime(×tamp
);
1298 gmtime_r(×tamp_t
, ×tamp
);
1302 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1306 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1308 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1311 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1313 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1317 sort the shadow copy data in ascending or descending order
1319 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1320 struct shadow_copy_data
*shadow_copy2_data
)
1322 int (*cmpfunc
)(const void *, const void *);
1324 struct shadow_copy2_config
*config
;
1326 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1329 sort
= config
->sort_order
;
1334 if (strcmp(sort
, "asc") == 0) {
1335 cmpfunc
= shadow_copy2_label_cmp_asc
;
1336 } else if (strcmp(sort
, "desc") == 0) {
1337 cmpfunc
= shadow_copy2_label_cmp_desc
;
1342 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1343 shadow_copy2_data
->labels
)
1345 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1346 shadow_copy2_data
->num_volumes
,
1351 static int shadow_copy2_get_shadow_copy_data(
1352 vfs_handle_struct
*handle
, files_struct
*fsp
,
1353 struct shadow_copy_data
*shadow_copy2_data
,
1357 const char *snapdir
;
1359 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1361 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1362 if (snapdir
== NULL
) {
1363 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1364 handle
->conn
->connectpath
));
1366 talloc_free(tmp_ctx
);
1370 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1373 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1374 " - %s\n", snapdir
, strerror(errno
)));
1375 talloc_free(tmp_ctx
);
1380 shadow_copy2_data
->num_volumes
= 0;
1381 shadow_copy2_data
->labels
= NULL
;
1383 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1384 char snapshot
[GMT_NAME_LEN
+1];
1385 SHADOW_COPY_LABEL
*tlabels
;
1388 * ignore names not of the right form in the snapshot
1391 if (!shadow_copy2_snapshot_to_gmt(
1393 snapshot
, sizeof(snapshot
))) {
1395 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1396 "ignoring %s\n", d
->d_name
));
1399 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1400 d
->d_name
, snapshot
));
1403 /* the caller doesn't want the labels */
1404 shadow_copy2_data
->num_volumes
++;
1408 tlabels
= talloc_realloc(shadow_copy2_data
,
1409 shadow_copy2_data
->labels
,
1411 shadow_copy2_data
->num_volumes
+1);
1412 if (tlabels
== NULL
) {
1413 DEBUG(0,("shadow_copy2: out of memory\n"));
1414 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1415 talloc_free(tmp_ctx
);
1419 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1422 shadow_copy2_data
->num_volumes
++;
1423 shadow_copy2_data
->labels
= tlabels
;
1426 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1428 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1430 talloc_free(tmp_ctx
);
1434 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1435 struct files_struct
*fsp
,
1436 uint32 security_info
,
1437 TALLOC_CTX
*mem_ctx
,
1438 struct security_descriptor
**ppdesc
)
1445 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1446 fsp
->fsp_name
->base_name
,
1447 ×tamp
, &stripped
)) {
1448 return map_nt_error_from_unix(errno
);
1450 if (timestamp
== 0) {
1451 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1455 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1456 TALLOC_FREE(stripped
);
1458 return map_nt_error_from_unix(errno
);
1460 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1466 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1468 uint32 security_info
,
1469 TALLOC_CTX
*mem_ctx
,
1470 struct security_descriptor
**ppdesc
)
1477 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1478 ×tamp
, &stripped
)) {
1479 return map_nt_error_from_unix(errno
);
1481 if (timestamp
== 0) {
1482 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1485 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1486 TALLOC_FREE(stripped
);
1488 return map_nt_error_from_unix(errno
);
1490 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1496 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1497 const char *fname
, mode_t mode
)
1501 int ret
, saved_errno
;
1504 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1505 ×tamp
, &stripped
)) {
1508 if (timestamp
== 0) {
1509 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1511 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1512 TALLOC_FREE(stripped
);
1516 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1517 saved_errno
= errno
;
1519 errno
= saved_errno
;
1523 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1527 int ret
, saved_errno
;
1530 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1531 ×tamp
, &stripped
)) {
1534 if (timestamp
== 0) {
1535 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1537 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1538 TALLOC_FREE(stripped
);
1542 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1543 saved_errno
= errno
;
1545 errno
= saved_errno
;
1549 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1554 int ret
, saved_errno
;
1557 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1558 ×tamp
, &stripped
)) {
1561 if (timestamp
== 0) {
1562 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1564 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1565 TALLOC_FREE(stripped
);
1569 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1570 saved_errno
= errno
;
1572 errno
= saved_errno
;
1576 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1577 const char *fname
, const char *aname
,
1578 void *value
, size_t size
)
1586 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1587 ×tamp
, &stripped
)) {
1590 if (timestamp
== 0) {
1591 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1594 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1595 TALLOC_FREE(stripped
);
1599 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1600 saved_errno
= errno
;
1602 errno
= saved_errno
;
1606 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1608 char *list
, size_t size
)
1616 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1617 ×tamp
, &stripped
)) {
1620 if (timestamp
== 0) {
1621 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1623 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1624 TALLOC_FREE(stripped
);
1628 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1629 saved_errno
= errno
;
1631 errno
= saved_errno
;
1635 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1636 const char *fname
, const char *aname
)
1640 int ret
, saved_errno
;
1643 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1644 ×tamp
, &stripped
)) {
1647 if (timestamp
== 0) {
1648 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1650 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1651 TALLOC_FREE(stripped
);
1655 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1656 saved_errno
= errno
;
1658 errno
= saved_errno
;
1662 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1664 const char *aname
, const void *value
,
1665 size_t size
, int flags
)
1673 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1674 ×tamp
, &stripped
)) {
1677 if (timestamp
== 0) {
1678 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1681 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1682 TALLOC_FREE(stripped
);
1686 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1687 saved_errno
= errno
;
1689 errno
= saved_errno
;
1693 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1694 const char *fname
, mode_t mode
)
1702 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1703 ×tamp
, &stripped
)) {
1706 if (timestamp
== 0) {
1707 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1709 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1710 TALLOC_FREE(stripped
);
1714 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1715 saved_errno
= errno
;
1717 errno
= saved_errno
;
1721 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1724 TALLOC_CTX
*mem_ctx
,
1733 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1734 ×tamp
, &stripped
)) {
1737 if (timestamp
== 0) {
1738 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1739 mem_ctx
, found_name
);
1741 if (stripped
[0] == '\0') {
1742 *found_name
= talloc_strdup(mem_ctx
, name
);
1743 if (*found_name
== NULL
) {
1749 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1750 TALLOC_FREE(stripped
);
1754 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1755 mem_ctx
, found_name
);
1756 saved_errno
= errno
;
1758 errno
= saved_errno
;
1762 static uint64_t shadow_copy2_disk_free(vfs_handle_struct
*handle
,
1763 const char *path
, bool small_query
,
1764 uint64_t *bsize
, uint64_t *dfree
,
1773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1774 ×tamp
, &stripped
)) {
1777 if (timestamp
== 0) {
1778 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1779 bsize
, dfree
, dsize
);
1782 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1783 TALLOC_FREE(stripped
);
1788 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1791 saved_errno
= errno
;
1793 errno
= saved_errno
;
1798 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1799 const char *service
, const char *user
)
1801 struct shadow_copy2_config
*config
;
1803 const char *snapdir
;
1804 const char *gmt_format
;
1805 const char *sort_order
;
1806 const char *basedir
;
1807 const char *mount_point
;
1809 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1810 (unsigned)handle
->conn
->cnum
,
1811 handle
->conn
->connectpath
));
1813 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1818 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1819 if (config
== NULL
) {
1820 DEBUG(0, ("talloc_zero() failed\n"));
1825 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1828 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1829 if (config
->gmt_format
== NULL
) {
1830 DEBUG(0, ("talloc_strdup() failed\n"));
1835 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1836 "shadow", "sscanf", false);
1838 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1839 "shadow", "localtime",
1842 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1843 "shadow", "snapdir",
1845 config
->snapdir
= talloc_strdup(config
, snapdir
);
1846 if (config
->snapdir
== NULL
) {
1847 DEBUG(0, ("talloc_strdup() failed\n"));
1852 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1854 "snapdirseverywhere",
1857 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1858 "shadow", "crossmountpoints",
1861 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1862 "shadow", "fixinodes",
1865 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1866 "shadow", "sort", "desc");
1867 config
->sort_order
= talloc_strdup(config
, sort_order
);
1868 if (config
->sort_order
== NULL
) {
1869 DEBUG(0, ("talloc_strdup() failed\n"));
1874 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1875 "shadow", "mountpoint", NULL
);
1876 if (mount_point
!= NULL
) {
1877 if (mount_point
[0] != '/') {
1878 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1879 "relative ('%s'), but it has to be an "
1880 "absolute path. Ignoring provided value.\n",
1885 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1886 if (p
!= handle
->conn
->connectpath
) {
1887 DEBUG(1, ("Warning: mount_point (%s) is not a "
1888 "subdirectory of the share root "
1889 "(%s). Ignoring provided value.\n",
1891 handle
->conn
->connectpath
));
1897 if (mount_point
!= NULL
) {
1898 config
->mount_point
= talloc_strdup(config
, mount_point
);
1899 if (config
->mount_point
== NULL
) {
1900 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1904 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1906 if (config
->mount_point
== NULL
) {
1907 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1908 " failed: %s\n", strerror(errno
)));
1913 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1914 "shadow", "basedir", NULL
);
1916 if (basedir
!= NULL
) {
1917 if (basedir
[0] != '/') {
1918 DEBUG(1, (__location__
" Warning: 'basedir' is "
1919 "relative ('%s'), but it has to be an "
1920 "absolute path. Disabling basedir.\n",
1924 p
= strstr(basedir
, config
->mount_point
);
1926 DEBUG(1, ("Warning: basedir (%s) is not a "
1927 "subdirectory of the share root's "
1928 "mount point (%s). "
1929 "Disabling basedir\n",
1930 basedir
, config
->mount_point
));
1932 config
->basedir
= talloc_strdup(config
,
1934 if (config
->basedir
== NULL
) {
1935 DEBUG(0, ("talloc_strdup() failed\n"));
1943 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1944 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1945 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1946 TALLOC_FREE(config
->basedir
);
1949 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1950 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1951 "with 'crossmountpoints'. Disabling basedir.\n"));
1952 TALLOC_FREE(config
->basedir
);
1955 if (config
->basedir
== NULL
) {
1956 config
->basedir
= config
->mount_point
;
1959 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1960 config
->rel_connectpath
= talloc_strdup(config
,
1961 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1962 if (config
->rel_connectpath
== NULL
) {
1963 DEBUG(0, ("talloc_strdup() failed\n"));
1969 if (config
->snapdir
[0] == '/') {
1970 config
->snapdir_absolute
= true;
1972 if (config
->snapdirseverywhere
== true) {
1973 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1974 "is incompatible with 'snapdirseverywhere', "
1975 "setting 'snapdirseverywhere' to false.\n"));
1976 config
->snapdirseverywhere
= false;
1979 if (config
->crossmountpoints
== true) {
1980 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1981 "is not supported with an absolute snapdir. "
1982 "Disabling it.\n"));
1983 config
->crossmountpoints
= false;
1986 config
->snapshot_basepath
= config
->snapdir
;
1988 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1989 config
->mount_point
, config
->snapdir
);
1990 if (config
->snapshot_basepath
== NULL
) {
1991 DEBUG(0, ("talloc_asprintf() failed\n"));
1997 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1998 " share root: '%s'\n"
2000 " mountpoint: '%s'\n"
2001 " rel share root: '%s'\n"
2003 " snapshot base path: '%s'\n"
2006 " snapdirs everywhere: %s\n"
2007 " cross mountpoints: %s\n"
2011 handle
->conn
->connectpath
,
2013 config
->mount_point
,
2014 config
->rel_connectpath
,
2016 config
->snapshot_basepath
,
2018 config
->use_sscanf
? "yes" : "no",
2019 config
->snapdirseverywhere
? "yes" : "no",
2020 config
->crossmountpoints
? "yes" : "no",
2021 config
->fixinodes
? "yes" : "no",
2026 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
2027 NULL
, struct shadow_copy2_config
,
2033 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
2034 .connect_fn
= shadow_copy2_connect
,
2035 .opendir_fn
= shadow_copy2_opendir
,
2036 .disk_free_fn
= shadow_copy2_disk_free
,
2037 .rename_fn
= shadow_copy2_rename
,
2038 .link_fn
= shadow_copy2_link
,
2039 .symlink_fn
= shadow_copy2_symlink
,
2040 .stat_fn
= shadow_copy2_stat
,
2041 .lstat_fn
= shadow_copy2_lstat
,
2042 .fstat_fn
= shadow_copy2_fstat
,
2043 .open_fn
= shadow_copy2_open
,
2044 .unlink_fn
= shadow_copy2_unlink
,
2045 .chmod_fn
= shadow_copy2_chmod
,
2046 .chown_fn
= shadow_copy2_chown
,
2047 .chdir_fn
= shadow_copy2_chdir
,
2048 .ntimes_fn
= shadow_copy2_ntimes
,
2049 .readlink_fn
= shadow_copy2_readlink
,
2050 .mknod_fn
= shadow_copy2_mknod
,
2051 .realpath_fn
= shadow_copy2_realpath
,
2052 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
2053 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
2054 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
2055 .mkdir_fn
= shadow_copy2_mkdir
,
2056 .rmdir_fn
= shadow_copy2_rmdir
,
2057 .getxattr_fn
= shadow_copy2_getxattr
,
2058 .listxattr_fn
= shadow_copy2_listxattr
,
2059 .removexattr_fn
= shadow_copy2_removexattr
,
2060 .setxattr_fn
= shadow_copy2_setxattr
,
2061 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
2062 .chflags_fn
= shadow_copy2_chflags
,
2063 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
2066 NTSTATUS
vfs_shadow_copy2_init(void);
2067 NTSTATUS
vfs_shadow_copy2_init(void)
2069 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2070 "shadow_copy2", &vfs_shadow_copy2_fns
);