shadow_copy2: add comment block explaining shadow_copy2_find_snapdir()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob783f670cd40cf04b98bbf7f940e7b39f700d9e00
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;
715 NTSTATUS status;
717 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
718 smb_fname->base_name,
719 &timestamp, &stripped)) {
720 return -1;
722 if (timestamp == 0) {
723 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
725 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
726 if (!NT_STATUS_IS_OK(status)) {
727 errno = ENOMEM;
728 return -1;
730 conv->base_name = shadow_copy2_convert(
731 conv, handle, stripped, timestamp);
732 TALLOC_FREE(stripped);
733 if (conv->base_name == NULL) {
734 return -1;
736 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
737 saved_errno = errno;
738 TALLOC_FREE(conv);
739 errno = saved_errno;
740 return ret;
743 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
744 mode_t mode)
746 time_t timestamp;
747 char *stripped;
748 int ret, saved_errno;
749 char *conv;
751 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
752 &timestamp, &stripped)) {
753 return -1;
755 if (timestamp == 0) {
756 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
758 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
759 TALLOC_FREE(stripped);
760 if (conv == NULL) {
761 return -1;
763 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
764 saved_errno = errno;
765 TALLOC_FREE(conv);
766 errno = saved_errno;
767 return ret;
770 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
771 uid_t uid, gid_t gid)
773 time_t timestamp;
774 char *stripped;
775 int ret, saved_errno;
776 char *conv;
778 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
779 &timestamp, &stripped)) {
780 return -1;
782 if (timestamp == 0) {
783 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
785 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
786 TALLOC_FREE(stripped);
787 if (conv == NULL) {
788 return -1;
790 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
791 saved_errno = errno;
792 TALLOC_FREE(conv);
793 errno = saved_errno;
794 return ret;
797 static int shadow_copy2_chdir(vfs_handle_struct *handle,
798 const char *fname)
800 time_t timestamp;
801 char *stripped;
802 int ret, saved_errno;
803 char *conv;
805 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
806 &timestamp, &stripped)) {
807 return -1;
809 if (timestamp == 0) {
810 return SMB_VFS_NEXT_CHDIR(handle, fname);
812 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
813 TALLOC_FREE(stripped);
814 if (conv == NULL) {
815 return -1;
817 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
818 saved_errno = errno;
819 TALLOC_FREE(conv);
820 errno = saved_errno;
821 return ret;
824 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
825 const struct smb_filename *smb_fname,
826 struct smb_file_time *ft)
828 time_t timestamp;
829 char *stripped;
830 int ret, saved_errno;
831 struct smb_filename *conv;
832 NTSTATUS status;
834 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
835 smb_fname->base_name,
836 &timestamp, &stripped)) {
837 return -1;
839 if (timestamp == 0) {
840 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
842 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
843 if (!NT_STATUS_IS_OK(status)) {
844 errno = ENOMEM;
845 return -1;
847 conv->base_name = shadow_copy2_convert(
848 conv, handle, stripped, timestamp);
849 TALLOC_FREE(stripped);
850 if (conv->base_name == NULL) {
851 return -1;
853 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
854 saved_errno = errno;
855 TALLOC_FREE(conv);
856 errno = saved_errno;
857 return ret;
860 static int shadow_copy2_readlink(vfs_handle_struct *handle,
861 const char *fname, char *buf, size_t bufsiz)
863 time_t timestamp;
864 char *stripped;
865 int ret, saved_errno;
866 char *conv;
868 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
869 &timestamp, &stripped)) {
870 return -1;
872 if (timestamp == 0) {
873 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
875 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
876 TALLOC_FREE(stripped);
877 if (conv == NULL) {
878 return -1;
880 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
881 saved_errno = errno;
882 TALLOC_FREE(conv);
883 errno = saved_errno;
884 return ret;
887 static int shadow_copy2_mknod(vfs_handle_struct *handle,
888 const char *fname, mode_t mode, SMB_DEV_T dev)
890 time_t timestamp;
891 char *stripped;
892 int ret, saved_errno;
893 char *conv;
895 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
896 &timestamp, &stripped)) {
897 return -1;
899 if (timestamp == 0) {
900 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
902 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
903 TALLOC_FREE(stripped);
904 if (conv == NULL) {
905 return -1;
907 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
908 saved_errno = errno;
909 TALLOC_FREE(conv);
910 errno = saved_errno;
911 return ret;
914 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
915 const char *fname)
917 time_t timestamp;
918 char *stripped = NULL;
919 char *tmp = NULL;
920 char *result = NULL;
921 char *inserted = NULL;
922 char *inserted_to, *inserted_end;
923 int saved_errno;
925 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
926 &timestamp, &stripped)) {
927 goto done;
929 if (timestamp == 0) {
930 return SMB_VFS_NEXT_REALPATH(handle, fname);
933 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
934 if (tmp == NULL) {
935 goto done;
938 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
939 if (result == NULL) {
940 goto done;
944 * Take away what we've inserted. This removes the @GMT-thingy
945 * completely, but will give a path under the share root.
947 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
948 if (inserted == NULL) {
949 goto done;
951 inserted_to = strstr_m(result, inserted);
952 if (inserted_to == NULL) {
953 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
954 goto done;
956 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
957 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
959 done:
960 saved_errno = errno;
961 TALLOC_FREE(inserted);
962 TALLOC_FREE(tmp);
963 TALLOC_FREE(stripped);
964 errno = saved_errno;
965 return result;
969 * Check whether a given directory contains a
970 * snapshot directory as direct subdirectory.
971 * If yes, return the path of the snapshot-subdir,
972 * otherwise return NULL.
974 static char *have_snapdir(struct vfs_handle_struct *handle,
975 const char *path)
977 struct smb_filename smb_fname;
978 int ret;
980 ZERO_STRUCT(smb_fname);
981 smb_fname.base_name = talloc_asprintf(
982 talloc_tos(), "%s/%s", path,
983 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
984 ".snapshots"));
985 if (smb_fname.base_name == NULL) {
986 return NULL;
989 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
990 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
991 return smb_fname.base_name;
993 TALLOC_FREE(smb_fname.base_name);
994 return NULL;
998 * Find the snapshot directory (if any) for the given
999 * filename (which is relative to the share).
1001 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1002 struct vfs_handle_struct *handle,
1003 struct smb_filename *smb_fname)
1005 char *path, *p;
1006 char *snapdir;
1008 path = talloc_asprintf(mem_ctx, "%s/%s",
1009 handle->conn->connectpath,
1010 smb_fname->base_name);
1011 if (path == NULL) {
1012 return NULL;
1015 snapdir = have_snapdir(handle, path);
1016 if (snapdir != NULL) {
1017 TALLOC_FREE(path);
1018 return snapdir;
1021 while ((p = strrchr(path, '/')) && (p > path)) {
1023 p[0] = '\0';
1025 snapdir = have_snapdir(handle, path);
1026 if (snapdir != NULL) {
1027 TALLOC_FREE(path);
1028 return snapdir;
1031 TALLOC_FREE(path);
1032 return NULL;
1035 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1036 const char *name,
1037 char *gmt, size_t gmt_len)
1039 struct tm timestamp;
1040 time_t timestamp_t;
1041 unsigned long int timestamp_long;
1042 const char *fmt;
1044 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1045 "format", GMT_FORMAT);
1047 ZERO_STRUCT(timestamp);
1048 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1049 if (sscanf(name, fmt, &timestamp_long) != 1) {
1050 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1051 "no sscanf match %s: %s\n",
1052 fmt, name));
1053 return false;
1055 timestamp_t = timestamp_long;
1056 gmtime_r(&timestamp_t, &timestamp);
1057 } else {
1058 if (strptime(name, fmt, &timestamp) == NULL) {
1059 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1060 "no match %s: %s\n",
1061 fmt, name));
1062 return false;
1064 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1065 fmt, name));
1067 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1068 timestamp.tm_isdst = -1;
1069 timestamp_t = mktime(&timestamp);
1070 gmtime_r(&timestamp_t, &timestamp);
1074 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1075 return true;
1078 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1080 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1083 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1085 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1089 sort the shadow copy data in ascending or descending order
1091 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1092 struct shadow_copy_data *shadow_copy2_data)
1094 int (*cmpfunc)(const void *, const void *);
1095 const char *sort;
1097 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1098 "sort", "desc");
1099 if (sort == NULL) {
1100 return;
1103 if (strcmp(sort, "asc") == 0) {
1104 cmpfunc = shadow_copy2_label_cmp_asc;
1105 } else if (strcmp(sort, "desc") == 0) {
1106 cmpfunc = shadow_copy2_label_cmp_desc;
1107 } else {
1108 return;
1111 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1112 shadow_copy2_data->labels)
1114 TYPESAFE_QSORT(shadow_copy2_data->labels,
1115 shadow_copy2_data->num_volumes,
1116 cmpfunc);
1120 static int shadow_copy2_get_shadow_copy_data(
1121 vfs_handle_struct *handle, files_struct *fsp,
1122 struct shadow_copy_data *shadow_copy2_data,
1123 bool labels)
1125 DIR *p;
1126 const char *snapdir;
1127 struct dirent *d;
1128 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1130 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1131 if (snapdir == NULL) {
1132 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1133 handle->conn->connectpath));
1134 errno = EINVAL;
1135 talloc_free(tmp_ctx);
1136 return -1;
1139 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1141 if (!p) {
1142 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1143 " - %s\n", snapdir, strerror(errno)));
1144 talloc_free(tmp_ctx);
1145 errno = ENOSYS;
1146 return -1;
1149 shadow_copy2_data->num_volumes = 0;
1150 shadow_copy2_data->labels = NULL;
1152 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1153 char snapshot[GMT_NAME_LEN+1];
1154 SHADOW_COPY_LABEL *tlabels;
1157 * ignore names not of the right form in the snapshot
1158 * directory
1160 if (!shadow_copy2_snapshot_to_gmt(
1161 handle, d->d_name,
1162 snapshot, sizeof(snapshot))) {
1164 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1165 "ignoring %s\n", d->d_name));
1166 continue;
1168 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1169 d->d_name, snapshot));
1171 if (!labels) {
1172 /* the caller doesn't want the labels */
1173 shadow_copy2_data->num_volumes++;
1174 continue;
1177 tlabels = talloc_realloc(shadow_copy2_data,
1178 shadow_copy2_data->labels,
1179 SHADOW_COPY_LABEL,
1180 shadow_copy2_data->num_volumes+1);
1181 if (tlabels == NULL) {
1182 DEBUG(0,("shadow_copy2: out of memory\n"));
1183 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1184 talloc_free(tmp_ctx);
1185 return -1;
1188 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1189 sizeof(*tlabels));
1191 shadow_copy2_data->num_volumes++;
1192 shadow_copy2_data->labels = tlabels;
1195 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1197 shadow_copy2_sort_data(handle, shadow_copy2_data);
1199 talloc_free(tmp_ctx);
1200 return 0;
1203 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1204 struct files_struct *fsp,
1205 uint32 security_info,
1206 TALLOC_CTX *mem_ctx,
1207 struct security_descriptor **ppdesc)
1209 time_t timestamp;
1210 char *stripped;
1211 NTSTATUS status;
1212 char *conv;
1214 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1215 fsp->fsp_name->base_name,
1216 &timestamp, &stripped)) {
1217 return map_nt_error_from_unix(errno);
1219 if (timestamp == 0) {
1220 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1221 mem_ctx,
1222 ppdesc);
1224 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1225 TALLOC_FREE(stripped);
1226 if (conv == NULL) {
1227 return map_nt_error_from_unix(errno);
1229 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1230 mem_ctx, ppdesc);
1231 TALLOC_FREE(conv);
1232 return status;
1235 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1236 const char *fname,
1237 uint32 security_info,
1238 TALLOC_CTX *mem_ctx,
1239 struct security_descriptor **ppdesc)
1241 time_t timestamp;
1242 char *stripped;
1243 NTSTATUS status;
1244 char *conv;
1246 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1247 &timestamp, &stripped)) {
1248 return map_nt_error_from_unix(errno);
1250 if (timestamp == 0) {
1251 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1252 mem_ctx, ppdesc);
1254 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1255 TALLOC_FREE(stripped);
1256 if (conv == NULL) {
1257 return map_nt_error_from_unix(errno);
1259 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1260 mem_ctx, ppdesc);
1261 TALLOC_FREE(conv);
1262 return status;
1265 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1266 const char *fname, mode_t mode)
1268 time_t timestamp;
1269 char *stripped;
1270 int ret, saved_errno;
1271 char *conv;
1273 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1274 &timestamp, &stripped)) {
1275 return -1;
1277 if (timestamp == 0) {
1278 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1280 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1281 TALLOC_FREE(stripped);
1282 if (conv == NULL) {
1283 return -1;
1285 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1286 saved_errno = errno;
1287 TALLOC_FREE(conv);
1288 errno = saved_errno;
1289 return ret;
1292 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1294 time_t timestamp;
1295 char *stripped;
1296 int ret, saved_errno;
1297 char *conv;
1299 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1300 &timestamp, &stripped)) {
1301 return -1;
1303 if (timestamp == 0) {
1304 return SMB_VFS_NEXT_RMDIR(handle, fname);
1306 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1307 TALLOC_FREE(stripped);
1308 if (conv == NULL) {
1309 return -1;
1311 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1312 saved_errno = errno;
1313 TALLOC_FREE(conv);
1314 errno = saved_errno;
1315 return ret;
1318 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1319 unsigned int flags)
1321 time_t timestamp;
1322 char *stripped;
1323 int ret, saved_errno;
1324 char *conv;
1326 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1327 &timestamp, &stripped)) {
1328 return -1;
1330 if (timestamp == 0) {
1331 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1333 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1334 TALLOC_FREE(stripped);
1335 if (conv == NULL) {
1336 return -1;
1338 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1339 saved_errno = errno;
1340 TALLOC_FREE(conv);
1341 errno = saved_errno;
1342 return ret;
1345 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1346 const char *fname, const char *aname,
1347 void *value, size_t size)
1349 time_t timestamp;
1350 char *stripped;
1351 ssize_t ret;
1352 int saved_errno;
1353 char *conv;
1355 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1356 &timestamp, &stripped)) {
1357 return -1;
1359 if (timestamp == 0) {
1360 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1361 size);
1363 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1364 TALLOC_FREE(stripped);
1365 if (conv == NULL) {
1366 return -1;
1368 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1369 saved_errno = errno;
1370 TALLOC_FREE(conv);
1371 errno = saved_errno;
1372 return ret;
1375 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1376 const char *fname,
1377 char *list, size_t size)
1379 time_t timestamp;
1380 char *stripped;
1381 ssize_t ret;
1382 int saved_errno;
1383 char *conv;
1385 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1386 &timestamp, &stripped)) {
1387 return -1;
1389 if (timestamp == 0) {
1390 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1392 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1393 TALLOC_FREE(stripped);
1394 if (conv == NULL) {
1395 return -1;
1397 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1398 saved_errno = errno;
1399 TALLOC_FREE(conv);
1400 errno = saved_errno;
1401 return ret;
1404 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1405 const char *fname, const char *aname)
1407 time_t timestamp;
1408 char *stripped;
1409 int ret, saved_errno;
1410 char *conv;
1412 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1413 &timestamp, &stripped)) {
1414 return -1;
1416 if (timestamp == 0) {
1417 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1419 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1420 TALLOC_FREE(stripped);
1421 if (conv == NULL) {
1422 return -1;
1424 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1425 saved_errno = errno;
1426 TALLOC_FREE(conv);
1427 errno = saved_errno;
1428 return ret;
1431 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1432 const char *fname,
1433 const char *aname, const void *value,
1434 size_t size, int flags)
1436 time_t timestamp;
1437 char *stripped;
1438 ssize_t ret;
1439 int saved_errno;
1440 char *conv;
1442 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1443 &timestamp, &stripped)) {
1444 return -1;
1446 if (timestamp == 0) {
1447 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1448 flags);
1450 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1451 TALLOC_FREE(stripped);
1452 if (conv == NULL) {
1453 return -1;
1455 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1456 saved_errno = errno;
1457 TALLOC_FREE(conv);
1458 errno = saved_errno;
1459 return ret;
1462 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1463 const char *fname, mode_t mode)
1465 time_t timestamp;
1466 char *stripped;
1467 ssize_t ret;
1468 int saved_errno;
1469 char *conv;
1471 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1472 &timestamp, &stripped)) {
1473 return -1;
1475 if (timestamp == 0) {
1476 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1478 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1479 TALLOC_FREE(stripped);
1480 if (conv == NULL) {
1481 return -1;
1483 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1484 saved_errno = errno;
1485 TALLOC_FREE(conv);
1486 errno = saved_errno;
1487 return ret;
1490 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1491 const char *path,
1492 const char *name,
1493 TALLOC_CTX *mem_ctx,
1494 char **found_name)
1496 time_t timestamp;
1497 char *stripped;
1498 ssize_t ret;
1499 int saved_errno;
1500 char *conv;
1502 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1503 &timestamp, &stripped)) {
1504 return -1;
1506 if (timestamp == 0) {
1507 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1508 mem_ctx, found_name);
1510 if (stripped[0] == '\0') {
1511 *found_name = talloc_strdup(mem_ctx, name);
1512 if (*found_name == NULL) {
1513 errno = ENOMEM;
1514 return -1;
1516 return 0;
1518 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1519 TALLOC_FREE(stripped);
1520 if (conv == NULL) {
1521 return -1;
1523 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1524 mem_ctx, found_name);
1525 saved_errno = errno;
1526 TALLOC_FREE(conv);
1527 errno = saved_errno;
1528 return ret;
1532 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1533 .opendir_fn = shadow_copy2_opendir,
1534 .rename_fn = shadow_copy2_rename,
1535 .link_fn = shadow_copy2_link,
1536 .symlink_fn = shadow_copy2_symlink,
1537 .stat_fn = shadow_copy2_stat,
1538 .lstat_fn = shadow_copy2_lstat,
1539 .fstat_fn = shadow_copy2_fstat,
1540 .open_fn = shadow_copy2_open,
1541 .unlink_fn = shadow_copy2_unlink,
1542 .chmod_fn = shadow_copy2_chmod,
1543 .chown_fn = shadow_copy2_chown,
1544 .chdir_fn = shadow_copy2_chdir,
1545 .ntimes_fn = shadow_copy2_ntimes,
1546 .readlink_fn = shadow_copy2_readlink,
1547 .mknod_fn = shadow_copy2_mknod,
1548 .realpath_fn = shadow_copy2_realpath,
1549 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1550 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1551 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1552 .mkdir_fn = shadow_copy2_mkdir,
1553 .rmdir_fn = shadow_copy2_rmdir,
1554 .getxattr_fn = shadow_copy2_getxattr,
1555 .listxattr_fn = shadow_copy2_listxattr,
1556 .removexattr_fn = shadow_copy2_removexattr,
1557 .setxattr_fn = shadow_copy2_setxattr,
1558 .chmod_acl_fn = shadow_copy2_chmod_acl,
1559 .chflags_fn = shadow_copy2_chflags,
1560 .get_real_filename_fn = shadow_copy2_get_real_filename,
1563 NTSTATUS vfs_shadow_copy2_init(void);
1564 NTSTATUS vfs_shadow_copy2_init(void)
1566 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1567 "shadow_copy2", &vfs_shadow_copy2_fns);