shadow_copy2: add header comment explaining have_snapdir()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob932f93f693bdf467ed0f4fdfed46442f4dd5dd43
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);
190 * Strip a snapshot component from an filename as
191 * handed in via the smb layer.
192 * Returns the parsed timestamp and the stripped filename.
194 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
195 struct vfs_handle_struct *handle,
196 const char *name,
197 time_t *ptimestamp,
198 char **pstripped)
200 struct tm tm;
201 time_t timestamp;
202 const char *p;
203 char *q;
204 char *stripped;
205 size_t rest_len, dst_len;
207 p = strstr_m(name, "@GMT-");
208 if (p == NULL) {
209 goto no_snapshot;
211 if ((p > name) && (p[-1] != '/')) {
212 goto no_snapshot;
214 q = strptime(p, GMT_FORMAT, &tm);
215 if (q == NULL) {
216 goto no_snapshot;
218 tm.tm_isdst = -1;
219 timestamp = timegm(&tm);
220 if (timestamp == (time_t)-1) {
221 goto no_snapshot;
223 if ((p == name) && (q[0] == '\0')) {
224 if (pstripped != NULL) {
225 stripped = talloc_strdup(mem_ctx, "");
226 if (stripped == NULL) {
227 return false;
229 *pstripped = stripped;
231 *ptimestamp = timestamp;
232 return true;
234 if (q[0] != '/') {
235 goto no_snapshot;
237 q += 1;
239 rest_len = strlen(q);
240 dst_len = (p-name) + rest_len;
242 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
243 false)) {
244 char *insert;
245 bool have_insert;
246 insert = shadow_copy2_insert_string(talloc_tos(), handle,
247 timestamp);
248 if (insert == NULL) {
249 errno = ENOMEM;
250 return false;
253 have_insert = (strstr(name, insert+1) != NULL);
254 TALLOC_FREE(insert);
255 if (have_insert) {
256 goto no_snapshot;
260 if (pstripped != NULL) {
261 stripped = talloc_array(mem_ctx, char, dst_len+1);
262 if (stripped == NULL) {
263 errno = ENOMEM;
264 return false;
266 if (p > name) {
267 memcpy(stripped, name, p-name);
269 if (rest_len > 0) {
270 memcpy(stripped + (p-name), q, rest_len);
272 stripped[dst_len] = '\0';
273 *pstripped = stripped;
275 *ptimestamp = timestamp;
276 return true;
277 no_snapshot:
278 *ptimestamp = 0;
279 return true;
282 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
283 vfs_handle_struct *handle)
285 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
286 dev_t dev;
287 struct stat st;
288 char *p;
290 if (stat(path, &st) != 0) {
291 talloc_free(path);
292 return NULL;
295 dev = st.st_dev;
297 while ((p = strrchr(path, '/')) && p > path) {
298 *p = 0;
299 if (stat(path, &st) != 0) {
300 talloc_free(path);
301 return NULL;
303 if (st.st_dev != dev) {
304 *p = '/';
305 break;
309 return path;
312 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
313 struct vfs_handle_struct *handle,
314 const char *name, time_t timestamp)
316 struct smb_filename converted_fname;
317 char *result = NULL;
318 size_t *slashes = NULL;
319 unsigned num_slashes;
320 char *path = NULL;
321 size_t pathlen;
322 char *insert = NULL;
323 char *converted = NULL;
324 size_t insertlen;
325 int i, saved_errno;
326 size_t min_offset;
328 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
329 name);
330 if (path == NULL) {
331 errno = ENOMEM;
332 goto fail;
334 pathlen = talloc_get_size(path)-1;
336 DEBUG(10, ("converting %s\n", path));
338 if (!shadow_copy2_find_slashes(talloc_tos(), path,
339 &slashes, &num_slashes)) {
340 goto fail;
342 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
343 if (insert == NULL) {
344 goto fail;
346 insertlen = talloc_get_size(insert)-1;
347 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
348 if (converted == NULL) {
349 goto fail;
352 if (path[pathlen-1] != '/') {
354 * Append a fake slash to find the snapshot root
356 size_t *tmp;
357 tmp = talloc_realloc(talloc_tos(), slashes,
358 size_t, num_slashes+1);
359 if (tmp == NULL) {
360 goto fail;
362 slashes = tmp;
363 slashes[num_slashes] = pathlen;
364 num_slashes += 1;
367 min_offset = 0;
369 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
370 false)) {
371 char *mount_point;
373 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
374 handle);
375 if (mount_point == NULL) {
376 goto fail;
378 min_offset = strlen(mount_point);
379 TALLOC_FREE(mount_point);
382 memcpy(converted, path, pathlen+1);
383 converted[pathlen+insertlen] = '\0';
385 ZERO_STRUCT(converted_fname);
386 converted_fname.base_name = converted;
388 for (i = num_slashes-1; i>=0; i--) {
389 int ret;
390 size_t offset;
392 offset = slashes[i];
394 if (offset < min_offset) {
395 errno = ENOENT;
396 goto fail;
399 memcpy(converted+offset, insert, insertlen);
401 offset += insertlen;
402 memcpy(converted+offset, path + slashes[i],
403 pathlen - slashes[i]);
405 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
407 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
408 ret, ret == 0 ? "ok" : strerror(errno)));
409 if (ret == 0) {
410 /* success */
411 break;
413 if (errno == ENOTDIR) {
415 * This is a valid condition: We appended the
416 * .snaphots/@GMT.. to a file name. Just try
417 * with the upper levels.
419 continue;
421 if (errno != ENOENT) {
422 /* Other problem than "not found" */
423 goto fail;
427 if (i >= 0) {
429 * Found something
431 DEBUG(10, ("Found %s\n", converted));
432 result = converted;
433 converted = NULL;
434 } else {
435 errno = ENOENT;
437 fail:
438 saved_errno = errno;
439 TALLOC_FREE(converted);
440 TALLOC_FREE(insert);
441 TALLOC_FREE(slashes);
442 TALLOC_FREE(path);
443 errno = saved_errno;
444 return result;
448 modify a sbuf return to ensure that inodes in the shadow directory
449 are different from those in the main directory
451 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
452 SMB_STRUCT_STAT *sbuf)
454 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
455 /* some snapshot systems, like GPFS, return the name
456 device:inode for the snapshot files as the current
457 files. That breaks the 'restore' button in the shadow copy
458 GUI, as the client gets a sharing violation.
460 This is a crude way of allowing both files to be
461 open at once. It has a slight chance of inode
462 number collision, but I can't see a better approach
463 without significant VFS changes
465 uint32_t shash;
467 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
468 if (shash == 0) {
469 shash = 1;
471 sbuf->st_ex_ino ^= shash;
475 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
476 const char *fname,
477 const char *mask,
478 uint32 attr)
480 time_t timestamp;
481 char *stripped;
482 DIR *ret;
483 int saved_errno;
484 char *conv;
486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
487 &timestamp, &stripped)) {
488 return NULL;
490 if (timestamp == 0) {
491 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
493 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
494 TALLOC_FREE(stripped);
495 if (conv == NULL) {
496 return NULL;
498 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
499 saved_errno = errno;
500 TALLOC_FREE(conv);
501 errno = saved_errno;
502 return ret;
505 static int shadow_copy2_rename(vfs_handle_struct *handle,
506 const struct smb_filename *smb_fname_src,
507 const struct smb_filename *smb_fname_dst)
509 time_t timestamp_src, timestamp_dst;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
512 smb_fname_src->base_name,
513 &timestamp_src, NULL)) {
514 return -1;
516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
517 smb_fname_dst->base_name,
518 &timestamp_dst, NULL)) {
519 return -1;
521 if (timestamp_src != 0) {
522 errno = EXDEV;
523 return -1;
525 if (timestamp_dst != 0) {
526 errno = EROFS;
527 return -1;
529 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
532 static int shadow_copy2_symlink(vfs_handle_struct *handle,
533 const char *oldname, const char *newname)
535 time_t timestamp_old, timestamp_new;
537 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
538 &timestamp_old, NULL)) {
539 return -1;
541 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
542 &timestamp_new, NULL)) {
543 return -1;
545 if ((timestamp_old != 0) || (timestamp_new != 0)) {
546 errno = EROFS;
547 return -1;
549 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
552 static int shadow_copy2_link(vfs_handle_struct *handle,
553 const char *oldname, const char *newname)
555 time_t timestamp_old, timestamp_new;
557 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
558 &timestamp_old, NULL)) {
559 return -1;
561 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
562 &timestamp_new, NULL)) {
563 return -1;
565 if ((timestamp_old != 0) || (timestamp_new != 0)) {
566 errno = EROFS;
567 return -1;
569 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
572 static int shadow_copy2_stat(vfs_handle_struct *handle,
573 struct smb_filename *smb_fname)
575 time_t timestamp;
576 char *stripped, *tmp;
577 int ret, saved_errno;
579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
580 smb_fname->base_name,
581 &timestamp, &stripped)) {
582 return -1;
584 if (timestamp == 0) {
585 return SMB_VFS_NEXT_STAT(handle, smb_fname);
588 tmp = smb_fname->base_name;
589 smb_fname->base_name = shadow_copy2_convert(
590 talloc_tos(), handle, stripped, timestamp);
591 TALLOC_FREE(stripped);
593 if (smb_fname->base_name == NULL) {
594 smb_fname->base_name = tmp;
595 return -1;
598 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
599 saved_errno = errno;
601 TALLOC_FREE(smb_fname->base_name);
602 smb_fname->base_name = tmp;
604 if (ret == 0) {
605 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
607 errno = saved_errno;
608 return ret;
611 static int shadow_copy2_lstat(vfs_handle_struct *handle,
612 struct smb_filename *smb_fname)
614 time_t timestamp;
615 char *stripped, *tmp;
616 int ret, saved_errno;
618 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
619 smb_fname->base_name,
620 &timestamp, &stripped)) {
621 return -1;
623 if (timestamp == 0) {
624 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
627 tmp = smb_fname->base_name;
628 smb_fname->base_name = shadow_copy2_convert(
629 talloc_tos(), handle, stripped, timestamp);
630 TALLOC_FREE(stripped);
632 if (smb_fname->base_name == NULL) {
633 smb_fname->base_name = tmp;
634 return -1;
637 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
638 saved_errno = errno;
640 TALLOC_FREE(smb_fname->base_name);
641 smb_fname->base_name = tmp;
643 if (ret == 0) {
644 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
646 errno = saved_errno;
647 return ret;
650 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
651 SMB_STRUCT_STAT *sbuf)
653 time_t timestamp;
654 int ret;
656 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
657 if (ret == -1) {
658 return ret;
660 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
661 fsp->fsp_name->base_name,
662 &timestamp, NULL)) {
663 return 0;
665 if (timestamp != 0) {
666 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
668 return 0;
671 static int shadow_copy2_open(vfs_handle_struct *handle,
672 struct smb_filename *smb_fname, files_struct *fsp,
673 int flags, mode_t mode)
675 time_t timestamp;
676 char *stripped, *tmp;
677 int ret, saved_errno;
679 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
680 smb_fname->base_name,
681 &timestamp, &stripped)) {
682 return -1;
684 if (timestamp == 0) {
685 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
688 tmp = smb_fname->base_name;
689 smb_fname->base_name = shadow_copy2_convert(
690 talloc_tos(), handle, stripped, timestamp);
691 TALLOC_FREE(stripped);
693 if (smb_fname->base_name == NULL) {
694 smb_fname->base_name = tmp;
695 return -1;
698 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
699 saved_errno = errno;
701 TALLOC_FREE(smb_fname->base_name);
702 smb_fname->base_name = tmp;
704 errno = saved_errno;
705 return ret;
708 static int shadow_copy2_unlink(vfs_handle_struct *handle,
709 const struct smb_filename *smb_fname)
711 time_t timestamp;
712 char *stripped;
713 int ret, saved_errno;
714 struct smb_filename *conv;
716 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
717 smb_fname->base_name,
718 &timestamp, &stripped)) {
719 return -1;
721 if (timestamp == 0) {
722 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
724 conv = cp_smb_filename(talloc_tos(), smb_fname);
725 if (conv == NULL) {
726 errno = ENOMEM;
727 return -1;
729 conv->base_name = shadow_copy2_convert(
730 conv, handle, stripped, timestamp);
731 TALLOC_FREE(stripped);
732 if (conv->base_name == NULL) {
733 return -1;
735 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
736 saved_errno = errno;
737 TALLOC_FREE(conv);
738 errno = saved_errno;
739 return ret;
742 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
743 mode_t mode)
745 time_t timestamp;
746 char *stripped;
747 int ret, saved_errno;
748 char *conv;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
751 &timestamp, &stripped)) {
752 return -1;
754 if (timestamp == 0) {
755 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
757 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
758 TALLOC_FREE(stripped);
759 if (conv == NULL) {
760 return -1;
762 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
763 saved_errno = errno;
764 TALLOC_FREE(conv);
765 errno = saved_errno;
766 return ret;
769 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
770 uid_t uid, gid_t gid)
772 time_t timestamp;
773 char *stripped;
774 int ret, saved_errno;
775 char *conv;
777 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
778 &timestamp, &stripped)) {
779 return -1;
781 if (timestamp == 0) {
782 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
784 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
785 TALLOC_FREE(stripped);
786 if (conv == NULL) {
787 return -1;
789 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
790 saved_errno = errno;
791 TALLOC_FREE(conv);
792 errno = saved_errno;
793 return ret;
796 static int shadow_copy2_chdir(vfs_handle_struct *handle,
797 const char *fname)
799 time_t timestamp;
800 char *stripped;
801 int ret, saved_errno;
802 char *conv;
804 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
805 &timestamp, &stripped)) {
806 return -1;
808 if (timestamp == 0) {
809 return SMB_VFS_NEXT_CHDIR(handle, fname);
811 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
812 TALLOC_FREE(stripped);
813 if (conv == NULL) {
814 return -1;
816 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
817 saved_errno = errno;
818 TALLOC_FREE(conv);
819 errno = saved_errno;
820 return ret;
823 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
824 const struct smb_filename *smb_fname,
825 struct smb_file_time *ft)
827 time_t timestamp;
828 char *stripped;
829 int ret, saved_errno;
830 struct smb_filename *conv;
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 conv = cp_smb_filename(talloc_tos(), smb_fname);
841 if (conv == NULL) {
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;
967 * Check whether a given directory contains a
968 * snapshot directory as direct subdirectory.
969 * If yes, return the path of the snapshot-subdir,
970 * otherwise return NULL.
972 static char *have_snapdir(struct vfs_handle_struct *handle,
973 const char *path)
975 struct smb_filename smb_fname;
976 int ret;
978 ZERO_STRUCT(smb_fname);
979 smb_fname.base_name = talloc_asprintf(
980 talloc_tos(), "%s/%s", path,
981 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
982 ".snapshots"));
983 if (smb_fname.base_name == NULL) {
984 return NULL;
987 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
988 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
989 return smb_fname.base_name;
991 TALLOC_FREE(smb_fname.base_name);
992 return NULL;
995 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
996 struct vfs_handle_struct *handle,
997 struct smb_filename *smb_fname)
999 char *path, *p;
1000 char *snapdir;
1002 path = talloc_asprintf(mem_ctx, "%s/%s",
1003 handle->conn->connectpath,
1004 smb_fname->base_name);
1005 if (path == NULL) {
1006 return NULL;
1009 snapdir = have_snapdir(handle, path);
1010 if (snapdir != NULL) {
1011 TALLOC_FREE(path);
1012 return snapdir;
1015 while ((p = strrchr(path, '/')) && (p > path)) {
1017 p[0] = '\0';
1019 snapdir = have_snapdir(handle, path);
1020 if (snapdir != NULL) {
1021 TALLOC_FREE(path);
1022 return snapdir;
1025 TALLOC_FREE(path);
1026 return NULL;
1029 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1030 const char *name,
1031 char *gmt, size_t gmt_len)
1033 struct tm timestamp;
1034 time_t timestamp_t;
1035 unsigned long int timestamp_long;
1036 const char *fmt;
1038 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1039 "format", GMT_FORMAT);
1041 ZERO_STRUCT(timestamp);
1042 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1043 if (sscanf(name, fmt, &timestamp_long) != 1) {
1044 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1045 "no sscanf match %s: %s\n",
1046 fmt, name));
1047 return false;
1049 timestamp_t = timestamp_long;
1050 gmtime_r(&timestamp_t, &timestamp);
1051 } else {
1052 if (strptime(name, fmt, &timestamp) == NULL) {
1053 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1054 "no match %s: %s\n",
1055 fmt, name));
1056 return false;
1058 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1059 fmt, name));
1061 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1062 timestamp.tm_isdst = -1;
1063 timestamp_t = mktime(&timestamp);
1064 gmtime_r(&timestamp_t, &timestamp);
1068 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1069 return true;
1072 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1074 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1077 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1079 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1083 sort the shadow copy data in ascending or descending order
1085 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1086 struct shadow_copy_data *shadow_copy2_data)
1088 int (*cmpfunc)(const void *, const void *);
1089 const char *sort;
1091 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1092 "sort", "desc");
1093 if (sort == NULL) {
1094 return;
1097 if (strcmp(sort, "asc") == 0) {
1098 cmpfunc = shadow_copy2_label_cmp_asc;
1099 } else if (strcmp(sort, "desc") == 0) {
1100 cmpfunc = shadow_copy2_label_cmp_desc;
1101 } else {
1102 return;
1105 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1106 shadow_copy2_data->labels)
1108 TYPESAFE_QSORT(shadow_copy2_data->labels,
1109 shadow_copy2_data->num_volumes,
1110 cmpfunc);
1114 static int shadow_copy2_get_shadow_copy_data(
1115 vfs_handle_struct *handle, files_struct *fsp,
1116 struct shadow_copy_data *shadow_copy2_data,
1117 bool labels)
1119 DIR *p;
1120 const char *snapdir;
1121 struct dirent *d;
1122 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1124 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1125 if (snapdir == NULL) {
1126 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1127 handle->conn->connectpath));
1128 errno = EINVAL;
1129 talloc_free(tmp_ctx);
1130 return -1;
1133 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1135 if (!p) {
1136 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1137 " - %s\n", snapdir, strerror(errno)));
1138 talloc_free(tmp_ctx);
1139 errno = ENOSYS;
1140 return -1;
1143 shadow_copy2_data->num_volumes = 0;
1144 shadow_copy2_data->labels = NULL;
1146 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1147 char snapshot[GMT_NAME_LEN+1];
1148 SHADOW_COPY_LABEL *tlabels;
1151 * ignore names not of the right form in the snapshot
1152 * directory
1154 if (!shadow_copy2_snapshot_to_gmt(
1155 handle, d->d_name,
1156 snapshot, sizeof(snapshot))) {
1158 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1159 "ignoring %s\n", d->d_name));
1160 continue;
1162 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1163 d->d_name, snapshot));
1165 if (!labels) {
1166 /* the caller doesn't want the labels */
1167 shadow_copy2_data->num_volumes++;
1168 continue;
1171 tlabels = talloc_realloc(shadow_copy2_data,
1172 shadow_copy2_data->labels,
1173 SHADOW_COPY_LABEL,
1174 shadow_copy2_data->num_volumes+1);
1175 if (tlabels == NULL) {
1176 DEBUG(0,("shadow_copy2: out of memory\n"));
1177 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1178 talloc_free(tmp_ctx);
1179 return -1;
1182 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1183 sizeof(*tlabels));
1185 shadow_copy2_data->num_volumes++;
1186 shadow_copy2_data->labels = tlabels;
1189 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1191 shadow_copy2_sort_data(handle, shadow_copy2_data);
1193 talloc_free(tmp_ctx);
1194 return 0;
1197 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1198 struct files_struct *fsp,
1199 uint32 security_info,
1200 TALLOC_CTX *mem_ctx,
1201 struct security_descriptor **ppdesc)
1203 time_t timestamp;
1204 char *stripped;
1205 NTSTATUS status;
1206 char *conv;
1208 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1209 fsp->fsp_name->base_name,
1210 &timestamp, &stripped)) {
1211 return map_nt_error_from_unix(errno);
1213 if (timestamp == 0) {
1214 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1215 mem_ctx,
1216 ppdesc);
1218 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1219 TALLOC_FREE(stripped);
1220 if (conv == NULL) {
1221 return map_nt_error_from_unix(errno);
1223 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1224 mem_ctx, ppdesc);
1225 TALLOC_FREE(conv);
1226 return status;
1229 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1230 const char *fname,
1231 uint32 security_info,
1232 TALLOC_CTX *mem_ctx,
1233 struct security_descriptor **ppdesc)
1235 time_t timestamp;
1236 char *stripped;
1237 NTSTATUS status;
1238 char *conv;
1240 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1241 &timestamp, &stripped)) {
1242 return map_nt_error_from_unix(errno);
1244 if (timestamp == 0) {
1245 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1246 mem_ctx, ppdesc);
1248 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1249 TALLOC_FREE(stripped);
1250 if (conv == NULL) {
1251 return map_nt_error_from_unix(errno);
1253 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1254 mem_ctx, ppdesc);
1255 TALLOC_FREE(conv);
1256 return status;
1259 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1260 const char *fname, mode_t mode)
1262 time_t timestamp;
1263 char *stripped;
1264 int ret, saved_errno;
1265 char *conv;
1267 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1268 &timestamp, &stripped)) {
1269 return -1;
1271 if (timestamp == 0) {
1272 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1274 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1275 TALLOC_FREE(stripped);
1276 if (conv == NULL) {
1277 return -1;
1279 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1280 saved_errno = errno;
1281 TALLOC_FREE(conv);
1282 errno = saved_errno;
1283 return ret;
1286 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1288 time_t timestamp;
1289 char *stripped;
1290 int ret, saved_errno;
1291 char *conv;
1293 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1294 &timestamp, &stripped)) {
1295 return -1;
1297 if (timestamp == 0) {
1298 return SMB_VFS_NEXT_RMDIR(handle, fname);
1300 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1301 TALLOC_FREE(stripped);
1302 if (conv == NULL) {
1303 return -1;
1305 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1306 saved_errno = errno;
1307 TALLOC_FREE(conv);
1308 errno = saved_errno;
1309 return ret;
1312 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1313 unsigned int flags)
1315 time_t timestamp;
1316 char *stripped;
1317 int ret, saved_errno;
1318 char *conv;
1320 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1321 &timestamp, &stripped)) {
1322 return -1;
1324 if (timestamp == 0) {
1325 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1327 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1328 TALLOC_FREE(stripped);
1329 if (conv == NULL) {
1330 return -1;
1332 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1333 saved_errno = errno;
1334 TALLOC_FREE(conv);
1335 errno = saved_errno;
1336 return ret;
1339 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1340 const char *fname, const char *aname,
1341 void *value, size_t size)
1343 time_t timestamp;
1344 char *stripped;
1345 ssize_t ret;
1346 int saved_errno;
1347 char *conv;
1349 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1350 &timestamp, &stripped)) {
1351 return -1;
1353 if (timestamp == 0) {
1354 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1355 size);
1357 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1358 TALLOC_FREE(stripped);
1359 if (conv == NULL) {
1360 return -1;
1362 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1363 saved_errno = errno;
1364 TALLOC_FREE(conv);
1365 errno = saved_errno;
1366 return ret;
1369 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1370 const char *fname,
1371 char *list, size_t size)
1373 time_t timestamp;
1374 char *stripped;
1375 ssize_t ret;
1376 int saved_errno;
1377 char *conv;
1379 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1380 &timestamp, &stripped)) {
1381 return -1;
1383 if (timestamp == 0) {
1384 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1386 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1387 TALLOC_FREE(stripped);
1388 if (conv == NULL) {
1389 return -1;
1391 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1392 saved_errno = errno;
1393 TALLOC_FREE(conv);
1394 errno = saved_errno;
1395 return ret;
1398 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1399 const char *fname, const char *aname)
1401 time_t timestamp;
1402 char *stripped;
1403 int ret, saved_errno;
1404 char *conv;
1406 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1407 &timestamp, &stripped)) {
1408 return -1;
1410 if (timestamp == 0) {
1411 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1413 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1414 TALLOC_FREE(stripped);
1415 if (conv == NULL) {
1416 return -1;
1418 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1419 saved_errno = errno;
1420 TALLOC_FREE(conv);
1421 errno = saved_errno;
1422 return ret;
1425 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1426 const char *fname,
1427 const char *aname, const void *value,
1428 size_t size, int flags)
1430 time_t timestamp;
1431 char *stripped;
1432 ssize_t ret;
1433 int saved_errno;
1434 char *conv;
1436 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1437 &timestamp, &stripped)) {
1438 return -1;
1440 if (timestamp == 0) {
1441 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1442 flags);
1444 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1445 TALLOC_FREE(stripped);
1446 if (conv == NULL) {
1447 return -1;
1449 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1450 saved_errno = errno;
1451 TALLOC_FREE(conv);
1452 errno = saved_errno;
1453 return ret;
1456 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1457 const char *fname, mode_t mode)
1459 time_t timestamp;
1460 char *stripped;
1461 ssize_t ret;
1462 int saved_errno;
1463 char *conv;
1465 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1466 &timestamp, &stripped)) {
1467 return -1;
1469 if (timestamp == 0) {
1470 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1472 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1473 TALLOC_FREE(stripped);
1474 if (conv == NULL) {
1475 return -1;
1477 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1478 saved_errno = errno;
1479 TALLOC_FREE(conv);
1480 errno = saved_errno;
1481 return ret;
1484 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1485 const char *path,
1486 const char *name,
1487 TALLOC_CTX *mem_ctx,
1488 char **found_name)
1490 time_t timestamp;
1491 char *stripped;
1492 ssize_t ret;
1493 int saved_errno;
1494 char *conv;
1496 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1497 &timestamp, &stripped)) {
1498 return -1;
1500 if (timestamp == 0) {
1501 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1502 mem_ctx, found_name);
1504 if (stripped[0] == '\0') {
1505 *found_name = talloc_strdup(mem_ctx, name);
1506 if (*found_name == NULL) {
1507 errno = ENOMEM;
1508 return -1;
1510 return 0;
1512 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1513 TALLOC_FREE(stripped);
1514 if (conv == NULL) {
1515 return -1;
1517 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1518 mem_ctx, found_name);
1519 saved_errno = errno;
1520 TALLOC_FREE(conv);
1521 errno = saved_errno;
1522 return ret;
1526 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1527 .opendir_fn = shadow_copy2_opendir,
1528 .rename_fn = shadow_copy2_rename,
1529 .link_fn = shadow_copy2_link,
1530 .symlink_fn = shadow_copy2_symlink,
1531 .stat_fn = shadow_copy2_stat,
1532 .lstat_fn = shadow_copy2_lstat,
1533 .fstat_fn = shadow_copy2_fstat,
1534 .open_fn = shadow_copy2_open,
1535 .unlink_fn = shadow_copy2_unlink,
1536 .chmod_fn = shadow_copy2_chmod,
1537 .chown_fn = shadow_copy2_chown,
1538 .chdir_fn = shadow_copy2_chdir,
1539 .ntimes_fn = shadow_copy2_ntimes,
1540 .readlink_fn = shadow_copy2_readlink,
1541 .mknod_fn = shadow_copy2_mknod,
1542 .realpath_fn = shadow_copy2_realpath,
1543 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1544 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1545 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1546 .mkdir_fn = shadow_copy2_mkdir,
1547 .rmdir_fn = shadow_copy2_rmdir,
1548 .getxattr_fn = shadow_copy2_getxattr,
1549 .listxattr_fn = shadow_copy2_listxattr,
1550 .removexattr_fn = shadow_copy2_removexattr,
1551 .setxattr_fn = shadow_copy2_setxattr,
1552 .chmod_acl_fn = shadow_copy2_chmod_acl,
1553 .chflags_fn = shadow_copy2_chflags,
1554 .get_real_filename_fn = shadow_copy2_get_real_filename,
1557 NTSTATUS vfs_shadow_copy2_init(void);
1558 NTSTATUS vfs_shadow_copy2_init(void)
1560 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1561 "shadow_copy2", &vfs_shadow_copy2_fns);