s3-torture/denytest.c: replace cli_read_old() with cli_read()
[Samba/gebeck_regimport.git] / source3 / modules / vfs_shadow_copy2.c
blobca33b6d34442ff41a5e97638e70fa6bd251f6bb5
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:localtime = yes/no (default is no)
89 This is an optional parameter that indicates whether the
90 snapshot names are in UTC/GMT or the local time.
93 The following command would generate a correctly formatted directory name
94 for use with the default parameters:
95 date -u +@GMT-%Y.%m.%d-%H.%M.%S
98 #include "includes.h"
99 #include "system/filesys.h"
100 #include "include/ntioctl.h"
101 #include "smbd/proto.h"
102 #include <tdb.h>
103 #include "util_tdb.h"
105 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
106 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
108 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
109 size_t **poffsets,
110 unsigned *pnum_offsets)
112 unsigned num_offsets;
113 size_t *offsets;
114 const char *p;
116 num_offsets = 0;
118 p = str;
119 while ((p = strchr(p, '/')) != NULL) {
120 num_offsets += 1;
121 p += 1;
124 offsets = talloc_array(mem_ctx, size_t, num_offsets);
125 if (offsets == NULL) {
126 return false;
129 p = str;
130 num_offsets = 0;
131 while ((p = strchr(p, '/')) != NULL) {
132 offsets[num_offsets] = p-str;
133 num_offsets += 1;
134 p += 1;
137 *poffsets = offsets;
138 *pnum_offsets = num_offsets;
139 return true;
142 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
143 struct vfs_handle_struct *handle,
144 time_t snapshot)
146 struct tm snap_tm;
147 fstring gmt;
148 size_t gmt_len;
150 if (localtime_r(&snapshot, &snap_tm) == 0) {
151 DEBUG(10, ("gmtime_r failed\n"));
152 return NULL;
154 gmt_len = strftime(gmt, sizeof(gmt),
155 lp_parm_const_string(SNUM(handle->conn), "shadow",
156 "format", GMT_FORMAT),
157 &snap_tm);
158 if (gmt_len == 0) {
159 DEBUG(10, ("strftime failed\n"));
160 return NULL;
162 return talloc_asprintf(talloc_tos(), "/%s/%s",
163 lp_parm_const_string(
164 SNUM(handle->conn), "shadow", "snapdir",
165 ".snapshots"),
166 gmt);
169 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
170 struct vfs_handle_struct *handle,
171 const char *name,
172 time_t *ptimestamp,
173 char **pstripped)
175 struct tm tm;
176 time_t timestamp;
177 const char *p;
178 char *q;
179 char *stripped;
180 size_t rest_len, dst_len;
182 p = strstr_m(name, "@GMT-");
183 if (p == NULL) {
184 goto no_snapshot;
186 if ((p > name) && (p[-1] != '/')) {
187 goto no_snapshot;
189 q = strptime(p, GMT_FORMAT, &tm);
190 if (q == NULL) {
191 goto no_snapshot;
193 tm.tm_isdst = -1;
194 timestamp = mktime(&tm);
195 if (timestamp == (time_t)-1) {
196 goto no_snapshot;
198 if ((p == name) && (q[0] == '\0')) {
199 if (pstripped != NULL) {
200 stripped = talloc_strdup(mem_ctx, "");
201 if (stripped == NULL) {
202 return false;
204 *pstripped = stripped;
206 *ptimestamp = timestamp;
207 return true;
209 if (q[0] != '/') {
210 goto no_snapshot;
212 q += 1;
214 rest_len = strlen(q);
215 dst_len = (p-name) + rest_len;
217 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
218 false)) {
219 char *insert;
220 bool have_insert;
221 insert = shadow_copy2_insert_string(talloc_tos(), handle,
222 timestamp);
223 if (insert == NULL) {
224 errno = ENOMEM;
225 return false;
228 have_insert = (strstr(name, insert+1) != NULL);
229 TALLOC_FREE(insert);
230 if (have_insert) {
231 goto no_snapshot;
235 if (pstripped != NULL) {
236 stripped = talloc_array(mem_ctx, char, dst_len+1);
237 if (stripped == NULL) {
238 errno = ENOMEM;
239 return false;
241 if (p > name) {
242 memcpy(stripped, name, p-name);
244 if (rest_len > 0) {
245 memcpy(stripped + (p-name), q, rest_len);
247 stripped[dst_len] = '\0';
248 *pstripped = stripped;
250 *ptimestamp = timestamp;
251 return true;
252 no_snapshot:
253 *ptimestamp = 0;
254 return true;
257 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
258 vfs_handle_struct *handle)
260 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
261 dev_t dev;
262 struct stat st;
263 char *p;
265 if (stat(path, &st) != 0) {
266 talloc_free(path);
267 return NULL;
270 dev = st.st_dev;
272 while ((p = strrchr(path, '/')) && p > path) {
273 *p = 0;
274 if (stat(path, &st) != 0) {
275 talloc_free(path);
276 return NULL;
278 if (st.st_dev != dev) {
279 *p = '/';
280 break;
284 return path;
287 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
288 struct vfs_handle_struct *handle,
289 const char *name, time_t timestamp)
291 struct smb_filename converted_fname;
292 char *result = NULL;
293 size_t *slashes = NULL;
294 unsigned num_slashes;
295 char *path = NULL;
296 size_t pathlen;
297 char *insert = NULL;
298 char *converted = NULL;
299 size_t insertlen;
300 int i, saved_errno;
301 size_t min_offset;
303 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
304 name);
305 if (path == NULL) {
306 errno = ENOMEM;
307 goto fail;
309 pathlen = talloc_get_size(path)-1;
311 DEBUG(10, ("converting %s\n", path));
313 if (!shadow_copy2_find_slashes(talloc_tos(), path,
314 &slashes, &num_slashes)) {
315 goto fail;
317 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
318 if (insert == NULL) {
319 goto fail;
321 insertlen = talloc_get_size(insert)-1;
322 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
323 if (converted == NULL) {
324 goto fail;
327 if (path[pathlen-1] != '/') {
329 * Append a fake slash to find the snapshot root
331 size_t *tmp;
332 tmp = talloc_realloc(talloc_tos(), slashes,
333 size_t, num_slashes+1);
334 if (tmp == NULL) {
335 goto fail;
337 slashes = tmp;
338 slashes[num_slashes] = pathlen;
339 num_slashes += 1;
342 min_offset = 0;
344 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
345 false)) {
346 char *mount_point;
348 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
349 handle);
350 if (mount_point == NULL) {
351 goto fail;
353 min_offset = strlen(mount_point);
354 TALLOC_FREE(mount_point);
357 memcpy(converted, path, pathlen+1);
358 converted[pathlen+insertlen] = '\0';
360 ZERO_STRUCT(converted_fname);
361 converted_fname.base_name = converted;
363 for (i = num_slashes-1; i>=0; i--) {
364 int ret;
365 size_t offset;
367 offset = slashes[i];
369 if (offset < min_offset) {
370 errno = ENOENT;
371 goto fail;
374 memcpy(converted+offset, insert, insertlen);
376 offset += insertlen;
377 memcpy(converted+offset, path + slashes[i],
378 pathlen - slashes[i]);
380 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
382 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
383 ret, ret == 0 ? "ok" : strerror(errno)));
384 if (ret == 0) {
385 /* success */
386 break;
388 if (errno == ENOTDIR) {
390 * This is a valid condition: We appended the
391 * .snaphots/@GMT.. to a file name. Just try
392 * with the upper levels.
394 continue;
396 if (errno != ENOENT) {
397 /* Other problem than "not found" */
398 goto fail;
402 if (i >= 0) {
404 * Found something
406 DEBUG(10, ("Found %s\n", converted));
407 result = converted;
408 converted = NULL;
409 } else {
410 errno = ENOENT;
412 fail:
413 saved_errno = errno;
414 TALLOC_FREE(converted);
415 TALLOC_FREE(insert);
416 TALLOC_FREE(slashes);
417 TALLOC_FREE(path);
418 errno = saved_errno;
419 return result;
423 modify a sbuf return to ensure that inodes in the shadow directory
424 are different from those in the main directory
426 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
427 SMB_STRUCT_STAT *sbuf)
429 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
430 /* some snapshot systems, like GPFS, return the name
431 device:inode for the snapshot files as the current
432 files. That breaks the 'restore' button in the shadow copy
433 GUI, as the client gets a sharing violation.
435 This is a crude way of allowing both files to be
436 open at once. It has a slight chance of inode
437 number collision, but I can't see a better approach
438 without significant VFS changes
440 uint32_t shash;
441 TDB_DATA data = string_tdb_data(fname);
443 shash = tdb_jenkins_hash(&data) & 0xFF000000;
444 if (shash == 0) {
445 shash = 1;
447 sbuf->st_ex_ino ^= shash;
451 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
452 const char *fname,
453 const char *mask,
454 uint32 attr)
456 time_t timestamp;
457 char *stripped;
458 SMB_STRUCT_DIR *ret;
459 int saved_errno;
460 char *conv;
462 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
463 &timestamp, &stripped)) {
464 return NULL;
466 if (timestamp == 0) {
467 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
469 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
470 TALLOC_FREE(stripped);
471 if (conv == NULL) {
472 return NULL;
474 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
475 saved_errno = errno;
476 TALLOC_FREE(conv);
477 errno = saved_errno;
478 return ret;
481 static int shadow_copy2_rename(vfs_handle_struct *handle,
482 const struct smb_filename *smb_fname_src,
483 const struct smb_filename *smb_fname_dst)
485 time_t timestamp_src, timestamp_dst;
487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
488 smb_fname_src->base_name,
489 &timestamp_src, NULL)) {
490 return -1;
492 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
493 smb_fname_dst->base_name,
494 &timestamp_dst, NULL)) {
495 return -1;
497 if (timestamp_src != 0) {
498 errno = EXDEV;
499 return -1;
501 if (timestamp_dst != 0) {
502 errno = EROFS;
503 return -1;
505 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
508 static int shadow_copy2_symlink(vfs_handle_struct *handle,
509 const char *oldname, const char *newname)
511 time_t timestamp_old, timestamp_new;
513 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
514 &timestamp_old, NULL)) {
515 return -1;
517 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
518 &timestamp_new, NULL)) {
519 return -1;
521 if ((timestamp_old != 0) || (timestamp_new != 0)) {
522 errno = EROFS;
523 return -1;
525 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
528 static int shadow_copy2_link(vfs_handle_struct *handle,
529 const char *oldname, const char *newname)
531 time_t timestamp_old, timestamp_new;
533 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
534 &timestamp_old, NULL)) {
535 return -1;
537 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
538 &timestamp_new, NULL)) {
539 return -1;
541 if ((timestamp_old != 0) || (timestamp_new != 0)) {
542 errno = EROFS;
543 return -1;
545 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
548 static int shadow_copy2_stat(vfs_handle_struct *handle,
549 struct smb_filename *smb_fname)
551 time_t timestamp;
552 char *stripped, *tmp;
553 int ret, saved_errno;
555 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
556 smb_fname->base_name,
557 &timestamp, &stripped)) {
558 return -1;
560 if (timestamp == 0) {
561 return SMB_VFS_NEXT_STAT(handle, smb_fname);
564 tmp = smb_fname->base_name;
565 smb_fname->base_name = shadow_copy2_convert(
566 talloc_tos(), handle, stripped, timestamp);
567 TALLOC_FREE(stripped);
569 if (smb_fname->base_name == NULL) {
570 smb_fname->base_name = tmp;
571 return -1;
574 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
575 saved_errno = errno;
577 TALLOC_FREE(smb_fname->base_name);
578 smb_fname->base_name = tmp;
580 if (ret == 0) {
581 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
583 errno = saved_errno;
584 return ret;
587 static int shadow_copy2_lstat(vfs_handle_struct *handle,
588 struct smb_filename *smb_fname)
590 time_t timestamp;
591 char *stripped, *tmp;
592 int ret, saved_errno;
594 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
595 smb_fname->base_name,
596 &timestamp, &stripped)) {
597 return -1;
599 if (timestamp == 0) {
600 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
603 tmp = smb_fname->base_name;
604 smb_fname->base_name = shadow_copy2_convert(
605 talloc_tos(), handle, stripped, timestamp);
606 TALLOC_FREE(stripped);
608 if (smb_fname->base_name == NULL) {
609 smb_fname->base_name = tmp;
610 return -1;
613 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
614 saved_errno = errno;
616 TALLOC_FREE(smb_fname->base_name);
617 smb_fname->base_name = tmp;
619 if (ret == 0) {
620 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
622 errno = saved_errno;
623 return ret;
626 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
627 SMB_STRUCT_STAT *sbuf)
629 time_t timestamp;
630 int ret;
632 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
633 if (ret == -1) {
634 return ret;
636 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
637 fsp->fsp_name->base_name,
638 &timestamp, NULL)) {
639 return 0;
641 if (timestamp != 0) {
642 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
644 return 0;
647 static int shadow_copy2_open(vfs_handle_struct *handle,
648 struct smb_filename *smb_fname, files_struct *fsp,
649 int flags, mode_t mode)
651 time_t timestamp;
652 char *stripped, *tmp;
653 int ret, saved_errno;
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
656 smb_fname->base_name,
657 &timestamp, &stripped)) {
658 return -1;
660 if (timestamp == 0) {
661 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
664 tmp = smb_fname->base_name;
665 smb_fname->base_name = shadow_copy2_convert(
666 talloc_tos(), handle, stripped, timestamp);
667 TALLOC_FREE(stripped);
669 if (smb_fname->base_name == NULL) {
670 smb_fname->base_name = tmp;
671 return -1;
674 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
675 saved_errno = errno;
677 TALLOC_FREE(smb_fname->base_name);
678 smb_fname->base_name = tmp;
680 errno = saved_errno;
681 return ret;
684 static int shadow_copy2_unlink(vfs_handle_struct *handle,
685 const struct smb_filename *smb_fname)
687 time_t timestamp;
688 char *stripped;
689 int ret, saved_errno;
690 struct smb_filename *conv;
691 NTSTATUS status;
693 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
694 smb_fname->base_name,
695 &timestamp, &stripped)) {
696 return -1;
698 if (timestamp == 0) {
699 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
701 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
702 if (!NT_STATUS_IS_OK(status)) {
703 errno = ENOMEM;
704 return -1;
706 conv->base_name = shadow_copy2_convert(
707 conv, handle, stripped, timestamp);
708 TALLOC_FREE(stripped);
709 if (conv->base_name == NULL) {
710 return -1;
712 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
713 saved_errno = errno;
714 TALLOC_FREE(conv);
715 errno = saved_errno;
716 return ret;
719 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
720 mode_t mode)
722 time_t timestamp;
723 char *stripped;
724 int ret, saved_errno;
725 char *conv;
727 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
728 &timestamp, &stripped)) {
729 return -1;
731 if (timestamp == 0) {
732 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
734 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
735 TALLOC_FREE(stripped);
736 if (conv == NULL) {
737 return -1;
739 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
740 saved_errno = errno;
741 TALLOC_FREE(conv);
742 errno = saved_errno;
743 return ret;
746 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
747 uid_t uid, gid_t gid)
749 time_t timestamp;
750 char *stripped;
751 int ret, saved_errno;
752 char *conv;
754 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
755 &timestamp, &stripped)) {
756 return -1;
758 if (timestamp == 0) {
759 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
761 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
762 TALLOC_FREE(stripped);
763 if (conv == NULL) {
764 return -1;
766 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
767 saved_errno = errno;
768 TALLOC_FREE(conv);
769 errno = saved_errno;
770 return ret;
773 static int shadow_copy2_chdir(vfs_handle_struct *handle,
774 const char *fname)
776 time_t timestamp;
777 char *stripped;
778 int ret, saved_errno;
779 char *conv;
781 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
782 &timestamp, &stripped)) {
783 return -1;
785 if (timestamp == 0) {
786 return SMB_VFS_NEXT_CHDIR(handle, fname);
788 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
789 TALLOC_FREE(stripped);
790 if (conv == NULL) {
791 return -1;
793 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
794 saved_errno = errno;
795 TALLOC_FREE(conv);
796 errno = saved_errno;
797 return ret;
800 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
801 const struct smb_filename *smb_fname,
802 struct smb_file_time *ft)
804 time_t timestamp;
805 char *stripped;
806 int ret, saved_errno;
807 struct smb_filename *conv;
808 NTSTATUS status;
810 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
811 smb_fname->base_name,
812 &timestamp, &stripped)) {
813 return -1;
815 if (timestamp == 0) {
816 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
818 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
819 if (!NT_STATUS_IS_OK(status)) {
820 errno = ENOMEM;
821 return -1;
823 conv->base_name = shadow_copy2_convert(
824 conv, handle, stripped, timestamp);
825 TALLOC_FREE(stripped);
826 if (conv->base_name == NULL) {
827 return -1;
829 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
830 saved_errno = errno;
831 TALLOC_FREE(conv);
832 errno = saved_errno;
833 return ret;
836 static int shadow_copy2_readlink(vfs_handle_struct *handle,
837 const char *fname, char *buf, size_t bufsiz)
839 time_t timestamp;
840 char *stripped;
841 int ret, saved_errno;
842 char *conv;
844 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
845 &timestamp, &stripped)) {
846 return -1;
848 if (timestamp == 0) {
849 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
851 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
852 TALLOC_FREE(stripped);
853 if (conv == NULL) {
854 return -1;
856 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
857 saved_errno = errno;
858 TALLOC_FREE(conv);
859 errno = saved_errno;
860 return ret;
863 static int shadow_copy2_mknod(vfs_handle_struct *handle,
864 const char *fname, mode_t mode, SMB_DEV_T dev)
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_MKNOD(handle, fname, mode, dev);
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_MKNOD(handle, conv, mode, dev);
884 saved_errno = errno;
885 TALLOC_FREE(conv);
886 errno = saved_errno;
887 return ret;
890 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
891 const char *fname)
893 time_t timestamp;
894 char *stripped = NULL;
895 char *tmp = NULL;
896 char *result = NULL;
897 char *inserted = NULL;
898 char *inserted_to, *inserted_end;
899 int saved_errno;
901 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
902 &timestamp, &stripped)) {
903 goto done;
905 if (timestamp == 0) {
906 return SMB_VFS_NEXT_REALPATH(handle, fname);
909 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
910 if (tmp == NULL) {
911 goto done;
914 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
915 if (result == NULL) {
916 goto done;
920 * Take away what we've inserted. This removes the @GMT-thingy
921 * completely, but will give a path under the share root.
923 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
924 if (inserted == NULL) {
925 goto done;
927 inserted_to = strstr_m(result, inserted);
928 if (inserted_to == NULL) {
929 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
930 goto done;
932 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
933 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
935 done:
936 saved_errno = errno;
937 TALLOC_FREE(inserted);
938 TALLOC_FREE(tmp);
939 TALLOC_FREE(stripped);
940 errno = saved_errno;
941 return result;
944 static char *have_snapdir(struct vfs_handle_struct *handle,
945 const char *path)
947 struct smb_filename smb_fname;
948 int ret;
950 ZERO_STRUCT(smb_fname);
951 smb_fname.base_name = talloc_asprintf(
952 talloc_tos(), "%s/%s", path,
953 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
954 ".snapshots"));
955 if (smb_fname.base_name == NULL) {
956 return NULL;
959 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
960 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
961 return smb_fname.base_name;
963 TALLOC_FREE(smb_fname.base_name);
964 return NULL;
967 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
968 struct vfs_handle_struct *handle,
969 struct smb_filename *smb_fname)
971 char *path, *p;
972 char *snapdir;
974 path = talloc_asprintf(mem_ctx, "%s/%s",
975 handle->conn->connectpath,
976 smb_fname->base_name);
977 if (path == NULL) {
978 return NULL;
981 snapdir = have_snapdir(handle, path);
982 if (snapdir != NULL) {
983 TALLOC_FREE(path);
984 return snapdir;
987 while ((p = strrchr(path, '/')) && (p > path)) {
989 p[0] = '\0';
991 snapdir = have_snapdir(handle, path);
992 if (snapdir != NULL) {
993 TALLOC_FREE(path);
994 return snapdir;
997 TALLOC_FREE(path);
998 return NULL;
1001 static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
1002 vfs_handle_struct *handle,
1003 const char *name,
1004 char *gmt, size_t gmt_len)
1006 struct tm timestamp;
1007 time_t timestamp_t;
1008 const char *fmt;
1010 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1011 "format", GMT_FORMAT);
1013 ZERO_STRUCT(timestamp);
1014 if (strptime(name, fmt, &timestamp) == NULL) {
1015 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1016 fmt, name));
1017 return false;
1020 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1022 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1023 timestamp.tm_isdst = -1;
1024 timestamp_t = mktime(&timestamp);
1025 gmtime_r(&timestamp_t, &timestamp);
1027 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1028 return true;
1031 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1033 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1036 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1038 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1042 sort the shadow copy data in ascending or descending order
1044 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1045 struct shadow_copy_data *shadow_copy2_data)
1047 int (*cmpfunc)(const void *, const void *);
1048 const char *sort;
1050 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1051 "sort", NULL);
1052 if (sort == NULL) {
1053 return;
1056 if (strcmp(sort, "asc") == 0) {
1057 cmpfunc = shadow_copy2_label_cmp_asc;
1058 } else if (strcmp(sort, "desc") == 0) {
1059 cmpfunc = shadow_copy2_label_cmp_desc;
1060 } else {
1061 return;
1064 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1065 shadow_copy2_data->labels)
1067 TYPESAFE_QSORT(shadow_copy2_data->labels,
1068 shadow_copy2_data->num_volumes,
1069 cmpfunc);
1072 return;
1075 static int shadow_copy2_get_shadow_copy_data(
1076 vfs_handle_struct *handle, files_struct *fsp,
1077 struct shadow_copy_data *shadow_copy2_data,
1078 bool labels)
1080 SMB_STRUCT_DIR *p;
1081 const char *snapdir;
1082 SMB_STRUCT_DIRENT *d;
1083 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1085 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1086 if (snapdir == NULL) {
1087 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1088 handle->conn->connectpath));
1089 errno = EINVAL;
1090 talloc_free(tmp_ctx);
1091 return -1;
1094 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1096 if (!p) {
1097 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1098 " - %s\n", snapdir, strerror(errno)));
1099 talloc_free(tmp_ctx);
1100 errno = ENOSYS;
1101 return -1;
1104 shadow_copy2_data->num_volumes = 0;
1105 shadow_copy2_data->labels = NULL;
1107 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1108 char snapshot[GMT_NAME_LEN+1];
1109 SHADOW_COPY_LABEL *tlabels;
1112 * ignore names not of the right form in the snapshot
1113 * directory
1115 if (!shadow_copy2_snapshot_to_gmt(
1116 tmp_ctx, handle, d->d_name,
1117 snapshot, sizeof(snapshot))) {
1119 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1120 "ignoring %s\n", d->d_name));
1121 continue;
1123 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1124 d->d_name, snapshot));
1126 if (!labels) {
1127 /* the caller doesn't want the labels */
1128 shadow_copy2_data->num_volumes++;
1129 continue;
1132 tlabels = talloc_realloc(shadow_copy2_data,
1133 shadow_copy2_data->labels,
1134 SHADOW_COPY_LABEL,
1135 shadow_copy2_data->num_volumes+1);
1136 if (tlabels == NULL) {
1137 DEBUG(0,("shadow_copy2: out of memory\n"));
1138 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1139 talloc_free(tmp_ctx);
1140 return -1;
1143 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1144 sizeof(*tlabels));
1146 shadow_copy2_data->num_volumes++;
1147 shadow_copy2_data->labels = tlabels;
1150 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1152 shadow_copy2_sort_data(handle, shadow_copy2_data);
1154 talloc_free(tmp_ctx);
1155 return 0;
1158 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1159 struct files_struct *fsp,
1160 uint32 security_info,
1161 struct security_descriptor **ppdesc)
1163 time_t timestamp;
1164 char *stripped;
1165 NTSTATUS status;
1166 char *conv;
1168 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1169 fsp->fsp_name->base_name,
1170 &timestamp, &stripped)) {
1171 return map_nt_error_from_unix(errno);
1173 if (timestamp == 0) {
1174 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1175 ppdesc);
1177 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1178 TALLOC_FREE(stripped);
1179 if (conv == NULL) {
1180 return map_nt_error_from_unix(errno);
1182 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1183 TALLOC_FREE(conv);
1184 return status;
1187 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1188 const char *fname,
1189 uint32 security_info,
1190 struct security_descriptor **ppdesc)
1192 time_t timestamp;
1193 char *stripped;
1194 NTSTATUS status;
1195 char *conv;
1197 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1198 &timestamp, &stripped)) {
1199 return map_nt_error_from_unix(errno);
1201 if (timestamp == 0) {
1202 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1203 ppdesc);
1205 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1206 TALLOC_FREE(stripped);
1207 if (conv == NULL) {
1208 return map_nt_error_from_unix(errno);
1210 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1211 TALLOC_FREE(conv);
1212 return status;
1215 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1216 const char *fname, mode_t mode)
1218 time_t timestamp;
1219 char *stripped;
1220 int ret, saved_errno;
1221 char *conv;
1223 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1224 &timestamp, &stripped)) {
1225 return -1;
1227 if (timestamp == 0) {
1228 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1230 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1231 TALLOC_FREE(stripped);
1232 if (conv == NULL) {
1233 return -1;
1235 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1236 saved_errno = errno;
1237 TALLOC_FREE(conv);
1238 errno = saved_errno;
1239 return ret;
1242 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1244 time_t timestamp;
1245 char *stripped;
1246 int ret, saved_errno;
1247 char *conv;
1249 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1250 &timestamp, &stripped)) {
1251 return -1;
1253 if (timestamp == 0) {
1254 return SMB_VFS_NEXT_RMDIR(handle, fname);
1256 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1257 TALLOC_FREE(stripped);
1258 if (conv == NULL) {
1259 return -1;
1261 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1262 saved_errno = errno;
1263 TALLOC_FREE(conv);
1264 errno = saved_errno;
1265 return ret;
1268 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1269 unsigned int flags)
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_CHFLAGS(handle, fname, flags);
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_CHFLAGS(handle, conv, flags);
1289 saved_errno = errno;
1290 TALLOC_FREE(conv);
1291 errno = saved_errno;
1292 return ret;
1295 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1296 const char *fname, const char *aname,
1297 void *value, size_t size)
1299 time_t timestamp;
1300 char *stripped;
1301 ssize_t ret;
1302 int saved_errno;
1303 char *conv;
1305 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1306 &timestamp, &stripped)) {
1307 return -1;
1309 if (timestamp == 0) {
1310 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1311 size);
1313 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1314 TALLOC_FREE(stripped);
1315 if (conv == NULL) {
1316 return -1;
1318 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1319 saved_errno = errno;
1320 TALLOC_FREE(conv);
1321 errno = saved_errno;
1322 return ret;
1325 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
1326 const char *fname, const char *aname,
1327 void *value, size_t size)
1329 time_t timestamp;
1330 char *stripped;
1331 ssize_t ret;
1332 int saved_errno;
1333 char *conv;
1335 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1336 &timestamp, &stripped)) {
1337 return -1;
1339 if (timestamp == 0) {
1340 return SMB_VFS_NEXT_LGETXATTR(handle, fname, aname, value,
1341 size);
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_LGETXATTR(handle, conv, aname, value, size);
1349 saved_errno = errno;
1350 TALLOC_FREE(conv);
1351 errno = saved_errno;
1352 return ret;
1355 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1356 const char *fname,
1357 char *list, 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_LISTXATTR(handle, fname, list, size);
1372 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1373 TALLOC_FREE(stripped);
1374 if (conv == NULL) {
1375 return -1;
1377 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1378 saved_errno = errno;
1379 TALLOC_FREE(conv);
1380 errno = saved_errno;
1381 return ret;
1384 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1385 const char *fname, const char *aname)
1387 time_t timestamp;
1388 char *stripped;
1389 int ret, saved_errno;
1390 char *conv;
1392 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1393 &timestamp, &stripped)) {
1394 return -1;
1396 if (timestamp == 0) {
1397 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1399 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1400 TALLOC_FREE(stripped);
1401 if (conv == NULL) {
1402 return -1;
1404 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1405 saved_errno = errno;
1406 TALLOC_FREE(conv);
1407 errno = saved_errno;
1408 return ret;
1411 static int shadow_copy2_lremovexattr(vfs_handle_struct *handle,
1412 const char *fname, const char *aname)
1414 time_t timestamp;
1415 char *stripped;
1416 int ret, saved_errno;
1417 char *conv;
1419 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1420 &timestamp, &stripped)) {
1421 return -1;
1423 if (timestamp == 0) {
1424 return SMB_VFS_NEXT_LREMOVEXATTR(handle, fname, aname);
1426 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1427 TALLOC_FREE(stripped);
1428 if (conv == NULL) {
1429 return -1;
1431 ret = SMB_VFS_NEXT_LREMOVEXATTR(handle, conv, aname);
1432 saved_errno = errno;
1433 TALLOC_FREE(conv);
1434 errno = saved_errno;
1435 return ret;
1438 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1439 const char *fname,
1440 const char *aname, const void *value,
1441 size_t size, int flags)
1443 time_t timestamp;
1444 char *stripped;
1445 ssize_t ret;
1446 int saved_errno;
1447 char *conv;
1449 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1450 &timestamp, &stripped)) {
1451 return -1;
1453 if (timestamp == 0) {
1454 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1455 flags);
1457 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1458 TALLOC_FREE(stripped);
1459 if (conv == NULL) {
1460 return -1;
1462 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1463 saved_errno = errno;
1464 TALLOC_FREE(conv);
1465 errno = saved_errno;
1466 return ret;
1469 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle,
1470 const char *fname,
1471 const char *aname, const void *value,
1472 size_t size, int flags)
1474 time_t timestamp;
1475 char *stripped;
1476 ssize_t ret;
1477 int saved_errno;
1478 char *conv;
1480 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1481 &timestamp, &stripped)) {
1482 return -1;
1484 if (timestamp == 0) {
1485 return SMB_VFS_NEXT_LSETXATTR(handle, fname, aname, value,
1486 size, flags);
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_LSETXATTR(handle, conv, aname, value, size, flags);
1494 saved_errno = errno;
1495 TALLOC_FREE(conv);
1496 errno = saved_errno;
1497 return ret;
1500 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1501 const char *fname, mode_t mode)
1503 time_t timestamp;
1504 char *stripped;
1505 ssize_t ret;
1506 int saved_errno;
1507 char *conv;
1509 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1510 &timestamp, &stripped)) {
1511 return -1;
1513 if (timestamp == 0) {
1514 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1516 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1517 TALLOC_FREE(stripped);
1518 if (conv == NULL) {
1519 return -1;
1521 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1522 saved_errno = errno;
1523 TALLOC_FREE(conv);
1524 errno = saved_errno;
1525 return ret;
1528 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1529 const char *path,
1530 const char *name,
1531 TALLOC_CTX *mem_ctx,
1532 char **found_name)
1534 time_t timestamp;
1535 char *stripped;
1536 ssize_t ret;
1537 int saved_errno;
1538 char *conv;
1540 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1541 &timestamp, &stripped)) {
1542 return -1;
1544 if (timestamp == 0) {
1545 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1546 mem_ctx, found_name);
1548 if (stripped[0] == '\0') {
1549 *found_name = talloc_strdup(mem_ctx, name);
1550 if (*found_name == NULL) {
1551 errno = ENOMEM;
1552 return -1;
1554 return 0;
1556 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1557 TALLOC_FREE(stripped);
1558 if (conv == NULL) {
1559 return -1;
1561 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1562 mem_ctx, found_name);
1563 saved_errno = errno;
1564 TALLOC_FREE(conv);
1565 errno = saved_errno;
1566 return ret;
1570 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1571 .opendir = shadow_copy2_opendir,
1572 .rename = shadow_copy2_rename,
1573 .link = shadow_copy2_link,
1574 .symlink = shadow_copy2_symlink,
1575 .stat = shadow_copy2_stat,
1576 .lstat = shadow_copy2_lstat,
1577 .fstat = shadow_copy2_fstat,
1578 .open_fn = shadow_copy2_open,
1579 .unlink = shadow_copy2_unlink,
1580 .chmod = shadow_copy2_chmod,
1581 .chown = shadow_copy2_chown,
1582 .chdir = shadow_copy2_chdir,
1583 .ntimes = shadow_copy2_ntimes,
1584 .vfs_readlink = shadow_copy2_readlink,
1585 .mknod = shadow_copy2_mknod,
1586 .realpath = shadow_copy2_realpath,
1587 .get_nt_acl = shadow_copy2_get_nt_acl,
1588 .fget_nt_acl = shadow_copy2_fget_nt_acl,
1589 .get_shadow_copy_data = shadow_copy2_get_shadow_copy_data,
1590 .mkdir = shadow_copy2_mkdir,
1591 .rmdir = shadow_copy2_rmdir,
1592 .getxattr = shadow_copy2_getxattr,
1593 .lgetxattr = shadow_copy2_lgetxattr,
1594 .listxattr = shadow_copy2_listxattr,
1595 .removexattr = shadow_copy2_removexattr,
1596 .lremovexattr = shadow_copy2_lremovexattr,
1597 .setxattr = shadow_copy2_setxattr,
1598 .lsetxattr = shadow_copy2_lsetxattr,
1599 .chmod_acl = shadow_copy2_chmod_acl,
1600 .chflags = shadow_copy2_chflags,
1601 .get_real_filename = shadow_copy2_get_real_filename,
1604 NTSTATUS vfs_shadow_copy2_init(void);
1605 NTSTATUS vfs_shadow_copy2_init(void)
1607 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1608 "shadow_copy2", &vfs_shadow_copy2_fns);