shadow_copy2: break overly long lines in shadow_copy2_snapshot_to_gmt()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobf3931d36b490ca826dc3e6e0027c6f299286e71a
1 /*
2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
28 following features:
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
46 rather than UTC.
48 Module options:
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
60 mount point
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:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
91 time.
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
111 size_t **poffsets,
112 unsigned *pnum_offsets)
114 unsigned num_offsets;
115 size_t *offsets;
116 const char *p;
118 num_offsets = 0;
120 p = str;
121 while ((p = strchr(p, '/')) != NULL) {
122 num_offsets += 1;
123 p += 1;
126 offsets = talloc_array(mem_ctx, size_t, num_offsets);
127 if (offsets == NULL) {
128 return false;
131 p = str;
132 num_offsets = 0;
133 while ((p = strchr(p, '/')) != NULL) {
134 offsets[num_offsets] = p-str;
135 num_offsets += 1;
136 p += 1;
139 *poffsets = offsets;
140 *pnum_offsets = num_offsets;
141 return true;
144 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
145 struct vfs_handle_struct *handle,
146 time_t snapshot)
148 const char *fmt;
149 struct tm snap_tm;
150 fstring snaptime_string;
151 size_t snaptime_len;
153 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
154 "format", GMT_FORMAT);
156 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
157 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
158 (unsigned long)snapshot);
159 if (snaptime_len <= 0) {
160 DEBUG(10, ("snprintf failed\n"));
161 return NULL;
163 } else {
164 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
165 if (localtime_r(&snapshot, &snap_tm) == 0) {
166 DEBUG(10, ("gmtime_r failed\n"));
167 return NULL;
169 } else {
170 if (gmtime_r(&snapshot, &snap_tm) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
172 return NULL;
175 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
176 &snap_tm);
177 if (snaptime_len == 0) {
178 DEBUG(10, ("strftime failed\n"));
179 return NULL;
182 return talloc_asprintf(mem_ctx, "/%s/%s",
183 lp_parm_const_string(
184 SNUM(handle->conn), "shadow", "snapdir",
185 ".snapshots"),
186 snaptime_string);
189 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
190 struct vfs_handle_struct *handle,
191 const char *name,
192 time_t *ptimestamp,
193 char **pstripped)
195 struct tm tm;
196 time_t timestamp;
197 const char *p;
198 char *q;
199 char *stripped;
200 size_t rest_len, dst_len;
202 p = strstr_m(name, "@GMT-");
203 if (p == NULL) {
204 goto no_snapshot;
206 if ((p > name) && (p[-1] != '/')) {
207 goto no_snapshot;
209 q = strptime(p, GMT_FORMAT, &tm);
210 if (q == NULL) {
211 goto no_snapshot;
213 tm.tm_isdst = -1;
214 timestamp = timegm(&tm);
215 if (timestamp == (time_t)-1) {
216 goto no_snapshot;
218 if ((p == name) && (q[0] == '\0')) {
219 if (pstripped != NULL) {
220 stripped = talloc_strdup(mem_ctx, "");
221 if (stripped == NULL) {
222 return false;
224 *pstripped = stripped;
226 *ptimestamp = timestamp;
227 return true;
229 if (q[0] != '/') {
230 goto no_snapshot;
232 q += 1;
234 rest_len = strlen(q);
235 dst_len = (p-name) + rest_len;
237 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
238 false)) {
239 char *insert;
240 bool have_insert;
241 insert = shadow_copy2_insert_string(talloc_tos(), handle,
242 timestamp);
243 if (insert == NULL) {
244 errno = ENOMEM;
245 return false;
248 have_insert = (strstr(name, insert+1) != NULL);
249 TALLOC_FREE(insert);
250 if (have_insert) {
251 goto no_snapshot;
255 if (pstripped != NULL) {
256 stripped = talloc_array(mem_ctx, char, dst_len+1);
257 if (stripped == NULL) {
258 errno = ENOMEM;
259 return false;
261 if (p > name) {
262 memcpy(stripped, name, p-name);
264 if (rest_len > 0) {
265 memcpy(stripped + (p-name), q, rest_len);
267 stripped[dst_len] = '\0';
268 *pstripped = stripped;
270 *ptimestamp = timestamp;
271 return true;
272 no_snapshot:
273 *ptimestamp = 0;
274 return true;
277 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
278 vfs_handle_struct *handle)
280 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
281 dev_t dev;
282 struct stat st;
283 char *p;
285 if (stat(path, &st) != 0) {
286 talloc_free(path);
287 return NULL;
290 dev = st.st_dev;
292 while ((p = strrchr(path, '/')) && p > path) {
293 *p = 0;
294 if (stat(path, &st) != 0) {
295 talloc_free(path);
296 return NULL;
298 if (st.st_dev != dev) {
299 *p = '/';
300 break;
304 return path;
307 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
308 struct vfs_handle_struct *handle,
309 const char *name, time_t timestamp)
311 struct smb_filename converted_fname;
312 char *result = NULL;
313 size_t *slashes = NULL;
314 unsigned num_slashes;
315 char *path = NULL;
316 size_t pathlen;
317 char *insert = NULL;
318 char *converted = NULL;
319 size_t insertlen;
320 int i, saved_errno;
321 size_t min_offset;
323 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
324 name);
325 if (path == NULL) {
326 errno = ENOMEM;
327 goto fail;
329 pathlen = talloc_get_size(path)-1;
331 DEBUG(10, ("converting %s\n", path));
333 if (!shadow_copy2_find_slashes(talloc_tos(), path,
334 &slashes, &num_slashes)) {
335 goto fail;
337 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
338 if (insert == NULL) {
339 goto fail;
341 insertlen = talloc_get_size(insert)-1;
342 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
343 if (converted == NULL) {
344 goto fail;
347 if (path[pathlen-1] != '/') {
349 * Append a fake slash to find the snapshot root
351 size_t *tmp;
352 tmp = talloc_realloc(talloc_tos(), slashes,
353 size_t, num_slashes+1);
354 if (tmp == NULL) {
355 goto fail;
357 slashes = tmp;
358 slashes[num_slashes] = pathlen;
359 num_slashes += 1;
362 min_offset = 0;
364 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
365 false)) {
366 char *mount_point;
368 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
369 handle);
370 if (mount_point == NULL) {
371 goto fail;
373 min_offset = strlen(mount_point);
374 TALLOC_FREE(mount_point);
377 memcpy(converted, path, pathlen+1);
378 converted[pathlen+insertlen] = '\0';
380 ZERO_STRUCT(converted_fname);
381 converted_fname.base_name = converted;
383 for (i = num_slashes-1; i>=0; i--) {
384 int ret;
385 size_t offset;
387 offset = slashes[i];
389 if (offset < min_offset) {
390 errno = ENOENT;
391 goto fail;
394 memcpy(converted+offset, insert, insertlen);
396 offset += insertlen;
397 memcpy(converted+offset, path + slashes[i],
398 pathlen - slashes[i]);
400 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
402 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
403 ret, ret == 0 ? "ok" : strerror(errno)));
404 if (ret == 0) {
405 /* success */
406 break;
408 if (errno == ENOTDIR) {
410 * This is a valid condition: We appended the
411 * .snaphots/@GMT.. to a file name. Just try
412 * with the upper levels.
414 continue;
416 if (errno != ENOENT) {
417 /* Other problem than "not found" */
418 goto fail;
422 if (i >= 0) {
424 * Found something
426 DEBUG(10, ("Found %s\n", converted));
427 result = converted;
428 converted = NULL;
429 } else {
430 errno = ENOENT;
432 fail:
433 saved_errno = errno;
434 TALLOC_FREE(converted);
435 TALLOC_FREE(insert);
436 TALLOC_FREE(slashes);
437 TALLOC_FREE(path);
438 errno = saved_errno;
439 return result;
443 modify a sbuf return to ensure that inodes in the shadow directory
444 are different from those in the main directory
446 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
447 SMB_STRUCT_STAT *sbuf)
449 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
450 /* some snapshot systems, like GPFS, return the name
451 device:inode for the snapshot files as the current
452 files. That breaks the 'restore' button in the shadow copy
453 GUI, as the client gets a sharing violation.
455 This is a crude way of allowing both files to be
456 open at once. It has a slight chance of inode
457 number collision, but I can't see a better approach
458 without significant VFS changes
460 uint32_t shash;
462 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
463 if (shash == 0) {
464 shash = 1;
466 sbuf->st_ex_ino ^= shash;
470 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
471 const char *fname,
472 const char *mask,
473 uint32 attr)
475 time_t timestamp;
476 char *stripped;
477 DIR *ret;
478 int saved_errno;
479 char *conv;
481 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
482 &timestamp, &stripped)) {
483 return NULL;
485 if (timestamp == 0) {
486 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
488 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
489 TALLOC_FREE(stripped);
490 if (conv == NULL) {
491 return NULL;
493 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
494 saved_errno = errno;
495 TALLOC_FREE(conv);
496 errno = saved_errno;
497 return ret;
500 static int shadow_copy2_rename(vfs_handle_struct *handle,
501 const struct smb_filename *smb_fname_src,
502 const struct smb_filename *smb_fname_dst)
504 time_t timestamp_src, timestamp_dst;
506 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
507 smb_fname_src->base_name,
508 &timestamp_src, NULL)) {
509 return -1;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
512 smb_fname_dst->base_name,
513 &timestamp_dst, NULL)) {
514 return -1;
516 if (timestamp_src != 0) {
517 errno = EXDEV;
518 return -1;
520 if (timestamp_dst != 0) {
521 errno = EROFS;
522 return -1;
524 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
527 static int shadow_copy2_symlink(vfs_handle_struct *handle,
528 const char *oldname, const char *newname)
530 time_t timestamp_old, timestamp_new;
532 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
533 &timestamp_old, NULL)) {
534 return -1;
536 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
537 &timestamp_new, NULL)) {
538 return -1;
540 if ((timestamp_old != 0) || (timestamp_new != 0)) {
541 errno = EROFS;
542 return -1;
544 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
547 static int shadow_copy2_link(vfs_handle_struct *handle,
548 const char *oldname, const char *newname)
550 time_t timestamp_old, timestamp_new;
552 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
553 &timestamp_old, NULL)) {
554 return -1;
556 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
557 &timestamp_new, NULL)) {
558 return -1;
560 if ((timestamp_old != 0) || (timestamp_new != 0)) {
561 errno = EROFS;
562 return -1;
564 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
567 static int shadow_copy2_stat(vfs_handle_struct *handle,
568 struct smb_filename *smb_fname)
570 time_t timestamp;
571 char *stripped, *tmp;
572 int ret, saved_errno;
574 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
575 smb_fname->base_name,
576 &timestamp, &stripped)) {
577 return -1;
579 if (timestamp == 0) {
580 return SMB_VFS_NEXT_STAT(handle, smb_fname);
583 tmp = smb_fname->base_name;
584 smb_fname->base_name = shadow_copy2_convert(
585 talloc_tos(), handle, stripped, timestamp);
586 TALLOC_FREE(stripped);
588 if (smb_fname->base_name == NULL) {
589 smb_fname->base_name = tmp;
590 return -1;
593 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
594 saved_errno = errno;
596 TALLOC_FREE(smb_fname->base_name);
597 smb_fname->base_name = tmp;
599 if (ret == 0) {
600 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
602 errno = saved_errno;
603 return ret;
606 static int shadow_copy2_lstat(vfs_handle_struct *handle,
607 struct smb_filename *smb_fname)
609 time_t timestamp;
610 char *stripped, *tmp;
611 int ret, saved_errno;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
614 smb_fname->base_name,
615 &timestamp, &stripped)) {
616 return -1;
618 if (timestamp == 0) {
619 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
622 tmp = smb_fname->base_name;
623 smb_fname->base_name = shadow_copy2_convert(
624 talloc_tos(), handle, stripped, timestamp);
625 TALLOC_FREE(stripped);
627 if (smb_fname->base_name == NULL) {
628 smb_fname->base_name = tmp;
629 return -1;
632 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
633 saved_errno = errno;
635 TALLOC_FREE(smb_fname->base_name);
636 smb_fname->base_name = tmp;
638 if (ret == 0) {
639 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
641 errno = saved_errno;
642 return ret;
645 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
646 SMB_STRUCT_STAT *sbuf)
648 time_t timestamp;
649 int ret;
651 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
652 if (ret == -1) {
653 return ret;
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
656 fsp->fsp_name->base_name,
657 &timestamp, NULL)) {
658 return 0;
660 if (timestamp != 0) {
661 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
663 return 0;
666 static int shadow_copy2_open(vfs_handle_struct *handle,
667 struct smb_filename *smb_fname, files_struct *fsp,
668 int flags, mode_t mode)
670 time_t timestamp;
671 char *stripped, *tmp;
672 int ret, saved_errno;
674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
675 smb_fname->base_name,
676 &timestamp, &stripped)) {
677 return -1;
679 if (timestamp == 0) {
680 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
683 tmp = smb_fname->base_name;
684 smb_fname->base_name = shadow_copy2_convert(
685 talloc_tos(), handle, stripped, timestamp);
686 TALLOC_FREE(stripped);
688 if (smb_fname->base_name == NULL) {
689 smb_fname->base_name = tmp;
690 return -1;
693 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
694 saved_errno = errno;
696 TALLOC_FREE(smb_fname->base_name);
697 smb_fname->base_name = tmp;
699 errno = saved_errno;
700 return ret;
703 static int shadow_copy2_unlink(vfs_handle_struct *handle,
704 const struct smb_filename *smb_fname)
706 time_t timestamp;
707 char *stripped;
708 int ret, saved_errno;
709 struct smb_filename *conv;
710 NTSTATUS status;
712 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
713 smb_fname->base_name,
714 &timestamp, &stripped)) {
715 return -1;
717 if (timestamp == 0) {
718 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
720 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
721 if (!NT_STATUS_IS_OK(status)) {
722 errno = ENOMEM;
723 return -1;
725 conv->base_name = shadow_copy2_convert(
726 conv, handle, stripped, timestamp);
727 TALLOC_FREE(stripped);
728 if (conv->base_name == NULL) {
729 return -1;
731 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
732 saved_errno = errno;
733 TALLOC_FREE(conv);
734 errno = saved_errno;
735 return ret;
738 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
739 mode_t mode)
741 time_t timestamp;
742 char *stripped;
743 int ret, saved_errno;
744 char *conv;
746 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
747 &timestamp, &stripped)) {
748 return -1;
750 if (timestamp == 0) {
751 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
753 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
754 TALLOC_FREE(stripped);
755 if (conv == NULL) {
756 return -1;
758 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
759 saved_errno = errno;
760 TALLOC_FREE(conv);
761 errno = saved_errno;
762 return ret;
765 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
766 uid_t uid, gid_t gid)
768 time_t timestamp;
769 char *stripped;
770 int ret, saved_errno;
771 char *conv;
773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
774 &timestamp, &stripped)) {
775 return -1;
777 if (timestamp == 0) {
778 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
780 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
781 TALLOC_FREE(stripped);
782 if (conv == NULL) {
783 return -1;
785 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
786 saved_errno = errno;
787 TALLOC_FREE(conv);
788 errno = saved_errno;
789 return ret;
792 static int shadow_copy2_chdir(vfs_handle_struct *handle,
793 const char *fname)
795 time_t timestamp;
796 char *stripped;
797 int ret, saved_errno;
798 char *conv;
800 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
801 &timestamp, &stripped)) {
802 return -1;
804 if (timestamp == 0) {
805 return SMB_VFS_NEXT_CHDIR(handle, fname);
807 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
808 TALLOC_FREE(stripped);
809 if (conv == NULL) {
810 return -1;
812 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
813 saved_errno = errno;
814 TALLOC_FREE(conv);
815 errno = saved_errno;
816 return ret;
819 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
820 const struct smb_filename *smb_fname,
821 struct smb_file_time *ft)
823 time_t timestamp;
824 char *stripped;
825 int ret, saved_errno;
826 struct smb_filename *conv;
827 NTSTATUS status;
829 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
830 smb_fname->base_name,
831 &timestamp, &stripped)) {
832 return -1;
834 if (timestamp == 0) {
835 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
837 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
838 if (!NT_STATUS_IS_OK(status)) {
839 errno = ENOMEM;
840 return -1;
842 conv->base_name = shadow_copy2_convert(
843 conv, handle, stripped, timestamp);
844 TALLOC_FREE(stripped);
845 if (conv->base_name == NULL) {
846 return -1;
848 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
849 saved_errno = errno;
850 TALLOC_FREE(conv);
851 errno = saved_errno;
852 return ret;
855 static int shadow_copy2_readlink(vfs_handle_struct *handle,
856 const char *fname, char *buf, size_t bufsiz)
858 time_t timestamp;
859 char *stripped;
860 int ret, saved_errno;
861 char *conv;
863 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
864 &timestamp, &stripped)) {
865 return -1;
867 if (timestamp == 0) {
868 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
870 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
871 TALLOC_FREE(stripped);
872 if (conv == NULL) {
873 return -1;
875 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
876 saved_errno = errno;
877 TALLOC_FREE(conv);
878 errno = saved_errno;
879 return ret;
882 static int shadow_copy2_mknod(vfs_handle_struct *handle,
883 const char *fname, mode_t mode, SMB_DEV_T dev)
885 time_t timestamp;
886 char *stripped;
887 int ret, saved_errno;
888 char *conv;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
891 &timestamp, &stripped)) {
892 return -1;
894 if (timestamp == 0) {
895 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
897 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
898 TALLOC_FREE(stripped);
899 if (conv == NULL) {
900 return -1;
902 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
903 saved_errno = errno;
904 TALLOC_FREE(conv);
905 errno = saved_errno;
906 return ret;
909 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
910 const char *fname)
912 time_t timestamp;
913 char *stripped = NULL;
914 char *tmp = NULL;
915 char *result = NULL;
916 char *inserted = NULL;
917 char *inserted_to, *inserted_end;
918 int saved_errno;
920 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
921 &timestamp, &stripped)) {
922 goto done;
924 if (timestamp == 0) {
925 return SMB_VFS_NEXT_REALPATH(handle, fname);
928 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
929 if (tmp == NULL) {
930 goto done;
933 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
934 if (result == NULL) {
935 goto done;
939 * Take away what we've inserted. This removes the @GMT-thingy
940 * completely, but will give a path under the share root.
942 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
943 if (inserted == NULL) {
944 goto done;
946 inserted_to = strstr_m(result, inserted);
947 if (inserted_to == NULL) {
948 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
949 goto done;
951 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
952 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
954 done:
955 saved_errno = errno;
956 TALLOC_FREE(inserted);
957 TALLOC_FREE(tmp);
958 TALLOC_FREE(stripped);
959 errno = saved_errno;
960 return result;
963 static char *have_snapdir(struct vfs_handle_struct *handle,
964 const char *path)
966 struct smb_filename smb_fname;
967 int ret;
969 ZERO_STRUCT(smb_fname);
970 smb_fname.base_name = talloc_asprintf(
971 talloc_tos(), "%s/%s", path,
972 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
973 ".snapshots"));
974 if (smb_fname.base_name == NULL) {
975 return NULL;
978 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
979 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
980 return smb_fname.base_name;
982 TALLOC_FREE(smb_fname.base_name);
983 return NULL;
986 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
987 struct vfs_handle_struct *handle,
988 struct smb_filename *smb_fname)
990 char *path, *p;
991 char *snapdir;
993 path = talloc_asprintf(mem_ctx, "%s/%s",
994 handle->conn->connectpath,
995 smb_fname->base_name);
996 if (path == NULL) {
997 return NULL;
1000 snapdir = have_snapdir(handle, path);
1001 if (snapdir != NULL) {
1002 TALLOC_FREE(path);
1003 return snapdir;
1006 while ((p = strrchr(path, '/')) && (p > path)) {
1008 p[0] = '\0';
1010 snapdir = have_snapdir(handle, path);
1011 if (snapdir != NULL) {
1012 TALLOC_FREE(path);
1013 return snapdir;
1016 TALLOC_FREE(path);
1017 return NULL;
1020 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1021 const char *name,
1022 char *gmt, size_t gmt_len)
1024 struct tm timestamp;
1025 time_t timestamp_t;
1026 unsigned long int timestamp_long;
1027 const char *fmt;
1029 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1030 "format", GMT_FORMAT);
1032 ZERO_STRUCT(timestamp);
1033 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1034 if (sscanf(name, fmt, &timestamp_long) != 1) {
1035 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1036 "no sscanf match %s: %s\n",
1037 fmt, name));
1038 return false;
1040 timestamp_t = timestamp_long;
1041 gmtime_r(&timestamp_t, &timestamp);
1042 } else {
1043 if (strptime(name, fmt, &timestamp) == NULL) {
1044 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1045 "no match %s: %s\n",
1046 fmt, name));
1047 return false;
1049 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1050 fmt, name));
1052 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1053 timestamp.tm_isdst = -1;
1054 timestamp_t = mktime(&timestamp);
1055 gmtime_r(&timestamp_t, &timestamp);
1059 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1060 return true;
1063 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1065 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1068 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1070 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1074 sort the shadow copy data in ascending or descending order
1076 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1077 struct shadow_copy_data *shadow_copy2_data)
1079 int (*cmpfunc)(const void *, const void *);
1080 const char *sort;
1082 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1083 "sort", "desc");
1084 if (sort == NULL) {
1085 return;
1088 if (strcmp(sort, "asc") == 0) {
1089 cmpfunc = shadow_copy2_label_cmp_asc;
1090 } else if (strcmp(sort, "desc") == 0) {
1091 cmpfunc = shadow_copy2_label_cmp_desc;
1092 } else {
1093 return;
1096 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1097 shadow_copy2_data->labels)
1099 TYPESAFE_QSORT(shadow_copy2_data->labels,
1100 shadow_copy2_data->num_volumes,
1101 cmpfunc);
1105 static int shadow_copy2_get_shadow_copy_data(
1106 vfs_handle_struct *handle, files_struct *fsp,
1107 struct shadow_copy_data *shadow_copy2_data,
1108 bool labels)
1110 DIR *p;
1111 const char *snapdir;
1112 struct dirent *d;
1113 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1115 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1116 if (snapdir == NULL) {
1117 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1118 handle->conn->connectpath));
1119 errno = EINVAL;
1120 talloc_free(tmp_ctx);
1121 return -1;
1124 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1126 if (!p) {
1127 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1128 " - %s\n", snapdir, strerror(errno)));
1129 talloc_free(tmp_ctx);
1130 errno = ENOSYS;
1131 return -1;
1134 shadow_copy2_data->num_volumes = 0;
1135 shadow_copy2_data->labels = NULL;
1137 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1138 char snapshot[GMT_NAME_LEN+1];
1139 SHADOW_COPY_LABEL *tlabels;
1142 * ignore names not of the right form in the snapshot
1143 * directory
1145 if (!shadow_copy2_snapshot_to_gmt(
1146 handle, d->d_name,
1147 snapshot, sizeof(snapshot))) {
1149 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1150 "ignoring %s\n", d->d_name));
1151 continue;
1153 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1154 d->d_name, snapshot));
1156 if (!labels) {
1157 /* the caller doesn't want the labels */
1158 shadow_copy2_data->num_volumes++;
1159 continue;
1162 tlabels = talloc_realloc(shadow_copy2_data,
1163 shadow_copy2_data->labels,
1164 SHADOW_COPY_LABEL,
1165 shadow_copy2_data->num_volumes+1);
1166 if (tlabels == NULL) {
1167 DEBUG(0,("shadow_copy2: out of memory\n"));
1168 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1169 talloc_free(tmp_ctx);
1170 return -1;
1173 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1174 sizeof(*tlabels));
1176 shadow_copy2_data->num_volumes++;
1177 shadow_copy2_data->labels = tlabels;
1180 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1182 shadow_copy2_sort_data(handle, shadow_copy2_data);
1184 talloc_free(tmp_ctx);
1185 return 0;
1188 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1189 struct files_struct *fsp,
1190 uint32 security_info,
1191 TALLOC_CTX *mem_ctx,
1192 struct security_descriptor **ppdesc)
1194 time_t timestamp;
1195 char *stripped;
1196 NTSTATUS status;
1197 char *conv;
1199 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1200 fsp->fsp_name->base_name,
1201 &timestamp, &stripped)) {
1202 return map_nt_error_from_unix(errno);
1204 if (timestamp == 0) {
1205 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1206 mem_ctx,
1207 ppdesc);
1209 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1210 TALLOC_FREE(stripped);
1211 if (conv == NULL) {
1212 return map_nt_error_from_unix(errno);
1214 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1215 mem_ctx, ppdesc);
1216 TALLOC_FREE(conv);
1217 return status;
1220 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1221 const char *fname,
1222 uint32 security_info,
1223 TALLOC_CTX *mem_ctx,
1224 struct security_descriptor **ppdesc)
1226 time_t timestamp;
1227 char *stripped;
1228 NTSTATUS status;
1229 char *conv;
1231 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1232 &timestamp, &stripped)) {
1233 return map_nt_error_from_unix(errno);
1235 if (timestamp == 0) {
1236 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1237 mem_ctx, ppdesc);
1239 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1240 TALLOC_FREE(stripped);
1241 if (conv == NULL) {
1242 return map_nt_error_from_unix(errno);
1244 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1245 mem_ctx, ppdesc);
1246 TALLOC_FREE(conv);
1247 return status;
1250 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1251 const char *fname, mode_t mode)
1253 time_t timestamp;
1254 char *stripped;
1255 int ret, saved_errno;
1256 char *conv;
1258 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1259 &timestamp, &stripped)) {
1260 return -1;
1262 if (timestamp == 0) {
1263 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1265 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1266 TALLOC_FREE(stripped);
1267 if (conv == NULL) {
1268 return -1;
1270 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1271 saved_errno = errno;
1272 TALLOC_FREE(conv);
1273 errno = saved_errno;
1274 return ret;
1277 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1279 time_t timestamp;
1280 char *stripped;
1281 int ret, saved_errno;
1282 char *conv;
1284 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1285 &timestamp, &stripped)) {
1286 return -1;
1288 if (timestamp == 0) {
1289 return SMB_VFS_NEXT_RMDIR(handle, fname);
1291 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1292 TALLOC_FREE(stripped);
1293 if (conv == NULL) {
1294 return -1;
1296 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1297 saved_errno = errno;
1298 TALLOC_FREE(conv);
1299 errno = saved_errno;
1300 return ret;
1303 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1304 unsigned int flags)
1306 time_t timestamp;
1307 char *stripped;
1308 int ret, saved_errno;
1309 char *conv;
1311 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1312 &timestamp, &stripped)) {
1313 return -1;
1315 if (timestamp == 0) {
1316 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1318 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1319 TALLOC_FREE(stripped);
1320 if (conv == NULL) {
1321 return -1;
1323 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1324 saved_errno = errno;
1325 TALLOC_FREE(conv);
1326 errno = saved_errno;
1327 return ret;
1330 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1331 const char *fname, const char *aname,
1332 void *value, size_t size)
1334 time_t timestamp;
1335 char *stripped;
1336 ssize_t ret;
1337 int saved_errno;
1338 char *conv;
1340 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1341 &timestamp, &stripped)) {
1342 return -1;
1344 if (timestamp == 0) {
1345 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1346 size);
1348 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1349 TALLOC_FREE(stripped);
1350 if (conv == NULL) {
1351 return -1;
1353 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1354 saved_errno = errno;
1355 TALLOC_FREE(conv);
1356 errno = saved_errno;
1357 return ret;
1360 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1361 const char *fname,
1362 char *list, size_t size)
1364 time_t timestamp;
1365 char *stripped;
1366 ssize_t ret;
1367 int saved_errno;
1368 char *conv;
1370 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1371 &timestamp, &stripped)) {
1372 return -1;
1374 if (timestamp == 0) {
1375 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1377 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1378 TALLOC_FREE(stripped);
1379 if (conv == NULL) {
1380 return -1;
1382 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1383 saved_errno = errno;
1384 TALLOC_FREE(conv);
1385 errno = saved_errno;
1386 return ret;
1389 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1390 const char *fname, const char *aname)
1392 time_t timestamp;
1393 char *stripped;
1394 int ret, saved_errno;
1395 char *conv;
1397 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1398 &timestamp, &stripped)) {
1399 return -1;
1401 if (timestamp == 0) {
1402 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1404 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1405 TALLOC_FREE(stripped);
1406 if (conv == NULL) {
1407 return -1;
1409 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1410 saved_errno = errno;
1411 TALLOC_FREE(conv);
1412 errno = saved_errno;
1413 return ret;
1416 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1417 const char *fname,
1418 const char *aname, const void *value,
1419 size_t size, int flags)
1421 time_t timestamp;
1422 char *stripped;
1423 ssize_t ret;
1424 int saved_errno;
1425 char *conv;
1427 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1428 &timestamp, &stripped)) {
1429 return -1;
1431 if (timestamp == 0) {
1432 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1433 flags);
1435 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1436 TALLOC_FREE(stripped);
1437 if (conv == NULL) {
1438 return -1;
1440 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1441 saved_errno = errno;
1442 TALLOC_FREE(conv);
1443 errno = saved_errno;
1444 return ret;
1447 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1448 const char *fname, mode_t mode)
1450 time_t timestamp;
1451 char *stripped;
1452 ssize_t ret;
1453 int saved_errno;
1454 char *conv;
1456 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1457 &timestamp, &stripped)) {
1458 return -1;
1460 if (timestamp == 0) {
1461 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1463 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1464 TALLOC_FREE(stripped);
1465 if (conv == NULL) {
1466 return -1;
1468 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1469 saved_errno = errno;
1470 TALLOC_FREE(conv);
1471 errno = saved_errno;
1472 return ret;
1475 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1476 const char *path,
1477 const char *name,
1478 TALLOC_CTX *mem_ctx,
1479 char **found_name)
1481 time_t timestamp;
1482 char *stripped;
1483 ssize_t ret;
1484 int saved_errno;
1485 char *conv;
1487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1488 &timestamp, &stripped)) {
1489 return -1;
1491 if (timestamp == 0) {
1492 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1493 mem_ctx, found_name);
1495 if (stripped[0] == '\0') {
1496 *found_name = talloc_strdup(mem_ctx, name);
1497 if (*found_name == NULL) {
1498 errno = ENOMEM;
1499 return -1;
1501 return 0;
1503 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1504 TALLOC_FREE(stripped);
1505 if (conv == NULL) {
1506 return -1;
1508 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1509 mem_ctx, found_name);
1510 saved_errno = errno;
1511 TALLOC_FREE(conv);
1512 errno = saved_errno;
1513 return ret;
1517 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1518 .opendir_fn = shadow_copy2_opendir,
1519 .rename_fn = shadow_copy2_rename,
1520 .link_fn = shadow_copy2_link,
1521 .symlink_fn = shadow_copy2_symlink,
1522 .stat_fn = shadow_copy2_stat,
1523 .lstat_fn = shadow_copy2_lstat,
1524 .fstat_fn = shadow_copy2_fstat,
1525 .open_fn = shadow_copy2_open,
1526 .unlink_fn = shadow_copy2_unlink,
1527 .chmod_fn = shadow_copy2_chmod,
1528 .chown_fn = shadow_copy2_chown,
1529 .chdir_fn = shadow_copy2_chdir,
1530 .ntimes_fn = shadow_copy2_ntimes,
1531 .readlink_fn = shadow_copy2_readlink,
1532 .mknod_fn = shadow_copy2_mknod,
1533 .realpath_fn = shadow_copy2_realpath,
1534 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1535 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1536 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1537 .mkdir_fn = shadow_copy2_mkdir,
1538 .rmdir_fn = shadow_copy2_rmdir,
1539 .getxattr_fn = shadow_copy2_getxattr,
1540 .listxattr_fn = shadow_copy2_listxattr,
1541 .removexattr_fn = shadow_copy2_removexattr,
1542 .setxattr_fn = shadow_copy2_setxattr,
1543 .chmod_acl_fn = shadow_copy2_chmod_acl,
1544 .chflags_fn = shadow_copy2_chflags,
1545 .get_real_filename_fn = shadow_copy2_get_real_filename,
1548 NTSTATUS vfs_shadow_copy2_init(void);
1549 NTSTATUS vfs_shadow_copy2_init(void)
1551 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1552 "shadow_copy2", &vfs_shadow_copy2_fns);