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 if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
542 SHADOW2_NEXT2_SMB_FNAME(RENAME,
543 (handle, smb_fname_src, smb_fname_dst));
546 static int shadow_copy2_symlink(vfs_handle_struct *handle,
547 const char *oldname, const char *newname)
549 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
552 static int shadow_copy2_link(vfs_handle_struct *handle,
553 const char *oldname, const char *newname)
555 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
558 static int shadow_copy2_open(vfs_handle_struct *handle,
559 struct smb_filename *smb_fname, files_struct *fsp,
560 int flags, mode_t mode)
562 SHADOW2_NEXT_SMB_FNAME(OPEN,
563 (handle, smb_fname, fsp, flags, mode),
567 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
568 const char *fname, const char *mask, uint32 attr)
570 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
573 static int shadow_copy2_stat(vfs_handle_struct *handle,
574 struct smb_filename *smb_fname)
576 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
577 convert_sbuf(handle, smb_fname->base_name,
581 static int shadow_copy2_lstat(vfs_handle_struct *handle,
582 struct smb_filename *smb_fname)
584 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
585 convert_sbuf(handle, smb_fname->base_name,
589 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
591 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
592 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
593 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
598 static int shadow_copy2_unlink(vfs_handle_struct *handle,
599 const struct smb_filename *smb_fname_in)
601 struct smb_filename *smb_fname = NULL;
604 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
605 if (!NT_STATUS_IS_OK(status)) {
606 errno = map_errno_from_nt_status(status);
610 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
613 static int shadow_copy2_chmod(vfs_handle_struct *handle,
614 const char *fname, mode_t mode)
616 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
619 static int shadow_copy2_chown(vfs_handle_struct *handle,
620 const char *fname, uid_t uid, gid_t gid)
622 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
625 static int shadow_copy2_chdir(vfs_handle_struct *handle,
628 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
631 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
632 const struct smb_filename *smb_fname_in,
633 struct smb_file_time *ft)
635 struct smb_filename *smb_fname = NULL;
638 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
639 if (!NT_STATUS_IS_OK(status)) {
640 errno = map_errno_from_nt_status(status);
644 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
647 static int shadow_copy2_readlink(vfs_handle_struct *handle,
648 const char *fname, char *buf, size_t bufsiz)
650 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
653 static int shadow_copy2_mknod(vfs_handle_struct *handle,
654 const char *fname, mode_t mode, SMB_DEV_T dev)
656 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
659 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
664 if (shadow_copy2_match_name(fname, &gmt)
665 && (gmt[GMT_NAME_LEN] == '\0')) {
668 copy = talloc_strdup(talloc_tos(), fname);
674 copy[gmt - fname] = '.';
675 copy[gmt - fname + 1] = '\0';
677 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
678 SHADOW2_NEXT(REALPATH, (handle, name), char *,
681 SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
684 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
688 const char *snapdir, *baseoffset, *basedir, *gmt_start;
692 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
694 if (!shadow_copy2_match_name(fname, &gmt_start)) {
695 return handle->conn->connectpath;
699 * We have to create a real temporary context because we have
700 * to put our result on talloc_tos(). Thus we can't use a
701 * talloc_stackframe() here.
703 tmp_ctx = talloc_new(talloc_tos());
705 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
707 TALLOC_FREE(tmp_ctx);
711 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
712 if (snapdir == NULL) {
713 DEBUG(2,("no snapdir found for share at %s\n",
714 handle->conn->connectpath));
715 TALLOC_FREE(tmp_ctx);
719 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
720 if (basedir == NULL) {
721 DEBUG(2,("no basedir found for share at %s\n",
722 handle->conn->connectpath));
723 TALLOC_FREE(tmp_ctx);
727 baselen = strlen(basedir);
728 baseoffset = handle->conn->connectpath + baselen;
730 /* some sanity checks */
731 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
732 (handle->conn->connectpath[baselen] != 0
733 && handle->conn->connectpath[baselen] != '/')) {
734 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
735 "parent of %s\n", basedir,
736 handle->conn->connectpath));
737 TALLOC_FREE(tmp_ctx);
741 if (*baseoffset == '/') baseoffset++;
743 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
747 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
748 TALLOC_FREE(tmp_ctx);
752 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
753 const char *fname, uint32 security_info,
754 struct security_descriptor **ppdesc)
756 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
759 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
761 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
764 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
766 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
769 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
772 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
775 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
776 const char *fname, const char *aname, void *value, size_t size)
778 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
781 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
782 const char *fname, const char *aname, void *value, size_t size)
784 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
787 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
788 char *list, size_t size)
790 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
793 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
796 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
799 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
802 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
805 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
806 const char *aname, const void *value, size_t size, int flags)
808 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
811 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
812 const char *aname, const void *value, size_t size, int flags)
814 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
817 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
818 const char *fname, mode_t mode)
820 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
823 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
825 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
828 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
830 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
834 sort the shadow copy data in ascending or descending order
836 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
837 SHADOW_COPY_DATA *shadow_copy2_data)
839 int (*cmpfunc)(const void *, const void *);
842 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
843 "sort", SHADOW_COPY2_DEFAULT_SORT);
848 if (strcmp(sort, "asc") == 0) {
849 cmpfunc = shadow_copy2_label_cmp_asc;
850 } else if (strcmp(sort, "desc") == 0) {
851 cmpfunc = shadow_copy2_label_cmp_desc;
856 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
857 shadow_copy2_data->labels)
859 TYPESAFE_QSORT(shadow_copy2_data->labels,
860 shadow_copy2_data->num_volumes,
867 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
869 SHADOW_COPY_DATA *shadow_copy2_data,
874 SMB_STRUCT_DIRENT *d;
875 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
878 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
879 if (snapdir == NULL) {
880 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
881 handle->conn->connectpath));
883 talloc_free(tmp_ctx);
887 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
890 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
891 " - %s\n", snapdir, strerror(errno)));
892 talloc_free(tmp_ctx);
897 shadow_copy2_data->num_volumes = 0;
898 shadow_copy2_data->labels = NULL;
900 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
901 SHADOW_COPY_LABEL *tlabels;
903 /* ignore names not of the right form in the snapshot directory */
904 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
906 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
907 d->d_name, snapshot));
913 /* the caller doesn't want the labels */
914 shadow_copy2_data->num_volumes++;
918 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
919 shadow_copy2_data->labels,
920 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
921 if (tlabels == NULL) {
922 DEBUG(0,("shadow_copy2: out of memory\n"));
923 SMB_VFS_NEXT_CLOSEDIR(handle, p);
924 talloc_free(tmp_ctx);
928 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
930 talloc_free(snapshot);
932 shadow_copy2_data->num_volumes++;
933 shadow_copy2_data->labels = tlabels;
936 SMB_VFS_NEXT_CLOSEDIR(handle,p);
938 shadow_copy2_sort_data(handle, shadow_copy2_data);
940 talloc_free(tmp_ctx);
944 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
945 .opendir = shadow_copy2_opendir,
946 .mkdir = shadow_copy2_mkdir,
947 .rmdir = shadow_copy2_rmdir,
948 .chflags = shadow_copy2_chflags,
949 .getxattr = shadow_copy2_getxattr,
950 .lgetxattr = shadow_copy2_lgetxattr,
951 .listxattr = shadow_copy2_listxattr,
952 .removexattr = shadow_copy2_removexattr,
953 .lremovexattr = shadow_copy2_lremovexattr,
954 .setxattr = shadow_copy2_setxattr,
955 .lsetxattr = shadow_copy2_lsetxattr,
956 .open = shadow_copy2_open,
957 .rename = shadow_copy2_rename,
958 .stat = shadow_copy2_stat,
959 .lstat = shadow_copy2_lstat,
960 .fstat = shadow_copy2_fstat,
961 .unlink = shadow_copy2_unlink,
962 .chmod = shadow_copy2_chmod,
963 .chown = shadow_copy2_chown,
964 .chdir = shadow_copy2_chdir,
965 .ntimes = shadow_copy2_ntimes,
966 .symlink = shadow_copy2_symlink,
967 .vfs_readlink = shadow_copy2_readlink,
968 .link = shadow_copy2_link,
969 .mknod = shadow_copy2_mknod,
970 .realpath = shadow_copy2_realpath,
971 .connectpath = shadow_copy2_connectpath,
972 .get_nt_acl = shadow_copy2_get_nt_acl,
973 .chmod_acl = shadow_copy2_chmod_acl,
974 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
977 NTSTATUS vfs_shadow_copy2_init(void);
978 NTSTATUS vfs_shadow_copy2_init(void)
982 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
983 &vfs_shadow_copy2_fns);
985 if (!NT_STATUS_IS_OK(ret))
988 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
989 if (vfs_shadow_copy2_debug_level == -1) {
990 vfs_shadow_copy2_debug_level = DBGC_VFS;
991 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
992 "vfs_shadow_copy2_init"));
994 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
995 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));