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.
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
29 This is a 2nd implemetation of a shadow copy module for exposing
30 snapshots to windows clients as shadow copies. This version has the
33 1) you don't need to populate your shares with symlinks to the
34 snapshots. This can be very important when you have thousands of
35 shares, or use [homes]
37 2) the inode number of the files is altered so it is different
38 from the original. This allows the 'restore' button to work
39 without a sharing violation
41 3) shadow copy results can be sorted before being sent to the
42 client. This is beneficial for filesystems that don't read
43 directories alphabetically (the default unix).
45 4) vanity naming for snapshots. Snapshots can be named in any
46 format compatible with str[fp]time conversions.
48 5) time stamps in snapshot names can be represented in localtime
53 shadow:snapdir = <directory where snapshots are kept>
55 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
56 path it is used as-is. If it is a relative path, then it is taken relative to the mount
57 point of the filesystem that the root of this share is on
59 shadow:basedir = <base directory that snapshots are from>
61 This is an optional parameter that specifies the directory that
62 the snapshots are relative to. It defaults to the filesystem
65 shadow:fixinodes = yes/no
67 If you enable shadow:fixinodes then this module will modify the
68 apparent inode number of files in the snapshot directories using
69 a hash of the files path. This is needed for snapshot systems
70 where the snapshots have the same device:inode number as the
71 original files (such as happens with GPFS snapshots). If you
72 don't set this option then the 'restore' button in the shadow
73 copy UI will fail with a sharing violation.
75 shadow:sort = asc/desc, or not specified for unsorted (default)
77 This is an optional parameter that specifies that the shadow
78 copy directories should be sorted before sending them to the
79 client. This can be beneficial as unix filesystems are usually
80 not listed alphabetically sorted. If enabled, you typically
81 want to specify descending order.
83 shadow:format = <format specification for snapshot names>
85 This is an optional parameter that specifies the format
86 specification for the naming of snapshots. The format must
87 be compatible with the conversion specifications recognized
88 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
90 shadow:localtime = yes/no (default is no)
92 This is an optional parameter that indicates whether the
93 snapshot names are in UTC/GMT or the local time.
96 The following command would generate a correctly formatted directory name
97 for use with the default parameters:
98 date -u +@GMT-%Y.%m.%d-%H.%M.%S
102 static int vfs_shadow_copy2_debug_level
= DBGC_VFS
;
105 #define DBGC_CLASS vfs_shadow_copy2_debug_level
107 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
108 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
110 #define SHADOW_COPY2_DEFAULT_SORT NULL
111 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
112 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
115 make very sure it is one of our special names
117 static inline bool shadow_copy2_match_name(const char *name
, const char **gmt_start
)
119 unsigned year
, month
, day
, hr
, min
, sec
;
124 p
= strstr_m(name
, "@GMT-");
125 if (p
== NULL
) return false;
126 if (p
> name
&& p
[-1] != '/') return False
;
127 if (sscanf(p
, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year
, &month
,
128 &day
, &hr
, &min
, &sec
) != 6) {
131 if (p
[24] != 0 && p
[24] != '/') {
140 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX
*mem_ctx
,
141 vfs_handle_struct
*handle
, const char *name
)
145 char gmt
[GMT_NAME_LEN
+ 1];
148 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
149 "format", SHADOW_COPY2_DEFAULT_FORMAT
);
151 ZERO_STRUCT(timestamp
);
152 if (strptime(name
, fmt
, ×tamp
) == NULL
) {
153 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
158 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt
, name
));
159 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime",
160 SHADOW_COPY2_DEFAULT_LOCALTIME
))
162 timestamp
.tm_isdst
= -1;
163 timestamp_t
= mktime(×tamp
);
164 gmtime_r(×tamp_t
, ×tamp
);
166 strftime(gmt
, sizeof(gmt
), SHADOW_COPY2_GMT_FORMAT
, ×tamp
);
168 return talloc_strdup(mem_ctx
, gmt
);
172 shadow copy paths can also come into the server in this form:
174 /foo/bar/@GMT-XXXXX/some/file
176 This function normalises the filename to be of the form:
178 @GMT-XXXX/foo/bar/some/file
180 static const char *shadow_copy2_normalise_path(TALLOC_CTX
*mem_ctx
, const char *path
, const char *gmt_start
)
183 char buf
[GMT_NAME_LEN
];
186 if (path
== gmt_start
) {
190 prefix_len
= gmt_start
- path
- 1;
192 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path
, gmt_start
,
196 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
197 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
198 * processing. As many VFS calls provide a const char *,
199 * unfortunately we have to make a copy.
202 pcopy
= talloc_strdup(talloc_tos(), path
);
207 gmt_start
= pcopy
+ prefix_len
;
210 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
212 memcpy(buf
, gmt_start
+1, GMT_NAME_LEN
);
215 * Make space for it including a trailing /
217 memmove(pcopy
+ GMT_NAME_LEN
+ 1, pcopy
, prefix_len
);
220 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
222 memcpy(pcopy
, buf
, GMT_NAME_LEN
);
223 pcopy
[GMT_NAME_LEN
] = '/';
225 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path
, pcopy
));
231 convert a name to the shadow directory
234 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
235 const char *name = fname; \
236 const char *gmt_start; \
237 if (shadow_copy2_match_name(fname, &gmt_start)) { \
240 name2 = convert_shadow2_name(handle, fname, gmt_start); \
241 if (name2 == NULL) { \
246 ret = SMB_VFS_NEXT_ ## op args; \
247 talloc_free(name2); \
248 if (ret != eret) extra; \
251 return SMB_VFS_NEXT_ ## op args; \
255 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
256 const char *gmt_start; \
257 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
259 char *smb_base_name_tmp = NULL; \
261 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
262 if (name2 == NULL) { \
266 smb_base_name_tmp = smb_fname->base_name; \
267 smb_fname->base_name = name2; \
268 ret = SMB_VFS_NEXT_ ## op args; \
269 smb_fname->base_name = smb_base_name_tmp; \
270 talloc_free(name2); \
271 if (ret != eret) extra; \
274 return SMB_VFS_NEXT_ ## op args; \
279 convert a name to the shadow directory: NTSTATUS-specific handling
282 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
283 const char *name = fname; \
284 const char *gmt_start; \
285 if (shadow_copy2_match_name(fname, &gmt_start)) { \
288 name2 = convert_shadow2_name(handle, fname, gmt_start); \
289 if (name2 == NULL) { \
294 ret = SMB_VFS_NEXT_ ## op args; \
295 talloc_free(name2); \
296 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
299 return SMB_VFS_NEXT_ ## op args; \
303 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
305 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
307 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
309 #define SHADOW2_NEXT2(op, args) do { \
310 const char *gmt_start1, *gmt_start2; \
311 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
312 shadow_copy2_match_name(newname, &gmt_start2)) { \
316 return SMB_VFS_NEXT_ ## op args; \
320 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
321 const char *gmt_start1, *gmt_start2; \
322 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
323 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
327 return SMB_VFS_NEXT_ ## op args; \
333 find the mount point of a filesystem
335 static char *find_mount_point(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
337 char *path
= talloc_strdup(mem_ctx
, handle
->conn
->connectpath
);
342 if (stat(path
, &st
) != 0) {
349 while ((p
= strrchr(path
, '/')) && p
> path
) {
351 if (stat(path
, &st
) != 0) {
355 if (st
.st_dev
!= dev
) {
365 work out the location of the snapshot for this share
367 static const char *shadow_copy2_find_snapdir(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
373 snapdir
= lp_parm_const_string(SNUM(handle
->conn
), "shadow", "snapdir", NULL
);
374 if (snapdir
== NULL
) {
377 /* if its an absolute path, we're done */
378 if (*snapdir
== '/') {
382 /* other its relative to the filesystem mount point */
383 mount_point
= find_mount_point(mem_ctx
, handle
);
384 if (mount_point
== NULL
) {
388 ret
= talloc_asprintf(mem_ctx
, "%s/%s", mount_point
, snapdir
);
389 talloc_free(mount_point
);
394 work out the location of the base directory for snapshots of this share
396 static const char *shadow_copy2_find_basedir(TALLOC_CTX
*mem_ctx
, vfs_handle_struct
*handle
)
398 const char *basedir
= lp_parm_const_string(SNUM(handle
->conn
), "shadow", "basedir", NULL
);
400 /* other its the filesystem mount point */
401 if (basedir
== NULL
) {
402 basedir
= find_mount_point(mem_ctx
, handle
);
409 convert a filename from a share relative path, to a path in the
412 static char *convert_shadow2_name(vfs_handle_struct
*handle
, const char *fname
, const char *gmt_path
)
414 TALLOC_CTX
*tmp_ctx
= talloc_new(handle
->data
);
415 const char *snapdir
, *relpath
, *baseoffset
, *basedir
;
421 char snapshot
[MAXPATHLEN
];
424 fmt
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
425 "format", SHADOW_COPY2_DEFAULT_FORMAT
);
427 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
428 if (snapdir
== NULL
) {
429 DEBUG(2,("no snapdir found for share at %s\n", handle
->conn
->connectpath
));
430 talloc_free(tmp_ctx
);
434 basedir
= shadow_copy2_find_basedir(tmp_ctx
, handle
);
435 if (basedir
== NULL
) {
436 DEBUG(2,("no basedir found for share at %s\n", handle
->conn
->connectpath
));
437 talloc_free(tmp_ctx
);
441 prefix
= talloc_asprintf(tmp_ctx
, "%s/@GMT-", snapdir
);
442 if (strncmp(fname
, prefix
, (talloc_get_size(prefix
)-1)) == 0) {
443 /* this looks like as we have already normalized it, leave it untouched*/
444 talloc_free(tmp_ctx
);
445 return talloc_strdup(handle
->data
, fname
);
448 if (strncmp(fname
, "@GMT-", 5) != 0) {
449 fname
= shadow_copy2_normalise_path(tmp_ctx
, fname
, gmt_path
);
451 talloc_free(tmp_ctx
);
456 ZERO_STRUCT(timestamp
);
457 relpath
= strptime(fname
, SHADOW_COPY2_GMT_FORMAT
, ×tamp
);
458 if (relpath
== NULL
) {
459 talloc_free(tmp_ctx
);
463 /* relpath is the remaining portion of the path after the @GMT-xxx */
465 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime",
466 SHADOW_COPY2_DEFAULT_LOCALTIME
))
468 timestamp_t
= timegm(×tamp
);
469 localtime_r(×tamp_t
, ×tamp
);
472 strftime(snapshot
, MAXPATHLEN
, fmt
, ×tamp
);
474 baselen
= strlen(basedir
);
475 baseoffset
= handle
->conn
->connectpath
+ baselen
;
477 /* some sanity checks */
478 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
479 (handle
->conn
->connectpath
[baselen
] != 0 && handle
->conn
->connectpath
[baselen
] != '/')) {
480 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
481 basedir
, handle
->conn
->connectpath
));
482 talloc_free(tmp_ctx
);
486 if (*relpath
== '/') relpath
++;
487 if (*baseoffset
== '/') baseoffset
++;
489 ret
= talloc_asprintf(handle
->data
, "%s/%s/%s/%s",
494 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname
, ret
));
495 talloc_free(tmp_ctx
);
503 static uint32
string_hash(const char *s
)
507 n
= ((n
<< 5) + n
) ^ (uint32
)(*s
++);
513 modify a sbuf return to ensure that inodes in the shadow directory
514 are different from those in the main directory
516 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
, SMB_STRUCT_STAT
*sbuf
)
518 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
519 /* some snapshot systems, like GPFS, return the name
520 device:inode for the snapshot files as the current
521 files. That breaks the 'restore' button in the shadow copy
522 GUI, as the client gets a sharing violation.
524 This is a crude way of allowing both files to be
525 open at once. It has a slight chance of inode
526 number collision, but I can't see a better approach
527 without significant VFS changes
529 uint32_t shash
= string_hash(fname
) & 0xFF000000;
533 sbuf
->st_ex_ino
^= shash
;
537 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
538 const struct smb_filename
*smb_fname_src
,
539 const struct smb_filename
*smb_fname_dst
)
541 if (shadow_copy2_match_name(smb_fname_src
->base_name
, NULL
)) {
545 SHADOW2_NEXT2_SMB_FNAME(RENAME
,
546 (handle
, smb_fname_src
, smb_fname_dst
));
549 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
550 const char *oldname
, const char *newname
)
552 SHADOW2_NEXT2(SYMLINK
, (handle
, oldname
, newname
));
555 static int shadow_copy2_link(vfs_handle_struct
*handle
,
556 const char *oldname
, const char *newname
)
558 SHADOW2_NEXT2(LINK
, (handle
, oldname
, newname
));
561 static int shadow_copy2_open(vfs_handle_struct
*handle
,
562 struct smb_filename
*smb_fname
, files_struct
*fsp
,
563 int flags
, mode_t mode
)
565 SHADOW2_NEXT_SMB_FNAME(OPEN
,
566 (handle
, smb_fname
, fsp
, flags
, mode
),
570 static SMB_STRUCT_DIR
*shadow_copy2_opendir(vfs_handle_struct
*handle
,
571 const char *fname
, const char *mask
, uint32 attr
)
573 SHADOW2_NEXT(OPENDIR
, (handle
, name
, mask
, attr
), SMB_STRUCT_DIR
*, NULL
);
576 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
577 struct smb_filename
*smb_fname
)
579 _SHADOW2_NEXT_SMB_FNAME(STAT
, (handle
, smb_fname
), int, -1,
580 convert_sbuf(handle
, smb_fname
->base_name
,
584 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
585 struct smb_filename
*smb_fname
)
587 _SHADOW2_NEXT_SMB_FNAME(LSTAT
, (handle
, smb_fname
), int, -1,
588 convert_sbuf(handle
, smb_fname
->base_name
,
592 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
594 int ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
595 if (ret
== 0 && shadow_copy2_match_name(fsp
->fsp_name
->base_name
, NULL
)) {
596 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
601 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
602 const struct smb_filename
*smb_fname_in
)
604 struct smb_filename
*smb_fname
= NULL
;
607 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
608 if (!NT_STATUS_IS_OK(status
)) {
609 errno
= map_errno_from_nt_status(status
);
613 SHADOW2_NEXT_SMB_FNAME(UNLINK
, (handle
, smb_fname
), int, -1);
616 static int shadow_copy2_chmod(vfs_handle_struct
*handle
,
617 const char *fname
, mode_t mode
)
619 SHADOW2_NEXT(CHMOD
, (handle
, name
, mode
), int, -1);
622 static int shadow_copy2_chown(vfs_handle_struct
*handle
,
623 const char *fname
, uid_t uid
, gid_t gid
)
625 SHADOW2_NEXT(CHOWN
, (handle
, name
, uid
, gid
), int, -1);
628 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
631 SHADOW2_NEXT(CHDIR
, (handle
, name
), int, -1);
634 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
635 const struct smb_filename
*smb_fname_in
,
636 struct smb_file_time
*ft
)
638 struct smb_filename
*smb_fname
= NULL
;
641 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
642 if (!NT_STATUS_IS_OK(status
)) {
643 errno
= map_errno_from_nt_status(status
);
647 SHADOW2_NEXT_SMB_FNAME(NTIMES
, (handle
, smb_fname
, ft
), int, -1);
650 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
651 const char *fname
, char *buf
, size_t bufsiz
)
653 SHADOW2_NEXT(READLINK
, (handle
, name
, buf
, bufsiz
), int, -1);
656 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
657 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
659 SHADOW2_NEXT(MKNOD
, (handle
, name
, mode
, dev
), int, -1);
662 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
667 if (shadow_copy2_match_name(fname
, &gmt
)
668 && (gmt
[GMT_NAME_LEN
] == '\0')) {
671 copy
= talloc_strdup(talloc_tos(), fname
);
677 copy
[gmt
- fname
] = '.';
678 copy
[gmt
- fname
+ 1] = '\0';
680 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy
));
681 SHADOW2_NEXT(REALPATH
, (handle
, name
), char *,
684 SHADOW2_NEXT(REALPATH
, (handle
, name
), char *, NULL
);
687 static const char *shadow_copy2_connectpath(struct vfs_handle_struct
*handle
,
691 const char *snapdir
, *baseoffset
, *basedir
, *gmt_start
;
695 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname
));
697 if (!shadow_copy2_match_name(fname
, &gmt_start
)) {
698 return handle
->conn
->connectpath
;
702 * We have to create a real temporary context because we have
703 * to put our result on talloc_tos(). Thus we can't use a
704 * talloc_stackframe() here.
706 tmp_ctx
= talloc_new(talloc_tos());
708 fname
= shadow_copy2_normalise_path(tmp_ctx
, fname
, gmt_start
);
710 TALLOC_FREE(tmp_ctx
);
714 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
715 if (snapdir
== NULL
) {
716 DEBUG(2,("no snapdir found for share at %s\n",
717 handle
->conn
->connectpath
));
718 TALLOC_FREE(tmp_ctx
);
722 basedir
= shadow_copy2_find_basedir(tmp_ctx
, handle
);
723 if (basedir
== NULL
) {
724 DEBUG(2,("no basedir found for share at %s\n",
725 handle
->conn
->connectpath
));
726 TALLOC_FREE(tmp_ctx
);
730 baselen
= strlen(basedir
);
731 baseoffset
= handle
->conn
->connectpath
+ baselen
;
733 /* some sanity checks */
734 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
735 (handle
->conn
->connectpath
[baselen
] != 0
736 && handle
->conn
->connectpath
[baselen
] != '/')) {
737 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
738 "parent of %s\n", basedir
,
739 handle
->conn
->connectpath
));
740 TALLOC_FREE(tmp_ctx
);
744 if (*baseoffset
== '/') baseoffset
++;
746 ret
= talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
750 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname
, ret
));
751 TALLOC_FREE(tmp_ctx
);
755 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
756 const char *fname
, uint32 security_info
,
757 struct security_descriptor
**ppdesc
)
759 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL
, (handle
, name
, security_info
, ppdesc
), NT_STATUS_ACCESS_DENIED
);
762 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
764 SHADOW2_NEXT(MKDIR
, (handle
, name
, mode
), int, -1);
767 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
769 SHADOW2_NEXT(RMDIR
, (handle
, name
), int, -1);
772 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
775 SHADOW2_NEXT(CHFLAGS
, (handle
, name
, flags
), int, -1);
778 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
779 const char *fname
, const char *aname
, void *value
, size_t size
)
781 SHADOW2_NEXT(GETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
784 static ssize_t
shadow_copy2_lgetxattr(vfs_handle_struct
*handle
,
785 const char *fname
, const char *aname
, void *value
, size_t size
)
787 SHADOW2_NEXT(LGETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
790 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
, const char *fname
,
791 char *list
, size_t size
)
793 SHADOW2_NEXT(LISTXATTR
, (handle
, name
, list
, size
), ssize_t
, -1);
796 static int shadow_copy2_removexattr(struct vfs_handle_struct
*handle
, const char *fname
,
799 SHADOW2_NEXT(REMOVEXATTR
, (handle
, name
, aname
), int, -1);
802 static int shadow_copy2_lremovexattr(struct vfs_handle_struct
*handle
, const char *fname
,
805 SHADOW2_NEXT(LREMOVEXATTR
, (handle
, name
, aname
), int, -1);
808 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
, const char *fname
,
809 const char *aname
, const void *value
, size_t size
, int flags
)
811 SHADOW2_NEXT(SETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
814 static int shadow_copy2_lsetxattr(struct vfs_handle_struct
*handle
, const char *fname
,
815 const char *aname
, const void *value
, size_t size
, int flags
)
817 SHADOW2_NEXT(LSETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
820 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
821 const char *fname
, mode_t mode
)
823 SHADOW2_NEXT(CHMOD_ACL
, (handle
, name
, mode
), int, -1);
826 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
828 return strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
831 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
833 return -strncmp((const char *)x
, (const char *)y
, sizeof(SHADOW_COPY_LABEL
));
837 sort the shadow copy data in ascending or descending order
839 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
840 SHADOW_COPY_DATA
*shadow_copy2_data
)
842 int (*cmpfunc
)(const void *, const void *);
845 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
846 "sort", SHADOW_COPY2_DEFAULT_SORT
);
851 if (strcmp(sort
, "asc") == 0) {
852 cmpfunc
= shadow_copy2_label_cmp_asc
;
853 } else if (strcmp(sort
, "desc") == 0) {
854 cmpfunc
= shadow_copy2_label_cmp_desc
;
859 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
860 shadow_copy2_data
->labels
)
862 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
863 shadow_copy2_data
->num_volumes
,
870 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct
*handle
,
872 SHADOW_COPY_DATA
*shadow_copy2_data
,
877 SMB_STRUCT_DIRENT
*d
;
878 TALLOC_CTX
*tmp_ctx
= talloc_new(handle
->data
);
881 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
882 if (snapdir
== NULL
) {
883 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
884 handle
->conn
->connectpath
));
886 talloc_free(tmp_ctx
);
890 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
893 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
894 " - %s\n", snapdir
, strerror(errno
)));
895 talloc_free(tmp_ctx
);
900 shadow_copy2_data
->num_volumes
= 0;
901 shadow_copy2_data
->labels
= NULL
;
903 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
904 SHADOW_COPY_LABEL
*tlabels
;
906 /* ignore names not of the right form in the snapshot directory */
907 snapshot
= shadow_copy2_snapshot_to_gmt(tmp_ctx
, handle
,
909 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
910 d
->d_name
, snapshot
));
916 /* the caller doesn't want the labels */
917 shadow_copy2_data
->num_volumes
++;
921 tlabels
= talloc_realloc(shadow_copy2_data
->mem_ctx
,
922 shadow_copy2_data
->labels
,
923 SHADOW_COPY_LABEL
, shadow_copy2_data
->num_volumes
+1);
924 if (tlabels
== NULL
) {
925 DEBUG(0,("shadow_copy2: out of memory\n"));
926 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
927 talloc_free(tmp_ctx
);
931 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
933 talloc_free(snapshot
);
935 shadow_copy2_data
->num_volumes
++;
936 shadow_copy2_data
->labels
= tlabels
;
939 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
941 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
943 talloc_free(tmp_ctx
);
947 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
948 .opendir
= shadow_copy2_opendir
,
949 .mkdir
= shadow_copy2_mkdir
,
950 .rmdir
= shadow_copy2_rmdir
,
951 .chflags
= shadow_copy2_chflags
,
952 .getxattr
= shadow_copy2_getxattr
,
953 .lgetxattr
= shadow_copy2_lgetxattr
,
954 .listxattr
= shadow_copy2_listxattr
,
955 .removexattr
= shadow_copy2_removexattr
,
956 .lremovexattr
= shadow_copy2_lremovexattr
,
957 .setxattr
= shadow_copy2_setxattr
,
958 .lsetxattr
= shadow_copy2_lsetxattr
,
959 .open_fn
= shadow_copy2_open
,
960 .rename
= shadow_copy2_rename
,
961 .stat
= shadow_copy2_stat
,
962 .lstat
= shadow_copy2_lstat
,
963 .fstat
= shadow_copy2_fstat
,
964 .unlink
= shadow_copy2_unlink
,
965 .chmod
= shadow_copy2_chmod
,
966 .chown
= shadow_copy2_chown
,
967 .chdir
= shadow_copy2_chdir
,
968 .ntimes
= shadow_copy2_ntimes
,
969 .symlink
= shadow_copy2_symlink
,
970 .vfs_readlink
= shadow_copy2_readlink
,
971 .link
= shadow_copy2_link
,
972 .mknod
= shadow_copy2_mknod
,
973 .realpath
= shadow_copy2_realpath
,
974 .connectpath
= shadow_copy2_connectpath
,
975 .get_nt_acl
= shadow_copy2_get_nt_acl
,
976 .chmod_acl
= shadow_copy2_chmod_acl
,
977 .get_shadow_copy_data
= shadow_copy2_get_shadow_copy2_data
,
980 NTSTATUS
vfs_shadow_copy2_init(void);
981 NTSTATUS
vfs_shadow_copy2_init(void)
985 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "shadow_copy2",
986 &vfs_shadow_copy2_fns
);
988 if (!NT_STATUS_IS_OK(ret
))
991 vfs_shadow_copy2_debug_level
= debug_add_class("shadow_copy2");
992 if (vfs_shadow_copy2_debug_level
== -1) {
993 vfs_shadow_copy2_debug_level
= DBGC_VFS
;
994 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
995 "vfs_shadow_copy2_init"));
997 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
998 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level
));