shadow_copy2: add comment block explaining shadow_copy2_insert_string()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob06e5115f356e1088b27b7066a5371a4bc051922f
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;
145 * Given a timstamp, build the string to insert into a path
146 * as a path component for creating the local path to the
147 * snapshot at the given timestamp of the input path.
149 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
150 struct vfs_handle_struct *handle,
151 time_t snapshot)
153 const char *fmt;
154 struct tm snap_tm;
155 fstring snaptime_string;
156 size_t snaptime_len;
158 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
159 "format", GMT_FORMAT);
161 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
162 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
163 (unsigned long)snapshot);
164 if (snaptime_len <= 0) {
165 DEBUG(10, ("snprintf failed\n"));
166 return NULL;
168 } else {
169 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
170 if (localtime_r(&snapshot, &snap_tm) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
172 return NULL;
174 } else {
175 if (gmtime_r(&snapshot, &snap_tm) == 0) {
176 DEBUG(10, ("gmtime_r failed\n"));
177 return NULL;
180 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
181 &snap_tm);
182 if (snaptime_len == 0) {
183 DEBUG(10, ("strftime failed\n"));
184 return NULL;
187 return talloc_asprintf(mem_ctx, "/%s/%s",
188 lp_parm_const_string(
189 SNUM(handle->conn), "shadow", "snapdir",
190 ".snapshots"),
191 snaptime_string);
195 * Strip a snapshot component from an filename as
196 * handed in via the smb layer.
197 * Returns the parsed timestamp and the stripped filename.
199 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
200 struct vfs_handle_struct *handle,
201 const char *name,
202 time_t *ptimestamp,
203 char **pstripped)
205 struct tm tm;
206 time_t timestamp;
207 const char *p;
208 char *q;
209 char *stripped;
210 size_t rest_len, dst_len;
212 p = strstr_m(name, "@GMT-");
213 if (p == NULL) {
214 goto no_snapshot;
216 if ((p > name) && (p[-1] != '/')) {
217 goto no_snapshot;
219 q = strptime(p, GMT_FORMAT, &tm);
220 if (q == NULL) {
221 goto no_snapshot;
223 tm.tm_isdst = -1;
224 timestamp = timegm(&tm);
225 if (timestamp == (time_t)-1) {
226 goto no_snapshot;
228 if ((p == name) && (q[0] == '\0')) {
229 if (pstripped != NULL) {
230 stripped = talloc_strdup(mem_ctx, "");
231 if (stripped == NULL) {
232 return false;
234 *pstripped = stripped;
236 *ptimestamp = timestamp;
237 return true;
239 if (q[0] != '/') {
240 goto no_snapshot;
242 q += 1;
244 rest_len = strlen(q);
245 dst_len = (p-name) + rest_len;
247 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
248 false)) {
249 char *insert;
250 bool have_insert;
251 insert = shadow_copy2_insert_string(talloc_tos(), handle,
252 timestamp);
253 if (insert == NULL) {
254 errno = ENOMEM;
255 return false;
258 have_insert = (strstr(name, insert+1) != NULL);
259 TALLOC_FREE(insert);
260 if (have_insert) {
261 goto no_snapshot;
265 if (pstripped != NULL) {
266 stripped = talloc_array(mem_ctx, char, dst_len+1);
267 if (stripped == NULL) {
268 errno = ENOMEM;
269 return false;
271 if (p > name) {
272 memcpy(stripped, name, p-name);
274 if (rest_len > 0) {
275 memcpy(stripped + (p-name), q, rest_len);
277 stripped[dst_len] = '\0';
278 *pstripped = stripped;
280 *ptimestamp = timestamp;
281 return true;
282 no_snapshot:
283 *ptimestamp = 0;
284 return true;
287 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
288 vfs_handle_struct *handle)
290 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
291 dev_t dev;
292 struct stat st;
293 char *p;
295 if (stat(path, &st) != 0) {
296 talloc_free(path);
297 return NULL;
300 dev = st.st_dev;
302 while ((p = strrchr(path, '/')) && p > path) {
303 *p = 0;
304 if (stat(path, &st) != 0) {
305 talloc_free(path);
306 return NULL;
308 if (st.st_dev != dev) {
309 *p = '/';
310 break;
314 return path;
317 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
318 struct vfs_handle_struct *handle,
319 const char *name, time_t timestamp)
321 struct smb_filename converted_fname;
322 char *result = NULL;
323 size_t *slashes = NULL;
324 unsigned num_slashes;
325 char *path = NULL;
326 size_t pathlen;
327 char *insert = NULL;
328 char *converted = NULL;
329 size_t insertlen;
330 int i, saved_errno;
331 size_t min_offset;
333 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
334 name);
335 if (path == NULL) {
336 errno = ENOMEM;
337 goto fail;
339 pathlen = talloc_get_size(path)-1;
341 DEBUG(10, ("converting %s\n", path));
343 if (!shadow_copy2_find_slashes(talloc_tos(), path,
344 &slashes, &num_slashes)) {
345 goto fail;
347 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
348 if (insert == NULL) {
349 goto fail;
351 insertlen = talloc_get_size(insert)-1;
352 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
353 if (converted == NULL) {
354 goto fail;
357 if (path[pathlen-1] != '/') {
359 * Append a fake slash to find the snapshot root
361 size_t *tmp;
362 tmp = talloc_realloc(talloc_tos(), slashes,
363 size_t, num_slashes+1);
364 if (tmp == NULL) {
365 goto fail;
367 slashes = tmp;
368 slashes[num_slashes] = pathlen;
369 num_slashes += 1;
372 min_offset = 0;
374 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
375 false)) {
376 char *mount_point;
378 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
379 handle);
380 if (mount_point == NULL) {
381 goto fail;
383 min_offset = strlen(mount_point);
384 TALLOC_FREE(mount_point);
387 memcpy(converted, path, pathlen+1);
388 converted[pathlen+insertlen] = '\0';
390 ZERO_STRUCT(converted_fname);
391 converted_fname.base_name = converted;
393 for (i = num_slashes-1; i>=0; i--) {
394 int ret;
395 size_t offset;
397 offset = slashes[i];
399 if (offset < min_offset) {
400 errno = ENOENT;
401 goto fail;
404 memcpy(converted+offset, insert, insertlen);
406 offset += insertlen;
407 memcpy(converted+offset, path + slashes[i],
408 pathlen - slashes[i]);
410 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
412 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
413 ret, ret == 0 ? "ok" : strerror(errno)));
414 if (ret == 0) {
415 /* success */
416 break;
418 if (errno == ENOTDIR) {
420 * This is a valid condition: We appended the
421 * .snaphots/@GMT.. to a file name. Just try
422 * with the upper levels.
424 continue;
426 if (errno != ENOENT) {
427 /* Other problem than "not found" */
428 goto fail;
432 if (i >= 0) {
434 * Found something
436 DEBUG(10, ("Found %s\n", converted));
437 result = converted;
438 converted = NULL;
439 } else {
440 errno = ENOENT;
442 fail:
443 saved_errno = errno;
444 TALLOC_FREE(converted);
445 TALLOC_FREE(insert);
446 TALLOC_FREE(slashes);
447 TALLOC_FREE(path);
448 errno = saved_errno;
449 return result;
453 modify a sbuf return to ensure that inodes in the shadow directory
454 are different from those in the main directory
456 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
457 SMB_STRUCT_STAT *sbuf)
459 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
460 /* some snapshot systems, like GPFS, return the name
461 device:inode for the snapshot files as the current
462 files. That breaks the 'restore' button in the shadow copy
463 GUI, as the client gets a sharing violation.
465 This is a crude way of allowing both files to be
466 open at once. It has a slight chance of inode
467 number collision, but I can't see a better approach
468 without significant VFS changes
470 uint32_t shash;
472 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
473 if (shash == 0) {
474 shash = 1;
476 sbuf->st_ex_ino ^= shash;
480 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
481 const char *fname,
482 const char *mask,
483 uint32 attr)
485 time_t timestamp;
486 char *stripped;
487 DIR *ret;
488 int saved_errno;
489 char *conv;
491 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
492 &timestamp, &stripped)) {
493 return NULL;
495 if (timestamp == 0) {
496 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
498 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
499 TALLOC_FREE(stripped);
500 if (conv == NULL) {
501 return NULL;
503 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
504 saved_errno = errno;
505 TALLOC_FREE(conv);
506 errno = saved_errno;
507 return ret;
510 static int shadow_copy2_rename(vfs_handle_struct *handle,
511 const struct smb_filename *smb_fname_src,
512 const struct smb_filename *smb_fname_dst)
514 time_t timestamp_src, timestamp_dst;
516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
517 smb_fname_src->base_name,
518 &timestamp_src, NULL)) {
519 return -1;
521 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
522 smb_fname_dst->base_name,
523 &timestamp_dst, NULL)) {
524 return -1;
526 if (timestamp_src != 0) {
527 errno = EXDEV;
528 return -1;
530 if (timestamp_dst != 0) {
531 errno = EROFS;
532 return -1;
534 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
537 static int shadow_copy2_symlink(vfs_handle_struct *handle,
538 const char *oldname, const char *newname)
540 time_t timestamp_old, timestamp_new;
542 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
543 &timestamp_old, NULL)) {
544 return -1;
546 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
547 &timestamp_new, NULL)) {
548 return -1;
550 if ((timestamp_old != 0) || (timestamp_new != 0)) {
551 errno = EROFS;
552 return -1;
554 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
557 static int shadow_copy2_link(vfs_handle_struct *handle,
558 const char *oldname, const char *newname)
560 time_t timestamp_old, timestamp_new;
562 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
563 &timestamp_old, NULL)) {
564 return -1;
566 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
567 &timestamp_new, NULL)) {
568 return -1;
570 if ((timestamp_old != 0) || (timestamp_new != 0)) {
571 errno = EROFS;
572 return -1;
574 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
577 static int shadow_copy2_stat(vfs_handle_struct *handle,
578 struct smb_filename *smb_fname)
580 time_t timestamp;
581 char *stripped, *tmp;
582 int ret, saved_errno;
584 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
585 smb_fname->base_name,
586 &timestamp, &stripped)) {
587 return -1;
589 if (timestamp == 0) {
590 return SMB_VFS_NEXT_STAT(handle, smb_fname);
593 tmp = smb_fname->base_name;
594 smb_fname->base_name = shadow_copy2_convert(
595 talloc_tos(), handle, stripped, timestamp);
596 TALLOC_FREE(stripped);
598 if (smb_fname->base_name == NULL) {
599 smb_fname->base_name = tmp;
600 return -1;
603 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
604 saved_errno = errno;
606 TALLOC_FREE(smb_fname->base_name);
607 smb_fname->base_name = tmp;
609 if (ret == 0) {
610 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
612 errno = saved_errno;
613 return ret;
616 static int shadow_copy2_lstat(vfs_handle_struct *handle,
617 struct smb_filename *smb_fname)
619 time_t timestamp;
620 char *stripped, *tmp;
621 int ret, saved_errno;
623 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
624 smb_fname->base_name,
625 &timestamp, &stripped)) {
626 return -1;
628 if (timestamp == 0) {
629 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
632 tmp = smb_fname->base_name;
633 smb_fname->base_name = shadow_copy2_convert(
634 talloc_tos(), handle, stripped, timestamp);
635 TALLOC_FREE(stripped);
637 if (smb_fname->base_name == NULL) {
638 smb_fname->base_name = tmp;
639 return -1;
642 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
643 saved_errno = errno;
645 TALLOC_FREE(smb_fname->base_name);
646 smb_fname->base_name = tmp;
648 if (ret == 0) {
649 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
651 errno = saved_errno;
652 return ret;
655 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
656 SMB_STRUCT_STAT *sbuf)
658 time_t timestamp;
659 int ret;
661 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
662 if (ret == -1) {
663 return ret;
665 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
666 fsp->fsp_name->base_name,
667 &timestamp, NULL)) {
668 return 0;
670 if (timestamp != 0) {
671 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
673 return 0;
676 static int shadow_copy2_open(vfs_handle_struct *handle,
677 struct smb_filename *smb_fname, files_struct *fsp,
678 int flags, mode_t mode)
680 time_t timestamp;
681 char *stripped, *tmp;
682 int ret, saved_errno;
684 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
685 smb_fname->base_name,
686 &timestamp, &stripped)) {
687 return -1;
689 if (timestamp == 0) {
690 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
693 tmp = smb_fname->base_name;
694 smb_fname->base_name = shadow_copy2_convert(
695 talloc_tos(), handle, stripped, timestamp);
696 TALLOC_FREE(stripped);
698 if (smb_fname->base_name == NULL) {
699 smb_fname->base_name = tmp;
700 return -1;
703 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
704 saved_errno = errno;
706 TALLOC_FREE(smb_fname->base_name);
707 smb_fname->base_name = tmp;
709 errno = saved_errno;
710 return ret;
713 static int shadow_copy2_unlink(vfs_handle_struct *handle,
714 const struct smb_filename *smb_fname)
716 time_t timestamp;
717 char *stripped;
718 int ret, saved_errno;
719 struct smb_filename *conv;
721 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
722 smb_fname->base_name,
723 &timestamp, &stripped)) {
724 return -1;
726 if (timestamp == 0) {
727 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
729 conv = cp_smb_filename(talloc_tos(), smb_fname);
730 if (conv == NULL) {
731 errno = ENOMEM;
732 return -1;
734 conv->base_name = shadow_copy2_convert(
735 conv, handle, stripped, timestamp);
736 TALLOC_FREE(stripped);
737 if (conv->base_name == NULL) {
738 return -1;
740 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
741 saved_errno = errno;
742 TALLOC_FREE(conv);
743 errno = saved_errno;
744 return ret;
747 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
748 mode_t mode)
750 time_t timestamp;
751 char *stripped;
752 int ret, saved_errno;
753 char *conv;
755 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
756 &timestamp, &stripped)) {
757 return -1;
759 if (timestamp == 0) {
760 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
762 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
763 TALLOC_FREE(stripped);
764 if (conv == NULL) {
765 return -1;
767 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
768 saved_errno = errno;
769 TALLOC_FREE(conv);
770 errno = saved_errno;
771 return ret;
774 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
775 uid_t uid, gid_t gid)
777 time_t timestamp;
778 char *stripped;
779 int ret, saved_errno;
780 char *conv;
782 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
783 &timestamp, &stripped)) {
784 return -1;
786 if (timestamp == 0) {
787 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
789 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
790 TALLOC_FREE(stripped);
791 if (conv == NULL) {
792 return -1;
794 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
795 saved_errno = errno;
796 TALLOC_FREE(conv);
797 errno = saved_errno;
798 return ret;
801 static int shadow_copy2_chdir(vfs_handle_struct *handle,
802 const char *fname)
804 time_t timestamp;
805 char *stripped;
806 int ret, saved_errno;
807 char *conv;
809 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
810 &timestamp, &stripped)) {
811 return -1;
813 if (timestamp == 0) {
814 return SMB_VFS_NEXT_CHDIR(handle, fname);
816 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
817 TALLOC_FREE(stripped);
818 if (conv == NULL) {
819 return -1;
821 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
822 saved_errno = errno;
823 TALLOC_FREE(conv);
824 errno = saved_errno;
825 return ret;
828 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
829 const struct smb_filename *smb_fname,
830 struct smb_file_time *ft)
832 time_t timestamp;
833 char *stripped;
834 int ret, saved_errno;
835 struct smb_filename *conv;
837 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
838 smb_fname->base_name,
839 &timestamp, &stripped)) {
840 return -1;
842 if (timestamp == 0) {
843 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
845 conv = cp_smb_filename(talloc_tos(), smb_fname);
846 if (conv == NULL) {
847 errno = ENOMEM;
848 return -1;
850 conv->base_name = shadow_copy2_convert(
851 conv, handle, stripped, timestamp);
852 TALLOC_FREE(stripped);
853 if (conv->base_name == NULL) {
854 return -1;
856 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
857 saved_errno = errno;
858 TALLOC_FREE(conv);
859 errno = saved_errno;
860 return ret;
863 static int shadow_copy2_readlink(vfs_handle_struct *handle,
864 const char *fname, char *buf, size_t bufsiz)
866 time_t timestamp;
867 char *stripped;
868 int ret, saved_errno;
869 char *conv;
871 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
872 &timestamp, &stripped)) {
873 return -1;
875 if (timestamp == 0) {
876 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
878 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
879 TALLOC_FREE(stripped);
880 if (conv == NULL) {
881 return -1;
883 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
884 saved_errno = errno;
885 TALLOC_FREE(conv);
886 errno = saved_errno;
887 return ret;
890 static int shadow_copy2_mknod(vfs_handle_struct *handle,
891 const char *fname, mode_t mode, SMB_DEV_T dev)
893 time_t timestamp;
894 char *stripped;
895 int ret, saved_errno;
896 char *conv;
898 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
899 &timestamp, &stripped)) {
900 return -1;
902 if (timestamp == 0) {
903 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
905 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
906 TALLOC_FREE(stripped);
907 if (conv == NULL) {
908 return -1;
910 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
911 saved_errno = errno;
912 TALLOC_FREE(conv);
913 errno = saved_errno;
914 return ret;
917 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
918 const char *fname)
920 time_t timestamp;
921 char *stripped = NULL;
922 char *tmp = NULL;
923 char *result = NULL;
924 char *inserted = NULL;
925 char *inserted_to, *inserted_end;
926 int saved_errno;
928 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
929 &timestamp, &stripped)) {
930 goto done;
932 if (timestamp == 0) {
933 return SMB_VFS_NEXT_REALPATH(handle, fname);
936 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
937 if (tmp == NULL) {
938 goto done;
941 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
942 if (result == NULL) {
943 goto done;
947 * Take away what we've inserted. This removes the @GMT-thingy
948 * completely, but will give a path under the share root.
950 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
951 if (inserted == NULL) {
952 goto done;
954 inserted_to = strstr_m(result, inserted);
955 if (inserted_to == NULL) {
956 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
957 goto done;
959 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
960 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
962 done:
963 saved_errno = errno;
964 TALLOC_FREE(inserted);
965 TALLOC_FREE(tmp);
966 TALLOC_FREE(stripped);
967 errno = saved_errno;
968 return result;
972 * Check whether a given directory contains a
973 * snapshot directory as direct subdirectory.
974 * If yes, return the path of the snapshot-subdir,
975 * otherwise return NULL.
977 static char *have_snapdir(struct vfs_handle_struct *handle,
978 const char *path)
980 struct smb_filename smb_fname;
981 int ret;
983 ZERO_STRUCT(smb_fname);
984 smb_fname.base_name = talloc_asprintf(
985 talloc_tos(), "%s/%s", path,
986 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
987 ".snapshots"));
988 if (smb_fname.base_name == NULL) {
989 return NULL;
992 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
993 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
994 return smb_fname.base_name;
996 TALLOC_FREE(smb_fname.base_name);
997 return NULL;
1001 * Find the snapshot directory (if any) for the given
1002 * filename (which is relative to the share).
1004 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1005 struct vfs_handle_struct *handle,
1006 struct smb_filename *smb_fname)
1008 char *path, *p;
1009 char *snapdir;
1011 path = talloc_asprintf(mem_ctx, "%s/%s",
1012 handle->conn->connectpath,
1013 smb_fname->base_name);
1014 if (path == NULL) {
1015 return NULL;
1018 snapdir = have_snapdir(handle, path);
1019 if (snapdir != NULL) {
1020 TALLOC_FREE(path);
1021 return snapdir;
1024 while ((p = strrchr(path, '/')) && (p > path)) {
1026 p[0] = '\0';
1028 snapdir = have_snapdir(handle, path);
1029 if (snapdir != NULL) {
1030 TALLOC_FREE(path);
1031 return snapdir;
1034 TALLOC_FREE(path);
1035 return NULL;
1038 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1039 const char *name,
1040 char *gmt, size_t gmt_len)
1042 struct tm timestamp;
1043 time_t timestamp_t;
1044 unsigned long int timestamp_long;
1045 const char *fmt;
1047 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1048 "format", GMT_FORMAT);
1050 ZERO_STRUCT(timestamp);
1051 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1052 if (sscanf(name, fmt, &timestamp_long) != 1) {
1053 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1054 "no sscanf match %s: %s\n",
1055 fmt, name));
1056 return false;
1058 timestamp_t = timestamp_long;
1059 gmtime_r(&timestamp_t, &timestamp);
1060 } else {
1061 if (strptime(name, fmt, &timestamp) == NULL) {
1062 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1063 "no match %s: %s\n",
1064 fmt, name));
1065 return false;
1067 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1068 fmt, name));
1070 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1071 timestamp.tm_isdst = -1;
1072 timestamp_t = mktime(&timestamp);
1073 gmtime_r(&timestamp_t, &timestamp);
1077 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1078 return true;
1081 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1083 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1086 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1088 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1092 sort the shadow copy data in ascending or descending order
1094 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1095 struct shadow_copy_data *shadow_copy2_data)
1097 int (*cmpfunc)(const void *, const void *);
1098 const char *sort;
1100 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1101 "sort", "desc");
1102 if (sort == NULL) {
1103 return;
1106 if (strcmp(sort, "asc") == 0) {
1107 cmpfunc = shadow_copy2_label_cmp_asc;
1108 } else if (strcmp(sort, "desc") == 0) {
1109 cmpfunc = shadow_copy2_label_cmp_desc;
1110 } else {
1111 return;
1114 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1115 shadow_copy2_data->labels)
1117 TYPESAFE_QSORT(shadow_copy2_data->labels,
1118 shadow_copy2_data->num_volumes,
1119 cmpfunc);
1123 static int shadow_copy2_get_shadow_copy_data(
1124 vfs_handle_struct *handle, files_struct *fsp,
1125 struct shadow_copy_data *shadow_copy2_data,
1126 bool labels)
1128 DIR *p;
1129 const char *snapdir;
1130 struct dirent *d;
1131 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1133 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1134 if (snapdir == NULL) {
1135 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1136 handle->conn->connectpath));
1137 errno = EINVAL;
1138 talloc_free(tmp_ctx);
1139 return -1;
1142 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1144 if (!p) {
1145 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1146 " - %s\n", snapdir, strerror(errno)));
1147 talloc_free(tmp_ctx);
1148 errno = ENOSYS;
1149 return -1;
1152 shadow_copy2_data->num_volumes = 0;
1153 shadow_copy2_data->labels = NULL;
1155 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1156 char snapshot[GMT_NAME_LEN+1];
1157 SHADOW_COPY_LABEL *tlabels;
1160 * ignore names not of the right form in the snapshot
1161 * directory
1163 if (!shadow_copy2_snapshot_to_gmt(
1164 handle, d->d_name,
1165 snapshot, sizeof(snapshot))) {
1167 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1168 "ignoring %s\n", d->d_name));
1169 continue;
1171 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1172 d->d_name, snapshot));
1174 if (!labels) {
1175 /* the caller doesn't want the labels */
1176 shadow_copy2_data->num_volumes++;
1177 continue;
1180 tlabels = talloc_realloc(shadow_copy2_data,
1181 shadow_copy2_data->labels,
1182 SHADOW_COPY_LABEL,
1183 shadow_copy2_data->num_volumes+1);
1184 if (tlabels == NULL) {
1185 DEBUG(0,("shadow_copy2: out of memory\n"));
1186 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1187 talloc_free(tmp_ctx);
1188 return -1;
1191 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1192 sizeof(*tlabels));
1194 shadow_copy2_data->num_volumes++;
1195 shadow_copy2_data->labels = tlabels;
1198 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1200 shadow_copy2_sort_data(handle, shadow_copy2_data);
1202 talloc_free(tmp_ctx);
1203 return 0;
1206 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1207 struct files_struct *fsp,
1208 uint32 security_info,
1209 TALLOC_CTX *mem_ctx,
1210 struct security_descriptor **ppdesc)
1212 time_t timestamp;
1213 char *stripped;
1214 NTSTATUS status;
1215 char *conv;
1217 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1218 fsp->fsp_name->base_name,
1219 &timestamp, &stripped)) {
1220 return map_nt_error_from_unix(errno);
1222 if (timestamp == 0) {
1223 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1224 mem_ctx,
1225 ppdesc);
1227 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1228 TALLOC_FREE(stripped);
1229 if (conv == NULL) {
1230 return map_nt_error_from_unix(errno);
1232 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1233 mem_ctx, ppdesc);
1234 TALLOC_FREE(conv);
1235 return status;
1238 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1239 const char *fname,
1240 uint32 security_info,
1241 TALLOC_CTX *mem_ctx,
1242 struct security_descriptor **ppdesc)
1244 time_t timestamp;
1245 char *stripped;
1246 NTSTATUS status;
1247 char *conv;
1249 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1250 &timestamp, &stripped)) {
1251 return map_nt_error_from_unix(errno);
1253 if (timestamp == 0) {
1254 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1255 mem_ctx, ppdesc);
1257 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1258 TALLOC_FREE(stripped);
1259 if (conv == NULL) {
1260 return map_nt_error_from_unix(errno);
1262 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1263 mem_ctx, ppdesc);
1264 TALLOC_FREE(conv);
1265 return status;
1268 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1269 const char *fname, mode_t mode)
1271 time_t timestamp;
1272 char *stripped;
1273 int ret, saved_errno;
1274 char *conv;
1276 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1277 &timestamp, &stripped)) {
1278 return -1;
1280 if (timestamp == 0) {
1281 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1283 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1284 TALLOC_FREE(stripped);
1285 if (conv == NULL) {
1286 return -1;
1288 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1289 saved_errno = errno;
1290 TALLOC_FREE(conv);
1291 errno = saved_errno;
1292 return ret;
1295 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1297 time_t timestamp;
1298 char *stripped;
1299 int ret, saved_errno;
1300 char *conv;
1302 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1303 &timestamp, &stripped)) {
1304 return -1;
1306 if (timestamp == 0) {
1307 return SMB_VFS_NEXT_RMDIR(handle, fname);
1309 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1310 TALLOC_FREE(stripped);
1311 if (conv == NULL) {
1312 return -1;
1314 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1315 saved_errno = errno;
1316 TALLOC_FREE(conv);
1317 errno = saved_errno;
1318 return ret;
1321 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1322 unsigned int flags)
1324 time_t timestamp;
1325 char *stripped;
1326 int ret, saved_errno;
1327 char *conv;
1329 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1330 &timestamp, &stripped)) {
1331 return -1;
1333 if (timestamp == 0) {
1334 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1336 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1337 TALLOC_FREE(stripped);
1338 if (conv == NULL) {
1339 return -1;
1341 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1342 saved_errno = errno;
1343 TALLOC_FREE(conv);
1344 errno = saved_errno;
1345 return ret;
1348 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1349 const char *fname, const char *aname,
1350 void *value, size_t size)
1352 time_t timestamp;
1353 char *stripped;
1354 ssize_t ret;
1355 int saved_errno;
1356 char *conv;
1358 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1359 &timestamp, &stripped)) {
1360 return -1;
1362 if (timestamp == 0) {
1363 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1364 size);
1366 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1367 TALLOC_FREE(stripped);
1368 if (conv == NULL) {
1369 return -1;
1371 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1372 saved_errno = errno;
1373 TALLOC_FREE(conv);
1374 errno = saved_errno;
1375 return ret;
1378 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1379 const char *fname,
1380 char *list, size_t size)
1382 time_t timestamp;
1383 char *stripped;
1384 ssize_t ret;
1385 int saved_errno;
1386 char *conv;
1388 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1389 &timestamp, &stripped)) {
1390 return -1;
1392 if (timestamp == 0) {
1393 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1395 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1396 TALLOC_FREE(stripped);
1397 if (conv == NULL) {
1398 return -1;
1400 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1401 saved_errno = errno;
1402 TALLOC_FREE(conv);
1403 errno = saved_errno;
1404 return ret;
1407 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1408 const char *fname, const char *aname)
1410 time_t timestamp;
1411 char *stripped;
1412 int ret, saved_errno;
1413 char *conv;
1415 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1416 &timestamp, &stripped)) {
1417 return -1;
1419 if (timestamp == 0) {
1420 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1422 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1423 TALLOC_FREE(stripped);
1424 if (conv == NULL) {
1425 return -1;
1427 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1428 saved_errno = errno;
1429 TALLOC_FREE(conv);
1430 errno = saved_errno;
1431 return ret;
1434 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1435 const char *fname,
1436 const char *aname, const void *value,
1437 size_t size, int flags)
1439 time_t timestamp;
1440 char *stripped;
1441 ssize_t ret;
1442 int saved_errno;
1443 char *conv;
1445 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1446 &timestamp, &stripped)) {
1447 return -1;
1449 if (timestamp == 0) {
1450 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1451 flags);
1453 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1454 TALLOC_FREE(stripped);
1455 if (conv == NULL) {
1456 return -1;
1458 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1459 saved_errno = errno;
1460 TALLOC_FREE(conv);
1461 errno = saved_errno;
1462 return ret;
1465 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1466 const char *fname, mode_t mode)
1468 time_t timestamp;
1469 char *stripped;
1470 ssize_t ret;
1471 int saved_errno;
1472 char *conv;
1474 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1475 &timestamp, &stripped)) {
1476 return -1;
1478 if (timestamp == 0) {
1479 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1481 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1482 TALLOC_FREE(stripped);
1483 if (conv == NULL) {
1484 return -1;
1486 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1487 saved_errno = errno;
1488 TALLOC_FREE(conv);
1489 errno = saved_errno;
1490 return ret;
1493 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1494 const char *path,
1495 const char *name,
1496 TALLOC_CTX *mem_ctx,
1497 char **found_name)
1499 time_t timestamp;
1500 char *stripped;
1501 ssize_t ret;
1502 int saved_errno;
1503 char *conv;
1505 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1506 &timestamp, &stripped)) {
1507 return -1;
1509 if (timestamp == 0) {
1510 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1511 mem_ctx, found_name);
1513 if (stripped[0] == '\0') {
1514 *found_name = talloc_strdup(mem_ctx, name);
1515 if (*found_name == NULL) {
1516 errno = ENOMEM;
1517 return -1;
1519 return 0;
1521 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1522 TALLOC_FREE(stripped);
1523 if (conv == NULL) {
1524 return -1;
1526 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1527 mem_ctx, found_name);
1528 saved_errno = errno;
1529 TALLOC_FREE(conv);
1530 errno = saved_errno;
1531 return ret;
1535 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1536 .opendir_fn = shadow_copy2_opendir,
1537 .rename_fn = shadow_copy2_rename,
1538 .link_fn = shadow_copy2_link,
1539 .symlink_fn = shadow_copy2_symlink,
1540 .stat_fn = shadow_copy2_stat,
1541 .lstat_fn = shadow_copy2_lstat,
1542 .fstat_fn = shadow_copy2_fstat,
1543 .open_fn = shadow_copy2_open,
1544 .unlink_fn = shadow_copy2_unlink,
1545 .chmod_fn = shadow_copy2_chmod,
1546 .chown_fn = shadow_copy2_chown,
1547 .chdir_fn = shadow_copy2_chdir,
1548 .ntimes_fn = shadow_copy2_ntimes,
1549 .readlink_fn = shadow_copy2_readlink,
1550 .mknod_fn = shadow_copy2_mknod,
1551 .realpath_fn = shadow_copy2_realpath,
1552 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1553 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1554 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1555 .mkdir_fn = shadow_copy2_mkdir,
1556 .rmdir_fn = shadow_copy2_rmdir,
1557 .getxattr_fn = shadow_copy2_getxattr,
1558 .listxattr_fn = shadow_copy2_listxattr,
1559 .removexattr_fn = shadow_copy2_removexattr,
1560 .setxattr_fn = shadow_copy2_setxattr,
1561 .chmod_acl_fn = shadow_copy2_chmod_acl,
1562 .chflags_fn = shadow_copy2_chflags,
1563 .get_real_filename_fn = shadow_copy2_get_real_filename,
1566 NTSTATUS vfs_shadow_copy2_init(void);
1567 NTSTATUS vfs_shadow_copy2_init(void)
1569 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1570 "shadow_copy2", &vfs_shadow_copy2_fns);