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.
47 shadow:snapdir = <directory where snapshots are kept>
49 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
50 path it is used as-is. If it is a relative path, then it is taken relative to the mount
51 point of the filesystem that the root of this share is on
53 shadow:basedir = <base directory that snapshots are from>
55 This is an optional parameter that specifies the directory that
56 the snapshots are relative to. It defaults to the filesystem
59 shadow:fixinodes = yes/no
61 If you enable shadow:fixinodes then this module will modify the
62 apparent inode number of files in the snapshot directories using
63 a hash of the files path. This is needed for snapshot systems
64 where the snapshots have the same device:inode number as the
65 original files (such as happens with GPFS snapshots). If you
66 don't set this option then the 'restore' button in the shadow
67 copy UI will fail with a sharing violation.
69 shadow:sort = asc/desc, or not specified for unsorted (default)
71 This is an optional parameter that specifies that the shadow
72 copy directories should be sorted before sending them to the
73 client. This can be beneficial as unix filesystems are usually
74 not listed alphabetically sorted. If enabled, you typically
75 want to specify descending order.
77 shadow:format = <format specification for snapshot names>
79 This is an optional parameter that specifies the format
80 specification for the naming of snapshots. The format must
81 be compatible with the conversion specifications recognized
82 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
86 The following command would generate a correctly formatted directory name
87 for use with the default parameters:
88 date -u +@GMT-%Y.%m.%d-%H.%M.%S
92 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
95 #define DBGC_CLASS vfs_shadow_copy2_debug_level
97 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
98 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
100 #define SHADOW_COPY2_DEFAULT_SORT NULL
101 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
104 make very sure it is one of our special names
106 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
108 unsigned year, month, day, hr, min, sec;
113 p = strstr_m(name, "@GMT-");
114 if (p == NULL) return false;
115 if (p > name && p[-1] != '/') return False;
116 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
117 &day, &hr, &min, &sec) != 6) {
120 if (p[24] != 0 && p[24] != '/') {
129 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
130 vfs_handle_struct *handle, const char *name)
134 char gmt[GMT_NAME_LEN + 1];
137 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
138 "format", SHADOW_COPY2_DEFAULT_FORMAT);
140 ZERO_STRUCT(timestamp);
141 if (strptime(name, fmt, ×tamp) == NULL) {
142 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
147 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
148 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, ×tamp);
150 return talloc_strdup(mem_ctx, gmt);
154 shadow copy paths can also come into the server in this form:
156 /foo/bar/@GMT-XXXXX/some/file
158 This function normalises the filename to be of the form:
160 @GMT-XXXX/foo/bar/some/file
162 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
165 char buf[GMT_NAME_LEN];
168 if (path == gmt_start) {
172 prefix_len = gmt_start - path - 1;
174 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
178 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
179 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
180 * processing. As many VFS calls provide a const char *,
181 * unfortunately we have to make a copy.
184 pcopy = talloc_strdup(talloc_tos(), path);
189 gmt_start = pcopy + prefix_len;
192 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
194 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
197 * Make space for it including a trailing /
199 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
202 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
204 memcpy(pcopy, buf, GMT_NAME_LEN);
205 pcopy[GMT_NAME_LEN] = '/';
207 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
213 convert a name to the shadow directory
216 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
217 const char *name = fname; \
218 const char *gmt_start; \
219 if (shadow_copy2_match_name(fname, &gmt_start)) { \
222 name2 = convert_shadow2_name(handle, fname, gmt_start); \
223 if (name2 == NULL) { \
228 ret = SMB_VFS_NEXT_ ## op args; \
229 talloc_free(name2); \
230 if (ret != eret) extra; \
233 return SMB_VFS_NEXT_ ## op args; \
237 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
238 const char *gmt_start; \
239 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
241 char *smb_base_name_tmp = NULL; \
243 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
244 if (name2 == NULL) { \
248 smb_base_name_tmp = smb_fname->base_name; \
249 smb_fname->base_name = name2; \
250 ret = SMB_VFS_NEXT_ ## op args; \
251 smb_fname->base_name = smb_base_name_tmp; \
252 talloc_free(name2); \
253 if (ret != eret) extra; \
256 return SMB_VFS_NEXT_ ## op args; \
261 convert a name to the shadow directory: NTSTATUS-specific handling
264 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
265 const char *name = fname; \
266 const char *gmt_start; \
267 if (shadow_copy2_match_name(fname, &gmt_start)) { \
270 name2 = convert_shadow2_name(handle, fname, gmt_start); \
271 if (name2 == NULL) { \
276 ret = SMB_VFS_NEXT_ ## op args; \
277 talloc_free(name2); \
278 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
281 return SMB_VFS_NEXT_ ## op args; \
285 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
287 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
289 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
291 #define SHADOW2_NEXT2(op, args) do { \
292 const char *gmt_start1, *gmt_start2; \
293 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
294 shadow_copy2_match_name(newname, &gmt_start2)) { \
298 return SMB_VFS_NEXT_ ## op args; \
302 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
303 const char *gmt_start1, *gmt_start2; \
304 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
305 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
309 return SMB_VFS_NEXT_ ## op args; \
315 find the mount point of a filesystem
317 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
319 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
324 if (stat(path, &st) != 0) {
331 while ((p = strrchr(path, '/')) && p > path) {
333 if (stat(path, &st) != 0) {
337 if (st.st_dev != dev) {
347 work out the location of the snapshot for this share
349 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
355 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
356 if (snapdir == NULL) {
359 /* if its an absolute path, we're done */
360 if (*snapdir == '/') {
364 /* other its relative to the filesystem mount point */
365 mount_point = find_mount_point(mem_ctx, handle);
366 if (mount_point == NULL) {
370 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
371 talloc_free(mount_point);
376 work out the location of the base directory for snapshots of this share
378 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
380 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
382 /* other its the filesystem mount point */
383 if (basedir == NULL) {
384 basedir = find_mount_point(mem_ctx, handle);
391 convert a filename from a share relative path, to a path in the
394 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
396 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
397 const char *snapdir, *relpath, *baseoffset, *basedir;
403 char snapshot[MAXPATHLEN];
406 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
407 "format", SHADOW_COPY2_DEFAULT_FORMAT);
409 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
410 if (snapdir == NULL) {
411 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
412 talloc_free(tmp_ctx);
416 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
417 if (basedir == NULL) {
418 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
419 talloc_free(tmp_ctx);
423 if (strncmp(fname, "@GMT-", 5) != 0) {
424 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
426 talloc_free(tmp_ctx);
431 ZERO_STRUCT(timestamp);
432 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, ×tamp);
433 if (relpath == NULL) {
434 talloc_free(tmp_ctx);
438 /* relpath is the remaining portion of the path after the @GMT-xxx */
440 strftime(snapshot, MAXPATHLEN, fmt, ×tamp);
442 baselen = strlen(basedir);
443 baseoffset = handle->conn->connectpath + baselen;
445 /* some sanity checks */
446 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
447 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
448 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
449 basedir, handle->conn->connectpath));
450 talloc_free(tmp_ctx);
454 if (*relpath == '/') relpath++;
455 if (*baseoffset == '/') baseoffset++;
457 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
462 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
463 talloc_free(tmp_ctx);
471 static uint32 string_hash(const char *s)
475 n = ((n << 5) + n) ^ (uint32)(*s++);
481 modify a sbuf return to ensure that inodes in the shadow directory
482 are different from those in the main directory
484 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
486 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
487 /* some snapshot systems, like GPFS, return the name
488 device:inode for the snapshot files as the current
489 files. That breaks the 'restore' button in the shadow copy
490 GUI, as the client gets a sharing violation.
492 This is a crude way of allowing both files to be
493 open at once. It has a slight chance of inode
494 number collision, but I can't see a better approach
495 without significant VFS changes
497 uint32_t shash = string_hash(fname) & 0xFF000000;
501 sbuf->st_ex_ino ^= shash;
505 static int shadow_copy2_rename(vfs_handle_struct *handle,
506 const struct smb_filename *smb_fname_src,
507 const struct smb_filename *smb_fname_dst)
509 SHADOW2_NEXT2_SMB_FNAME(RENAME,
510 (handle, smb_fname_src, smb_fname_dst));
513 static int shadow_copy2_symlink(vfs_handle_struct *handle,
514 const char *oldname, const char *newname)
516 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
519 static int shadow_copy2_link(vfs_handle_struct *handle,
520 const char *oldname, const char *newname)
522 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
525 static int shadow_copy2_open(vfs_handle_struct *handle,
526 struct smb_filename *smb_fname, files_struct *fsp,
527 int flags, mode_t mode)
529 SHADOW2_NEXT_SMB_FNAME(OPEN,
530 (handle, smb_fname, fsp, flags, mode),
534 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
535 const char *fname, const char *mask, uint32 attr)
537 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
540 static int shadow_copy2_stat(vfs_handle_struct *handle,
541 struct smb_filename *smb_fname)
543 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
544 convert_sbuf(handle, smb_fname->base_name,
548 static int shadow_copy2_lstat(vfs_handle_struct *handle,
549 struct smb_filename *smb_fname)
551 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
552 convert_sbuf(handle, smb_fname->base_name,
556 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
558 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
559 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
560 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
565 static int shadow_copy2_unlink(vfs_handle_struct *handle,
566 const struct smb_filename *smb_fname_in)
568 struct smb_filename *smb_fname = NULL;
571 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
572 if (!NT_STATUS_IS_OK(status)) {
573 errno = map_errno_from_nt_status(status);
577 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
580 static int shadow_copy2_chmod(vfs_handle_struct *handle,
581 const char *fname, mode_t mode)
583 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
586 static int shadow_copy2_chown(vfs_handle_struct *handle,
587 const char *fname, uid_t uid, gid_t gid)
589 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
592 static int shadow_copy2_chdir(vfs_handle_struct *handle,
595 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
598 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
599 const struct smb_filename *smb_fname_in,
600 struct smb_file_time *ft)
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(NTIMES, (handle, smb_fname, ft), int, -1);
614 static int shadow_copy2_readlink(vfs_handle_struct *handle,
615 const char *fname, char *buf, size_t bufsiz)
617 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
620 static int shadow_copy2_mknod(vfs_handle_struct *handle,
621 const char *fname, mode_t mode, SMB_DEV_T dev)
623 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
626 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
627 const char *fname, char *resolved_path)
631 if (shadow_copy2_match_name(fname, &gmt)
632 && (gmt[GMT_NAME_LEN] == '\0')) {
635 copy = talloc_strdup(talloc_tos(), fname);
641 copy[gmt - fname] = '.';
643 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
644 result = SMB_VFS_NEXT_REALPATH(handle, copy, resolved_path);
648 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
651 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
654 TALLOC_CTX *tmp_ctx = talloc_stackframe();
655 const char *snapdir, *baseoffset, *basedir, *gmt_start;
659 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
661 if (!shadow_copy2_match_name(fname, &gmt_start)) {
662 return handle->conn->connectpath;
665 fname = shadow_copy2_normalise_path(talloc_tos(), fname, gmt_start);
667 TALLOC_FREE(tmp_ctx);
671 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
672 if (snapdir == NULL) {
673 DEBUG(2,("no snapdir found for share at %s\n",
674 handle->conn->connectpath));
675 TALLOC_FREE(tmp_ctx);
679 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
680 if (basedir == NULL) {
681 DEBUG(2,("no basedir found for share at %s\n",
682 handle->conn->connectpath));
683 TALLOC_FREE(tmp_ctx);
687 baselen = strlen(basedir);
688 baseoffset = handle->conn->connectpath + baselen;
690 /* some sanity checks */
691 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
692 (handle->conn->connectpath[baselen] != 0
693 && handle->conn->connectpath[baselen] != '/')) {
694 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
695 "parent of %s\n", basedir,
696 handle->conn->connectpath));
697 TALLOC_FREE(tmp_ctx);
701 if (*baseoffset == '/') baseoffset++;
703 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
707 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
708 TALLOC_FREE(tmp_ctx);
712 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
713 const char *fname, uint32 security_info,
714 struct security_descriptor **ppdesc)
716 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
719 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
721 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
724 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
726 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
729 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
732 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
735 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
736 const char *fname, const char *aname, void *value, size_t size)
738 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
741 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
742 const char *fname, const char *aname, void *value, size_t size)
744 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
747 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
748 char *list, size_t size)
750 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
753 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
756 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
759 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
762 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
765 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
766 const char *aname, const void *value, size_t size, int flags)
768 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
771 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
772 const char *aname, const void *value, size_t size, int flags)
774 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
777 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
778 const char *fname, mode_t mode)
780 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
783 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
785 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
788 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
790 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
794 sort the shadow copy data in ascending or descending order
796 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
797 SHADOW_COPY_DATA *shadow_copy2_data)
799 int (*cmpfunc)(const void *, const void *);
802 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
803 "sort", SHADOW_COPY2_DEFAULT_SORT);
808 if (strcmp(sort, "asc") == 0) {
809 cmpfunc = shadow_copy2_label_cmp_asc;
810 } else if (strcmp(sort, "desc") == 0) {
811 cmpfunc = shadow_copy2_label_cmp_desc;
816 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
817 shadow_copy2_data->labels)
819 qsort(shadow_copy2_data->labels,
820 shadow_copy2_data->num_volumes,
821 sizeof(SHADOW_COPY_LABEL), cmpfunc);
827 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
829 SHADOW_COPY_DATA *shadow_copy2_data,
834 SMB_STRUCT_DIRENT *d;
835 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
838 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
839 if (snapdir == NULL) {
840 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
841 handle->conn->connectpath));
843 talloc_free(tmp_ctx);
847 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
850 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
851 " - %s\n", snapdir, strerror(errno)));
852 talloc_free(tmp_ctx);
857 shadow_copy2_data->num_volumes = 0;
858 shadow_copy2_data->labels = NULL;
860 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
861 SHADOW_COPY_LABEL *tlabels;
863 /* ignore names not of the right form in the snapshot directory */
864 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
866 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
867 d->d_name, snapshot));
873 /* the caller doesn't want the labels */
874 shadow_copy2_data->num_volumes++;
878 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
879 shadow_copy2_data->labels,
880 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
881 if (tlabels == NULL) {
882 DEBUG(0,("shadow_copy2: out of memory\n"));
883 SMB_VFS_NEXT_CLOSEDIR(handle, p);
884 talloc_free(tmp_ctx);
888 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
890 talloc_free(snapshot);
892 shadow_copy2_data->num_volumes++;
893 shadow_copy2_data->labels = tlabels;
896 SMB_VFS_NEXT_CLOSEDIR(handle,p);
898 shadow_copy2_sort_data(handle, shadow_copy2_data);
900 talloc_free(tmp_ctx);
904 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
905 .opendir = shadow_copy2_opendir,
906 .mkdir = shadow_copy2_mkdir,
907 .rmdir = shadow_copy2_rmdir,
908 .chflags = shadow_copy2_chflags,
909 .getxattr = shadow_copy2_getxattr,
910 .lgetxattr = shadow_copy2_lgetxattr,
911 .listxattr = shadow_copy2_listxattr,
912 .removexattr = shadow_copy2_removexattr,
913 .lremovexattr = shadow_copy2_lremovexattr,
914 .setxattr = shadow_copy2_setxattr,
915 .lsetxattr = shadow_copy2_lsetxattr,
916 .open = shadow_copy2_open,
917 .rename = shadow_copy2_rename,
918 .stat = shadow_copy2_stat,
919 .lstat = shadow_copy2_lstat,
920 .fstat = shadow_copy2_fstat,
921 .unlink = shadow_copy2_unlink,
922 .chmod = shadow_copy2_chmod,
923 .chown = shadow_copy2_chown,
924 .chdir = shadow_copy2_chdir,
925 .ntimes = shadow_copy2_ntimes,
926 .symlink = shadow_copy2_symlink,
927 .vfs_readlink = shadow_copy2_readlink,
928 .link = shadow_copy2_link,
929 .mknod = shadow_copy2_mknod,
930 .realpath = shadow_copy2_realpath,
931 .connectpath = shadow_copy2_connectpath,
932 .get_nt_acl = shadow_copy2_get_nt_acl,
933 .chmod_acl = shadow_copy2_chmod_acl,
934 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
937 NTSTATUS vfs_shadow_copy2_init(void);
938 NTSTATUS vfs_shadow_copy2_init(void)
942 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
943 &vfs_shadow_copy2_fns);
945 if (!NT_STATUS_IS_OK(ret))
948 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
949 if (vfs_shadow_copy2_debug_level == -1) {
950 vfs_shadow_copy2_debug_level = DBGC_VFS;
951 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
952 "vfs_shadow_copy2_init"));
954 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
955 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));