shadow_copy2: add comment block explaining shadow_copy2_convert()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobb0ba3f249b8523f76ef99e3a9b184989769f1291
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;
318 * Convert from a name as handed in via the SMB layer
319 * and a timestamp into the local path of the snapshot
320 * of the provided file at the provided time.
322 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
323 struct vfs_handle_struct *handle,
324 const char *name, time_t timestamp)
326 struct smb_filename converted_fname;
327 char *result = NULL;
328 size_t *slashes = NULL;
329 unsigned num_slashes;
330 char *path = NULL;
331 size_t pathlen;
332 char *insert = NULL;
333 char *converted = NULL;
334 size_t insertlen;
335 int i, saved_errno;
336 size_t min_offset;
338 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
339 name);
340 if (path == NULL) {
341 errno = ENOMEM;
342 goto fail;
344 pathlen = talloc_get_size(path)-1;
346 DEBUG(10, ("converting %s\n", path));
348 if (!shadow_copy2_find_slashes(talloc_tos(), path,
349 &slashes, &num_slashes)) {
350 goto fail;
352 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
353 if (insert == NULL) {
354 goto fail;
356 insertlen = talloc_get_size(insert)-1;
357 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
358 if (converted == NULL) {
359 goto fail;
362 if (path[pathlen-1] != '/') {
364 * Append a fake slash to find the snapshot root
366 size_t *tmp;
367 tmp = talloc_realloc(talloc_tos(), slashes,
368 size_t, num_slashes+1);
369 if (tmp == NULL) {
370 goto fail;
372 slashes = tmp;
373 slashes[num_slashes] = pathlen;
374 num_slashes += 1;
377 min_offset = 0;
379 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
380 false)) {
381 char *mount_point;
383 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
384 handle);
385 if (mount_point == NULL) {
386 goto fail;
388 min_offset = strlen(mount_point);
389 TALLOC_FREE(mount_point);
392 memcpy(converted, path, pathlen+1);
393 converted[pathlen+insertlen] = '\0';
395 ZERO_STRUCT(converted_fname);
396 converted_fname.base_name = converted;
398 for (i = num_slashes-1; i>=0; i--) {
399 int ret;
400 size_t offset;
402 offset = slashes[i];
404 if (offset < min_offset) {
405 errno = ENOENT;
406 goto fail;
409 memcpy(converted+offset, insert, insertlen);
411 offset += insertlen;
412 memcpy(converted+offset, path + slashes[i],
413 pathlen - slashes[i]);
415 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
417 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
418 ret, ret == 0 ? "ok" : strerror(errno)));
419 if (ret == 0) {
420 /* success */
421 break;
423 if (errno == ENOTDIR) {
425 * This is a valid condition: We appended the
426 * .snaphots/@GMT.. to a file name. Just try
427 * with the upper levels.
429 continue;
431 if (errno != ENOENT) {
432 /* Other problem than "not found" */
433 goto fail;
437 if (i >= 0) {
439 * Found something
441 DEBUG(10, ("Found %s\n", converted));
442 result = converted;
443 converted = NULL;
444 } else {
445 errno = ENOENT;
447 fail:
448 saved_errno = errno;
449 TALLOC_FREE(converted);
450 TALLOC_FREE(insert);
451 TALLOC_FREE(slashes);
452 TALLOC_FREE(path);
453 errno = saved_errno;
454 return result;
458 modify a sbuf return to ensure that inodes in the shadow directory
459 are different from those in the main directory
461 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
462 SMB_STRUCT_STAT *sbuf)
464 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
465 /* some snapshot systems, like GPFS, return the name
466 device:inode for the snapshot files as the current
467 files. That breaks the 'restore' button in the shadow copy
468 GUI, as the client gets a sharing violation.
470 This is a crude way of allowing both files to be
471 open at once. It has a slight chance of inode
472 number collision, but I can't see a better approach
473 without significant VFS changes
475 uint32_t shash;
477 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
478 if (shash == 0) {
479 shash = 1;
481 sbuf->st_ex_ino ^= shash;
485 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
486 const char *fname,
487 const char *mask,
488 uint32 attr)
490 time_t timestamp;
491 char *stripped;
492 DIR *ret;
493 int saved_errno;
494 char *conv;
496 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
497 &timestamp, &stripped)) {
498 return NULL;
500 if (timestamp == 0) {
501 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
503 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
504 TALLOC_FREE(stripped);
505 if (conv == NULL) {
506 return NULL;
508 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
509 saved_errno = errno;
510 TALLOC_FREE(conv);
511 errno = saved_errno;
512 return ret;
515 static int shadow_copy2_rename(vfs_handle_struct *handle,
516 const struct smb_filename *smb_fname_src,
517 const struct smb_filename *smb_fname_dst)
519 time_t timestamp_src, timestamp_dst;
521 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
522 smb_fname_src->base_name,
523 &timestamp_src, NULL)) {
524 return -1;
526 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
527 smb_fname_dst->base_name,
528 &timestamp_dst, NULL)) {
529 return -1;
531 if (timestamp_src != 0) {
532 errno = EXDEV;
533 return -1;
535 if (timestamp_dst != 0) {
536 errno = EROFS;
537 return -1;
539 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
542 static int shadow_copy2_symlink(vfs_handle_struct *handle,
543 const char *oldname, const char *newname)
545 time_t timestamp_old, timestamp_new;
547 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
548 &timestamp_old, NULL)) {
549 return -1;
551 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
552 &timestamp_new, NULL)) {
553 return -1;
555 if ((timestamp_old != 0) || (timestamp_new != 0)) {
556 errno = EROFS;
557 return -1;
559 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
562 static int shadow_copy2_link(vfs_handle_struct *handle,
563 const char *oldname, const char *newname)
565 time_t timestamp_old, timestamp_new;
567 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
568 &timestamp_old, NULL)) {
569 return -1;
571 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
572 &timestamp_new, NULL)) {
573 return -1;
575 if ((timestamp_old != 0) || (timestamp_new != 0)) {
576 errno = EROFS;
577 return -1;
579 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
582 static int shadow_copy2_stat(vfs_handle_struct *handle,
583 struct smb_filename *smb_fname)
585 time_t timestamp;
586 char *stripped, *tmp;
587 int ret, saved_errno;
589 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
590 smb_fname->base_name,
591 &timestamp, &stripped)) {
592 return -1;
594 if (timestamp == 0) {
595 return SMB_VFS_NEXT_STAT(handle, smb_fname);
598 tmp = smb_fname->base_name;
599 smb_fname->base_name = shadow_copy2_convert(
600 talloc_tos(), handle, stripped, timestamp);
601 TALLOC_FREE(stripped);
603 if (smb_fname->base_name == NULL) {
604 smb_fname->base_name = tmp;
605 return -1;
608 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
609 saved_errno = errno;
611 TALLOC_FREE(smb_fname->base_name);
612 smb_fname->base_name = tmp;
614 if (ret == 0) {
615 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
617 errno = saved_errno;
618 return ret;
621 static int shadow_copy2_lstat(vfs_handle_struct *handle,
622 struct smb_filename *smb_fname)
624 time_t timestamp;
625 char *stripped, *tmp;
626 int ret, saved_errno;
628 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
629 smb_fname->base_name,
630 &timestamp, &stripped)) {
631 return -1;
633 if (timestamp == 0) {
634 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
637 tmp = smb_fname->base_name;
638 smb_fname->base_name = shadow_copy2_convert(
639 talloc_tos(), handle, stripped, timestamp);
640 TALLOC_FREE(stripped);
642 if (smb_fname->base_name == NULL) {
643 smb_fname->base_name = tmp;
644 return -1;
647 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
648 saved_errno = errno;
650 TALLOC_FREE(smb_fname->base_name);
651 smb_fname->base_name = tmp;
653 if (ret == 0) {
654 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
656 errno = saved_errno;
657 return ret;
660 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
661 SMB_STRUCT_STAT *sbuf)
663 time_t timestamp;
664 int ret;
666 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
667 if (ret == -1) {
668 return ret;
670 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
671 fsp->fsp_name->base_name,
672 &timestamp, NULL)) {
673 return 0;
675 if (timestamp != 0) {
676 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
678 return 0;
681 static int shadow_copy2_open(vfs_handle_struct *handle,
682 struct smb_filename *smb_fname, files_struct *fsp,
683 int flags, mode_t mode)
685 time_t timestamp;
686 char *stripped, *tmp;
687 int ret, saved_errno;
689 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
690 smb_fname->base_name,
691 &timestamp, &stripped)) {
692 return -1;
694 if (timestamp == 0) {
695 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
698 tmp = smb_fname->base_name;
699 smb_fname->base_name = shadow_copy2_convert(
700 talloc_tos(), handle, stripped, timestamp);
701 TALLOC_FREE(stripped);
703 if (smb_fname->base_name == NULL) {
704 smb_fname->base_name = tmp;
705 return -1;
708 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
709 saved_errno = errno;
711 TALLOC_FREE(smb_fname->base_name);
712 smb_fname->base_name = tmp;
714 errno = saved_errno;
715 return ret;
718 static int shadow_copy2_unlink(vfs_handle_struct *handle,
719 const struct smb_filename *smb_fname)
721 time_t timestamp;
722 char *stripped;
723 int ret, saved_errno;
724 struct smb_filename *conv;
725 NTSTATUS status;
727 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
728 smb_fname->base_name,
729 &timestamp, &stripped)) {
730 return -1;
732 if (timestamp == 0) {
733 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
735 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
736 if (!NT_STATUS_IS_OK(status)) {
737 errno = ENOMEM;
738 return -1;
740 conv->base_name = shadow_copy2_convert(
741 conv, handle, stripped, timestamp);
742 TALLOC_FREE(stripped);
743 if (conv->base_name == NULL) {
744 return -1;
746 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
747 saved_errno = errno;
748 TALLOC_FREE(conv);
749 errno = saved_errno;
750 return ret;
753 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
754 mode_t mode)
756 time_t timestamp;
757 char *stripped;
758 int ret, saved_errno;
759 char *conv;
761 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
762 &timestamp, &stripped)) {
763 return -1;
765 if (timestamp == 0) {
766 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
768 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
769 TALLOC_FREE(stripped);
770 if (conv == NULL) {
771 return -1;
773 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
774 saved_errno = errno;
775 TALLOC_FREE(conv);
776 errno = saved_errno;
777 return ret;
780 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
781 uid_t uid, gid_t gid)
783 time_t timestamp;
784 char *stripped;
785 int ret, saved_errno;
786 char *conv;
788 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
789 &timestamp, &stripped)) {
790 return -1;
792 if (timestamp == 0) {
793 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
795 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
796 TALLOC_FREE(stripped);
797 if (conv == NULL) {
798 return -1;
800 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
801 saved_errno = errno;
802 TALLOC_FREE(conv);
803 errno = saved_errno;
804 return ret;
807 static int shadow_copy2_chdir(vfs_handle_struct *handle,
808 const char *fname)
810 time_t timestamp;
811 char *stripped;
812 int ret, saved_errno;
813 char *conv;
815 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
816 &timestamp, &stripped)) {
817 return -1;
819 if (timestamp == 0) {
820 return SMB_VFS_NEXT_CHDIR(handle, fname);
822 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
823 TALLOC_FREE(stripped);
824 if (conv == NULL) {
825 return -1;
827 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
828 saved_errno = errno;
829 TALLOC_FREE(conv);
830 errno = saved_errno;
831 return ret;
834 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
835 const struct smb_filename *smb_fname,
836 struct smb_file_time *ft)
838 time_t timestamp;
839 char *stripped;
840 int ret, saved_errno;
841 struct smb_filename *conv;
842 NTSTATUS status;
844 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
845 smb_fname->base_name,
846 &timestamp, &stripped)) {
847 return -1;
849 if (timestamp == 0) {
850 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
852 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
853 if (!NT_STATUS_IS_OK(status)) {
854 errno = ENOMEM;
855 return -1;
857 conv->base_name = shadow_copy2_convert(
858 conv, handle, stripped, timestamp);
859 TALLOC_FREE(stripped);
860 if (conv->base_name == NULL) {
861 return -1;
863 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
864 saved_errno = errno;
865 TALLOC_FREE(conv);
866 errno = saved_errno;
867 return ret;
870 static int shadow_copy2_readlink(vfs_handle_struct *handle,
871 const char *fname, char *buf, size_t bufsiz)
873 time_t timestamp;
874 char *stripped;
875 int ret, saved_errno;
876 char *conv;
878 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
879 &timestamp, &stripped)) {
880 return -1;
882 if (timestamp == 0) {
883 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
885 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
886 TALLOC_FREE(stripped);
887 if (conv == NULL) {
888 return -1;
890 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
891 saved_errno = errno;
892 TALLOC_FREE(conv);
893 errno = saved_errno;
894 return ret;
897 static int shadow_copy2_mknod(vfs_handle_struct *handle,
898 const char *fname, mode_t mode, SMB_DEV_T dev)
900 time_t timestamp;
901 char *stripped;
902 int ret, saved_errno;
903 char *conv;
905 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
906 &timestamp, &stripped)) {
907 return -1;
909 if (timestamp == 0) {
910 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
912 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
913 TALLOC_FREE(stripped);
914 if (conv == NULL) {
915 return -1;
917 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
918 saved_errno = errno;
919 TALLOC_FREE(conv);
920 errno = saved_errno;
921 return ret;
924 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
925 const char *fname)
927 time_t timestamp;
928 char *stripped = NULL;
929 char *tmp = NULL;
930 char *result = NULL;
931 char *inserted = NULL;
932 char *inserted_to, *inserted_end;
933 int saved_errno;
935 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
936 &timestamp, &stripped)) {
937 goto done;
939 if (timestamp == 0) {
940 return SMB_VFS_NEXT_REALPATH(handle, fname);
943 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
944 if (tmp == NULL) {
945 goto done;
948 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
949 if (result == NULL) {
950 goto done;
954 * Take away what we've inserted. This removes the @GMT-thingy
955 * completely, but will give a path under the share root.
957 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
958 if (inserted == NULL) {
959 goto done;
961 inserted_to = strstr_m(result, inserted);
962 if (inserted_to == NULL) {
963 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
964 goto done;
966 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
967 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
969 done:
970 saved_errno = errno;
971 TALLOC_FREE(inserted);
972 TALLOC_FREE(tmp);
973 TALLOC_FREE(stripped);
974 errno = saved_errno;
975 return result;
979 * Check whether a given directory contains a
980 * snapshot directory as direct subdirectory.
981 * If yes, return the path of the snapshot-subdir,
982 * otherwise return NULL.
984 static char *have_snapdir(struct vfs_handle_struct *handle,
985 const char *path)
987 struct smb_filename smb_fname;
988 int ret;
990 ZERO_STRUCT(smb_fname);
991 smb_fname.base_name = talloc_asprintf(
992 talloc_tos(), "%s/%s", path,
993 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
994 ".snapshots"));
995 if (smb_fname.base_name == NULL) {
996 return NULL;
999 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1000 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1001 return smb_fname.base_name;
1003 TALLOC_FREE(smb_fname.base_name);
1004 return NULL;
1008 * Find the snapshot directory (if any) for the given
1009 * filename (which is relative to the share).
1011 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1012 struct vfs_handle_struct *handle,
1013 struct smb_filename *smb_fname)
1015 char *path, *p;
1016 char *snapdir;
1018 path = talloc_asprintf(mem_ctx, "%s/%s",
1019 handle->conn->connectpath,
1020 smb_fname->base_name);
1021 if (path == NULL) {
1022 return NULL;
1025 snapdir = have_snapdir(handle, path);
1026 if (snapdir != NULL) {
1027 TALLOC_FREE(path);
1028 return snapdir;
1031 while ((p = strrchr(path, '/')) && (p > path)) {
1033 p[0] = '\0';
1035 snapdir = have_snapdir(handle, path);
1036 if (snapdir != NULL) {
1037 TALLOC_FREE(path);
1038 return snapdir;
1041 TALLOC_FREE(path);
1042 return NULL;
1045 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1046 const char *name,
1047 char *gmt, size_t gmt_len)
1049 struct tm timestamp;
1050 time_t timestamp_t;
1051 unsigned long int timestamp_long;
1052 const char *fmt;
1054 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1055 "format", GMT_FORMAT);
1057 ZERO_STRUCT(timestamp);
1058 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1059 if (sscanf(name, fmt, &timestamp_long) != 1) {
1060 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1061 "no sscanf match %s: %s\n",
1062 fmt, name));
1063 return false;
1065 timestamp_t = timestamp_long;
1066 gmtime_r(&timestamp_t, &timestamp);
1067 } else {
1068 if (strptime(name, fmt, &timestamp) == NULL) {
1069 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1070 "no match %s: %s\n",
1071 fmt, name));
1072 return false;
1074 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1075 fmt, name));
1077 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1078 timestamp.tm_isdst = -1;
1079 timestamp_t = mktime(&timestamp);
1080 gmtime_r(&timestamp_t, &timestamp);
1084 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1085 return true;
1088 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1090 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1093 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1095 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1099 sort the shadow copy data in ascending or descending order
1101 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1102 struct shadow_copy_data *shadow_copy2_data)
1104 int (*cmpfunc)(const void *, const void *);
1105 const char *sort;
1107 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1108 "sort", "desc");
1109 if (sort == NULL) {
1110 return;
1113 if (strcmp(sort, "asc") == 0) {
1114 cmpfunc = shadow_copy2_label_cmp_asc;
1115 } else if (strcmp(sort, "desc") == 0) {
1116 cmpfunc = shadow_copy2_label_cmp_desc;
1117 } else {
1118 return;
1121 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1122 shadow_copy2_data->labels)
1124 TYPESAFE_QSORT(shadow_copy2_data->labels,
1125 shadow_copy2_data->num_volumes,
1126 cmpfunc);
1130 static int shadow_copy2_get_shadow_copy_data(
1131 vfs_handle_struct *handle, files_struct *fsp,
1132 struct shadow_copy_data *shadow_copy2_data,
1133 bool labels)
1135 DIR *p;
1136 const char *snapdir;
1137 struct dirent *d;
1138 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1140 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1141 if (snapdir == NULL) {
1142 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1143 handle->conn->connectpath));
1144 errno = EINVAL;
1145 talloc_free(tmp_ctx);
1146 return -1;
1149 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1151 if (!p) {
1152 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1153 " - %s\n", snapdir, strerror(errno)));
1154 talloc_free(tmp_ctx);
1155 errno = ENOSYS;
1156 return -1;
1159 shadow_copy2_data->num_volumes = 0;
1160 shadow_copy2_data->labels = NULL;
1162 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1163 char snapshot[GMT_NAME_LEN+1];
1164 SHADOW_COPY_LABEL *tlabels;
1167 * ignore names not of the right form in the snapshot
1168 * directory
1170 if (!shadow_copy2_snapshot_to_gmt(
1171 handle, d->d_name,
1172 snapshot, sizeof(snapshot))) {
1174 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1175 "ignoring %s\n", d->d_name));
1176 continue;
1178 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1179 d->d_name, snapshot));
1181 if (!labels) {
1182 /* the caller doesn't want the labels */
1183 shadow_copy2_data->num_volumes++;
1184 continue;
1187 tlabels = talloc_realloc(shadow_copy2_data,
1188 shadow_copy2_data->labels,
1189 SHADOW_COPY_LABEL,
1190 shadow_copy2_data->num_volumes+1);
1191 if (tlabels == NULL) {
1192 DEBUG(0,("shadow_copy2: out of memory\n"));
1193 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1194 talloc_free(tmp_ctx);
1195 return -1;
1198 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1199 sizeof(*tlabels));
1201 shadow_copy2_data->num_volumes++;
1202 shadow_copy2_data->labels = tlabels;
1205 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1207 shadow_copy2_sort_data(handle, shadow_copy2_data);
1209 talloc_free(tmp_ctx);
1210 return 0;
1213 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1214 struct files_struct *fsp,
1215 uint32 security_info,
1216 TALLOC_CTX *mem_ctx,
1217 struct security_descriptor **ppdesc)
1219 time_t timestamp;
1220 char *stripped;
1221 NTSTATUS status;
1222 char *conv;
1224 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1225 fsp->fsp_name->base_name,
1226 &timestamp, &stripped)) {
1227 return map_nt_error_from_unix(errno);
1229 if (timestamp == 0) {
1230 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1231 mem_ctx,
1232 ppdesc);
1234 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1235 TALLOC_FREE(stripped);
1236 if (conv == NULL) {
1237 return map_nt_error_from_unix(errno);
1239 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1240 mem_ctx, ppdesc);
1241 TALLOC_FREE(conv);
1242 return status;
1245 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1246 const char *fname,
1247 uint32 security_info,
1248 TALLOC_CTX *mem_ctx,
1249 struct security_descriptor **ppdesc)
1251 time_t timestamp;
1252 char *stripped;
1253 NTSTATUS status;
1254 char *conv;
1256 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1257 &timestamp, &stripped)) {
1258 return map_nt_error_from_unix(errno);
1260 if (timestamp == 0) {
1261 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1262 mem_ctx, ppdesc);
1264 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1265 TALLOC_FREE(stripped);
1266 if (conv == NULL) {
1267 return map_nt_error_from_unix(errno);
1269 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1270 mem_ctx, ppdesc);
1271 TALLOC_FREE(conv);
1272 return status;
1275 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1276 const char *fname, mode_t mode)
1278 time_t timestamp;
1279 char *stripped;
1280 int ret, saved_errno;
1281 char *conv;
1283 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1284 &timestamp, &stripped)) {
1285 return -1;
1287 if (timestamp == 0) {
1288 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1290 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1291 TALLOC_FREE(stripped);
1292 if (conv == NULL) {
1293 return -1;
1295 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1296 saved_errno = errno;
1297 TALLOC_FREE(conv);
1298 errno = saved_errno;
1299 return ret;
1302 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1304 time_t timestamp;
1305 char *stripped;
1306 int ret, saved_errno;
1307 char *conv;
1309 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1310 &timestamp, &stripped)) {
1311 return -1;
1313 if (timestamp == 0) {
1314 return SMB_VFS_NEXT_RMDIR(handle, fname);
1316 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1317 TALLOC_FREE(stripped);
1318 if (conv == NULL) {
1319 return -1;
1321 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1322 saved_errno = errno;
1323 TALLOC_FREE(conv);
1324 errno = saved_errno;
1325 return ret;
1328 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1329 unsigned int flags)
1331 time_t timestamp;
1332 char *stripped;
1333 int ret, saved_errno;
1334 char *conv;
1336 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1337 &timestamp, &stripped)) {
1338 return -1;
1340 if (timestamp == 0) {
1341 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1343 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1344 TALLOC_FREE(stripped);
1345 if (conv == NULL) {
1346 return -1;
1348 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1349 saved_errno = errno;
1350 TALLOC_FREE(conv);
1351 errno = saved_errno;
1352 return ret;
1355 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1356 const char *fname, const char *aname,
1357 void *value, size_t size)
1359 time_t timestamp;
1360 char *stripped;
1361 ssize_t ret;
1362 int saved_errno;
1363 char *conv;
1365 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1366 &timestamp, &stripped)) {
1367 return -1;
1369 if (timestamp == 0) {
1370 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1371 size);
1373 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1374 TALLOC_FREE(stripped);
1375 if (conv == NULL) {
1376 return -1;
1378 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1379 saved_errno = errno;
1380 TALLOC_FREE(conv);
1381 errno = saved_errno;
1382 return ret;
1385 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1386 const char *fname,
1387 char *list, size_t size)
1389 time_t timestamp;
1390 char *stripped;
1391 ssize_t ret;
1392 int saved_errno;
1393 char *conv;
1395 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1396 &timestamp, &stripped)) {
1397 return -1;
1399 if (timestamp == 0) {
1400 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1402 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1403 TALLOC_FREE(stripped);
1404 if (conv == NULL) {
1405 return -1;
1407 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1408 saved_errno = errno;
1409 TALLOC_FREE(conv);
1410 errno = saved_errno;
1411 return ret;
1414 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1415 const char *fname, const char *aname)
1417 time_t timestamp;
1418 char *stripped;
1419 int ret, saved_errno;
1420 char *conv;
1422 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1423 &timestamp, &stripped)) {
1424 return -1;
1426 if (timestamp == 0) {
1427 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1429 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1430 TALLOC_FREE(stripped);
1431 if (conv == NULL) {
1432 return -1;
1434 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1435 saved_errno = errno;
1436 TALLOC_FREE(conv);
1437 errno = saved_errno;
1438 return ret;
1441 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1442 const char *fname,
1443 const char *aname, const void *value,
1444 size_t size, int flags)
1446 time_t timestamp;
1447 char *stripped;
1448 ssize_t ret;
1449 int saved_errno;
1450 char *conv;
1452 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1453 &timestamp, &stripped)) {
1454 return -1;
1456 if (timestamp == 0) {
1457 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1458 flags);
1460 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1461 TALLOC_FREE(stripped);
1462 if (conv == NULL) {
1463 return -1;
1465 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1466 saved_errno = errno;
1467 TALLOC_FREE(conv);
1468 errno = saved_errno;
1469 return ret;
1472 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1473 const char *fname, mode_t mode)
1475 time_t timestamp;
1476 char *stripped;
1477 ssize_t ret;
1478 int saved_errno;
1479 char *conv;
1481 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1482 &timestamp, &stripped)) {
1483 return -1;
1485 if (timestamp == 0) {
1486 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1488 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1489 TALLOC_FREE(stripped);
1490 if (conv == NULL) {
1491 return -1;
1493 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1494 saved_errno = errno;
1495 TALLOC_FREE(conv);
1496 errno = saved_errno;
1497 return ret;
1500 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1501 const char *path,
1502 const char *name,
1503 TALLOC_CTX *mem_ctx,
1504 char **found_name)
1506 time_t timestamp;
1507 char *stripped;
1508 ssize_t ret;
1509 int saved_errno;
1510 char *conv;
1512 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1513 &timestamp, &stripped)) {
1514 return -1;
1516 if (timestamp == 0) {
1517 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1518 mem_ctx, found_name);
1520 if (stripped[0] == '\0') {
1521 *found_name = talloc_strdup(mem_ctx, name);
1522 if (*found_name == NULL) {
1523 errno = ENOMEM;
1524 return -1;
1526 return 0;
1528 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1529 TALLOC_FREE(stripped);
1530 if (conv == NULL) {
1531 return -1;
1533 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1534 mem_ctx, found_name);
1535 saved_errno = errno;
1536 TALLOC_FREE(conv);
1537 errno = saved_errno;
1538 return ret;
1542 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1543 .opendir_fn = shadow_copy2_opendir,
1544 .rename_fn = shadow_copy2_rename,
1545 .link_fn = shadow_copy2_link,
1546 .symlink_fn = shadow_copy2_symlink,
1547 .stat_fn = shadow_copy2_stat,
1548 .lstat_fn = shadow_copy2_lstat,
1549 .fstat_fn = shadow_copy2_fstat,
1550 .open_fn = shadow_copy2_open,
1551 .unlink_fn = shadow_copy2_unlink,
1552 .chmod_fn = shadow_copy2_chmod,
1553 .chown_fn = shadow_copy2_chown,
1554 .chdir_fn = shadow_copy2_chdir,
1555 .ntimes_fn = shadow_copy2_ntimes,
1556 .readlink_fn = shadow_copy2_readlink,
1557 .mknod_fn = shadow_copy2_mknod,
1558 .realpath_fn = shadow_copy2_realpath,
1559 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1560 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1561 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1562 .mkdir_fn = shadow_copy2_mkdir,
1563 .rmdir_fn = shadow_copy2_rmdir,
1564 .getxattr_fn = shadow_copy2_getxattr,
1565 .listxattr_fn = shadow_copy2_listxattr,
1566 .removexattr_fn = shadow_copy2_removexattr,
1567 .setxattr_fn = shadow_copy2_setxattr,
1568 .chmod_acl_fn = shadow_copy2_chmod_acl,
1569 .chflags_fn = shadow_copy2_chflags,
1570 .get_real_filename_fn = shadow_copy2_get_real_filename,
1573 NTSTATUS vfs_shadow_copy2_init(void);
1574 NTSTATUS vfs_shadow_copy2_init(void)
1576 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1577 "shadow_copy2", &vfs_shadow_copy2_fns);