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 "system/filesys.h"
27 This is a 2nd 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:localtime = yes/no (default is no)
90 This is an optional parameter that indicates whether the
91 snapshot names are in UTC/GMT or the local time.
94 The following command would generate a correctly formatted directory name
95 for use with the default parameters:
96 date -u +@GMT-%Y.%m.%d-%H.%M.%S
100 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
103 #define DBGC_CLASS vfs_shadow_copy2_debug_level
105 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
106 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
108 #define SHADOW_COPY2_DEFAULT_SORT NULL
109 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
110 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
113 make very sure it is one of our special names
115 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
117 unsigned year, month, day, hr, min, sec;
122 p = strstr_m(name, "@GMT-");
123 if (p == NULL) return false;
124 if (p > name && p[-1] != '/') return False;
125 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
126 &day, &hr, &min, &sec) != 6) {
129 if (p[24] != 0 && p[24] != '/') {
138 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
139 vfs_handle_struct *handle, const char *name)
143 char gmt[GMT_NAME_LEN + 1];
146 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
147 "format", SHADOW_COPY2_DEFAULT_FORMAT);
149 ZERO_STRUCT(timestamp);
150 if (strptime(name, fmt, ×tamp) == NULL) {
151 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
156 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
157 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
158 SHADOW_COPY2_DEFAULT_LOCALTIME))
160 timestamp.tm_isdst = -1;
161 timestamp_t = mktime(×tamp);
162 gmtime_r(×tamp_t, ×tamp);
164 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, ×tamp);
166 return talloc_strdup(mem_ctx, gmt);
170 shadow copy paths can also come into the server in this form:
172 /foo/bar/@GMT-XXXXX/some/file
174 This function normalises the filename to be of the form:
176 @GMT-XXXX/foo/bar/some/file
178 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
181 char buf[GMT_NAME_LEN];
184 if (path == gmt_start) {
188 prefix_len = gmt_start - path - 1;
190 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
194 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
195 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
196 * processing. As many VFS calls provide a const char *,
197 * unfortunately we have to make a copy.
200 pcopy = talloc_strdup(talloc_tos(), path);
205 gmt_start = pcopy + prefix_len;
208 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
210 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
213 * Make space for it including a trailing /
215 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
218 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
220 memcpy(pcopy, buf, GMT_NAME_LEN);
221 pcopy[GMT_NAME_LEN] = '/';
223 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
229 convert a name to the shadow directory
232 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
233 const char *name = fname; \
234 const char *gmt_start; \
235 if (shadow_copy2_match_name(fname, &gmt_start)) { \
238 name2 = convert_shadow2_name(handle, fname, gmt_start); \
239 if (name2 == NULL) { \
244 ret = SMB_VFS_NEXT_ ## op args; \
245 talloc_free(name2); \
246 if (ret != eret) extra; \
249 return SMB_VFS_NEXT_ ## op args; \
253 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
254 const char *gmt_start; \
255 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
257 char *smb_base_name_tmp = NULL; \
259 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
260 if (name2 == NULL) { \
264 smb_base_name_tmp = smb_fname->base_name; \
265 smb_fname->base_name = name2; \
266 ret = SMB_VFS_NEXT_ ## op args; \
267 smb_fname->base_name = smb_base_name_tmp; \
268 talloc_free(name2); \
269 if (ret != eret) extra; \
272 return SMB_VFS_NEXT_ ## op args; \
277 convert a name to the shadow directory: NTSTATUS-specific handling
280 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
281 const char *name = fname; \
282 const char *gmt_start; \
283 if (shadow_copy2_match_name(fname, &gmt_start)) { \
286 name2 = convert_shadow2_name(handle, fname, gmt_start); \
287 if (name2 == NULL) { \
292 ret = SMB_VFS_NEXT_ ## op args; \
293 talloc_free(name2); \
294 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
297 return SMB_VFS_NEXT_ ## op args; \
301 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
303 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
305 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
307 #define SHADOW2_NEXT2(op, args) do { \
308 const char *gmt_start1, *gmt_start2; \
309 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
310 shadow_copy2_match_name(newname, &gmt_start2)) { \
314 return SMB_VFS_NEXT_ ## op args; \
318 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
319 const char *gmt_start1, *gmt_start2; \
320 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
321 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
325 return SMB_VFS_NEXT_ ## op args; \
331 find the mount point of a filesystem
333 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
335 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
340 if (stat(path, &st) != 0) {
347 while ((p = strrchr(path, '/')) && p > path) {
349 if (stat(path, &st) != 0) {
353 if (st.st_dev != dev) {
363 work out the location of the snapshot for this share
365 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
371 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
372 if (snapdir == NULL) {
375 /* if its an absolute path, we're done */
376 if (*snapdir == '/') {
380 /* other its relative to the filesystem mount point */
381 mount_point = find_mount_point(mem_ctx, handle);
382 if (mount_point == NULL) {
386 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
387 talloc_free(mount_point);
392 work out the location of the base directory for snapshots of this share
394 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
396 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
398 /* other its the filesystem mount point */
399 if (basedir == NULL) {
400 basedir = find_mount_point(mem_ctx, handle);
407 convert a filename from a share relative path, to a path in the
410 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
412 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
413 const char *snapdir, *relpath, *baseoffset, *basedir;
419 char snapshot[MAXPATHLEN];
422 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
423 "format", SHADOW_COPY2_DEFAULT_FORMAT);
425 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
426 if (snapdir == NULL) {
427 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
428 talloc_free(tmp_ctx);
432 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
433 if (basedir == NULL) {
434 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
435 talloc_free(tmp_ctx);
439 prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
440 if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
441 /* this looks like as we have already normalized it, leave it untouched*/
442 talloc_free(tmp_ctx);
443 return talloc_strdup(handle->data, fname);
446 if (strncmp(fname, "@GMT-", 5) != 0) {
447 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
449 talloc_free(tmp_ctx);
454 ZERO_STRUCT(timestamp);
455 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, ×tamp);
456 if (relpath == NULL) {
457 talloc_free(tmp_ctx);
461 /* relpath is the remaining portion of the path after the @GMT-xxx */
463 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
464 SHADOW_COPY2_DEFAULT_LOCALTIME))
466 timestamp_t = timegm(×tamp);
467 localtime_r(×tamp_t, ×tamp);
470 strftime(snapshot, MAXPATHLEN, fmt, ×tamp);
472 baselen = strlen(basedir);
473 baseoffset = handle->conn->connectpath + baselen;
475 /* some sanity checks */
476 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
477 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
478 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
479 basedir, handle->conn->connectpath));
480 talloc_free(tmp_ctx);
484 if (*relpath == '/') relpath++;
485 if (*baseoffset == '/') baseoffset++;
487 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
492 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
493 talloc_free(tmp_ctx);
501 static uint32 string_hash(const char *s)
505 n = ((n << 5) + n) ^ (uint32)(*s++);
511 modify a sbuf return to ensure that inodes in the shadow directory
512 are different from those in the main directory
514 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
516 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
517 /* some snapshot systems, like GPFS, return the name
518 device:inode for the snapshot files as the current
519 files. That breaks the 'restore' button in the shadow copy
520 GUI, as the client gets a sharing violation.
522 This is a crude way of allowing both files to be
523 open at once. It has a slight chance of inode
524 number collision, but I can't see a better approach
525 without significant VFS changes
527 uint32_t shash = string_hash(fname) & 0xFF000000;
531 sbuf->st_ex_ino ^= shash;
535 static int shadow_copy2_rename(vfs_handle_struct *handle,
536 const struct smb_filename *smb_fname_src,
537 const struct smb_filename *smb_fname_dst)
539 if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
543 SHADOW2_NEXT2_SMB_FNAME(RENAME,
544 (handle, smb_fname_src, smb_fname_dst));
547 static int shadow_copy2_symlink(vfs_handle_struct *handle,
548 const char *oldname, const char *newname)
550 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
553 static int shadow_copy2_link(vfs_handle_struct *handle,
554 const char *oldname, const char *newname)
556 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
559 static int shadow_copy2_open(vfs_handle_struct *handle,
560 struct smb_filename *smb_fname, files_struct *fsp,
561 int flags, mode_t mode)
563 SHADOW2_NEXT_SMB_FNAME(OPEN,
564 (handle, smb_fname, fsp, flags, mode),
568 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
569 const char *fname, const char *mask, uint32 attr)
571 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
574 static int shadow_copy2_stat(vfs_handle_struct *handle,
575 struct smb_filename *smb_fname)
577 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
578 convert_sbuf(handle, smb_fname->base_name,
582 static int shadow_copy2_lstat(vfs_handle_struct *handle,
583 struct smb_filename *smb_fname)
585 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
586 convert_sbuf(handle, smb_fname->base_name,
590 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
592 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
593 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
594 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
599 static int shadow_copy2_unlink(vfs_handle_struct *handle,
600 const struct smb_filename *smb_fname_in)
602 struct smb_filename *smb_fname = NULL;
605 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
606 if (!NT_STATUS_IS_OK(status)) {
607 errno = map_errno_from_nt_status(status);
611 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
614 static int shadow_copy2_chmod(vfs_handle_struct *handle,
615 const char *fname, mode_t mode)
617 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
620 static int shadow_copy2_chown(vfs_handle_struct *handle,
621 const char *fname, uid_t uid, gid_t gid)
623 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
626 static int shadow_copy2_chdir(vfs_handle_struct *handle,
629 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
632 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
633 const struct smb_filename *smb_fname_in,
634 struct smb_file_time *ft)
636 struct smb_filename *smb_fname = NULL;
639 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
640 if (!NT_STATUS_IS_OK(status)) {
641 errno = map_errno_from_nt_status(status);
645 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
648 static int shadow_copy2_readlink(vfs_handle_struct *handle,
649 const char *fname, char *buf, size_t bufsiz)
651 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
654 static int shadow_copy2_mknod(vfs_handle_struct *handle,
655 const char *fname, mode_t mode, SMB_DEV_T dev)
657 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
660 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
665 if (shadow_copy2_match_name(fname, &gmt)
666 && (gmt[GMT_NAME_LEN] == '\0')) {
669 copy = talloc_strdup(talloc_tos(), fname);
675 copy[gmt - fname] = '.';
676 copy[gmt - fname + 1] = '\0';
678 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
679 SHADOW2_NEXT(REALPATH, (handle, name), char *,
682 SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
685 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
689 const char *snapdir, *baseoffset, *basedir, *gmt_start;
693 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
695 if (!shadow_copy2_match_name(fname, &gmt_start)) {
696 return handle->conn->connectpath;
700 * We have to create a real temporary context because we have
701 * to put our result on talloc_tos(). Thus we can't use a
702 * talloc_stackframe() here.
704 tmp_ctx = talloc_new(talloc_tos());
706 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
708 TALLOC_FREE(tmp_ctx);
712 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
713 if (snapdir == NULL) {
714 DEBUG(2,("no snapdir found for share at %s\n",
715 handle->conn->connectpath));
716 TALLOC_FREE(tmp_ctx);
720 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
721 if (basedir == NULL) {
722 DEBUG(2,("no basedir found for share at %s\n",
723 handle->conn->connectpath));
724 TALLOC_FREE(tmp_ctx);
728 baselen = strlen(basedir);
729 baseoffset = handle->conn->connectpath + baselen;
731 /* some sanity checks */
732 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
733 (handle->conn->connectpath[baselen] != 0
734 && handle->conn->connectpath[baselen] != '/')) {
735 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
736 "parent of %s\n", basedir,
737 handle->conn->connectpath));
738 TALLOC_FREE(tmp_ctx);
742 if (*baseoffset == '/') baseoffset++;
744 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
748 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
749 TALLOC_FREE(tmp_ctx);
753 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
754 const char *fname, uint32 security_info,
755 struct security_descriptor **ppdesc)
757 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
760 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
762 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
765 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
767 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
770 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
773 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
776 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
777 const char *fname, const char *aname, void *value, size_t size)
779 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
782 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
783 const char *fname, const char *aname, void *value, size_t size)
785 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
788 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
789 char *list, size_t size)
791 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
794 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
797 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
800 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
803 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
806 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
807 const char *aname, const void *value, size_t size, int flags)
809 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
812 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
813 const char *aname, const void *value, size_t size, int flags)
815 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
818 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
819 const char *fname, mode_t mode)
821 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
824 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
826 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
829 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
831 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
835 sort the shadow copy data in ascending or descending order
837 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
838 SHADOW_COPY_DATA *shadow_copy2_data)
840 int (*cmpfunc)(const void *, const void *);
843 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
844 "sort", SHADOW_COPY2_DEFAULT_SORT);
849 if (strcmp(sort, "asc") == 0) {
850 cmpfunc = shadow_copy2_label_cmp_asc;
851 } else if (strcmp(sort, "desc") == 0) {
852 cmpfunc = shadow_copy2_label_cmp_desc;
857 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
858 shadow_copy2_data->labels)
860 TYPESAFE_QSORT(shadow_copy2_data->labels,
861 shadow_copy2_data->num_volumes,
868 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
870 SHADOW_COPY_DATA *shadow_copy2_data,
875 SMB_STRUCT_DIRENT *d;
876 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
879 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
880 if (snapdir == NULL) {
881 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
882 handle->conn->connectpath));
884 talloc_free(tmp_ctx);
888 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
891 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
892 " - %s\n", snapdir, strerror(errno)));
893 talloc_free(tmp_ctx);
898 shadow_copy2_data->num_volumes = 0;
899 shadow_copy2_data->labels = NULL;
901 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
902 SHADOW_COPY_LABEL *tlabels;
904 /* ignore names not of the right form in the snapshot directory */
905 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
907 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
908 d->d_name, snapshot));
914 /* the caller doesn't want the labels */
915 shadow_copy2_data->num_volumes++;
919 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
920 shadow_copy2_data->labels,
921 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
922 if (tlabels == NULL) {
923 DEBUG(0,("shadow_copy2: out of memory\n"));
924 SMB_VFS_NEXT_CLOSEDIR(handle, p);
925 talloc_free(tmp_ctx);
929 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
931 talloc_free(snapshot);
933 shadow_copy2_data->num_volumes++;
934 shadow_copy2_data->labels = tlabels;
937 SMB_VFS_NEXT_CLOSEDIR(handle,p);
939 shadow_copy2_sort_data(handle, shadow_copy2_data);
941 talloc_free(tmp_ctx);
945 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
946 .opendir = shadow_copy2_opendir,
947 .mkdir = shadow_copy2_mkdir,
948 .rmdir = shadow_copy2_rmdir,
949 .chflags = shadow_copy2_chflags,
950 .getxattr = shadow_copy2_getxattr,
951 .lgetxattr = shadow_copy2_lgetxattr,
952 .listxattr = shadow_copy2_listxattr,
953 .removexattr = shadow_copy2_removexattr,
954 .lremovexattr = shadow_copy2_lremovexattr,
955 .setxattr = shadow_copy2_setxattr,
956 .lsetxattr = shadow_copy2_lsetxattr,
957 .open = shadow_copy2_open,
958 .rename = shadow_copy2_rename,
959 .stat = shadow_copy2_stat,
960 .lstat = shadow_copy2_lstat,
961 .fstat = shadow_copy2_fstat,
962 .unlink = shadow_copy2_unlink,
963 .chmod = shadow_copy2_chmod,
964 .chown = shadow_copy2_chown,
965 .chdir = shadow_copy2_chdir,
966 .ntimes = shadow_copy2_ntimes,
967 .symlink = shadow_copy2_symlink,
968 .vfs_readlink = shadow_copy2_readlink,
969 .link = shadow_copy2_link,
970 .mknod = shadow_copy2_mknod,
971 .realpath = shadow_copy2_realpath,
972 .connectpath = shadow_copy2_connectpath,
973 .get_nt_acl = shadow_copy2_get_nt_acl,
974 .chmod_acl = shadow_copy2_chmod_acl,
975 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
978 NTSTATUS vfs_shadow_copy2_init(void);
979 NTSTATUS vfs_shadow_copy2_init(void)
983 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
984 &vfs_shadow_copy2_fns);
986 if (!NT_STATUS_IS_OK(ret))
989 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
990 if (vfs_shadow_copy2_debug_level == -1) {
991 vfs_shadow_copy2_debug_level = DBGC_VFS;
992 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
993 "vfs_shadow_copy2_init"));
995 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
996 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));