doc-xml/smbdotconf: fix server [min|max] protocol documentation
[Samba/gebeck_regimport.git] / source3 / modules / vfs_shadow_copy2.c
blob60f96286003e0383d5244858d73e24c6b04f30db
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 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
111 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
113 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
114 size_t **poffsets,
115 unsigned *pnum_offsets)
117 unsigned num_offsets;
118 size_t *offsets;
119 const char *p;
121 num_offsets = 0;
123 p = str;
124 while ((p = strchr(p, '/')) != NULL) {
125 num_offsets += 1;
126 p += 1;
129 offsets = talloc_array(mem_ctx, size_t, num_offsets);
130 if (offsets == NULL) {
131 return false;
134 p = str;
135 num_offsets = 0;
136 while ((p = strchr(p, '/')) != NULL) {
137 offsets[num_offsets] = p-str;
138 num_offsets += 1;
139 p += 1;
142 *poffsets = offsets;
143 *pnum_offsets = num_offsets;
144 return true;
147 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
148 struct vfs_handle_struct *handle,
149 time_t snapshot)
151 const char *fmt;
152 struct tm snap_tm;
153 fstring snaptime_string;
154 size_t snaptime_len;
156 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
157 "format", GMT_FORMAT);
159 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
160 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
161 (unsigned long)snapshot);
162 if (snaptime_len <= 0) {
163 DEBUG(10, ("snprintf failed\n"));
164 return NULL;
166 } else {
167 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
168 if (localtime_r(&snapshot, &snap_tm) == 0) {
169 DEBUG(10, ("gmtime_r failed\n"));
170 return NULL;
172 } else {
173 if (gmtime_r(&snapshot, &snap_tm) == 0) {
174 DEBUG(10, ("gmtime_r failed\n"));
175 return NULL;
178 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
179 &snap_tm);
180 if (snaptime_len == 0) {
181 DEBUG(10, ("strftime failed\n"));
182 return NULL;
185 return talloc_asprintf(mem_ctx, "/%s/%s",
186 lp_parm_const_string(
187 SNUM(handle->conn), "shadow", "snapdir",
188 ".snapshots"),
189 snaptime_string);
192 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
193 struct vfs_handle_struct *handle,
194 const char *name,
195 time_t *ptimestamp,
196 char **pstripped)
198 struct tm tm;
199 time_t timestamp;
200 const char *p;
201 char *q;
202 char *stripped;
203 size_t rest_len, dst_len;
205 p = strstr_m(name, "@GMT-");
206 if (p == NULL) {
207 goto no_snapshot;
209 if ((p > name) && (p[-1] != '/')) {
210 goto no_snapshot;
212 q = strptime(p, GMT_FORMAT, &tm);
213 if (q == NULL) {
214 goto no_snapshot;
216 tm.tm_isdst = -1;
217 timestamp = timegm(&tm);
218 if (timestamp == (time_t)-1) {
219 goto no_snapshot;
221 if ((p == name) && (q[0] == '\0')) {
222 if (pstripped != NULL) {
223 stripped = talloc_strdup(mem_ctx, "");
224 if (stripped == NULL) {
225 return false;
227 *pstripped = stripped;
229 *ptimestamp = timestamp;
230 return true;
232 if (q[0] != '/') {
233 goto no_snapshot;
235 q += 1;
237 rest_len = strlen(q);
238 dst_len = (p-name) + rest_len;
240 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
241 false)) {
242 char *insert;
243 bool have_insert;
244 insert = shadow_copy2_insert_string(talloc_tos(), handle,
245 timestamp);
246 if (insert == NULL) {
247 errno = ENOMEM;
248 return false;
251 have_insert = (strstr(name, insert+1) != NULL);
252 TALLOC_FREE(insert);
253 if (have_insert) {
254 goto no_snapshot;
258 if (pstripped != NULL) {
259 stripped = talloc_array(mem_ctx, char, dst_len+1);
260 if (stripped == NULL) {
261 errno = ENOMEM;
262 return false;
264 if (p > name) {
265 memcpy(stripped, name, p-name);
267 if (rest_len > 0) {
268 memcpy(stripped + (p-name), q, rest_len);
270 stripped[dst_len] = '\0';
271 *pstripped = stripped;
273 *ptimestamp = timestamp;
274 return true;
275 no_snapshot:
276 *ptimestamp = 0;
277 return true;
280 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
281 vfs_handle_struct *handle)
283 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
284 dev_t dev;
285 struct stat st;
286 char *p;
288 if (stat(path, &st) != 0) {
289 talloc_free(path);
290 return NULL;
293 dev = st.st_dev;
295 while ((p = strrchr(path, '/')) && p > path) {
296 *p = 0;
297 if (stat(path, &st) != 0) {
298 talloc_free(path);
299 return NULL;
301 if (st.st_dev != dev) {
302 *p = '/';
303 break;
307 return path;
310 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
311 struct vfs_handle_struct *handle,
312 const char *name, time_t timestamp)
314 struct smb_filename converted_fname;
315 char *result = NULL;
316 size_t *slashes = NULL;
317 unsigned num_slashes;
318 char *path = NULL;
319 size_t pathlen;
320 char *insert = NULL;
321 char *converted = NULL;
322 size_t insertlen;
323 int i, saved_errno;
324 size_t min_offset;
326 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
327 name);
328 if (path == NULL) {
329 errno = ENOMEM;
330 goto fail;
332 pathlen = talloc_get_size(path)-1;
334 DEBUG(10, ("converting %s\n", path));
336 if (!shadow_copy2_find_slashes(talloc_tos(), path,
337 &slashes, &num_slashes)) {
338 goto fail;
340 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
341 if (insert == NULL) {
342 goto fail;
344 insertlen = talloc_get_size(insert)-1;
345 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
346 if (converted == NULL) {
347 goto fail;
350 if (path[pathlen-1] != '/') {
352 * Append a fake slash to find the snapshot root
354 size_t *tmp;
355 tmp = talloc_realloc(talloc_tos(), slashes,
356 size_t, num_slashes+1);
357 if (tmp == NULL) {
358 goto fail;
360 slashes = tmp;
361 slashes[num_slashes] = pathlen;
362 num_slashes += 1;
365 min_offset = 0;
367 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
368 false)) {
369 char *mount_point;
371 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
372 handle);
373 if (mount_point == NULL) {
374 goto fail;
376 min_offset = strlen(mount_point);
377 TALLOC_FREE(mount_point);
380 memcpy(converted, path, pathlen+1);
381 converted[pathlen+insertlen] = '\0';
383 ZERO_STRUCT(converted_fname);
384 converted_fname.base_name = converted;
386 for (i = num_slashes-1; i>=0; i--) {
387 int ret;
388 size_t offset;
390 offset = slashes[i];
392 if (offset < min_offset) {
393 errno = ENOENT;
394 goto fail;
397 memcpy(converted+offset, insert, insertlen);
399 offset += insertlen;
400 memcpy(converted+offset, path + slashes[i],
401 pathlen - slashes[i]);
403 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
405 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
406 ret, ret == 0 ? "ok" : strerror(errno)));
407 if (ret == 0) {
408 /* success */
409 break;
411 if (errno == ENOTDIR) {
413 * This is a valid condition: We appended the
414 * .snaphots/@GMT.. to a file name. Just try
415 * with the upper levels.
417 continue;
419 if (errno != ENOENT) {
420 /* Other problem than "not found" */
421 goto fail;
425 if (i >= 0) {
427 * Found something
429 DEBUG(10, ("Found %s\n", converted));
430 result = converted;
431 converted = NULL;
432 } else {
433 errno = ENOENT;
435 fail:
436 saved_errno = errno;
437 TALLOC_FREE(converted);
438 TALLOC_FREE(insert);
439 TALLOC_FREE(slashes);
440 TALLOC_FREE(path);
441 errno = saved_errno;
442 return result;
446 modify a sbuf return to ensure that inodes in the shadow directory
447 are different from those in the main directory
449 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
450 SMB_STRUCT_STAT *sbuf)
452 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
453 /* some snapshot systems, like GPFS, return the name
454 device:inode for the snapshot files as the current
455 files. That breaks the 'restore' button in the shadow copy
456 GUI, as the client gets a sharing violation.
458 This is a crude way of allowing both files to be
459 open at once. It has a slight chance of inode
460 number collision, but I can't see a better approach
461 without significant VFS changes
463 uint32_t shash;
465 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
466 if (shash == 0) {
467 shash = 1;
469 sbuf->st_ex_ino ^= shash;
473 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
474 const char *fname,
475 const char *mask,
476 uint32 attr)
478 time_t timestamp;
479 char *stripped;
480 DIR *ret;
481 int saved_errno;
482 char *conv;
484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
485 &timestamp, &stripped)) {
486 return NULL;
488 if (timestamp == 0) {
489 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
491 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
492 TALLOC_FREE(stripped);
493 if (conv == NULL) {
494 return NULL;
496 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
497 saved_errno = errno;
498 TALLOC_FREE(conv);
499 errno = saved_errno;
500 return ret;
503 static int shadow_copy2_rename(vfs_handle_struct *handle,
504 const struct smb_filename *smb_fname_src,
505 const struct smb_filename *smb_fname_dst)
507 time_t timestamp_src, timestamp_dst;
509 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
510 smb_fname_src->base_name,
511 &timestamp_src, NULL)) {
512 return -1;
514 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
515 smb_fname_dst->base_name,
516 &timestamp_dst, NULL)) {
517 return -1;
519 if (timestamp_src != 0) {
520 errno = EXDEV;
521 return -1;
523 if (timestamp_dst != 0) {
524 errno = EROFS;
525 return -1;
527 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
530 static int shadow_copy2_symlink(vfs_handle_struct *handle,
531 const char *oldname, const char *newname)
533 time_t timestamp_old, timestamp_new;
535 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
536 &timestamp_old, NULL)) {
537 return -1;
539 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
540 &timestamp_new, NULL)) {
541 return -1;
543 if ((timestamp_old != 0) || (timestamp_new != 0)) {
544 errno = EROFS;
545 return -1;
547 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
550 static int shadow_copy2_link(vfs_handle_struct *handle,
551 const char *oldname, const char *newname)
553 time_t timestamp_old, timestamp_new;
555 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
556 &timestamp_old, NULL)) {
557 return -1;
559 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
560 &timestamp_new, NULL)) {
561 return -1;
563 if ((timestamp_old != 0) || (timestamp_new != 0)) {
564 errno = EROFS;
565 return -1;
567 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
570 static int shadow_copy2_stat(vfs_handle_struct *handle,
571 struct smb_filename *smb_fname)
573 time_t timestamp;
574 char *stripped, *tmp;
575 int ret, saved_errno;
577 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
578 smb_fname->base_name,
579 &timestamp, &stripped)) {
580 return -1;
582 if (timestamp == 0) {
583 return SMB_VFS_NEXT_STAT(handle, smb_fname);
586 tmp = smb_fname->base_name;
587 smb_fname->base_name = shadow_copy2_convert(
588 talloc_tos(), handle, stripped, timestamp);
589 TALLOC_FREE(stripped);
591 if (smb_fname->base_name == NULL) {
592 smb_fname->base_name = tmp;
593 return -1;
596 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
597 saved_errno = errno;
599 TALLOC_FREE(smb_fname->base_name);
600 smb_fname->base_name = tmp;
602 if (ret == 0) {
603 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
605 errno = saved_errno;
606 return ret;
609 static int shadow_copy2_lstat(vfs_handle_struct *handle,
610 struct smb_filename *smb_fname)
612 time_t timestamp;
613 char *stripped, *tmp;
614 int ret, saved_errno;
616 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
617 smb_fname->base_name,
618 &timestamp, &stripped)) {
619 return -1;
621 if (timestamp == 0) {
622 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
625 tmp = smb_fname->base_name;
626 smb_fname->base_name = shadow_copy2_convert(
627 talloc_tos(), handle, stripped, timestamp);
628 TALLOC_FREE(stripped);
630 if (smb_fname->base_name == NULL) {
631 smb_fname->base_name = tmp;
632 return -1;
635 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
636 saved_errno = errno;
638 TALLOC_FREE(smb_fname->base_name);
639 smb_fname->base_name = tmp;
641 if (ret == 0) {
642 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
644 errno = saved_errno;
645 return ret;
648 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
649 SMB_STRUCT_STAT *sbuf)
651 time_t timestamp;
652 int ret;
654 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
655 if (ret == -1) {
656 return ret;
658 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
659 fsp->fsp_name->base_name,
660 &timestamp, NULL)) {
661 return 0;
663 if (timestamp != 0) {
664 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
666 return 0;
669 static int shadow_copy2_open(vfs_handle_struct *handle,
670 struct smb_filename *smb_fname, files_struct *fsp,
671 int flags, mode_t mode)
673 time_t timestamp;
674 char *stripped, *tmp;
675 int ret, saved_errno;
677 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
678 smb_fname->base_name,
679 &timestamp, &stripped)) {
680 return -1;
682 if (timestamp == 0) {
683 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
686 tmp = smb_fname->base_name;
687 smb_fname->base_name = shadow_copy2_convert(
688 talloc_tos(), handle, stripped, timestamp);
689 TALLOC_FREE(stripped);
691 if (smb_fname->base_name == NULL) {
692 smb_fname->base_name = tmp;
693 return -1;
696 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
697 saved_errno = errno;
699 TALLOC_FREE(smb_fname->base_name);
700 smb_fname->base_name = tmp;
702 errno = saved_errno;
703 return ret;
706 static int shadow_copy2_unlink(vfs_handle_struct *handle,
707 const struct smb_filename *smb_fname)
709 time_t timestamp;
710 char *stripped;
711 int ret, saved_errno;
712 struct smb_filename *conv;
714 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
715 smb_fname->base_name,
716 &timestamp, &stripped)) {
717 return -1;
719 if (timestamp == 0) {
720 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
722 conv = cp_smb_filename(talloc_tos(), smb_fname);
723 if (conv == NULL) {
724 errno = ENOMEM;
725 return -1;
727 conv->base_name = shadow_copy2_convert(
728 conv, handle, stripped, timestamp);
729 TALLOC_FREE(stripped);
730 if (conv->base_name == NULL) {
731 return -1;
733 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
734 saved_errno = errno;
735 TALLOC_FREE(conv);
736 errno = saved_errno;
737 return ret;
740 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
741 mode_t mode)
743 time_t timestamp;
744 char *stripped;
745 int ret, saved_errno;
746 char *conv;
748 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
749 &timestamp, &stripped)) {
750 return -1;
752 if (timestamp == 0) {
753 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
755 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
756 TALLOC_FREE(stripped);
757 if (conv == NULL) {
758 return -1;
760 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
761 saved_errno = errno;
762 TALLOC_FREE(conv);
763 errno = saved_errno;
764 return ret;
767 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
768 uid_t uid, gid_t gid)
770 time_t timestamp;
771 char *stripped;
772 int ret, saved_errno;
773 char *conv;
775 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
776 &timestamp, &stripped)) {
777 return -1;
779 if (timestamp == 0) {
780 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
782 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
783 TALLOC_FREE(stripped);
784 if (conv == NULL) {
785 return -1;
787 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
788 saved_errno = errno;
789 TALLOC_FREE(conv);
790 errno = saved_errno;
791 return ret;
794 static int shadow_copy2_chdir(vfs_handle_struct *handle,
795 const char *fname)
797 time_t timestamp;
798 char *stripped;
799 int ret, saved_errno;
800 char *conv;
802 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
803 &timestamp, &stripped)) {
804 return -1;
806 if (timestamp == 0) {
807 return SMB_VFS_NEXT_CHDIR(handle, fname);
809 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
810 TALLOC_FREE(stripped);
811 if (conv == NULL) {
812 return -1;
814 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
815 saved_errno = errno;
816 TALLOC_FREE(conv);
817 errno = saved_errno;
818 return ret;
821 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
822 const struct smb_filename *smb_fname,
823 struct smb_file_time *ft)
825 time_t timestamp;
826 char *stripped;
827 int ret, saved_errno;
828 struct smb_filename *conv;
830 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
831 smb_fname->base_name,
832 &timestamp, &stripped)) {
833 return -1;
835 if (timestamp == 0) {
836 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
838 conv = cp_smb_filename(talloc_tos(), smb_fname);
839 if (conv == NULL) {
840 errno = ENOMEM;
841 return -1;
843 conv->base_name = shadow_copy2_convert(
844 conv, handle, stripped, timestamp);
845 TALLOC_FREE(stripped);
846 if (conv->base_name == NULL) {
847 return -1;
849 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
850 saved_errno = errno;
851 TALLOC_FREE(conv);
852 errno = saved_errno;
853 return ret;
856 static int shadow_copy2_readlink(vfs_handle_struct *handle,
857 const char *fname, char *buf, size_t bufsiz)
859 time_t timestamp;
860 char *stripped;
861 int ret, saved_errno;
862 char *conv;
864 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
865 &timestamp, &stripped)) {
866 return -1;
868 if (timestamp == 0) {
869 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
871 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
872 TALLOC_FREE(stripped);
873 if (conv == NULL) {
874 return -1;
876 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
877 saved_errno = errno;
878 TALLOC_FREE(conv);
879 errno = saved_errno;
880 return ret;
883 static int shadow_copy2_mknod(vfs_handle_struct *handle,
884 const char *fname, mode_t mode, SMB_DEV_T dev)
886 time_t timestamp;
887 char *stripped;
888 int ret, saved_errno;
889 char *conv;
891 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
892 &timestamp, &stripped)) {
893 return -1;
895 if (timestamp == 0) {
896 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
898 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
899 TALLOC_FREE(stripped);
900 if (conv == NULL) {
901 return -1;
903 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
904 saved_errno = errno;
905 TALLOC_FREE(conv);
906 errno = saved_errno;
907 return ret;
910 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
911 const char *fname)
913 time_t timestamp;
914 char *stripped = NULL;
915 char *tmp = NULL;
916 char *result = NULL;
917 char *inserted = NULL;
918 char *inserted_to, *inserted_end;
919 int saved_errno;
921 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
922 &timestamp, &stripped)) {
923 goto done;
925 if (timestamp == 0) {
926 return SMB_VFS_NEXT_REALPATH(handle, fname);
929 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
930 if (tmp == NULL) {
931 goto done;
934 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
935 if (result == NULL) {
936 goto done;
940 * Take away what we've inserted. This removes the @GMT-thingy
941 * completely, but will give a path under the share root.
943 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
944 if (inserted == NULL) {
945 goto done;
947 inserted_to = strstr_m(result, inserted);
948 if (inserted_to == NULL) {
949 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
950 goto done;
952 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
953 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
955 done:
956 saved_errno = errno;
957 TALLOC_FREE(inserted);
958 TALLOC_FREE(tmp);
959 TALLOC_FREE(stripped);
960 errno = saved_errno;
961 return result;
964 static char *have_snapdir(struct vfs_handle_struct *handle,
965 const char *path)
967 struct smb_filename smb_fname;
968 int ret;
970 ZERO_STRUCT(smb_fname);
971 smb_fname.base_name = talloc_asprintf(
972 talloc_tos(), "%s/%s", path,
973 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
974 ".snapshots"));
975 if (smb_fname.base_name == NULL) {
976 return NULL;
979 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
980 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
981 return smb_fname.base_name;
983 TALLOC_FREE(smb_fname.base_name);
984 return NULL;
987 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
988 struct vfs_handle_struct *handle,
989 struct smb_filename *smb_fname)
991 char *path, *p;
992 char *snapdir;
994 path = talloc_asprintf(mem_ctx, "%s/%s",
995 handle->conn->connectpath,
996 smb_fname->base_name);
997 if (path == NULL) {
998 return NULL;
1001 snapdir = have_snapdir(handle, path);
1002 if (snapdir != NULL) {
1003 TALLOC_FREE(path);
1004 return snapdir;
1007 while ((p = strrchr(path, '/')) && (p > path)) {
1009 p[0] = '\0';
1011 snapdir = have_snapdir(handle, path);
1012 if (snapdir != NULL) {
1013 TALLOC_FREE(path);
1014 return snapdir;
1017 TALLOC_FREE(path);
1018 return NULL;
1021 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1022 const char *name,
1023 char *gmt, size_t gmt_len)
1025 struct tm timestamp;
1026 time_t timestamp_t;
1027 unsigned long int timestamp_long;
1028 const char *fmt;
1030 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1031 "format", GMT_FORMAT);
1033 ZERO_STRUCT(timestamp);
1034 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1035 if (sscanf(name, fmt, &timestamp_long) != 1) {
1036 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: 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: no match %s: %s\n",
1045 fmt, name));
1046 return false;
1048 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1050 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1051 timestamp.tm_isdst = -1;
1052 timestamp_t = mktime(&timestamp);
1053 gmtime_r(&timestamp_t, &timestamp);
1057 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1058 return true;
1061 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1063 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1066 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1068 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1072 sort the shadow copy data in ascending or descending order
1074 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1075 struct shadow_copy_data *shadow_copy2_data)
1077 int (*cmpfunc)(const void *, const void *);
1078 const char *sort;
1080 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1081 "sort", "desc");
1082 if (sort == NULL) {
1083 return;
1086 if (strcmp(sort, "asc") == 0) {
1087 cmpfunc = shadow_copy2_label_cmp_asc;
1088 } else if (strcmp(sort, "desc") == 0) {
1089 cmpfunc = shadow_copy2_label_cmp_desc;
1090 } else {
1091 return;
1094 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1095 shadow_copy2_data->labels)
1097 TYPESAFE_QSORT(shadow_copy2_data->labels,
1098 shadow_copy2_data->num_volumes,
1099 cmpfunc);
1103 static int shadow_copy2_get_shadow_copy_data(
1104 vfs_handle_struct *handle, files_struct *fsp,
1105 struct shadow_copy_data *shadow_copy2_data,
1106 bool labels)
1108 DIR *p;
1109 const char *snapdir;
1110 struct dirent *d;
1111 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1113 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1114 if (snapdir == NULL) {
1115 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1116 handle->conn->connectpath));
1117 errno = EINVAL;
1118 talloc_free(tmp_ctx);
1119 return -1;
1122 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1124 if (!p) {
1125 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1126 " - %s\n", snapdir, strerror(errno)));
1127 talloc_free(tmp_ctx);
1128 errno = ENOSYS;
1129 return -1;
1132 shadow_copy2_data->num_volumes = 0;
1133 shadow_copy2_data->labels = NULL;
1135 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1136 char snapshot[GMT_NAME_LEN+1];
1137 SHADOW_COPY_LABEL *tlabels;
1140 * ignore names not of the right form in the snapshot
1141 * directory
1143 if (!shadow_copy2_snapshot_to_gmt(
1144 handle, d->d_name,
1145 snapshot, sizeof(snapshot))) {
1147 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1148 "ignoring %s\n", d->d_name));
1149 continue;
1151 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1152 d->d_name, snapshot));
1154 if (!labels) {
1155 /* the caller doesn't want the labels */
1156 shadow_copy2_data->num_volumes++;
1157 continue;
1160 tlabels = talloc_realloc(shadow_copy2_data,
1161 shadow_copy2_data->labels,
1162 SHADOW_COPY_LABEL,
1163 shadow_copy2_data->num_volumes+1);
1164 if (tlabels == NULL) {
1165 DEBUG(0,("shadow_copy2: out of memory\n"));
1166 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1167 talloc_free(tmp_ctx);
1168 return -1;
1171 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1172 sizeof(*tlabels));
1174 shadow_copy2_data->num_volumes++;
1175 shadow_copy2_data->labels = tlabels;
1178 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1180 shadow_copy2_sort_data(handle, shadow_copy2_data);
1182 talloc_free(tmp_ctx);
1183 return 0;
1186 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1187 struct files_struct *fsp,
1188 uint32 security_info,
1189 TALLOC_CTX *mem_ctx,
1190 struct security_descriptor **ppdesc)
1192 time_t timestamp;
1193 char *stripped;
1194 NTSTATUS status;
1195 char *conv;
1197 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1198 fsp->fsp_name->base_name,
1199 &timestamp, &stripped)) {
1200 return map_nt_error_from_unix(errno);
1202 if (timestamp == 0) {
1203 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1204 mem_ctx,
1205 ppdesc);
1207 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1208 TALLOC_FREE(stripped);
1209 if (conv == NULL) {
1210 return map_nt_error_from_unix(errno);
1212 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1213 mem_ctx, ppdesc);
1214 TALLOC_FREE(conv);
1215 return status;
1218 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1219 const char *fname,
1220 uint32 security_info,
1221 TALLOC_CTX *mem_ctx,
1222 struct security_descriptor **ppdesc)
1224 time_t timestamp;
1225 char *stripped;
1226 NTSTATUS status;
1227 char *conv;
1229 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1230 &timestamp, &stripped)) {
1231 return map_nt_error_from_unix(errno);
1233 if (timestamp == 0) {
1234 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1235 mem_ctx, ppdesc);
1237 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1238 TALLOC_FREE(stripped);
1239 if (conv == NULL) {
1240 return map_nt_error_from_unix(errno);
1242 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1243 mem_ctx, ppdesc);
1244 TALLOC_FREE(conv);
1245 return status;
1248 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1249 const char *fname, mode_t mode)
1251 time_t timestamp;
1252 char *stripped;
1253 int ret, saved_errno;
1254 char *conv;
1256 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1257 &timestamp, &stripped)) {
1258 return -1;
1260 if (timestamp == 0) {
1261 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1263 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1264 TALLOC_FREE(stripped);
1265 if (conv == NULL) {
1266 return -1;
1268 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1269 saved_errno = errno;
1270 TALLOC_FREE(conv);
1271 errno = saved_errno;
1272 return ret;
1275 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1277 time_t timestamp;
1278 char *stripped;
1279 int ret, saved_errno;
1280 char *conv;
1282 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1283 &timestamp, &stripped)) {
1284 return -1;
1286 if (timestamp == 0) {
1287 return SMB_VFS_NEXT_RMDIR(handle, fname);
1289 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1290 TALLOC_FREE(stripped);
1291 if (conv == NULL) {
1292 return -1;
1294 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1295 saved_errno = errno;
1296 TALLOC_FREE(conv);
1297 errno = saved_errno;
1298 return ret;
1301 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1302 unsigned int flags)
1304 time_t timestamp;
1305 char *stripped;
1306 int ret, saved_errno;
1307 char *conv;
1309 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1310 &timestamp, &stripped)) {
1311 return -1;
1313 if (timestamp == 0) {
1314 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1316 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1317 TALLOC_FREE(stripped);
1318 if (conv == NULL) {
1319 return -1;
1321 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1322 saved_errno = errno;
1323 TALLOC_FREE(conv);
1324 errno = saved_errno;
1325 return ret;
1328 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1329 const char *fname, const char *aname,
1330 void *value, size_t size)
1332 time_t timestamp;
1333 char *stripped;
1334 ssize_t ret;
1335 int saved_errno;
1336 char *conv;
1338 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1339 &timestamp, &stripped)) {
1340 return -1;
1342 if (timestamp == 0) {
1343 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1344 size);
1346 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1347 TALLOC_FREE(stripped);
1348 if (conv == NULL) {
1349 return -1;
1351 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1352 saved_errno = errno;
1353 TALLOC_FREE(conv);
1354 errno = saved_errno;
1355 return ret;
1358 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1359 const char *fname,
1360 char *list, size_t size)
1362 time_t timestamp;
1363 char *stripped;
1364 ssize_t ret;
1365 int saved_errno;
1366 char *conv;
1368 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1369 &timestamp, &stripped)) {
1370 return -1;
1372 if (timestamp == 0) {
1373 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1375 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1376 TALLOC_FREE(stripped);
1377 if (conv == NULL) {
1378 return -1;
1380 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1381 saved_errno = errno;
1382 TALLOC_FREE(conv);
1383 errno = saved_errno;
1384 return ret;
1387 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1388 const char *fname, const char *aname)
1390 time_t timestamp;
1391 char *stripped;
1392 int ret, saved_errno;
1393 char *conv;
1395 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1396 &timestamp, &stripped)) {
1397 return -1;
1399 if (timestamp == 0) {
1400 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1402 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1403 TALLOC_FREE(stripped);
1404 if (conv == NULL) {
1405 return -1;
1407 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1408 saved_errno = errno;
1409 TALLOC_FREE(conv);
1410 errno = saved_errno;
1411 return ret;
1414 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1415 const char *fname,
1416 const char *aname, const void *value,
1417 size_t size, int flags)
1419 time_t timestamp;
1420 char *stripped;
1421 ssize_t ret;
1422 int saved_errno;
1423 char *conv;
1425 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1426 &timestamp, &stripped)) {
1427 return -1;
1429 if (timestamp == 0) {
1430 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1431 flags);
1433 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1434 TALLOC_FREE(stripped);
1435 if (conv == NULL) {
1436 return -1;
1438 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1439 saved_errno = errno;
1440 TALLOC_FREE(conv);
1441 errno = saved_errno;
1442 return ret;
1445 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1446 const char *fname, mode_t mode)
1448 time_t timestamp;
1449 char *stripped;
1450 ssize_t ret;
1451 int saved_errno;
1452 char *conv;
1454 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1455 &timestamp, &stripped)) {
1456 return -1;
1458 if (timestamp == 0) {
1459 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1461 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1462 TALLOC_FREE(stripped);
1463 if (conv == NULL) {
1464 return -1;
1466 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1467 saved_errno = errno;
1468 TALLOC_FREE(conv);
1469 errno = saved_errno;
1470 return ret;
1473 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1474 const char *path,
1475 const char *name,
1476 TALLOC_CTX *mem_ctx,
1477 char **found_name)
1479 time_t timestamp;
1480 char *stripped;
1481 ssize_t ret;
1482 int saved_errno;
1483 char *conv;
1485 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1486 &timestamp, &stripped)) {
1487 return -1;
1489 if (timestamp == 0) {
1490 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1491 mem_ctx, found_name);
1493 if (stripped[0] == '\0') {
1494 *found_name = talloc_strdup(mem_ctx, name);
1495 if (*found_name == NULL) {
1496 errno = ENOMEM;
1497 return -1;
1499 return 0;
1501 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1502 TALLOC_FREE(stripped);
1503 if (conv == NULL) {
1504 return -1;
1506 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1507 mem_ctx, found_name);
1508 saved_errno = errno;
1509 TALLOC_FREE(conv);
1510 errno = saved_errno;
1511 return ret;
1515 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1516 .opendir_fn = shadow_copy2_opendir,
1517 .rename_fn = shadow_copy2_rename,
1518 .link_fn = shadow_copy2_link,
1519 .symlink_fn = shadow_copy2_symlink,
1520 .stat_fn = shadow_copy2_stat,
1521 .lstat_fn = shadow_copy2_lstat,
1522 .fstat_fn = shadow_copy2_fstat,
1523 .open_fn = shadow_copy2_open,
1524 .unlink_fn = shadow_copy2_unlink,
1525 .chmod_fn = shadow_copy2_chmod,
1526 .chown_fn = shadow_copy2_chown,
1527 .chdir_fn = shadow_copy2_chdir,
1528 .ntimes_fn = shadow_copy2_ntimes,
1529 .readlink_fn = shadow_copy2_readlink,
1530 .mknod_fn = shadow_copy2_mknod,
1531 .realpath_fn = shadow_copy2_realpath,
1532 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1533 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1534 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1535 .mkdir_fn = shadow_copy2_mkdir,
1536 .rmdir_fn = shadow_copy2_rmdir,
1537 .getxattr_fn = shadow_copy2_getxattr,
1538 .listxattr_fn = shadow_copy2_listxattr,
1539 .removexattr_fn = shadow_copy2_removexattr,
1540 .setxattr_fn = shadow_copy2_setxattr,
1541 .chmod_acl_fn = shadow_copy2_chmod_acl,
1542 .chflags_fn = shadow_copy2_chflags,
1543 .get_real_filename_fn = shadow_copy2_get_real_filename,
1546 NTSTATUS vfs_shadow_copy2_init(void);
1547 NTSTATUS vfs_shadow_copy2_init(void)
1549 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1550 "shadow_copy2", &vfs_shadow_copy2_fns);