s3:modules: s/struct timed_event/struct tevent_timer
[Samba/gebeck_regimport.git] / source3 / modules / vfs_shadow_copy2.c
blob1cf8e37ba4ab188b84fad8c37514e89421543847
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;
713 NTSTATUS status;
715 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
716 smb_fname->base_name,
717 &timestamp, &stripped)) {
718 return -1;
720 if (timestamp == 0) {
721 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
723 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
724 if (!NT_STATUS_IS_OK(status)) {
725 errno = ENOMEM;
726 return -1;
728 conv->base_name = shadow_copy2_convert(
729 conv, handle, stripped, timestamp);
730 TALLOC_FREE(stripped);
731 if (conv->base_name == NULL) {
732 return -1;
734 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
735 saved_errno = errno;
736 TALLOC_FREE(conv);
737 errno = saved_errno;
738 return ret;
741 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
742 mode_t mode)
744 time_t timestamp;
745 char *stripped;
746 int ret, saved_errno;
747 char *conv;
749 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
750 &timestamp, &stripped)) {
751 return -1;
753 if (timestamp == 0) {
754 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
756 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
757 TALLOC_FREE(stripped);
758 if (conv == NULL) {
759 return -1;
761 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
762 saved_errno = errno;
763 TALLOC_FREE(conv);
764 errno = saved_errno;
765 return ret;
768 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
769 uid_t uid, gid_t gid)
771 time_t timestamp;
772 char *stripped;
773 int ret, saved_errno;
774 char *conv;
776 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
777 &timestamp, &stripped)) {
778 return -1;
780 if (timestamp == 0) {
781 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
783 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
784 TALLOC_FREE(stripped);
785 if (conv == NULL) {
786 return -1;
788 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
789 saved_errno = errno;
790 TALLOC_FREE(conv);
791 errno = saved_errno;
792 return ret;
795 static int shadow_copy2_chdir(vfs_handle_struct *handle,
796 const char *fname)
798 time_t timestamp;
799 char *stripped;
800 int ret, saved_errno;
801 char *conv;
803 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
804 &timestamp, &stripped)) {
805 return -1;
807 if (timestamp == 0) {
808 return SMB_VFS_NEXT_CHDIR(handle, fname);
810 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
811 TALLOC_FREE(stripped);
812 if (conv == NULL) {
813 return -1;
815 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
816 saved_errno = errno;
817 TALLOC_FREE(conv);
818 errno = saved_errno;
819 return ret;
822 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
823 const struct smb_filename *smb_fname,
824 struct smb_file_time *ft)
826 time_t timestamp;
827 char *stripped;
828 int ret, saved_errno;
829 struct smb_filename *conv;
830 NTSTATUS status;
832 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
833 smb_fname->base_name,
834 &timestamp, &stripped)) {
835 return -1;
837 if (timestamp == 0) {
838 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
840 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
841 if (!NT_STATUS_IS_OK(status)) {
842 errno = ENOMEM;
843 return -1;
845 conv->base_name = shadow_copy2_convert(
846 conv, handle, stripped, timestamp);
847 TALLOC_FREE(stripped);
848 if (conv->base_name == NULL) {
849 return -1;
851 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
852 saved_errno = errno;
853 TALLOC_FREE(conv);
854 errno = saved_errno;
855 return ret;
858 static int shadow_copy2_readlink(vfs_handle_struct *handle,
859 const char *fname, char *buf, size_t bufsiz)
861 time_t timestamp;
862 char *stripped;
863 int ret, saved_errno;
864 char *conv;
866 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
867 &timestamp, &stripped)) {
868 return -1;
870 if (timestamp == 0) {
871 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
873 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
874 TALLOC_FREE(stripped);
875 if (conv == NULL) {
876 return -1;
878 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
879 saved_errno = errno;
880 TALLOC_FREE(conv);
881 errno = saved_errno;
882 return ret;
885 static int shadow_copy2_mknod(vfs_handle_struct *handle,
886 const char *fname, mode_t mode, SMB_DEV_T dev)
888 time_t timestamp;
889 char *stripped;
890 int ret, saved_errno;
891 char *conv;
893 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
894 &timestamp, &stripped)) {
895 return -1;
897 if (timestamp == 0) {
898 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
900 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
901 TALLOC_FREE(stripped);
902 if (conv == NULL) {
903 return -1;
905 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
906 saved_errno = errno;
907 TALLOC_FREE(conv);
908 errno = saved_errno;
909 return ret;
912 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
913 const char *fname)
915 time_t timestamp;
916 char *stripped = NULL;
917 char *tmp = NULL;
918 char *result = NULL;
919 char *inserted = NULL;
920 char *inserted_to, *inserted_end;
921 int saved_errno;
923 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
924 &timestamp, &stripped)) {
925 goto done;
927 if (timestamp == 0) {
928 return SMB_VFS_NEXT_REALPATH(handle, fname);
931 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
932 if (tmp == NULL) {
933 goto done;
936 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
937 if (result == NULL) {
938 goto done;
942 * Take away what we've inserted. This removes the @GMT-thingy
943 * completely, but will give a path under the share root.
945 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
946 if (inserted == NULL) {
947 goto done;
949 inserted_to = strstr_m(result, inserted);
950 if (inserted_to == NULL) {
951 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
952 goto done;
954 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
955 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
957 done:
958 saved_errno = errno;
959 TALLOC_FREE(inserted);
960 TALLOC_FREE(tmp);
961 TALLOC_FREE(stripped);
962 errno = saved_errno;
963 return result;
966 static char *have_snapdir(struct vfs_handle_struct *handle,
967 const char *path)
969 struct smb_filename smb_fname;
970 int ret;
972 ZERO_STRUCT(smb_fname);
973 smb_fname.base_name = talloc_asprintf(
974 talloc_tos(), "%s/%s", path,
975 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
976 ".snapshots"));
977 if (smb_fname.base_name == NULL) {
978 return NULL;
981 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
982 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
983 return smb_fname.base_name;
985 TALLOC_FREE(smb_fname.base_name);
986 return NULL;
989 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
990 struct vfs_handle_struct *handle,
991 struct smb_filename *smb_fname)
993 char *path, *p;
994 char *snapdir;
996 path = talloc_asprintf(mem_ctx, "%s/%s",
997 handle->conn->connectpath,
998 smb_fname->base_name);
999 if (path == NULL) {
1000 return NULL;
1003 snapdir = have_snapdir(handle, path);
1004 if (snapdir != NULL) {
1005 TALLOC_FREE(path);
1006 return snapdir;
1009 while ((p = strrchr(path, '/')) && (p > path)) {
1011 p[0] = '\0';
1013 snapdir = have_snapdir(handle, path);
1014 if (snapdir != NULL) {
1015 TALLOC_FREE(path);
1016 return snapdir;
1019 TALLOC_FREE(path);
1020 return NULL;
1023 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1024 const char *name,
1025 char *gmt, size_t gmt_len)
1027 struct tm timestamp;
1028 time_t timestamp_t;
1029 unsigned long int timestamp_long;
1030 const char *fmt;
1032 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1033 "format", GMT_FORMAT);
1035 ZERO_STRUCT(timestamp);
1036 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1037 if (sscanf(name, fmt, &timestamp_long) != 1) {
1038 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no sscanf match %s: %s\n",
1039 fmt, name));
1040 return false;
1042 timestamp_t = timestamp_long;
1043 gmtime_r(&timestamp_t, &timestamp);
1044 } else {
1045 if (strptime(name, fmt, &timestamp) == NULL) {
1046 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1047 fmt, name));
1048 return false;
1050 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", 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);