2 * shadow_copy2: a shadow copy module (second implementation)
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
8 * Copyright (C) Michael Adam 2013
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 This is a 3rd implemetation of a shadow copy module for exposing
28 snapshots to windows clients as shadow copies. This version has the
31 1) you don't need to populate your shares with symlinks to the
32 snapshots. This can be very important when you have thousands of
33 shares, or use [homes]
35 2) the inode number of the files is altered so it is different
36 from the original. This allows the 'restore' button to work
37 without a sharing violation
39 3) shadow copy results can be sorted before being sent to the
40 client. This is beneficial for filesystems that don't read
41 directories alphabetically (the default unix).
43 4) vanity naming for snapshots. Snapshots can be named in any
44 format compatible with str[fp]time conversions.
46 5) time stamps in snapshot names can be represented in localtime
51 shadow:snapdir = <directory where snapshots are kept>
53 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
54 path it is used as-is. If it is a relative path, then it is taken relative to the mount
55 point of the filesystem that the root of this share is on
57 shadow:basedir = <base directory that snapshots are from>
59 This is an optional parameter that specifies the directory that
60 the snapshots are relative to. It defaults to the filesystem
63 shadow:fixinodes = yes/no
65 If you enable shadow:fixinodes then this module will modify the
66 apparent inode number of files in the snapshot directories using
67 a hash of the files path. This is needed for snapshot systems
68 where the snapshots have the same device:inode number as the
69 original files (such as happens with GPFS snapshots). If you
70 don't set this option then the 'restore' button in the shadow
71 copy UI will fail with a sharing violation.
73 shadow:sort = asc/desc, or not specified for unsorted (default)
75 This is an optional parameter that specifies that the shadow
76 copy directories should be sorted before sending them to the
77 client. This can be beneficial as unix filesystems are usually
78 not listed alphabetically sorted. If enabled, you typically
79 want to specify descending order.
81 shadow:format = <format specification for snapshot names>
83 This is an optional parameter that specifies the format
84 specification for the naming of snapshots. The format must
85 be compatible with the conversion specifications recognized
86 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
88 shadow:sscanf = yes/no (default is no)
90 The time is the unsigned long integer (%lu) in the format string
91 rather than a time strptime() can parse. The result must be a unix time_t
94 shadow:localtime = yes/no (default is no)
96 This is an optional parameter that indicates whether the
97 snapshot names are in UTC/GMT or the local time.
100 The following command would generate a correctly formatted directory name
101 for use with the default parameters:
102 date -u +@GMT-%Y.%m.%d-%H.%M.%S
105 #include "includes.h"
106 #include "system/filesys.h"
107 #include "include/ntioctl.h"
108 #include <ccan/hash/hash.h>
109 #include "util_tdb.h"
111 struct shadow_copy2_config
{
116 bool snapdirseverywhere
;
117 bool crossmountpoints
;
120 bool snapdir_absolute
;
123 char *rel_connectpath
; /* share root, relative to the basedir */
124 char *snapshot_basepath
; /* the absolute version of snapdir */
127 static bool shadow_copy2_find_slashes(TALLOC_CTX
*mem_ctx
, const char *str
,
129 unsigned *pnum_offsets
)
131 unsigned num_offsets
;
138 while ((p
= strchr(p
, '/')) != NULL
) {
143 offsets
= talloc_array(mem_ctx
, size_t, num_offsets
);
144 if (offsets
== NULL
) {
150 while ((p
= strchr(p
, '/')) != NULL
) {
151 offsets
[num_offsets
] = p
-str
;
157 *pnum_offsets
= num_offsets
;
162 * Given a timstamp, build the posix level GTM-tag string
163 * based on the configurable format.
165 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct
*handle
,
167 char *snaptime_string
,
172 struct shadow_copy2_config
*config
;
174 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
177 if (config
->use_sscanf
) {
178 snaptime_len
= snprintf(snaptime_string
,
181 (unsigned long)snapshot
);
182 if (snaptime_len
<= 0) {
183 DEBUG(10, ("snprintf failed\n"));
187 if (config
->use_localtime
) {
188 if (localtime_r(&snapshot
, &snap_tm
) == 0) {
189 DEBUG(10, ("gmtime_r failed\n"));
193 if (gmtime_r(&snapshot
, &snap_tm
) == 0) {
194 DEBUG(10, ("gmtime_r failed\n"));
198 snaptime_len
= strftime(snaptime_string
,
202 if (snaptime_len
== 0) {
203 DEBUG(10, ("strftime failed\n"));
212 * Given a timstamp, build the string to insert into a path
213 * as a path component for creating the local path to the
214 * snapshot at the given timestamp of the input path.
216 * In the case of a parallel snapdir (specified with an
217 * absolute path), this is the inital portion of the
218 * local path of any snapshot file. The complete path is
219 * obtained by appending the portion of the file's path
220 * below the share root's mountpoint.
222 static char *shadow_copy2_insert_string(TALLOC_CTX
*mem_ctx
,
223 struct vfs_handle_struct
*handle
,
226 fstring snaptime_string
;
227 size_t snaptime_len
= 0;
229 struct shadow_copy2_config
*config
;
231 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
234 snaptime_len
= shadow_copy2_posix_gmt_string(handle
,
237 sizeof(snaptime_string
));
238 if (snaptime_len
<= 0) {
242 if (config
->snapdir_absolute
) {
243 result
= talloc_asprintf(mem_ctx
, "%s/%s",
244 config
->snapdir
, snaptime_string
);
246 result
= talloc_asprintf(mem_ctx
, "/%s/%s",
247 config
->snapdir
, snaptime_string
);
249 if (result
== NULL
) {
250 DEBUG(1, (__location__
" talloc_asprintf failed\n"));
257 * Build the posix snapshot path for the connection
258 * at the given timestamp, i.e. the absolute posix path
259 * that contains the snapshot for this file system.
261 * This only applies to classical case, i.e. not
262 * to the "snapdirseverywhere" mode.
264 static char *shadow_copy2_snapshot_path(TALLOC_CTX
*mem_ctx
,
265 struct vfs_handle_struct
*handle
,
268 fstring snaptime_string
;
269 size_t snaptime_len
= 0;
271 struct shadow_copy2_config
*config
;
273 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
276 snaptime_len
= shadow_copy2_posix_gmt_string(handle
,
279 sizeof(snaptime_string
));
280 if (snaptime_len
<= 0) {
284 result
= talloc_asprintf(mem_ctx
, "%s/%s",
285 config
->snapshot_basepath
, snaptime_string
);
286 if (result
== NULL
) {
287 DEBUG(1, (__location__
" talloc_asprintf failed\n"));
294 * Strip a snapshot component from an filename as
295 * handed in via the smb layer.
296 * Returns the parsed timestamp and the stripped filename.
298 static bool shadow_copy2_strip_snapshot(TALLOC_CTX
*mem_ctx
,
299 struct vfs_handle_struct
*handle
,
309 size_t rest_len
, dst_len
;
310 struct shadow_copy2_config
*config
;
312 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
315 DEBUG(10, (__location__
": enter path '%s'\n", name
));
317 p
= strstr_m(name
, "@GMT-");
321 if ((p
> name
) && (p
[-1] != '/')) {
322 /* the GMT-token does not start a path-component */
325 q
= strptime(p
, GMT_FORMAT
, &tm
);
330 timestamp
= timegm(&tm
);
331 if (timestamp
== (time_t)-1) {
334 if ((p
== name
) && (q
[0] == '\0')) {
335 /* the name consists of only the GMT token */
336 if (pstripped
!= NULL
) {
337 stripped
= talloc_strdup(mem_ctx
, "");
338 if (stripped
== NULL
) {
341 *pstripped
= stripped
;
343 *ptimestamp
= timestamp
;
348 * The GMT token is either at the end of the path
349 * or it is not a complete path component, i.e. the
350 * path component continues after the gmt-token.
352 * TODO: Is this correct? Or would the GMT tag as the
353 * last component be a valid input?
359 rest_len
= strlen(q
);
360 dst_len
= (p
-name
) + rest_len
;
362 if (config
->snapdirseverywhere
) {
365 insert
= shadow_copy2_insert_string(talloc_tos(), handle
,
367 if (insert
== NULL
) {
372 DEBUG(10, (__location__
": snapdirseverywhere mode.\n"
374 "insert string '%s'\n", name
, insert
));
376 have_insert
= (strstr(name
, insert
+1) != NULL
);
378 DEBUG(10, (__location__
": insert string '%s' found in "
379 "path '%s' found in snapdirseverywhere mode "
380 "==> already converted\n", insert
, name
));
389 snapshot_path
= shadow_copy2_snapshot_path(talloc_tos(),
392 if (snapshot_path
== NULL
) {
397 DEBUG(10, (__location__
" path: '%s'.\n"
398 "snapshot path: '%s'\n", name
, snapshot_path
));
400 s
= strstr(name
, snapshot_path
);
403 * this starts with "snapshot_basepath/GMT-Token"
404 * so it is already a converted absolute
405 * path. Don't process further.
407 DEBUG(10, (__location__
": path '%s' starts with "
408 "snapshot path '%s' (not in "
409 "snapdirseverywhere mode) ==> "
410 "already converted\n", name
, snapshot_path
));
411 talloc_free(snapshot_path
);
414 talloc_free(snapshot_path
);
417 if (pstripped
!= NULL
) {
418 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
419 if (stripped
== NULL
) {
424 memcpy(stripped
, name
, p
-name
);
427 memcpy(stripped
+ (p
-name
), q
, rest_len
);
429 stripped
[dst_len
] = '\0';
430 *pstripped
= stripped
;
432 *ptimestamp
= timestamp
;
439 static char *shadow_copy2_find_mount_point(TALLOC_CTX
*mem_ctx
,
440 vfs_handle_struct
*handle
)
442 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
447 if (stat(path
, &st
) != 0) {
454 while ((p
= strrchr(path
, '/')) && p
> path
) {
456 if (stat(path
, &st
) != 0) {
460 if (st
.st_dev
!= dev
) {
470 * Convert from a name as handed in via the SMB layer
471 * and a timestamp into the local path of the snapshot
472 * of the provided file at the provided time.
474 static char *shadow_copy2_convert(TALLOC_CTX
*mem_ctx
,
475 struct vfs_handle_struct
*handle
,
476 const char *name
, time_t timestamp
)
478 struct smb_filename converted_fname
;
480 size_t *slashes
= NULL
;
481 unsigned num_slashes
;
485 char *converted
= NULL
;
489 struct shadow_copy2_config
*config
;
491 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
494 DEBUG(10, ("converting '%s'\n", name
));
496 if (!config
->snapdirseverywhere
) {
500 snapshot_path
= shadow_copy2_snapshot_path(talloc_tos(),
503 if (snapshot_path
== NULL
) {
507 if (config
->rel_connectpath
== NULL
) {
508 converted
= talloc_asprintf(mem_ctx
, "%s/%s",
509 snapshot_path
, name
);
511 converted
= talloc_asprintf(mem_ctx
, "%s/%s/%s",
513 config
->rel_connectpath
,
516 if (converted
== NULL
) {
520 ZERO_STRUCT(converted_fname
);
521 converted_fname
.base_name
= converted
;
523 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
524 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
526 ret
, ret
== 0 ? "ok" : strerror(errno
)));
528 DEBUG(10, ("Found %s\n", converted
));
536 /* never reached ... */
539 path
= talloc_asprintf(mem_ctx
, "%s/%s", handle
->conn
->connectpath
,
545 pathlen
= talloc_get_size(path
)-1;
547 if (!shadow_copy2_find_slashes(talloc_tos(), path
,
548 &slashes
, &num_slashes
)) {
552 insert
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
553 if (insert
== NULL
) {
556 insertlen
= talloc_get_size(insert
)-1;
558 converted
= talloc_zero_array(mem_ctx
, char, pathlen
+ insertlen
+ 1);
559 if (converted
== NULL
) {
563 if (path
[pathlen
-1] != '/') {
565 * Append a fake slash to find the snapshot root
568 tmp
= talloc_realloc(talloc_tos(), slashes
,
569 size_t, num_slashes
+1);
574 slashes
[num_slashes
] = pathlen
;
580 if (!config
->crossmountpoints
) {
581 min_offset
= strlen(config
->mount_point
);
584 memcpy(converted
, path
, pathlen
+1);
585 converted
[pathlen
+insertlen
] = '\0';
587 ZERO_STRUCT(converted_fname
);
588 converted_fname
.base_name
= converted
;
590 for (i
= num_slashes
-1; i
>=0; i
--) {
596 if (offset
< min_offset
) {
601 memcpy(converted
+offset
, insert
, insertlen
);
604 memcpy(converted
+offset
, path
+ slashes
[i
],
605 pathlen
- slashes
[i
]);
607 ret
= SMB_VFS_NEXT_LSTAT(handle
, &converted_fname
);
609 DEBUG(10, ("Trying[snapdirseverywhere] %s: %d (%s)\n",
611 ret
, ret
== 0 ? "ok" : strerror(errno
)));
616 if (errno
== ENOTDIR
) {
618 * This is a valid condition: We appended the
619 * .snaphots/@GMT.. to a file name. Just try
620 * with the upper levels.
624 if (errno
!= ENOENT
) {
625 /* Other problem than "not found" */
634 DEBUG(10, ("Found %s\n", converted
));
642 TALLOC_FREE(converted
);
644 TALLOC_FREE(slashes
);
651 modify a sbuf return to ensure that inodes in the shadow directory
652 are different from those in the main directory
654 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
,
655 SMB_STRUCT_STAT
*sbuf
)
657 struct shadow_copy2_config
*config
;
659 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
662 if (config
->fixinodes
) {
663 /* some snapshot systems, like GPFS, return the name
664 device:inode for the snapshot files as the current
665 files. That breaks the 'restore' button in the shadow copy
666 GUI, as the client gets a sharing violation.
668 This is a crude way of allowing both files to be
669 open at once. It has a slight chance of inode
670 number collision, but I can't see a better approach
671 without significant VFS changes
675 shash
= hash(fname
, strlen(fname
), 0) & 0xFF000000;
679 sbuf
->st_ex_ino
^= shash
;
683 static DIR *shadow_copy2_opendir(vfs_handle_struct
*handle
,
694 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
695 ×tamp
, &stripped
)) {
698 if (timestamp
== 0) {
699 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
701 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
702 TALLOC_FREE(stripped
);
706 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
713 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
714 const struct smb_filename
*smb_fname_src
,
715 const struct smb_filename
*smb_fname_dst
)
717 time_t timestamp_src
, timestamp_dst
;
719 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
720 smb_fname_src
->base_name
,
721 ×tamp_src
, NULL
)) {
724 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
725 smb_fname_dst
->base_name
,
726 ×tamp_dst
, NULL
)) {
729 if (timestamp_src
!= 0) {
733 if (timestamp_dst
!= 0) {
737 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
740 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
741 const char *oldname
, const char *newname
)
743 time_t timestamp_old
, timestamp_new
;
745 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
746 ×tamp_old
, NULL
)) {
749 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
750 ×tamp_new
, NULL
)) {
753 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
757 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
760 static int shadow_copy2_link(vfs_handle_struct
*handle
,
761 const char *oldname
, const char *newname
)
763 time_t timestamp_old
, timestamp_new
;
765 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, oldname
,
766 ×tamp_old
, NULL
)) {
769 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, newname
,
770 ×tamp_new
, NULL
)) {
773 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
777 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
780 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
781 struct smb_filename
*smb_fname
)
784 char *stripped
, *tmp
;
785 int ret
, saved_errno
;
787 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
788 smb_fname
->base_name
,
789 ×tamp
, &stripped
)) {
792 if (timestamp
== 0) {
793 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
796 tmp
= smb_fname
->base_name
;
797 smb_fname
->base_name
= shadow_copy2_convert(
798 talloc_tos(), handle
, stripped
, timestamp
);
799 TALLOC_FREE(stripped
);
801 if (smb_fname
->base_name
== NULL
) {
802 smb_fname
->base_name
= tmp
;
806 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
809 TALLOC_FREE(smb_fname
->base_name
);
810 smb_fname
->base_name
= tmp
;
813 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
819 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
820 struct smb_filename
*smb_fname
)
823 char *stripped
, *tmp
;
824 int ret
, saved_errno
;
826 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
827 smb_fname
->base_name
,
828 ×tamp
, &stripped
)) {
831 if (timestamp
== 0) {
832 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
835 tmp
= smb_fname
->base_name
;
836 smb_fname
->base_name
= shadow_copy2_convert(
837 talloc_tos(), handle
, stripped
, timestamp
);
838 TALLOC_FREE(stripped
);
840 if (smb_fname
->base_name
== NULL
) {
841 smb_fname
->base_name
= tmp
;
845 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
848 TALLOC_FREE(smb_fname
->base_name
);
849 smb_fname
->base_name
= tmp
;
852 convert_sbuf(handle
, smb_fname
->base_name
, &smb_fname
->st
);
858 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
859 SMB_STRUCT_STAT
*sbuf
)
864 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
868 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
869 fsp
->fsp_name
->base_name
,
873 if (timestamp
!= 0) {
874 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
879 static int shadow_copy2_open(vfs_handle_struct
*handle
,
880 struct smb_filename
*smb_fname
, files_struct
*fsp
,
881 int flags
, mode_t mode
)
884 char *stripped
, *tmp
;
885 int ret
, saved_errno
;
887 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
888 smb_fname
->base_name
,
889 ×tamp
, &stripped
)) {
892 if (timestamp
== 0) {
893 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
896 tmp
= smb_fname
->base_name
;
897 smb_fname
->base_name
= shadow_copy2_convert(
898 talloc_tos(), handle
, stripped
, timestamp
);
899 TALLOC_FREE(stripped
);
901 if (smb_fname
->base_name
== NULL
) {
902 smb_fname
->base_name
= tmp
;
906 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
909 TALLOC_FREE(smb_fname
->base_name
);
910 smb_fname
->base_name
= tmp
;
916 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
917 const struct smb_filename
*smb_fname
)
921 int ret
, saved_errno
;
922 struct smb_filename
*conv
;
925 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
926 smb_fname
->base_name
,
927 ×tamp
, &stripped
)) {
930 if (timestamp
== 0) {
931 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
933 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
934 if (!NT_STATUS_IS_OK(status
)) {
938 conv
->base_name
= shadow_copy2_convert(
939 conv
, handle
, stripped
, timestamp
);
940 TALLOC_FREE(stripped
);
941 if (conv
->base_name
== NULL
) {
944 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
951 static int shadow_copy2_chmod(vfs_handle_struct
*handle
, const char *fname
,
956 int ret
, saved_errno
;
959 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
960 ×tamp
, &stripped
)) {
963 if (timestamp
== 0) {
964 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
966 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
967 TALLOC_FREE(stripped
);
971 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
978 static int shadow_copy2_chown(vfs_handle_struct
*handle
, const char *fname
,
979 uid_t uid
, gid_t gid
)
983 int ret
, saved_errno
;
986 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
987 ×tamp
, &stripped
)) {
990 if (timestamp
== 0) {
991 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
993 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
994 TALLOC_FREE(stripped
);
998 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1001 errno
= saved_errno
;
1005 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
1010 int ret
, saved_errno
;
1013 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1014 ×tamp
, &stripped
)) {
1017 if (timestamp
== 0) {
1018 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1020 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1021 TALLOC_FREE(stripped
);
1025 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1026 saved_errno
= errno
;
1028 errno
= saved_errno
;
1032 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
1033 const struct smb_filename
*smb_fname
,
1034 struct smb_file_time
*ft
)
1038 int ret
, saved_errno
;
1039 struct smb_filename
*conv
;
1042 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1043 smb_fname
->base_name
,
1044 ×tamp
, &stripped
)) {
1047 if (timestamp
== 0) {
1048 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1050 status
= copy_smb_filename(talloc_tos(), smb_fname
, &conv
);
1051 if (!NT_STATUS_IS_OK(status
)) {
1055 conv
->base_name
= shadow_copy2_convert(
1056 conv
, handle
, stripped
, timestamp
);
1057 TALLOC_FREE(stripped
);
1058 if (conv
->base_name
== NULL
) {
1061 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1062 saved_errno
= errno
;
1064 errno
= saved_errno
;
1068 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
1069 const char *fname
, char *buf
, size_t bufsiz
)
1073 int ret
, saved_errno
;
1076 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1077 ×tamp
, &stripped
)) {
1080 if (timestamp
== 0) {
1081 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1083 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1084 TALLOC_FREE(stripped
);
1088 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1089 saved_errno
= errno
;
1091 errno
= saved_errno
;
1095 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
1096 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1100 int ret
, saved_errno
;
1103 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1104 ×tamp
, &stripped
)) {
1107 if (timestamp
== 0) {
1108 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1110 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1111 TALLOC_FREE(stripped
);
1115 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1116 saved_errno
= errno
;
1118 errno
= saved_errno
;
1122 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
1126 char *stripped
= NULL
;
1128 char *result
= NULL
;
1129 char *inserted
= NULL
;
1130 char *inserted_to
, *inserted_end
;
1133 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1134 ×tamp
, &stripped
)) {
1137 if (timestamp
== 0) {
1138 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1141 tmp
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1146 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1147 if (result
== NULL
) {
1152 * Take away what we've inserted. This removes the @GMT-thingy
1153 * completely, but will give a path under the share root.
1155 inserted
= shadow_copy2_insert_string(talloc_tos(), handle
, timestamp
);
1156 if (inserted
== NULL
) {
1159 inserted_to
= strstr_m(result
, inserted
);
1160 if (inserted_to
== NULL
) {
1161 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted
));
1164 inserted_end
= inserted_to
+ talloc_get_size(inserted
) - 1;
1165 memmove(inserted_to
, inserted_end
, strlen(inserted_end
)+1);
1168 saved_errno
= errno
;
1169 TALLOC_FREE(inserted
);
1171 TALLOC_FREE(stripped
);
1172 errno
= saved_errno
;
1177 * Check whether a given directory contains a
1178 * snapshot directory as direct subdirectory.
1179 * If yes, return the path of the snapshot-subdir,
1180 * otherwise return NULL.
1182 static char *have_snapdir(struct vfs_handle_struct
*handle
,
1185 struct smb_filename smb_fname
;
1187 struct shadow_copy2_config
*config
;
1189 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1192 ZERO_STRUCT(smb_fname
);
1193 smb_fname
.base_name
= talloc_asprintf(talloc_tos(), "%s/%s",
1194 path
, config
->snapdir
);
1195 if (smb_fname
.base_name
== NULL
) {
1199 ret
= SMB_VFS_NEXT_STAT(handle
, &smb_fname
);
1200 if ((ret
== 0) && (S_ISDIR(smb_fname
.st
.st_ex_mode
))) {
1201 return smb_fname
.base_name
;
1203 TALLOC_FREE(smb_fname
.base_name
);
1208 * Find the snapshot directory (if any) for the given
1209 * filename (which is relative to the share).
1211 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
,
1212 struct vfs_handle_struct
*handle
,
1213 struct smb_filename
*smb_fname
)
1216 const char *snapdir
;
1217 struct shadow_copy2_config
*config
;
1219 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1223 * If the non-snapdisrseverywhere mode, we should not search!
1225 if (!config
->snapdirseverywhere
) {
1226 return config
->snapshot_basepath
;
1229 path
= talloc_asprintf(mem_ctx
, "%s/%s",
1230 handle
->conn
->connectpath
,
1231 smb_fname
->base_name
);
1236 snapdir
= have_snapdir(handle
, path
);
1237 if (snapdir
!= NULL
) {
1242 while ((p
= strrchr(path
, '/')) && (p
> path
)) {
1246 snapdir
= have_snapdir(handle
, path
);
1247 if (snapdir
!= NULL
) {
1256 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct
*handle
,
1258 char *gmt
, size_t gmt_len
)
1260 struct tm timestamp
;
1262 unsigned long int timestamp_long
;
1264 struct shadow_copy2_config
*config
;
1266 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1269 fmt
= config
->gmt_format
;
1271 ZERO_STRUCT(timestamp
);
1272 if (config
->use_sscanf
) {
1273 if (sscanf(name
, fmt
, ×tamp_long
) != 1) {
1274 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1275 "no sscanf match %s: %s\n",
1279 timestamp_t
= timestamp_long
;
1280 gmtime_r(×tamp_t
, ×tamp
);
1282 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
1283 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1284 "no match %s: %s\n",
1288 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1291 if (config
->use_localtime
) {
1292 timestamp
.tm_isdst
= -1;
1293 timestamp_t
= mktime(×tamp
);
1294 gmtime_r(×tamp_t
, ×tamp
);
1298 strftime(gmt
, gmt_len
, GMT_FORMAT
, ×tamp
);
1302 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
1304 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1307 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
1309 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
1313 sort the shadow copy data in ascending or descending order
1315 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
1316 struct shadow_copy_data
*shadow_copy2_data
)
1318 int (*cmpfunc
)(const void *, const void *);
1320 struct shadow_copy2_config
*config
;
1322 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct shadow_copy2_config
,
1325 sort
= config
->sort_order
;
1330 if (strcmp(sort
, "asc") == 0) {
1331 cmpfunc
= shadow_copy2_label_cmp_asc
;
1332 } else if (strcmp(sort
, "desc") == 0) {
1333 cmpfunc
= shadow_copy2_label_cmp_desc
;
1338 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
1339 shadow_copy2_data
->labels
)
1341 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
1342 shadow_copy2_data
->num_volumes
,
1347 static int shadow_copy2_get_shadow_copy_data(
1348 vfs_handle_struct
*handle
, files_struct
*fsp
,
1349 struct shadow_copy_data
*shadow_copy2_data
,
1353 const char *snapdir
;
1355 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
1357 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
, fsp
->fsp_name
);
1358 if (snapdir
== NULL
) {
1359 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1360 handle
->conn
->connectpath
));
1362 talloc_free(tmp_ctx
);
1366 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
1369 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1370 " - %s\n", snapdir
, strerror(errno
)));
1371 talloc_free(tmp_ctx
);
1376 shadow_copy2_data
->num_volumes
= 0;
1377 shadow_copy2_data
->labels
= NULL
;
1379 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
1380 char snapshot
[GMT_NAME_LEN
+1];
1381 SHADOW_COPY_LABEL
*tlabels
;
1384 * ignore names not of the right form in the snapshot
1387 if (!shadow_copy2_snapshot_to_gmt(
1389 snapshot
, sizeof(snapshot
))) {
1391 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1392 "ignoring %s\n", d
->d_name
));
1395 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1396 d
->d_name
, snapshot
));
1399 /* the caller doesn't want the labels */
1400 shadow_copy2_data
->num_volumes
++;
1404 tlabels
= talloc_realloc(shadow_copy2_data
,
1405 shadow_copy2_data
->labels
,
1407 shadow_copy2_data
->num_volumes
+1);
1408 if (tlabels
== NULL
) {
1409 DEBUG(0,("shadow_copy2: out of memory\n"));
1410 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
1411 talloc_free(tmp_ctx
);
1415 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
1418 shadow_copy2_data
->num_volumes
++;
1419 shadow_copy2_data
->labels
= tlabels
;
1422 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
1424 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
1426 talloc_free(tmp_ctx
);
1430 static NTSTATUS
shadow_copy2_fget_nt_acl(vfs_handle_struct
*handle
,
1431 struct files_struct
*fsp
,
1432 uint32 security_info
,
1433 TALLOC_CTX
*mem_ctx
,
1434 struct security_descriptor
**ppdesc
)
1441 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
,
1442 fsp
->fsp_name
->base_name
,
1443 ×tamp
, &stripped
)) {
1444 return map_nt_error_from_unix(errno
);
1446 if (timestamp
== 0) {
1447 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1451 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1452 TALLOC_FREE(stripped
);
1454 return map_nt_error_from_unix(errno
);
1456 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1462 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
1464 uint32 security_info
,
1465 TALLOC_CTX
*mem_ctx
,
1466 struct security_descriptor
**ppdesc
)
1473 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1474 ×tamp
, &stripped
)) {
1475 return map_nt_error_from_unix(errno
);
1477 if (timestamp
== 0) {
1478 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1481 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1482 TALLOC_FREE(stripped
);
1484 return map_nt_error_from_unix(errno
);
1486 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1492 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
,
1493 const char *fname
, mode_t mode
)
1497 int ret
, saved_errno
;
1500 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1501 ×tamp
, &stripped
)) {
1504 if (timestamp
== 0) {
1505 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1507 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1508 TALLOC_FREE(stripped
);
1512 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1513 saved_errno
= errno
;
1515 errno
= saved_errno
;
1519 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1523 int ret
, saved_errno
;
1526 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1527 ×tamp
, &stripped
)) {
1530 if (timestamp
== 0) {
1531 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1533 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1534 TALLOC_FREE(stripped
);
1538 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1539 saved_errno
= errno
;
1541 errno
= saved_errno
;
1545 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
1550 int ret
, saved_errno
;
1553 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1554 ×tamp
, &stripped
)) {
1557 if (timestamp
== 0) {
1558 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1560 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1561 TALLOC_FREE(stripped
);
1565 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1566 saved_errno
= errno
;
1568 errno
= saved_errno
;
1572 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
1573 const char *fname
, const char *aname
,
1574 void *value
, size_t size
)
1582 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1583 ×tamp
, &stripped
)) {
1586 if (timestamp
== 0) {
1587 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1590 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1591 TALLOC_FREE(stripped
);
1595 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1596 saved_errno
= errno
;
1598 errno
= saved_errno
;
1602 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
,
1604 char *list
, size_t size
)
1612 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1613 ×tamp
, &stripped
)) {
1616 if (timestamp
== 0) {
1617 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1619 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1620 TALLOC_FREE(stripped
);
1624 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1625 saved_errno
= errno
;
1627 errno
= saved_errno
;
1631 static int shadow_copy2_removexattr(vfs_handle_struct
*handle
,
1632 const char *fname
, const char *aname
)
1636 int ret
, saved_errno
;
1639 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1640 ×tamp
, &stripped
)) {
1643 if (timestamp
== 0) {
1644 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1646 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1647 TALLOC_FREE(stripped
);
1651 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1652 saved_errno
= errno
;
1654 errno
= saved_errno
;
1658 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
,
1660 const char *aname
, const void *value
,
1661 size_t size
, int flags
)
1669 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1670 ×tamp
, &stripped
)) {
1673 if (timestamp
== 0) {
1674 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1677 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1678 TALLOC_FREE(stripped
);
1682 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1683 saved_errno
= errno
;
1685 errno
= saved_errno
;
1689 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
1690 const char *fname
, mode_t mode
)
1698 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, fname
,
1699 ×tamp
, &stripped
)) {
1702 if (timestamp
== 0) {
1703 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1705 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1706 TALLOC_FREE(stripped
);
1710 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1711 saved_errno
= errno
;
1713 errno
= saved_errno
;
1717 static int shadow_copy2_get_real_filename(struct vfs_handle_struct
*handle
,
1720 TALLOC_CTX
*mem_ctx
,
1729 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1730 ×tamp
, &stripped
)) {
1733 if (timestamp
== 0) {
1734 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1735 mem_ctx
, found_name
);
1737 if (stripped
[0] == '\0') {
1738 *found_name
= talloc_strdup(mem_ctx
, name
);
1739 if (*found_name
== NULL
) {
1745 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1746 TALLOC_FREE(stripped
);
1750 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1751 mem_ctx
, found_name
);
1752 saved_errno
= errno
;
1754 errno
= saved_errno
;
1758 static uint64_t shadow_copy2_disk_free(vfs_handle_struct
*handle
,
1759 const char *path
, bool small_query
,
1760 uint64_t *bsize
, uint64_t *dfree
,
1769 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle
, path
,
1770 ×tamp
, &stripped
)) {
1773 if (timestamp
== 0) {
1774 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1775 bsize
, dfree
, dsize
);
1778 conv
= shadow_copy2_convert(talloc_tos(), handle
, stripped
, timestamp
);
1779 TALLOC_FREE(stripped
);
1784 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1787 saved_errno
= errno
;
1789 errno
= saved_errno
;
1794 static int shadow_copy2_connect(struct vfs_handle_struct
*handle
,
1795 const char *service
, const char *user
)
1797 struct shadow_copy2_config
*config
;
1799 const char *snapdir
;
1800 const char *gmt_format
;
1801 const char *sort_order
;
1802 const char *basedir
;
1803 const char *mount_point
;
1805 DEBUG(10, (__location__
": cnum[%u], connectpath[%s]\n",
1806 (unsigned)handle
->conn
->cnum
,
1807 handle
->conn
->connectpath
));
1809 ret
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1814 config
= talloc_zero(handle
->conn
, struct shadow_copy2_config
);
1815 if (config
== NULL
) {
1816 DEBUG(0, ("talloc_zero() failed\n"));
1821 gmt_format
= lp_parm_const_string(SNUM(handle
->conn
),
1824 config
->gmt_format
= talloc_strdup(config
, gmt_format
);
1825 if (config
->gmt_format
== NULL
) {
1826 DEBUG(0, ("talloc_strdup() failed\n"));
1831 config
->use_sscanf
= lp_parm_bool(SNUM(handle
->conn
),
1832 "shadow", "sscanf", false);
1834 config
->use_localtime
= lp_parm_bool(SNUM(handle
->conn
),
1835 "shadow", "localtime",
1838 snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
1839 "shadow", "snapdir",
1841 config
->snapdir
= talloc_strdup(config
, snapdir
);
1842 if (config
->snapdir
== NULL
) {
1843 DEBUG(0, ("talloc_strdup() failed\n"));
1848 config
->snapdirseverywhere
= lp_parm_bool(SNUM(handle
->conn
),
1850 "snapdirseverywhere",
1853 config
->crossmountpoints
= lp_parm_bool(SNUM(handle
->conn
),
1854 "shadow", "crossmountpoints",
1857 config
->fixinodes
= lp_parm_bool(SNUM(handle
->conn
),
1858 "shadow", "fixinodes",
1861 sort_order
= lp_parm_const_string(SNUM(handle
->conn
),
1862 "shadow", "sort", "desc");
1863 config
->sort_order
= talloc_strdup(config
, sort_order
);
1864 if (config
->sort_order
== NULL
) {
1865 DEBUG(0, ("talloc_strdup() failed\n"));
1870 mount_point
= lp_parm_const_string(SNUM(handle
->conn
),
1871 "shadow", "mountpoint", NULL
);
1872 if (mount_point
!= NULL
) {
1873 if (mount_point
[0] != '/') {
1874 DEBUG(1, (__location__
" Warning: 'mountpoint' is "
1875 "relative ('%s'), but it has to be an "
1876 "absolute path. Ignoring provided value.\n",
1881 p
= strstr(handle
->conn
->connectpath
, mount_point
);
1882 if (p
!= handle
->conn
->connectpath
) {
1883 DEBUG(1, ("Warning: mount_point (%s) is not a "
1884 "subdirectory of the share root "
1885 "(%s). Ignoring provided value.\n",
1887 handle
->conn
->connectpath
));
1893 if (mount_point
!= NULL
) {
1894 config
->mount_point
= talloc_strdup(config
, mount_point
);
1895 if (config
->mount_point
== NULL
) {
1896 DEBUG(0, (__location__
" talloc_strdup() failed\n"));
1900 config
->mount_point
= shadow_copy2_find_mount_point(config
,
1902 if (config
->mount_point
== NULL
) {
1903 DEBUG(0, (__location__
": shadow_copy2_find_mount_point"
1904 " failed: %s\n", strerror(errno
)));
1909 basedir
= lp_parm_const_string(SNUM(handle
->conn
),
1910 "shadow", "basedir", NULL
);
1912 if (basedir
!= NULL
) {
1913 if (basedir
[0] != '/') {
1914 DEBUG(1, (__location__
" Warning: 'basedir' is "
1915 "relative ('%s'), but it has to be an "
1916 "absolute path. Disabling basedir.\n",
1920 p
= strstr(basedir
, config
->mount_point
);
1922 DEBUG(1, ("Warning: basedir (%s) is not a "
1923 "subdirectory of the share root's "
1924 "mount point (%s). "
1925 "Disabling basedir\n",
1926 basedir
, config
->mount_point
));
1928 config
->basedir
= talloc_strdup(config
,
1930 if (config
->basedir
== NULL
) {
1931 DEBUG(0, ("talloc_strdup() failed\n"));
1939 if (config
->snapdirseverywhere
&& config
->basedir
!= NULL
) {
1940 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1941 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1942 TALLOC_FREE(config
->basedir
);
1945 if (config
->crossmountpoints
&& config
->basedir
!= NULL
) {
1946 DEBUG(1, (__location__
" Warning: 'basedir' is incompatible "
1947 "with 'crossmountpoints'. Disabling basedir.\n"));
1948 TALLOC_FREE(config
->basedir
);
1951 if (config
->basedir
== NULL
) {
1952 config
->basedir
= config
->mount_point
;
1955 if (strlen(config
->basedir
) != strlen(handle
->conn
->connectpath
)) {
1956 config
->rel_connectpath
= talloc_strdup(config
,
1957 handle
->conn
->connectpath
+ strlen(config
->basedir
));
1958 if (config
->rel_connectpath
== NULL
) {
1959 DEBUG(0, ("talloc_strdup() failed\n"));
1965 if (config
->snapdir
[0] == '/') {
1966 config
->snapdir_absolute
= true;
1968 if (config
->snapdirseverywhere
== true) {
1969 DEBUG(1, (__location__
" Warning: An absolute snapdir "
1970 "is incompatible with 'snapdirseverywhere', "
1971 "setting 'snapdirseverywhere' to false.\n"));
1972 config
->snapdirseverywhere
= false;
1975 if (config
->crossmountpoints
== true) {
1976 DEBUG(1, (__location__
" Warning: 'crossmountpoints' "
1977 "is not supported with an absolute snapdir. "
1978 "Disabling it.\n"));
1979 config
->crossmountpoints
= false;
1982 config
->snapshot_basepath
= config
->snapdir
;
1984 config
->snapshot_basepath
= talloc_asprintf(config
, "%s/%s",
1985 config
->mount_point
, config
->snapdir
);
1986 if (config
->snapshot_basepath
== NULL
) {
1987 DEBUG(0, ("talloc_asprintf() failed\n"));
1993 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1994 " share root: '%s'\n"
1996 " mountpoint: '%s'\n"
1997 " rel share root: '%s'\n"
1999 " snapshot base path: '%s'\n"
2002 " snapdirs everywhere: %s\n"
2003 " cross mountpoints: %s\n"
2007 handle
->conn
->connectpath
,
2009 config
->mount_point
,
2010 config
->rel_connectpath
,
2012 config
->snapshot_basepath
,
2014 config
->use_sscanf
? "yes" : "no",
2015 config
->snapdirseverywhere
? "yes" : "no",
2016 config
->crossmountpoints
? "yes" : "no",
2017 config
->fixinodes
? "yes" : "no",
2022 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
2023 NULL
, struct shadow_copy2_config
,
2029 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
2030 .connect_fn
= shadow_copy2_connect
,
2031 .opendir_fn
= shadow_copy2_opendir
,
2032 .disk_free_fn
= shadow_copy2_disk_free
,
2033 .rename_fn
= shadow_copy2_rename
,
2034 .link_fn
= shadow_copy2_link
,
2035 .symlink_fn
= shadow_copy2_symlink
,
2036 .stat_fn
= shadow_copy2_stat
,
2037 .lstat_fn
= shadow_copy2_lstat
,
2038 .fstat_fn
= shadow_copy2_fstat
,
2039 .open_fn
= shadow_copy2_open
,
2040 .unlink_fn
= shadow_copy2_unlink
,
2041 .chmod_fn
= shadow_copy2_chmod
,
2042 .chown_fn
= shadow_copy2_chown
,
2043 .chdir_fn
= shadow_copy2_chdir
,
2044 .ntimes_fn
= shadow_copy2_ntimes
,
2045 .readlink_fn
= shadow_copy2_readlink
,
2046 .mknod_fn
= shadow_copy2_mknod
,
2047 .realpath_fn
= shadow_copy2_realpath
,
2048 .get_nt_acl_fn
= shadow_copy2_get_nt_acl
,
2049 .fget_nt_acl_fn
= shadow_copy2_fget_nt_acl
,
2050 .get_shadow_copy_data_fn
= shadow_copy2_get_shadow_copy_data
,
2051 .mkdir_fn
= shadow_copy2_mkdir
,
2052 .rmdir_fn
= shadow_copy2_rmdir
,
2053 .getxattr_fn
= shadow_copy2_getxattr
,
2054 .listxattr_fn
= shadow_copy2_listxattr
,
2055 .removexattr_fn
= shadow_copy2_removexattr
,
2056 .setxattr_fn
= shadow_copy2_setxattr
,
2057 .chmod_acl_fn
= shadow_copy2_chmod_acl
,
2058 .chflags_fn
= shadow_copy2_chflags
,
2059 .get_real_filename_fn
= shadow_copy2_get_real_filename
,
2062 NTSTATUS
vfs_shadow_copy2_init(void);
2063 NTSTATUS
vfs_shadow_copy2_init(void)
2065 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2066 "shadow_copy2", &vfs_shadow_copy2_fns
);