2 * implementation of an Shadow Copy module - version 2
4 * Copyright (C) Andrew Tridgell 2007
5 * Copyright (C) Ed Plese 2009
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 2nd 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:localtime = yes/no (default is no)
89 This is an optional parameter that indicates whether the
90 snapshot names are in UTC/GMT or the local time.
93 The following command would generate a correctly formatted directory name
94 for use with the default parameters:
95 date -u +@GMT-%Y.%m.%d-%H.%M.%S
99 static int vfs_shadow_copy2_debug_level
= DBGC_VFS
;
102 #define DBGC_CLASS vfs_shadow_copy2_debug_level
104 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
105 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
107 #define SHADOW_COPY2_DEFAULT_SORT NULL
108 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
109 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
112 make very sure it is one of our special names
114 static inline bool shadow_copy2_match_name(const char *name
, const char **gmt_start
)
116 unsigned year
, month
, day
, hr
, min
, sec
;
121 p
= strstr_m(name
, "@GMT-");
122 if (p
== NULL
) return false;
123 if (p
> name
&& p
[-1] != '/') return False
;
124 if (sscanf(p
, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year
, &month
,
125 &day
, &hr
, &min
, &sec
) != 6) {
128 if (p
[24] != 0 && p
[24] != '/') {
137 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX
*mem_ctx
,
138 vfs_handle_struct
*handle
, const char *name
)
142 char gmt
[GMT_NAME_LEN
+ 1];
145 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
146 "format", SHADOW_COPY2_DEFAULT_FORMAT
);
148 ZERO_STRUCT(timestamp
);
149 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
150 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
155 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
156 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime",
157 SHADOW_COPY2_DEFAULT_LOCALTIME
))
159 timestamp
.tm_isdst
= -1;
160 timestamp_t
= mktime(×tamp
);
161 gmtime_r(×tamp_t
, ×tamp
);
163 strftime(gmt
, sizeof(gmt
), SHADOW_COPY2_GMT_FORMAT
, ×tamp
);
165 return talloc_strdup(mem_ctx
, gmt
);
169 shadow copy paths can also come into the server in this form:
171 /foo/bar/@GMT-XXXXX/some/file
173 This function normalises the filename to be of the form:
175 @GMT-XXXX/foo/bar/some/file
177 static const char *shadow_copy2_normalise_path(TALLOC_CTX
*mem_ctx
, const char *path
, const char *gmt_start
)
180 char buf
[GMT_NAME_LEN
];
183 if (path
== gmt_start
) {
187 prefix_len
= gmt_start
- path
- 1;
189 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path
, gmt_start
,
193 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
194 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
195 * processing. As many VFS calls provide a const char *,
196 * unfortunately we have to make a copy.
199 pcopy
= talloc_strdup(talloc_tos(), path
);
204 gmt_start
= pcopy
+ prefix_len
;
207 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
209 memcpy(buf
, gmt_start
+1, GMT_NAME_LEN
);
212 * Make space for it including a trailing /
214 memmove(pcopy
+ GMT_NAME_LEN
+ 1, pcopy
, prefix_len
);
217 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
219 memcpy(pcopy
, buf
, GMT_NAME_LEN
);
220 pcopy
[GMT_NAME_LEN
] = '/';
222 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path
, pcopy
));
228 convert a name to the shadow directory
231 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
232 const char *name = fname; \
233 const char *gmt_start; \
234 if (shadow_copy2_match_name(fname, &gmt_start)) { \
237 name2 = convert_shadow2_name(handle, fname, gmt_start); \
238 if (name2 == NULL) { \
243 ret = SMB_VFS_NEXT_ ## op args; \
244 talloc_free(name2); \
245 if (ret != eret) extra; \
248 return SMB_VFS_NEXT_ ## op args; \
252 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
253 const char *gmt_start; \
254 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
256 char *smb_base_name_tmp = NULL; \
258 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
259 if (name2 == NULL) { \
263 smb_base_name_tmp = smb_fname->base_name; \
264 smb_fname->base_name = name2; \
265 ret = SMB_VFS_NEXT_ ## op args; \
266 smb_fname->base_name = smb_base_name_tmp; \
267 talloc_free(name2); \
268 if (ret != eret) extra; \
271 return SMB_VFS_NEXT_ ## op args; \
276 convert a name to the shadow directory: NTSTATUS-specific handling
279 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
280 const char *name = fname; \
281 const char *gmt_start; \
282 if (shadow_copy2_match_name(fname, &gmt_start)) { \
285 name2 = convert_shadow2_name(handle, fname, gmt_start); \
286 if (name2 == NULL) { \
291 ret = SMB_VFS_NEXT_ ## op args; \
292 talloc_free(name2); \
293 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
296 return SMB_VFS_NEXT_ ## op args; \
300 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
302 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
304 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
306 #define SHADOW2_NEXT2(op, args) do { \
307 const char *gmt_start1, *gmt_start2; \
308 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
309 shadow_copy2_match_name(newname, &gmt_start2)) { \
313 return SMB_VFS_NEXT_ ## op args; \
317 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
318 const char *gmt_start1, *gmt_start2; \
319 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
320 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
324 return SMB_VFS_NEXT_ ## op args; \
330 find the mount point of a filesystem
332 static char *find_mount_point(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
334 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
339 if (stat(path
, &st
) != 0) {
346 while ((p
= strrchr(path
, '/')) && p
> path
) {
348 if (stat(path
, &st
) != 0) {
352 if (st
.st_dev
!= dev
) {
362 work out the location of the snapshot for this share
364 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
370 snapdir
= lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir", NULL
);
371 if (snapdir
== NULL
) {
374 /* if its an absolute path, we're done */
375 if (*snapdir
== '/') {
379 /* other its relative to the filesystem mount point */
380 mount_point
= find_mount_point(mem_ctx
, handle
);
381 if (mount_point
== NULL
) {
385 ret
= talloc_asprintf(mem_ctx
, "%s/%s", mount_point
, snapdir
);
386 talloc_free(mount_point
);
391 work out the location of the base directory for snapshots of this share
393 static const char *shadow_copy2_find_basedir(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
395 const char *basedir
= lp_parm_const_string(SNUM(handle
->conn
), "shadow", "basedir", NULL
);
397 /* other its the filesystem mount point */
398 if (basedir
== NULL
) {
399 basedir
= find_mount_point(mem_ctx
, handle
);
406 convert a filename from a share relative path, to a path in the
409 static char *convert_shadow2_name(vfs_handle_struct
*handle
, const char *fname
, const char *gmt_path
)
411 TALLOC_CTX
*tmp_ctx
= talloc_new(handle
->data
);
412 const char *snapdir
, *relpath
, *baseoffset
, *basedir
;
418 char snapshot
[MAXPATHLEN
];
421 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
422 "format", SHADOW_COPY2_DEFAULT_FORMAT
);
424 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
425 if (snapdir
== NULL
) {
426 DEBUG(2,("no snapdir found for share at %s\n", handle
->conn
->connectpath
));
427 talloc_free(tmp_ctx
);
431 basedir
= shadow_copy2_find_basedir(tmp_ctx
, handle
);
432 if (basedir
== NULL
) {
433 DEBUG(2,("no basedir found for share at %s\n", handle
->conn
->connectpath
));
434 talloc_free(tmp_ctx
);
438 if (strncmp(fname
, "@GMT-", 5) != 0) {
439 fname
= shadow_copy2_normalise_path(tmp_ctx
, fname
, gmt_path
);
441 talloc_free(tmp_ctx
);
446 ZERO_STRUCT(timestamp
);
447 relpath
= strptime(fname
, SHADOW_COPY2_GMT_FORMAT
, ×tamp
);
448 if (relpath
== NULL
) {
449 talloc_free(tmp_ctx
);
453 /* relpath is the remaining portion of the path after the @GMT-xxx */
455 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime",
456 SHADOW_COPY2_DEFAULT_LOCALTIME
))
458 timestamp_t
= timegm(×tamp
);
459 localtime_r(×tamp_t
, ×tamp
);
462 strftime(snapshot
, MAXPATHLEN
, fmt
, ×tamp
);
464 baselen
= strlen(basedir
);
465 baseoffset
= handle
->conn
->connectpath
+ baselen
;
467 /* some sanity checks */
468 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
469 (handle
->conn
->connectpath
[baselen
] != 0 && handle
->conn
->connectpath
[baselen
] != '/')) {
470 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
471 basedir
, handle
->conn
->connectpath
));
472 talloc_free(tmp_ctx
);
476 if (*relpath
== '/') relpath
++;
477 if (*baseoffset
== '/') baseoffset
++;
479 ret
= talloc_asprintf(handle
->data
, "%s/%s/%s/%s",
484 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname
, ret
));
485 talloc_free(tmp_ctx
);
493 static uint32
string_hash(const char *s
)
497 n
= ((n
<< 5) + n
) ^ (uint32
)(*s
++);
503 modify a sbuf return to ensure that inodes in the shadow directory
504 are different from those in the main directory
506 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
, SMB_STRUCT_STAT
*sbuf
)
508 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
509 /* some snapshot systems, like GPFS, return the name
510 device:inode for the snapshot files as the current
511 files. That breaks the 'restore' button in the shadow copy
512 GUI, as the client gets a sharing violation.
514 This is a crude way of allowing both files to be
515 open at once. It has a slight chance of inode
516 number collision, but I can't see a better approach
517 without significant VFS changes
519 uint32_t shash
= string_hash(fname
) & 0xFF000000;
523 sbuf
->st_ex_ino
^= shash
;
527 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
528 const struct smb_filename
*smb_fname_src
,
529 const struct smb_filename
*smb_fname_dst
)
531 SHADOW2_NEXT2_SMB_FNAME(RENAME
,
532 (handle
, smb_fname_src
, smb_fname_dst
));
535 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
536 const char *oldname
, const char *newname
)
538 SHADOW2_NEXT2(SYMLINK
, (handle
, oldname
, newname
));
541 static int shadow_copy2_link(vfs_handle_struct
*handle
,
542 const char *oldname
, const char *newname
)
544 SHADOW2_NEXT2(LINK
, (handle
, oldname
, newname
));
547 static int shadow_copy2_open(vfs_handle_struct
*handle
,
548 struct smb_filename
*smb_fname
, files_struct
*fsp
,
549 int flags
, mode_t mode
)
551 SHADOW2_NEXT_SMB_FNAME(OPEN
,
552 (handle
, smb_fname
, fsp
, flags
, mode
),
556 static SMB_STRUCT_DIR
*shadow_copy2_opendir(vfs_handle_struct
*handle
,
557 const char *fname
, const char *mask
, uint32 attr
)
559 SHADOW2_NEXT(OPENDIR
, (handle
, name
, mask
, attr
), SMB_STRUCT_DIR
*, NULL
);
562 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
563 struct smb_filename
*smb_fname
)
565 _SHADOW2_NEXT_SMB_FNAME(STAT
, (handle
, smb_fname
), int, -1,
566 convert_sbuf(handle
, smb_fname
->base_name
,
570 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
571 struct smb_filename
*smb_fname
)
573 _SHADOW2_NEXT_SMB_FNAME(LSTAT
, (handle
, smb_fname
), int, -1,
574 convert_sbuf(handle
, smb_fname
->base_name
,
578 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
580 int ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
581 if (ret
== 0 && shadow_copy2_match_name(fsp
->fsp_name
->base_name
, NULL
)) {
582 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
587 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
588 const struct smb_filename
*smb_fname_in
)
590 struct smb_filename
*smb_fname
= NULL
;
593 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
594 if (!NT_STATUS_IS_OK(status
)) {
595 errno
= map_errno_from_nt_status(status
);
599 SHADOW2_NEXT_SMB_FNAME(UNLINK
, (handle
, smb_fname
), int, -1);
602 static int shadow_copy2_chmod(vfs_handle_struct
*handle
,
603 const char *fname
, mode_t mode
)
605 SHADOW2_NEXT(CHMOD
, (handle
, name
, mode
), int, -1);
608 static int shadow_copy2_chown(vfs_handle_struct
*handle
,
609 const char *fname
, uid_t uid
, gid_t gid
)
611 SHADOW2_NEXT(CHOWN
, (handle
, name
, uid
, gid
), int, -1);
614 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
617 SHADOW2_NEXT(CHDIR
, (handle
, name
), int, -1);
620 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
621 const struct smb_filename
*smb_fname_in
,
622 struct smb_file_time
*ft
)
624 struct smb_filename
*smb_fname
= NULL
;
627 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
628 if (!NT_STATUS_IS_OK(status
)) {
629 errno
= map_errno_from_nt_status(status
);
633 SHADOW2_NEXT_SMB_FNAME(NTIMES
, (handle
, smb_fname
, ft
), int, -1);
636 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
637 const char *fname
, char *buf
, size_t bufsiz
)
639 SHADOW2_NEXT(READLINK
, (handle
, name
, buf
, bufsiz
), int, -1);
642 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
643 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
645 SHADOW2_NEXT(MKNOD
, (handle
, name
, mode
, dev
), int, -1);
648 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
649 const char *fname
, char *resolved_path
)
653 if (shadow_copy2_match_name(fname
, &gmt
)
654 && (gmt
[GMT_NAME_LEN
] == '\0')) {
657 copy
= talloc_strdup(talloc_tos(), fname
);
663 copy
[gmt
- fname
] = '.';
665 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy
));
666 result
= SMB_VFS_NEXT_REALPATH(handle
, copy
, resolved_path
);
670 SHADOW2_NEXT(REALPATH
, (handle
, name
, resolved_path
), char *, NULL
);
673 static const char *shadow_copy2_connectpath(struct vfs_handle_struct
*handle
,
676 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
677 const char *snapdir
, *baseoffset
, *basedir
, *gmt_start
;
681 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname
));
683 if (!shadow_copy2_match_name(fname
, &gmt_start
)) {
684 return handle
->conn
->connectpath
;
687 fname
= shadow_copy2_normalise_path(talloc_tos(), fname
, gmt_start
);
689 TALLOC_FREE(tmp_ctx
);
693 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
694 if (snapdir
== NULL
) {
695 DEBUG(2,("no snapdir found for share at %s\n",
696 handle
->conn
->connectpath
));
697 TALLOC_FREE(tmp_ctx
);
701 basedir
= shadow_copy2_find_basedir(tmp_ctx
, handle
);
702 if (basedir
== NULL
) {
703 DEBUG(2,("no basedir found for share at %s\n",
704 handle
->conn
->connectpath
));
705 TALLOC_FREE(tmp_ctx
);
709 baselen
= strlen(basedir
);
710 baseoffset
= handle
->conn
->connectpath
+ baselen
;
712 /* some sanity checks */
713 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
714 (handle
->conn
->connectpath
[baselen
] != 0
715 && handle
->conn
->connectpath
[baselen
] != '/')) {
716 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
717 "parent of %s\n", basedir
,
718 handle
->conn
->connectpath
));
719 TALLOC_FREE(tmp_ctx
);
723 if (*baseoffset
== '/') baseoffset
++;
725 ret
= talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
729 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname
, ret
));
730 TALLOC_FREE(tmp_ctx
);
734 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
735 const char *fname
, uint32 security_info
,
736 struct security_descriptor
**ppdesc
)
738 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL
, (handle
, name
, security_info
, ppdesc
), NT_STATUS_ACCESS_DENIED
);
741 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
743 SHADOW2_NEXT(MKDIR
, (handle
, name
, mode
), int, -1);
746 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
748 SHADOW2_NEXT(RMDIR
, (handle
, name
), int, -1);
751 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
754 SHADOW2_NEXT(CHFLAGS
, (handle
, name
, flags
), int, -1);
757 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
758 const char *fname
, const char *aname
, void *value
, size_t size
)
760 SHADOW2_NEXT(GETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
763 static ssize_t
shadow_copy2_lgetxattr(vfs_handle_struct
*handle
,
764 const char *fname
, const char *aname
, void *value
, size_t size
)
766 SHADOW2_NEXT(LGETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
769 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
, const char *fname
,
770 char *list
, size_t size
)
772 SHADOW2_NEXT(LISTXATTR
, (handle
, name
, list
, size
), ssize_t
, -1);
775 static int shadow_copy2_removexattr(struct vfs_handle_struct
*handle
, const char *fname
,
778 SHADOW2_NEXT(REMOVEXATTR
, (handle
, name
, aname
), int, -1);
781 static int shadow_copy2_lremovexattr(struct vfs_handle_struct
*handle
, const char *fname
,
784 SHADOW2_NEXT(LREMOVEXATTR
, (handle
, name
, aname
), int, -1);
787 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
, const char *fname
,
788 const char *aname
, const void *value
, size_t size
, int flags
)
790 SHADOW2_NEXT(SETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
793 static int shadow_copy2_lsetxattr(struct vfs_handle_struct
*handle
, const char *fname
,
794 const char *aname
, const void *value
, size_t size
, int flags
)
796 SHADOW2_NEXT(LSETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
799 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
800 const char *fname
, mode_t mode
)
802 SHADOW2_NEXT(CHMOD_ACL
, (handle
, name
, mode
), int, -1);
805 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
807 return strncmp((char *)x
, (char *)y
, sizeof(SHADOW_COPY_LABEL
));
810 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
812 return -strncmp((char *)x
, (char *)y
, sizeof(SHADOW_COPY_LABEL
));
816 sort the shadow copy data in ascending or descending order
818 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
819 SHADOW_COPY_DATA
*shadow_copy2_data
)
821 int (*cmpfunc
)(const void *, const void *);
824 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
825 "sort", SHADOW_COPY2_DEFAULT_SORT
);
830 if (strcmp(sort
, "asc") == 0) {
831 cmpfunc
= shadow_copy2_label_cmp_asc
;
832 } else if (strcmp(sort
, "desc") == 0) {
833 cmpfunc
= shadow_copy2_label_cmp_desc
;
838 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
839 shadow_copy2_data
->labels
)
841 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
842 shadow_copy2_data
->num_volumes
,
849 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct
*handle
,
851 SHADOW_COPY_DATA
*shadow_copy2_data
,
856 SMB_STRUCT_DIRENT
*d
;
857 TALLOC_CTX
*tmp_ctx
= talloc_new(handle
->data
);
860 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
861 if (snapdir
== NULL
) {
862 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
863 handle
->conn
->connectpath
));
865 talloc_free(tmp_ctx
);
869 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
872 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
873 " - %s\n", snapdir
, strerror(errno
)));
874 talloc_free(tmp_ctx
);
879 shadow_copy2_data
->num_volumes
= 0;
880 shadow_copy2_data
->labels
= NULL
;
882 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
883 SHADOW_COPY_LABEL
*tlabels
;
885 /* ignore names not of the right form in the snapshot directory */
886 snapshot
= shadow_copy2_snapshot_to_gmt(tmp_ctx
, handle
,
888 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
889 d
->d_name
, snapshot
));
895 /* the caller doesn't want the labels */
896 shadow_copy2_data
->num_volumes
++;
900 tlabels
= talloc_realloc(shadow_copy2_data
->mem_ctx
,
901 shadow_copy2_data
->labels
,
902 SHADOW_COPY_LABEL
, shadow_copy2_data
->num_volumes
+1);
903 if (tlabels
== NULL
) {
904 DEBUG(0,("shadow_copy2: out of memory\n"));
905 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
906 talloc_free(tmp_ctx
);
910 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
912 talloc_free(snapshot
);
914 shadow_copy2_data
->num_volumes
++;
915 shadow_copy2_data
->labels
= tlabels
;
918 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
920 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
922 talloc_free(tmp_ctx
);
926 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
927 .opendir
= shadow_copy2_opendir
,
928 .mkdir
= shadow_copy2_mkdir
,
929 .rmdir
= shadow_copy2_rmdir
,
930 .chflags
= shadow_copy2_chflags
,
931 .getxattr
= shadow_copy2_getxattr
,
932 .lgetxattr
= shadow_copy2_lgetxattr
,
933 .listxattr
= shadow_copy2_listxattr
,
934 .removexattr
= shadow_copy2_removexattr
,
935 .lremovexattr
= shadow_copy2_lremovexattr
,
936 .setxattr
= shadow_copy2_setxattr
,
937 .lsetxattr
= shadow_copy2_lsetxattr
,
938 .open
= shadow_copy2_open
,
939 .rename
= shadow_copy2_rename
,
940 .stat
= shadow_copy2_stat
,
941 .lstat
= shadow_copy2_lstat
,
942 .fstat
= shadow_copy2_fstat
,
943 .unlink
= shadow_copy2_unlink
,
944 .chmod
= shadow_copy2_chmod
,
945 .chown
= shadow_copy2_chown
,
946 .chdir
= shadow_copy2_chdir
,
947 .ntimes
= shadow_copy2_ntimes
,
948 .symlink
= shadow_copy2_symlink
,
949 .vfs_readlink
= shadow_copy2_readlink
,
950 .link
= shadow_copy2_link
,
951 .mknod
= shadow_copy2_mknod
,
952 .realpath
= shadow_copy2_realpath
,
953 .connectpath
= shadow_copy2_connectpath
,
954 .get_nt_acl
= shadow_copy2_get_nt_acl
,
955 .chmod_acl
= shadow_copy2_chmod_acl
,
956 .get_shadow_copy_data
= shadow_copy2_get_shadow_copy2_data
,
959 NTSTATUS
vfs_shadow_copy2_init(void);
960 NTSTATUS
vfs_shadow_copy2_init(void)
964 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "shadow_copy2",
965 &vfs_shadow_copy2_fns
);
967 if (!NT_STATUS_IS_OK(ret
))
970 vfs_shadow_copy2_debug_level
= debug_add_class("shadow_copy2");
971 if (vfs_shadow_copy2_debug_level
== -1) {
972 vfs_shadow_copy2_debug_level
= DBGC_VFS
;
973 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
974 "vfs_shadow_copy2_init"));
976 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
977 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level
));