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 prefix
= talloc_asprintf(tmp_ctx
, "%s/@GMT-", snapdir
);
439 if (strncmp(fname
, prefix
, (talloc_get_size(prefix
)-1)) == 0) {
440 /* this looks like as we have already normalized it, leave it untouched*/
441 talloc_free(tmp_ctx
);
442 return talloc_strdup(handle
->data
, fname
);
445 if (strncmp(fname
, "@GMT-", 5) != 0) {
446 fname
= shadow_copy2_normalise_path(tmp_ctx
, fname
, gmt_path
);
448 talloc_free(tmp_ctx
);
453 ZERO_STRUCT(timestamp
);
454 relpath
= strptime(fname
, SHADOW_COPY2_GMT_FORMAT
, ×tamp
);
455 if (relpath
== NULL
) {
456 talloc_free(tmp_ctx
);
460 /* relpath is the remaining portion of the path after the @GMT-xxx */
462 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "localtime",
463 SHADOW_COPY2_DEFAULT_LOCALTIME
))
465 timestamp_t
= timegm(×tamp
);
466 localtime_r(×tamp_t
, ×tamp
);
469 strftime(snapshot
, MAXPATHLEN
, fmt
, ×tamp
);
471 baselen
= strlen(basedir
);
472 baseoffset
= handle
->conn
->connectpath
+ baselen
;
474 /* some sanity checks */
475 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
476 (handle
->conn
->connectpath
[baselen
] != 0 && handle
->conn
->connectpath
[baselen
] != '/')) {
477 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
478 basedir
, handle
->conn
->connectpath
));
479 talloc_free(tmp_ctx
);
483 if (*relpath
== '/') relpath
++;
484 if (*baseoffset
== '/') baseoffset
++;
486 ret
= talloc_asprintf(handle
->data
, "%s/%s/%s/%s",
491 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname
, ret
));
492 talloc_free(tmp_ctx
);
500 static uint32
string_hash(const char *s
)
504 n
= ((n
<< 5) + n
) ^ (uint32
)(*s
++);
510 modify a sbuf return to ensure that inodes in the shadow directory
511 are different from those in the main directory
513 static void convert_sbuf(vfs_handle_struct
*handle
, const char *fname
, SMB_STRUCT_STAT
*sbuf
)
515 if (lp_parm_bool(SNUM(handle
->conn
), "shadow", "fixinodes", False
)) {
516 /* some snapshot systems, like GPFS, return the name
517 device:inode for the snapshot files as the current
518 files. That breaks the 'restore' button in the shadow copy
519 GUI, as the client gets a sharing violation.
521 This is a crude way of allowing both files to be
522 open at once. It has a slight chance of inode
523 number collision, but I can't see a better approach
524 without significant VFS changes
526 uint32_t shash
= string_hash(fname
) & 0xFF000000;
530 sbuf
->st_ex_ino
^= shash
;
534 static int shadow_copy2_rename(vfs_handle_struct
*handle
,
535 const struct smb_filename
*smb_fname_src
,
536 const struct smb_filename
*smb_fname_dst
)
538 SHADOW2_NEXT2_SMB_FNAME(RENAME
,
539 (handle
, smb_fname_src
, smb_fname_dst
));
542 static int shadow_copy2_symlink(vfs_handle_struct
*handle
,
543 const char *oldname
, const char *newname
)
545 SHADOW2_NEXT2(SYMLINK
, (handle
, oldname
, newname
));
548 static int shadow_copy2_link(vfs_handle_struct
*handle
,
549 const char *oldname
, const char *newname
)
551 SHADOW2_NEXT2(LINK
, (handle
, oldname
, newname
));
554 static int shadow_copy2_open(vfs_handle_struct
*handle
,
555 struct smb_filename
*smb_fname
, files_struct
*fsp
,
556 int flags
, mode_t mode
)
558 SHADOW2_NEXT_SMB_FNAME(OPEN
,
559 (handle
, smb_fname
, fsp
, flags
, mode
),
563 static SMB_STRUCT_DIR
*shadow_copy2_opendir(vfs_handle_struct
*handle
,
564 const char *fname
, const char *mask
, uint32 attr
)
566 SHADOW2_NEXT(OPENDIR
, (handle
, name
, mask
, attr
), SMB_STRUCT_DIR
*, NULL
);
569 static int shadow_copy2_stat(vfs_handle_struct
*handle
,
570 struct smb_filename
*smb_fname
)
572 _SHADOW2_NEXT_SMB_FNAME(STAT
, (handle
, smb_fname
), int, -1,
573 convert_sbuf(handle
, smb_fname
->base_name
,
577 static int shadow_copy2_lstat(vfs_handle_struct
*handle
,
578 struct smb_filename
*smb_fname
)
580 _SHADOW2_NEXT_SMB_FNAME(LSTAT
, (handle
, smb_fname
), int, -1,
581 convert_sbuf(handle
, smb_fname
->base_name
,
585 static int shadow_copy2_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
, SMB_STRUCT_STAT
*sbuf
)
587 int ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
588 if (ret
== 0 && shadow_copy2_match_name(fsp
->fsp_name
->base_name
, NULL
)) {
589 convert_sbuf(handle
, fsp
->fsp_name
->base_name
, sbuf
);
594 static int shadow_copy2_unlink(vfs_handle_struct
*handle
,
595 const struct smb_filename
*smb_fname_in
)
597 struct smb_filename
*smb_fname
= NULL
;
600 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
601 if (!NT_STATUS_IS_OK(status
)) {
602 errno
= map_errno_from_nt_status(status
);
606 SHADOW2_NEXT_SMB_FNAME(UNLINK
, (handle
, smb_fname
), int, -1);
609 static int shadow_copy2_chmod(vfs_handle_struct
*handle
,
610 const char *fname
, mode_t mode
)
612 SHADOW2_NEXT(CHMOD
, (handle
, name
, mode
), int, -1);
615 static int shadow_copy2_chown(vfs_handle_struct
*handle
,
616 const char *fname
, uid_t uid
, gid_t gid
)
618 SHADOW2_NEXT(CHOWN
, (handle
, name
, uid
, gid
), int, -1);
621 static int shadow_copy2_chdir(vfs_handle_struct
*handle
,
624 SHADOW2_NEXT(CHDIR
, (handle
, name
), int, -1);
627 static int shadow_copy2_ntimes(vfs_handle_struct
*handle
,
628 const struct smb_filename
*smb_fname_in
,
629 struct smb_file_time
*ft
)
631 struct smb_filename
*smb_fname
= NULL
;
634 status
= copy_smb_filename(talloc_tos(), smb_fname_in
, &smb_fname
);
635 if (!NT_STATUS_IS_OK(status
)) {
636 errno
= map_errno_from_nt_status(status
);
640 SHADOW2_NEXT_SMB_FNAME(NTIMES
, (handle
, smb_fname
, ft
), int, -1);
643 static int shadow_copy2_readlink(vfs_handle_struct
*handle
,
644 const char *fname
, char *buf
, size_t bufsiz
)
646 SHADOW2_NEXT(READLINK
, (handle
, name
, buf
, bufsiz
), int, -1);
649 static int shadow_copy2_mknod(vfs_handle_struct
*handle
,
650 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
652 SHADOW2_NEXT(MKNOD
, (handle
, name
, mode
, dev
), int, -1);
655 static char *shadow_copy2_realpath(vfs_handle_struct
*handle
,
660 if (shadow_copy2_match_name(fname
, &gmt
)
661 && (gmt
[GMT_NAME_LEN
] == '\0')) {
664 copy
= talloc_strdup(talloc_tos(), fname
);
670 copy
[gmt
- fname
] = '.';
671 copy
[gmt
- fname
+ 1] = '\0';
673 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy
));
674 SHADOW2_NEXT(REALPATH
, (handle
, fname
), char *,
677 SHADOW2_NEXT(REALPATH
, (handle
, fname
), char *, NULL
);
680 static const char *shadow_copy2_connectpath(struct vfs_handle_struct
*handle
,
683 TALLOC_CTX
*tmp_ctx
= talloc_stackframe();
684 const char *snapdir
, *baseoffset
, *basedir
, *gmt_start
;
688 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname
));
690 if (!shadow_copy2_match_name(fname
, &gmt_start
)) {
691 return handle
->conn
->connectpath
;
694 fname
= shadow_copy2_normalise_path(talloc_tos(), fname
, gmt_start
);
696 TALLOC_FREE(tmp_ctx
);
700 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
701 if (snapdir
== NULL
) {
702 DEBUG(2,("no snapdir found for share at %s\n",
703 handle
->conn
->connectpath
));
704 TALLOC_FREE(tmp_ctx
);
708 basedir
= shadow_copy2_find_basedir(tmp_ctx
, handle
);
709 if (basedir
== NULL
) {
710 DEBUG(2,("no basedir found for share at %s\n",
711 handle
->conn
->connectpath
));
712 TALLOC_FREE(tmp_ctx
);
716 baselen
= strlen(basedir
);
717 baseoffset
= handle
->conn
->connectpath
+ baselen
;
719 /* some sanity checks */
720 if (strncmp(basedir
, handle
->conn
->connectpath
, baselen
) != 0 ||
721 (handle
->conn
->connectpath
[baselen
] != 0
722 && handle
->conn
->connectpath
[baselen
] != '/')) {
723 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
724 "parent of %s\n", basedir
,
725 handle
->conn
->connectpath
));
726 TALLOC_FREE(tmp_ctx
);
730 if (*baseoffset
== '/') baseoffset
++;
732 ret
= talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
736 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname
, ret
));
737 TALLOC_FREE(tmp_ctx
);
741 static NTSTATUS
shadow_copy2_get_nt_acl(vfs_handle_struct
*handle
,
742 const char *fname
, uint32 security_info
,
743 struct security_descriptor
**ppdesc
)
745 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL
, (handle
, name
, security_info
, ppdesc
), NT_STATUS_ACCESS_DENIED
);
748 static int shadow_copy2_mkdir(vfs_handle_struct
*handle
, const char *fname
, mode_t mode
)
750 SHADOW2_NEXT(MKDIR
, (handle
, name
, mode
), int, -1);
753 static int shadow_copy2_rmdir(vfs_handle_struct
*handle
, const char *fname
)
755 SHADOW2_NEXT(RMDIR
, (handle
, name
), int, -1);
758 static int shadow_copy2_chflags(vfs_handle_struct
*handle
, const char *fname
,
761 SHADOW2_NEXT(CHFLAGS
, (handle
, name
, flags
), int, -1);
764 static ssize_t
shadow_copy2_getxattr(vfs_handle_struct
*handle
,
765 const char *fname
, const char *aname
, void *value
, size_t size
)
767 SHADOW2_NEXT(GETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
770 static ssize_t
shadow_copy2_lgetxattr(vfs_handle_struct
*handle
,
771 const char *fname
, const char *aname
, void *value
, size_t size
)
773 SHADOW2_NEXT(LGETXATTR
, (handle
, name
, aname
, value
, size
), ssize_t
, -1);
776 static ssize_t
shadow_copy2_listxattr(struct vfs_handle_struct
*handle
, const char *fname
,
777 char *list
, size_t size
)
779 SHADOW2_NEXT(LISTXATTR
, (handle
, name
, list
, size
), ssize_t
, -1);
782 static int shadow_copy2_removexattr(struct vfs_handle_struct
*handle
, const char *fname
,
785 SHADOW2_NEXT(REMOVEXATTR
, (handle
, name
, aname
), int, -1);
788 static int shadow_copy2_lremovexattr(struct vfs_handle_struct
*handle
, const char *fname
,
791 SHADOW2_NEXT(LREMOVEXATTR
, (handle
, name
, aname
), int, -1);
794 static int shadow_copy2_setxattr(struct vfs_handle_struct
*handle
, const char *fname
,
795 const char *aname
, const void *value
, size_t size
, int flags
)
797 SHADOW2_NEXT(SETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
800 static int shadow_copy2_lsetxattr(struct vfs_handle_struct
*handle
, const char *fname
,
801 const char *aname
, const void *value
, size_t size
, int flags
)
803 SHADOW2_NEXT(LSETXATTR
, (handle
, name
, aname
, value
, size
, flags
), int, -1);
806 static int shadow_copy2_chmod_acl(vfs_handle_struct
*handle
,
807 const char *fname
, mode_t mode
)
809 SHADOW2_NEXT(CHMOD_ACL
, (handle
, name
, mode
), int, -1);
812 static int shadow_copy2_label_cmp_asc(const void *x
, const void *y
)
814 return strncmp((char *)x
, (char *)y
, sizeof(SHADOW_COPY_LABEL
));
817 static int shadow_copy2_label_cmp_desc(const void *x
, const void *y
)
819 return -strncmp((char *)x
, (char *)y
, sizeof(SHADOW_COPY_LABEL
));
823 sort the shadow copy data in ascending or descending order
825 static void shadow_copy2_sort_data(vfs_handle_struct
*handle
,
826 SHADOW_COPY_DATA
*shadow_copy2_data
)
828 int (*cmpfunc
)(const void *, const void *);
831 sort
= lp_parm_const_string(SNUM(handle
->conn
), "shadow",
832 "sort", SHADOW_COPY2_DEFAULT_SORT
);
837 if (strcmp(sort
, "asc") == 0) {
838 cmpfunc
= shadow_copy2_label_cmp_asc
;
839 } else if (strcmp(sort
, "desc") == 0) {
840 cmpfunc
= shadow_copy2_label_cmp_desc
;
845 if (shadow_copy2_data
&& shadow_copy2_data
->num_volumes
> 0 &&
846 shadow_copy2_data
->labels
)
848 TYPESAFE_QSORT(shadow_copy2_data
->labels
,
849 shadow_copy2_data
->num_volumes
,
856 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct
*handle
,
858 SHADOW_COPY_DATA
*shadow_copy2_data
,
863 SMB_STRUCT_DIRENT
*d
;
864 TALLOC_CTX
*tmp_ctx
= talloc_new(handle
->data
);
867 snapdir
= shadow_copy2_find_snapdir(tmp_ctx
, handle
);
868 if (snapdir
== NULL
) {
869 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
870 handle
->conn
->connectpath
));
872 talloc_free(tmp_ctx
);
876 p
= SMB_VFS_NEXT_OPENDIR(handle
, snapdir
, NULL
, 0);
879 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
880 " - %s\n", snapdir
, strerror(errno
)));
881 talloc_free(tmp_ctx
);
886 shadow_copy2_data
->num_volumes
= 0;
887 shadow_copy2_data
->labels
= NULL
;
889 while ((d
= SMB_VFS_NEXT_READDIR(handle
, p
, NULL
))) {
890 SHADOW_COPY_LABEL
*tlabels
;
892 /* ignore names not of the right form in the snapshot directory */
893 snapshot
= shadow_copy2_snapshot_to_gmt(tmp_ctx
, handle
,
895 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
896 d
->d_name
, snapshot
));
902 /* the caller doesn't want the labels */
903 shadow_copy2_data
->num_volumes
++;
907 tlabels
= talloc_realloc(shadow_copy2_data
->mem_ctx
,
908 shadow_copy2_data
->labels
,
909 SHADOW_COPY_LABEL
, shadow_copy2_data
->num_volumes
+1);
910 if (tlabels
== NULL
) {
911 DEBUG(0,("shadow_copy2: out of memory\n"));
912 SMB_VFS_NEXT_CLOSEDIR(handle
, p
);
913 talloc_free(tmp_ctx
);
917 strlcpy(tlabels
[shadow_copy2_data
->num_volumes
], snapshot
,
919 talloc_free(snapshot
);
921 shadow_copy2_data
->num_volumes
++;
922 shadow_copy2_data
->labels
= tlabels
;
925 SMB_VFS_NEXT_CLOSEDIR(handle
,p
);
927 shadow_copy2_sort_data(handle
, shadow_copy2_data
);
929 talloc_free(tmp_ctx
);
933 static struct vfs_fn_pointers vfs_shadow_copy2_fns
= {
934 .opendir
= shadow_copy2_opendir
,
935 .mkdir
= shadow_copy2_mkdir
,
936 .rmdir
= shadow_copy2_rmdir
,
937 .chflags
= shadow_copy2_chflags
,
938 .getxattr
= shadow_copy2_getxattr
,
939 .lgetxattr
= shadow_copy2_lgetxattr
,
940 .listxattr
= shadow_copy2_listxattr
,
941 .removexattr
= shadow_copy2_removexattr
,
942 .lremovexattr
= shadow_copy2_lremovexattr
,
943 .setxattr
= shadow_copy2_setxattr
,
944 .lsetxattr
= shadow_copy2_lsetxattr
,
945 .open
= shadow_copy2_open
,
946 .rename
= shadow_copy2_rename
,
947 .stat
= shadow_copy2_stat
,
948 .lstat
= shadow_copy2_lstat
,
949 .fstat
= shadow_copy2_fstat
,
950 .unlink
= shadow_copy2_unlink
,
951 .chmod
= shadow_copy2_chmod
,
952 .chown
= shadow_copy2_chown
,
953 .chdir
= shadow_copy2_chdir
,
954 .ntimes
= shadow_copy2_ntimes
,
955 .symlink
= shadow_copy2_symlink
,
956 .vfs_readlink
= shadow_copy2_readlink
,
957 .link
= shadow_copy2_link
,
958 .mknod
= shadow_copy2_mknod
,
959 .realpath
= shadow_copy2_realpath
,
960 .connectpath
= shadow_copy2_connectpath
,
961 .get_nt_acl
= shadow_copy2_get_nt_acl
,
962 .chmod_acl
= shadow_copy2_chmod_acl
,
963 .get_shadow_copy_data
= shadow_copy2_get_shadow_copy2_data
,
966 NTSTATUS
vfs_shadow_copy2_init(void);
967 NTSTATUS
vfs_shadow_copy2_init(void)
971 ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "shadow_copy2",
972 &vfs_shadow_copy2_fns
);
974 if (!NT_STATUS_IS_OK(ret
))
977 vfs_shadow_copy2_debug_level
= debug_add_class("shadow_copy2");
978 if (vfs_shadow_copy2_debug_level
== -1) {
979 vfs_shadow_copy2_debug_level
= DBGC_VFS
;
980 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
981 "vfs_shadow_copy2_init"));
983 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
984 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level
));