2 * implementation of an Shadow Copy module - version 2
4 * Copyright (C) Andrew Tridgell 2007
5 * Copyright (C) Ed Plese 2009
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
28 This is a 2nd implemetation of a shadow copy module for exposing
29 snapshots to windows clients as shadow copies. This version has the
32 1) you don't need to populate your shares with symlinks to the
33 snapshots. This can be very important when you have thousands of
34 shares, or use [homes]
36 2) the inode number of the files is altered so it is different
37 from the original. This allows the 'restore' button to work
38 without a sharing violation
40 3) shadow copy results can be sorted before being sent to the
41 client. This is beneficial for filesystems that don't read
42 directories alphabetically (the default unix).
44 4) vanity naming for snapshots. Snapshots can be named in any
45 format compatible with str[fp]time conversions.
47 5) time stamps in snapshot names can be represented in localtime
52 shadow:snapdir = <directory where snapshots are kept>
54 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
55 path it is used as-is. If it is a relative path, then it is taken relative to the mount
56 point of the filesystem that the root of this share is on
58 shadow:basedir = <base directory that snapshots are from>
60 This is an optional parameter that specifies the directory that
61 the snapshots are relative to. It defaults to the filesystem
64 shadow:fixinodes = yes/no
66 If you enable shadow:fixinodes then this module will modify the
67 apparent inode number of files in the snapshot directories using
68 a hash of the files path. This is needed for snapshot systems
69 where the snapshots have the same device:inode number as the
70 original files (such as happens with GPFS snapshots). If you
71 don't set this option then the 'restore' button in the shadow
72 copy UI will fail with a sharing violation.
74 shadow:sort = asc/desc, or not specified for unsorted (default)
76 This is an optional parameter that specifies that the shadow
77 copy directories should be sorted before sending them to the
78 client. This can be beneficial as unix filesystems are usually
79 not listed alphabetically sorted. If enabled, you typically
80 want to specify descending order.
82 shadow:format = <format specification for snapshot names>
84 This is an optional parameter that specifies the format
85 specification for the naming of snapshots. The format must
86 be compatible with the conversion specifications recognized
87 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
89 shadow:localtime = yes/no (default is no)
91 This is an optional parameter that indicates whether the
92 snapshot names are in UTC/GMT or the local time.
95 The following command would generate a correctly formatted directory name
96 for use with the default parameters:
97 date -u +@GMT-%Y.%m.%d-%H.%M.%S
101 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
104 #define DBGC_CLASS vfs_shadow_copy2_debug_level
106 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
107 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
109 #define SHADOW_COPY2_DEFAULT_SORT NULL
110 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
111 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
114 make very sure it is one of our special names
116 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
118 unsigned year, month, day, hr, min, sec;
123 p = strstr_m(name, "@GMT-");
124 if (p == NULL) return false;
125 if (p > name && p[-1] != '/') return False;
126 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
127 &day, &hr, &min, &sec) != 6) {
130 if (p[24] != 0 && p[24] != '/') {
139 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
140 vfs_handle_struct *handle, const char *name)
144 char gmt[GMT_NAME_LEN + 1];
147 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
148 "format", SHADOW_COPY2_DEFAULT_FORMAT);
150 ZERO_STRUCT(timestamp);
151 if (strptime(name, fmt, ×tamp) == NULL) {
152 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
157 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
158 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
159 SHADOW_COPY2_DEFAULT_LOCALTIME))
161 timestamp.tm_isdst = -1;
162 timestamp_t = mktime(×tamp);
163 gmtime_r(×tamp_t, ×tamp);
165 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, ×tamp);
167 return talloc_strdup(mem_ctx, gmt);
171 shadow copy paths can also come into the server in this form:
173 /foo/bar/@GMT-XXXXX/some/file
175 This function normalises the filename to be of the form:
177 @GMT-XXXX/foo/bar/some/file
179 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
182 char buf[GMT_NAME_LEN];
185 if (path == gmt_start) {
189 prefix_len = gmt_start - path - 1;
191 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
195 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
196 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
197 * processing. As many VFS calls provide a const char *,
198 * unfortunately we have to make a copy.
201 pcopy = talloc_strdup(talloc_tos(), path);
206 gmt_start = pcopy + prefix_len;
209 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
211 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
214 * Make space for it including a trailing /
216 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
219 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
221 memcpy(pcopy, buf, GMT_NAME_LEN);
222 pcopy[GMT_NAME_LEN] = '/';
224 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
230 convert a name to the shadow directory
233 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
234 const char *name = fname; \
235 const char *gmt_start; \
236 if (shadow_copy2_match_name(fname, &gmt_start)) { \
239 name2 = convert_shadow2_name(handle, fname, gmt_start); \
240 if (name2 == NULL) { \
245 ret = SMB_VFS_NEXT_ ## op args; \
246 talloc_free(name2); \
247 if (ret != eret) extra; \
250 return SMB_VFS_NEXT_ ## op args; \
254 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
255 const char *gmt_start; \
256 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
258 char *smb_base_name_tmp = NULL; \
260 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
261 if (name2 == NULL) { \
265 smb_base_name_tmp = smb_fname->base_name; \
266 smb_fname->base_name = name2; \
267 ret = SMB_VFS_NEXT_ ## op args; \
268 smb_fname->base_name = smb_base_name_tmp; \
269 talloc_free(name2); \
270 if (ret != eret) extra; \
273 return SMB_VFS_NEXT_ ## op args; \
278 convert a name to the shadow directory: NTSTATUS-specific handling
281 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
282 const char *name = fname; \
283 const char *gmt_start; \
284 if (shadow_copy2_match_name(fname, &gmt_start)) { \
287 name2 = convert_shadow2_name(handle, fname, gmt_start); \
288 if (name2 == NULL) { \
293 ret = SMB_VFS_NEXT_ ## op args; \
294 talloc_free(name2); \
295 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
298 return SMB_VFS_NEXT_ ## op args; \
302 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
304 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
306 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
308 #define SHADOW2_NEXT2(op, args) do { \
309 const char *gmt_start1, *gmt_start2; \
310 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
311 shadow_copy2_match_name(newname, &gmt_start2)) { \
315 return SMB_VFS_NEXT_ ## op args; \
319 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
320 const char *gmt_start1, *gmt_start2; \
321 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
322 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
326 return SMB_VFS_NEXT_ ## op args; \
332 find the mount point of a filesystem
334 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
336 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
341 if (stat(path, &st) != 0) {
348 while ((p = strrchr(path, '/')) && p > path) {
350 if (stat(path, &st) != 0) {
354 if (st.st_dev != dev) {
364 work out the location of the snapshot for this share
366 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
372 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
373 if (snapdir == NULL) {
376 /* if its an absolute path, we're done */
377 if (*snapdir == '/') {
381 /* other its relative to the filesystem mount point */
382 mount_point = find_mount_point(mem_ctx, handle);
383 if (mount_point == NULL) {
387 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
388 talloc_free(mount_point);
393 work out the location of the base directory for snapshots of this share
395 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
397 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
399 /* other its the filesystem mount point */
400 if (basedir == NULL) {
401 basedir = find_mount_point(mem_ctx, handle);
408 convert a filename from a share relative path, to a path in the
411 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
413 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
414 const char *snapdir, *relpath, *baseoffset, *basedir;
420 char snapshot[MAXPATHLEN];
423 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
424 "format", SHADOW_COPY2_DEFAULT_FORMAT);
426 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
427 if (snapdir == NULL) {
428 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
429 talloc_free(tmp_ctx);
433 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
434 if (basedir == NULL) {
435 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
436 talloc_free(tmp_ctx);
440 prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
441 if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
442 /* this looks like as we have already normalized it, leave it untouched*/
443 talloc_free(tmp_ctx);
444 return talloc_strdup(handle->data, fname);
447 if (strncmp(fname, "@GMT-", 5) != 0) {
448 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
450 talloc_free(tmp_ctx);
455 ZERO_STRUCT(timestamp);
456 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, ×tamp);
457 if (relpath == NULL) {
458 talloc_free(tmp_ctx);
462 /* relpath is the remaining portion of the path after the @GMT-xxx */
464 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
465 SHADOW_COPY2_DEFAULT_LOCALTIME))
467 timestamp_t = timegm(×tamp);
468 localtime_r(×tamp_t, ×tamp);
471 strftime(snapshot, MAXPATHLEN, fmt, ×tamp);
473 baselen = strlen(basedir);
474 baseoffset = handle->conn->connectpath + baselen;
476 /* some sanity checks */
477 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
478 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
479 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
480 basedir, handle->conn->connectpath));
481 talloc_free(tmp_ctx);
485 if (*relpath == '/') relpath++;
486 if (*baseoffset == '/') baseoffset++;
488 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
493 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
494 talloc_free(tmp_ctx);
502 static uint32 string_hash(const char *s)
506 n = ((n << 5) + n) ^ (uint32)(*s++);
512 modify a sbuf return to ensure that inodes in the shadow directory
513 are different from those in the main directory
515 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
517 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
518 /* some snapshot systems, like GPFS, return the name
519 device:inode for the snapshot files as the current
520 files. That breaks the 'restore' button in the shadow copy
521 GUI, as the client gets a sharing violation.
523 This is a crude way of allowing both files to be
524 open at once. It has a slight chance of inode
525 number collision, but I can't see a better approach
526 without significant VFS changes
528 uint32_t shash = string_hash(fname) & 0xFF000000;
532 sbuf->st_ex_ino ^= shash;
536 static int shadow_copy2_rename(vfs_handle_struct *handle,
537 const struct smb_filename *smb_fname_src,
538 const struct smb_filename *smb_fname_dst)
540 if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
544 SHADOW2_NEXT2_SMB_FNAME(RENAME,
545 (handle, smb_fname_src, smb_fname_dst));
548 static int shadow_copy2_symlink(vfs_handle_struct *handle,
549 const char *oldname, const char *newname)
551 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
554 static int shadow_copy2_link(vfs_handle_struct *handle,
555 const char *oldname, const char *newname)
557 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
560 static int shadow_copy2_open(vfs_handle_struct *handle,
561 struct smb_filename *smb_fname, files_struct *fsp,
562 int flags, mode_t mode)
564 SHADOW2_NEXT_SMB_FNAME(OPEN,
565 (handle, smb_fname, fsp, flags, mode),
569 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
570 const char *fname, const char *mask, uint32 attr)
572 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
575 static int shadow_copy2_stat(vfs_handle_struct *handle,
576 struct smb_filename *smb_fname)
578 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
579 convert_sbuf(handle, smb_fname->base_name,
583 static int shadow_copy2_lstat(vfs_handle_struct *handle,
584 struct smb_filename *smb_fname)
586 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
587 convert_sbuf(handle, smb_fname->base_name,
591 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
593 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
594 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
595 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
600 static int shadow_copy2_unlink(vfs_handle_struct *handle,
601 const struct smb_filename *smb_fname_in)
603 struct smb_filename *smb_fname = NULL;
606 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
607 if (!NT_STATUS_IS_OK(status)) {
608 errno = map_errno_from_nt_status(status);
612 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
615 static int shadow_copy2_chmod(vfs_handle_struct *handle,
616 const char *fname, mode_t mode)
618 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
621 static int shadow_copy2_chown(vfs_handle_struct *handle,
622 const char *fname, uid_t uid, gid_t gid)
624 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
627 static int shadow_copy2_chdir(vfs_handle_struct *handle,
630 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
633 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
634 const struct smb_filename *smb_fname_in,
635 struct smb_file_time *ft)
637 struct smb_filename *smb_fname = NULL;
640 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
641 if (!NT_STATUS_IS_OK(status)) {
642 errno = map_errno_from_nt_status(status);
646 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
649 static int shadow_copy2_readlink(vfs_handle_struct *handle,
650 const char *fname, char *buf, size_t bufsiz)
652 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
655 static int shadow_copy2_mknod(vfs_handle_struct *handle,
656 const char *fname, mode_t mode, SMB_DEV_T dev)
658 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
661 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
666 if (shadow_copy2_match_name(fname, &gmt)
667 && (gmt[GMT_NAME_LEN] == '\0')) {
670 copy = talloc_strdup(talloc_tos(), fname);
676 copy[gmt - fname] = '.';
677 copy[gmt - fname + 1] = '\0';
679 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
680 SHADOW2_NEXT(REALPATH, (handle, name), char *,
683 SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
686 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
690 const char *snapdir, *baseoffset, *basedir, *gmt_start;
694 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
696 if (!shadow_copy2_match_name(fname, &gmt_start)) {
697 return handle->conn->connectpath;
701 * We have to create a real temporary context because we have
702 * to put our result on talloc_tos(). Thus we can't use a
703 * talloc_stackframe() here.
705 tmp_ctx = talloc_new(talloc_tos());
707 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
709 TALLOC_FREE(tmp_ctx);
713 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
714 if (snapdir == NULL) {
715 DEBUG(2,("no snapdir found for share at %s\n",
716 handle->conn->connectpath));
717 TALLOC_FREE(tmp_ctx);
721 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
722 if (basedir == NULL) {
723 DEBUG(2,("no basedir found for share at %s\n",
724 handle->conn->connectpath));
725 TALLOC_FREE(tmp_ctx);
729 baselen = strlen(basedir);
730 baseoffset = handle->conn->connectpath + baselen;
732 /* some sanity checks */
733 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
734 (handle->conn->connectpath[baselen] != 0
735 && handle->conn->connectpath[baselen] != '/')) {
736 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
737 "parent of %s\n", basedir,
738 handle->conn->connectpath));
739 TALLOC_FREE(tmp_ctx);
743 if (*baseoffset == '/') baseoffset++;
745 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
749 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
750 TALLOC_FREE(tmp_ctx);
754 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
755 const char *fname, uint32 security_info,
756 struct security_descriptor **ppdesc)
758 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
761 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
763 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
766 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
768 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
771 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
774 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
777 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
778 const char *fname, const char *aname, void *value, size_t size)
780 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
783 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
784 const char *fname, const char *aname, void *value, size_t size)
786 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
789 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
790 char *list, size_t size)
792 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
795 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
798 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
801 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
804 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
807 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
808 const char *aname, const void *value, size_t size, int flags)
810 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
813 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
814 const char *aname, const void *value, size_t size, int flags)
816 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
819 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
820 const char *fname, mode_t mode)
822 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
825 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
827 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
830 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
832 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
836 sort the shadow copy data in ascending or descending order
838 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
839 SHADOW_COPY_DATA *shadow_copy2_data)
841 int (*cmpfunc)(const void *, const void *);
844 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
845 "sort", SHADOW_COPY2_DEFAULT_SORT);
850 if (strcmp(sort, "asc") == 0) {
851 cmpfunc = shadow_copy2_label_cmp_asc;
852 } else if (strcmp(sort, "desc") == 0) {
853 cmpfunc = shadow_copy2_label_cmp_desc;
858 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
859 shadow_copy2_data->labels)
861 TYPESAFE_QSORT(shadow_copy2_data->labels,
862 shadow_copy2_data->num_volumes,
869 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
871 SHADOW_COPY_DATA *shadow_copy2_data,
876 SMB_STRUCT_DIRENT *d;
877 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
880 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
881 if (snapdir == NULL) {
882 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
883 handle->conn->connectpath));
885 talloc_free(tmp_ctx);
889 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
892 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
893 " - %s\n", snapdir, strerror(errno)));
894 talloc_free(tmp_ctx);
899 shadow_copy2_data->num_volumes = 0;
900 shadow_copy2_data->labels = NULL;
902 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
903 SHADOW_COPY_LABEL *tlabels;
905 /* ignore names not of the right form in the snapshot directory */
906 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
908 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
909 d->d_name, snapshot));
915 /* the caller doesn't want the labels */
916 shadow_copy2_data->num_volumes++;
920 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
921 shadow_copy2_data->labels,
922 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
923 if (tlabels == NULL) {
924 DEBUG(0,("shadow_copy2: out of memory\n"));
925 SMB_VFS_NEXT_CLOSEDIR(handle, p);
926 talloc_free(tmp_ctx);
930 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
932 talloc_free(snapshot);
934 shadow_copy2_data->num_volumes++;
935 shadow_copy2_data->labels = tlabels;
938 SMB_VFS_NEXT_CLOSEDIR(handle,p);
940 shadow_copy2_sort_data(handle, shadow_copy2_data);
942 talloc_free(tmp_ctx);
946 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
947 .opendir = shadow_copy2_opendir,
948 .mkdir = shadow_copy2_mkdir,
949 .rmdir = shadow_copy2_rmdir,
950 .chflags = shadow_copy2_chflags,
951 .getxattr = shadow_copy2_getxattr,
952 .lgetxattr = shadow_copy2_lgetxattr,
953 .listxattr = shadow_copy2_listxattr,
954 .removexattr = shadow_copy2_removexattr,
955 .lremovexattr = shadow_copy2_lremovexattr,
956 .setxattr = shadow_copy2_setxattr,
957 .lsetxattr = shadow_copy2_lsetxattr,
958 .open = shadow_copy2_open,
959 .rename = shadow_copy2_rename,
960 .stat = shadow_copy2_stat,
961 .lstat = shadow_copy2_lstat,
962 .fstat = shadow_copy2_fstat,
963 .unlink = shadow_copy2_unlink,
964 .chmod = shadow_copy2_chmod,
965 .chown = shadow_copy2_chown,
966 .chdir = shadow_copy2_chdir,
967 .ntimes = shadow_copy2_ntimes,
968 .symlink = shadow_copy2_symlink,
969 .vfs_readlink = shadow_copy2_readlink,
970 .link = shadow_copy2_link,
971 .mknod = shadow_copy2_mknod,
972 .realpath = shadow_copy2_realpath,
973 .connectpath = shadow_copy2_connectpath,
974 .get_nt_acl = shadow_copy2_get_nt_acl,
975 .chmod_acl = shadow_copy2_chmod_acl,
976 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
979 NTSTATUS vfs_shadow_copy2_init(void);
980 NTSTATUS vfs_shadow_copy2_init(void)
984 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
985 &vfs_shadow_copy2_fns);
987 if (!NT_STATUS_IS_OK(ret))
990 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
991 if (vfs_shadow_copy2_debug_level == -1) {
992 vfs_shadow_copy2_debug_level = DBGC_VFS;
993 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
994 "vfs_shadow_copy2_init"));
996 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
997 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));