shadow_copy2: introduce config struct and function shadow_copy2_connect()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobd5b16536c33ecc215a35268920f079eda2697c6f
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 struct shadow_copy2_config {
111 char *gmt_format;
112 bool use_sscanf;
113 bool use_localtime;
114 char *snapdir;
115 bool snapdirseverywhere;
116 bool crossmountpoints;
117 bool fixinodes;
118 char *sort_order;
119 bool snapdir_absolute;
122 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
123 size_t **poffsets,
124 unsigned *pnum_offsets)
126 unsigned num_offsets;
127 size_t *offsets;
128 const char *p;
130 num_offsets = 0;
132 p = str;
133 while ((p = strchr(p, '/')) != NULL) {
134 num_offsets += 1;
135 p += 1;
138 offsets = talloc_array(mem_ctx, size_t, num_offsets);
139 if (offsets == NULL) {
140 return false;
143 p = str;
144 num_offsets = 0;
145 while ((p = strchr(p, '/')) != NULL) {
146 offsets[num_offsets] = p-str;
147 num_offsets += 1;
148 p += 1;
151 *poffsets = offsets;
152 *pnum_offsets = num_offsets;
153 return true;
157 * Given a timstamp, build the string to insert into a path
158 * as a path component for creating the local path to the
159 * snapshot at the given timestamp of the input path.
161 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
162 struct vfs_handle_struct *handle,
163 time_t snapshot)
165 struct tm snap_tm;
166 fstring snaptime_string;
167 size_t snaptime_len;
168 struct shadow_copy2_config *config;
170 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
171 return NULL);
173 if (config->use_sscanf) {
174 snaptime_len = snprintf(snaptime_string,
175 sizeof(snaptime_string),
176 config->gmt_format,
177 (unsigned long)snapshot);
178 if (snaptime_len <= 0) {
179 DEBUG(10, ("snprintf failed\n"));
180 return NULL;
182 } else {
183 if (config->use_localtime) {
184 if (localtime_r(&snapshot, &snap_tm) == 0) {
185 DEBUG(10, ("gmtime_r failed\n"));
186 return NULL;
188 } else {
189 if (gmtime_r(&snapshot, &snap_tm) == 0) {
190 DEBUG(10, ("gmtime_r failed\n"));
191 return NULL;
194 snaptime_len = strftime(snaptime_string,
195 sizeof(snaptime_string),
196 config->gmt_format,
197 &snap_tm);
198 if (snaptime_len == 0) {
199 DEBUG(10, ("strftime failed\n"));
200 return NULL;
203 return talloc_asprintf(mem_ctx, "/%s/%s",
204 config->snapdir, snaptime_string);
208 * Strip a snapshot component from an filename as
209 * handed in via the smb layer.
210 * Returns the parsed timestamp and the stripped filename.
212 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
213 struct vfs_handle_struct *handle,
214 const char *name,
215 time_t *ptimestamp,
216 char **pstripped)
218 struct tm tm;
219 time_t timestamp;
220 const char *p;
221 char *q;
222 char *stripped;
223 size_t rest_len, dst_len;
224 struct shadow_copy2_config *config;
226 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
227 return false);
229 p = strstr_m(name, "@GMT-");
230 if (p == NULL) {
231 goto no_snapshot;
233 if ((p > name) && (p[-1] != '/')) {
234 goto no_snapshot;
236 q = strptime(p, GMT_FORMAT, &tm);
237 if (q == NULL) {
238 goto no_snapshot;
240 tm.tm_isdst = -1;
241 timestamp = timegm(&tm);
242 if (timestamp == (time_t)-1) {
243 goto no_snapshot;
245 if ((p == name) && (q[0] == '\0')) {
246 if (pstripped != NULL) {
247 stripped = talloc_strdup(mem_ctx, "");
248 if (stripped == NULL) {
249 return false;
251 *pstripped = stripped;
253 *ptimestamp = timestamp;
254 return true;
256 if (q[0] != '/') {
257 goto no_snapshot;
259 q += 1;
261 rest_len = strlen(q);
262 dst_len = (p-name) + rest_len;
264 if (config->snapdirseverywhere) {
265 char *insert;
266 bool have_insert;
267 insert = shadow_copy2_insert_string(talloc_tos(), handle,
268 timestamp);
269 if (insert == NULL) {
270 errno = ENOMEM;
271 return false;
274 have_insert = (strstr(name, insert+1) != NULL);
275 TALLOC_FREE(insert);
276 if (have_insert) {
277 goto no_snapshot;
281 if (pstripped != NULL) {
282 stripped = talloc_array(mem_ctx, char, dst_len+1);
283 if (stripped == NULL) {
284 errno = ENOMEM;
285 return false;
287 if (p > name) {
288 memcpy(stripped, name, p-name);
290 if (rest_len > 0) {
291 memcpy(stripped + (p-name), q, rest_len);
293 stripped[dst_len] = '\0';
294 *pstripped = stripped;
296 *ptimestamp = timestamp;
297 return true;
298 no_snapshot:
299 *ptimestamp = 0;
300 return true;
303 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
304 vfs_handle_struct *handle)
306 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
307 dev_t dev;
308 struct stat st;
309 char *p;
311 if (stat(path, &st) != 0) {
312 talloc_free(path);
313 return NULL;
316 dev = st.st_dev;
318 while ((p = strrchr(path, '/')) && p > path) {
319 *p = 0;
320 if (stat(path, &st) != 0) {
321 talloc_free(path);
322 return NULL;
324 if (st.st_dev != dev) {
325 *p = '/';
326 break;
330 return path;
334 * Convert from a name as handed in via the SMB layer
335 * and a timestamp into the local path of the snapshot
336 * of the provided file at the provided time.
338 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
339 struct vfs_handle_struct *handle,
340 const char *name, time_t timestamp)
342 struct smb_filename converted_fname;
343 char *result = NULL;
344 size_t *slashes = NULL;
345 unsigned num_slashes;
346 char *path = NULL;
347 size_t pathlen;
348 char *insert = NULL;
349 char *converted = NULL;
350 size_t insertlen;
351 int i, saved_errno;
352 size_t min_offset;
353 struct shadow_copy2_config *config;
355 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
356 return NULL);
358 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
359 name);
360 if (path == NULL) {
361 errno = ENOMEM;
362 goto fail;
364 pathlen = talloc_get_size(path)-1;
366 DEBUG(10, ("converting %s\n", path));
368 if (!shadow_copy2_find_slashes(talloc_tos(), path,
369 &slashes, &num_slashes)) {
370 goto fail;
372 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
373 if (insert == NULL) {
374 goto fail;
376 insertlen = talloc_get_size(insert)-1;
377 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
378 if (converted == NULL) {
379 goto fail;
382 if (path[pathlen-1] != '/') {
384 * Append a fake slash to find the snapshot root
386 size_t *tmp;
387 tmp = talloc_realloc(talloc_tos(), slashes,
388 size_t, num_slashes+1);
389 if (tmp == NULL) {
390 goto fail;
392 slashes = tmp;
393 slashes[num_slashes] = pathlen;
394 num_slashes += 1;
397 min_offset = 0;
399 if (!config->crossmountpoints) {
400 char *mount_point;
402 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
403 handle);
404 if (mount_point == NULL) {
405 goto fail;
407 min_offset = strlen(mount_point);
408 TALLOC_FREE(mount_point);
411 memcpy(converted, path, pathlen+1);
412 converted[pathlen+insertlen] = '\0';
414 ZERO_STRUCT(converted_fname);
415 converted_fname.base_name = converted;
417 for (i = num_slashes-1; i>=0; i--) {
418 int ret;
419 size_t offset;
421 offset = slashes[i];
423 if (offset < min_offset) {
424 errno = ENOENT;
425 goto fail;
428 memcpy(converted+offset, insert, insertlen);
430 offset += insertlen;
431 memcpy(converted+offset, path + slashes[i],
432 pathlen - slashes[i]);
434 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
436 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
437 ret, ret == 0 ? "ok" : strerror(errno)));
438 if (ret == 0) {
439 /* success */
440 break;
442 if (errno == ENOTDIR) {
444 * This is a valid condition: We appended the
445 * .snaphots/@GMT.. to a file name. Just try
446 * with the upper levels.
448 continue;
450 if (errno != ENOENT) {
451 /* Other problem than "not found" */
452 goto fail;
456 if (i >= 0) {
458 * Found something
460 DEBUG(10, ("Found %s\n", converted));
461 result = converted;
462 converted = NULL;
463 } else {
464 errno = ENOENT;
466 fail:
467 saved_errno = errno;
468 TALLOC_FREE(converted);
469 TALLOC_FREE(insert);
470 TALLOC_FREE(slashes);
471 TALLOC_FREE(path);
472 errno = saved_errno;
473 return result;
477 modify a sbuf return to ensure that inodes in the shadow directory
478 are different from those in the main directory
480 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
481 SMB_STRUCT_STAT *sbuf)
483 struct shadow_copy2_config *config;
485 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
486 return);
488 if (config->fixinodes) {
489 /* some snapshot systems, like GPFS, return the name
490 device:inode for the snapshot files as the current
491 files. That breaks the 'restore' button in the shadow copy
492 GUI, as the client gets a sharing violation.
494 This is a crude way of allowing both files to be
495 open at once. It has a slight chance of inode
496 number collision, but I can't see a better approach
497 without significant VFS changes
499 uint32_t shash;
501 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
502 if (shash == 0) {
503 shash = 1;
505 sbuf->st_ex_ino ^= shash;
509 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
510 const char *fname,
511 const char *mask,
512 uint32 attr)
514 time_t timestamp;
515 char *stripped;
516 DIR *ret;
517 int saved_errno;
518 char *conv;
520 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
521 &timestamp, &stripped)) {
522 return NULL;
524 if (timestamp == 0) {
525 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
527 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
528 TALLOC_FREE(stripped);
529 if (conv == NULL) {
530 return NULL;
532 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
533 saved_errno = errno;
534 TALLOC_FREE(conv);
535 errno = saved_errno;
536 return ret;
539 static int shadow_copy2_rename(vfs_handle_struct *handle,
540 const struct smb_filename *smb_fname_src,
541 const struct smb_filename *smb_fname_dst)
543 time_t timestamp_src, timestamp_dst;
545 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
546 smb_fname_src->base_name,
547 &timestamp_src, NULL)) {
548 return -1;
550 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
551 smb_fname_dst->base_name,
552 &timestamp_dst, NULL)) {
553 return -1;
555 if (timestamp_src != 0) {
556 errno = EXDEV;
557 return -1;
559 if (timestamp_dst != 0) {
560 errno = EROFS;
561 return -1;
563 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
566 static int shadow_copy2_symlink(vfs_handle_struct *handle,
567 const char *oldname, const char *newname)
569 time_t timestamp_old, timestamp_new;
571 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
572 &timestamp_old, NULL)) {
573 return -1;
575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
576 &timestamp_new, NULL)) {
577 return -1;
579 if ((timestamp_old != 0) || (timestamp_new != 0)) {
580 errno = EROFS;
581 return -1;
583 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
586 static int shadow_copy2_link(vfs_handle_struct *handle,
587 const char *oldname, const char *newname)
589 time_t timestamp_old, timestamp_new;
591 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
592 &timestamp_old, NULL)) {
593 return -1;
595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
596 &timestamp_new, NULL)) {
597 return -1;
599 if ((timestamp_old != 0) || (timestamp_new != 0)) {
600 errno = EROFS;
601 return -1;
603 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
606 static int shadow_copy2_stat(vfs_handle_struct *handle,
607 struct smb_filename *smb_fname)
609 time_t timestamp;
610 char *stripped, *tmp;
611 int ret, saved_errno;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
614 smb_fname->base_name,
615 &timestamp, &stripped)) {
616 return -1;
618 if (timestamp == 0) {
619 return SMB_VFS_NEXT_STAT(handle, smb_fname);
622 tmp = smb_fname->base_name;
623 smb_fname->base_name = shadow_copy2_convert(
624 talloc_tos(), handle, stripped, timestamp);
625 TALLOC_FREE(stripped);
627 if (smb_fname->base_name == NULL) {
628 smb_fname->base_name = tmp;
629 return -1;
632 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
633 saved_errno = errno;
635 TALLOC_FREE(smb_fname->base_name);
636 smb_fname->base_name = tmp;
638 if (ret == 0) {
639 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
641 errno = saved_errno;
642 return ret;
645 static int shadow_copy2_lstat(vfs_handle_struct *handle,
646 struct smb_filename *smb_fname)
648 time_t timestamp;
649 char *stripped, *tmp;
650 int ret, saved_errno;
652 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
653 smb_fname->base_name,
654 &timestamp, &stripped)) {
655 return -1;
657 if (timestamp == 0) {
658 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
661 tmp = smb_fname->base_name;
662 smb_fname->base_name = shadow_copy2_convert(
663 talloc_tos(), handle, stripped, timestamp);
664 TALLOC_FREE(stripped);
666 if (smb_fname->base_name == NULL) {
667 smb_fname->base_name = tmp;
668 return -1;
671 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
672 saved_errno = errno;
674 TALLOC_FREE(smb_fname->base_name);
675 smb_fname->base_name = tmp;
677 if (ret == 0) {
678 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
680 errno = saved_errno;
681 return ret;
684 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
685 SMB_STRUCT_STAT *sbuf)
687 time_t timestamp;
688 int ret;
690 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
691 if (ret == -1) {
692 return ret;
694 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
695 fsp->fsp_name->base_name,
696 &timestamp, NULL)) {
697 return 0;
699 if (timestamp != 0) {
700 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
702 return 0;
705 static int shadow_copy2_open(vfs_handle_struct *handle,
706 struct smb_filename *smb_fname, files_struct *fsp,
707 int flags, mode_t mode)
709 time_t timestamp;
710 char *stripped, *tmp;
711 int ret, saved_errno;
713 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
714 smb_fname->base_name,
715 &timestamp, &stripped)) {
716 return -1;
718 if (timestamp == 0) {
719 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
722 tmp = smb_fname->base_name;
723 smb_fname->base_name = shadow_copy2_convert(
724 talloc_tos(), handle, stripped, timestamp);
725 TALLOC_FREE(stripped);
727 if (smb_fname->base_name == NULL) {
728 smb_fname->base_name = tmp;
729 return -1;
732 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
733 saved_errno = errno;
735 TALLOC_FREE(smb_fname->base_name);
736 smb_fname->base_name = tmp;
738 errno = saved_errno;
739 return ret;
742 static int shadow_copy2_unlink(vfs_handle_struct *handle,
743 const struct smb_filename *smb_fname)
745 time_t timestamp;
746 char *stripped;
747 int ret, saved_errno;
748 struct smb_filename *conv;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
751 smb_fname->base_name,
752 &timestamp, &stripped)) {
753 return -1;
755 if (timestamp == 0) {
756 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
758 conv = cp_smb_filename(talloc_tos(), smb_fname);
759 if (conv == NULL) {
760 errno = ENOMEM;
761 return -1;
763 conv->base_name = shadow_copy2_convert(
764 conv, handle, stripped, timestamp);
765 TALLOC_FREE(stripped);
766 if (conv->base_name == NULL) {
767 return -1;
769 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
770 saved_errno = errno;
771 TALLOC_FREE(conv);
772 errno = saved_errno;
773 return ret;
776 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
777 mode_t mode)
779 time_t timestamp;
780 char *stripped;
781 int ret, saved_errno;
782 char *conv;
784 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
785 &timestamp, &stripped)) {
786 return -1;
788 if (timestamp == 0) {
789 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
791 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
792 TALLOC_FREE(stripped);
793 if (conv == NULL) {
794 return -1;
796 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
797 saved_errno = errno;
798 TALLOC_FREE(conv);
799 errno = saved_errno;
800 return ret;
803 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
804 uid_t uid, gid_t gid)
806 time_t timestamp;
807 char *stripped;
808 int ret, saved_errno;
809 char *conv;
811 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
812 &timestamp, &stripped)) {
813 return -1;
815 if (timestamp == 0) {
816 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
818 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
819 TALLOC_FREE(stripped);
820 if (conv == NULL) {
821 return -1;
823 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
824 saved_errno = errno;
825 TALLOC_FREE(conv);
826 errno = saved_errno;
827 return ret;
830 static int shadow_copy2_chdir(vfs_handle_struct *handle,
831 const char *fname)
833 time_t timestamp;
834 char *stripped;
835 int ret, saved_errno;
836 char *conv;
838 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
839 &timestamp, &stripped)) {
840 return -1;
842 if (timestamp == 0) {
843 return SMB_VFS_NEXT_CHDIR(handle, fname);
845 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
846 TALLOC_FREE(stripped);
847 if (conv == NULL) {
848 return -1;
850 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
851 saved_errno = errno;
852 TALLOC_FREE(conv);
853 errno = saved_errno;
854 return ret;
857 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
858 const struct smb_filename *smb_fname,
859 struct smb_file_time *ft)
861 time_t timestamp;
862 char *stripped;
863 int ret, saved_errno;
864 struct smb_filename *conv;
866 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
867 smb_fname->base_name,
868 &timestamp, &stripped)) {
869 return -1;
871 if (timestamp == 0) {
872 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
874 conv = cp_smb_filename(talloc_tos(), smb_fname);
875 if (conv == NULL) {
876 errno = ENOMEM;
877 return -1;
879 conv->base_name = shadow_copy2_convert(
880 conv, handle, stripped, timestamp);
881 TALLOC_FREE(stripped);
882 if (conv->base_name == NULL) {
883 return -1;
885 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
886 saved_errno = errno;
887 TALLOC_FREE(conv);
888 errno = saved_errno;
889 return ret;
892 static int shadow_copy2_readlink(vfs_handle_struct *handle,
893 const char *fname, char *buf, size_t bufsiz)
895 time_t timestamp;
896 char *stripped;
897 int ret, saved_errno;
898 char *conv;
900 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
901 &timestamp, &stripped)) {
902 return -1;
904 if (timestamp == 0) {
905 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
907 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
908 TALLOC_FREE(stripped);
909 if (conv == NULL) {
910 return -1;
912 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
913 saved_errno = errno;
914 TALLOC_FREE(conv);
915 errno = saved_errno;
916 return ret;
919 static int shadow_copy2_mknod(vfs_handle_struct *handle,
920 const char *fname, mode_t mode, SMB_DEV_T dev)
922 time_t timestamp;
923 char *stripped;
924 int ret, saved_errno;
925 char *conv;
927 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
928 &timestamp, &stripped)) {
929 return -1;
931 if (timestamp == 0) {
932 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
934 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
935 TALLOC_FREE(stripped);
936 if (conv == NULL) {
937 return -1;
939 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
940 saved_errno = errno;
941 TALLOC_FREE(conv);
942 errno = saved_errno;
943 return ret;
946 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
947 const char *fname)
949 time_t timestamp;
950 char *stripped = NULL;
951 char *tmp = NULL;
952 char *result = NULL;
953 char *inserted = NULL;
954 char *inserted_to, *inserted_end;
955 int saved_errno;
957 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
958 &timestamp, &stripped)) {
959 goto done;
961 if (timestamp == 0) {
962 return SMB_VFS_NEXT_REALPATH(handle, fname);
965 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
966 if (tmp == NULL) {
967 goto done;
970 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
971 if (result == NULL) {
972 goto done;
976 * Take away what we've inserted. This removes the @GMT-thingy
977 * completely, but will give a path under the share root.
979 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
980 if (inserted == NULL) {
981 goto done;
983 inserted_to = strstr_m(result, inserted);
984 if (inserted_to == NULL) {
985 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
986 goto done;
988 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
989 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
991 done:
992 saved_errno = errno;
993 TALLOC_FREE(inserted);
994 TALLOC_FREE(tmp);
995 TALLOC_FREE(stripped);
996 errno = saved_errno;
997 return result;
1001 * Check whether a given directory contains a
1002 * snapshot directory as direct subdirectory.
1003 * If yes, return the path of the snapshot-subdir,
1004 * otherwise return NULL.
1006 static char *have_snapdir(struct vfs_handle_struct *handle,
1007 const char *path)
1009 struct smb_filename smb_fname;
1010 int ret;
1011 struct shadow_copy2_config *config;
1013 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1014 return NULL);
1016 ZERO_STRUCT(smb_fname);
1017 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1018 path, config->snapdir);
1019 if (smb_fname.base_name == NULL) {
1020 return NULL;
1023 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1024 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1025 return smb_fname.base_name;
1027 TALLOC_FREE(smb_fname.base_name);
1028 return NULL;
1032 * Find the snapshot directory (if any) for the given
1033 * filename (which is relative to the share).
1035 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1036 struct vfs_handle_struct *handle,
1037 struct smb_filename *smb_fname)
1039 char *path, *p;
1040 char *snapdir;
1041 struct shadow_copy2_config *config;
1043 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1044 return NULL);
1046 path = talloc_asprintf(mem_ctx, "%s/%s",
1047 handle->conn->connectpath,
1048 smb_fname->base_name);
1049 if (path == NULL) {
1050 return NULL;
1053 snapdir = have_snapdir(handle, path);
1054 if (snapdir != NULL) {
1055 TALLOC_FREE(path);
1056 return snapdir;
1059 while ((p = strrchr(path, '/')) && (p > path)) {
1061 p[0] = '\0';
1063 snapdir = have_snapdir(handle, path);
1064 if (snapdir != NULL) {
1065 TALLOC_FREE(path);
1066 return snapdir;
1069 TALLOC_FREE(path);
1070 return NULL;
1073 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1074 const char *name,
1075 char *gmt, size_t gmt_len)
1077 struct tm timestamp;
1078 time_t timestamp_t;
1079 unsigned long int timestamp_long;
1080 const char *fmt;
1081 struct shadow_copy2_config *config;
1083 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1084 return NULL);
1086 fmt = config->gmt_format;
1088 ZERO_STRUCT(timestamp);
1089 if (config->use_sscanf) {
1090 if (sscanf(name, fmt, &timestamp_long) != 1) {
1091 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1092 "no sscanf match %s: %s\n",
1093 fmt, name));
1094 return false;
1096 timestamp_t = timestamp_long;
1097 gmtime_r(&timestamp_t, &timestamp);
1098 } else {
1099 if (strptime(name, fmt, &timestamp) == NULL) {
1100 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1101 "no match %s: %s\n",
1102 fmt, name));
1103 return false;
1105 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1106 fmt, name));
1108 if (config->use_localtime) {
1109 timestamp.tm_isdst = -1;
1110 timestamp_t = mktime(&timestamp);
1111 gmtime_r(&timestamp_t, &timestamp);
1115 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1116 return true;
1119 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1121 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1124 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1126 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1130 sort the shadow copy data in ascending or descending order
1132 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1133 struct shadow_copy_data *shadow_copy2_data)
1135 int (*cmpfunc)(const void *, const void *);
1136 const char *sort;
1137 struct shadow_copy2_config *config;
1139 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1140 return);
1142 sort = config->sort_order;
1143 if (sort == NULL) {
1144 return;
1147 if (strcmp(sort, "asc") == 0) {
1148 cmpfunc = shadow_copy2_label_cmp_asc;
1149 } else if (strcmp(sort, "desc") == 0) {
1150 cmpfunc = shadow_copy2_label_cmp_desc;
1151 } else {
1152 return;
1155 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1156 shadow_copy2_data->labels)
1158 TYPESAFE_QSORT(shadow_copy2_data->labels,
1159 shadow_copy2_data->num_volumes,
1160 cmpfunc);
1164 static int shadow_copy2_get_shadow_copy_data(
1165 vfs_handle_struct *handle, files_struct *fsp,
1166 struct shadow_copy_data *shadow_copy2_data,
1167 bool labels)
1169 DIR *p;
1170 const char *snapdir;
1171 struct dirent *d;
1172 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1174 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1175 if (snapdir == NULL) {
1176 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1177 handle->conn->connectpath));
1178 errno = EINVAL;
1179 talloc_free(tmp_ctx);
1180 return -1;
1183 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1185 if (!p) {
1186 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1187 " - %s\n", snapdir, strerror(errno)));
1188 talloc_free(tmp_ctx);
1189 errno = ENOSYS;
1190 return -1;
1193 shadow_copy2_data->num_volumes = 0;
1194 shadow_copy2_data->labels = NULL;
1196 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1197 char snapshot[GMT_NAME_LEN+1];
1198 SHADOW_COPY_LABEL *tlabels;
1201 * ignore names not of the right form in the snapshot
1202 * directory
1204 if (!shadow_copy2_snapshot_to_gmt(
1205 handle, d->d_name,
1206 snapshot, sizeof(snapshot))) {
1208 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1209 "ignoring %s\n", d->d_name));
1210 continue;
1212 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1213 d->d_name, snapshot));
1215 if (!labels) {
1216 /* the caller doesn't want the labels */
1217 shadow_copy2_data->num_volumes++;
1218 continue;
1221 tlabels = talloc_realloc(shadow_copy2_data,
1222 shadow_copy2_data->labels,
1223 SHADOW_COPY_LABEL,
1224 shadow_copy2_data->num_volumes+1);
1225 if (tlabels == NULL) {
1226 DEBUG(0,("shadow_copy2: out of memory\n"));
1227 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1228 talloc_free(tmp_ctx);
1229 return -1;
1232 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1233 sizeof(*tlabels));
1235 shadow_copy2_data->num_volumes++;
1236 shadow_copy2_data->labels = tlabels;
1239 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1241 shadow_copy2_sort_data(handle, shadow_copy2_data);
1243 talloc_free(tmp_ctx);
1244 return 0;
1247 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1248 struct files_struct *fsp,
1249 uint32 security_info,
1250 TALLOC_CTX *mem_ctx,
1251 struct security_descriptor **ppdesc)
1253 time_t timestamp;
1254 char *stripped;
1255 NTSTATUS status;
1256 char *conv;
1258 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1259 fsp->fsp_name->base_name,
1260 &timestamp, &stripped)) {
1261 return map_nt_error_from_unix(errno);
1263 if (timestamp == 0) {
1264 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1265 mem_ctx,
1266 ppdesc);
1268 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1269 TALLOC_FREE(stripped);
1270 if (conv == NULL) {
1271 return map_nt_error_from_unix(errno);
1273 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1274 mem_ctx, ppdesc);
1275 TALLOC_FREE(conv);
1276 return status;
1279 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1280 const char *fname,
1281 uint32 security_info,
1282 TALLOC_CTX *mem_ctx,
1283 struct security_descriptor **ppdesc)
1285 time_t timestamp;
1286 char *stripped;
1287 NTSTATUS status;
1288 char *conv;
1290 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1291 &timestamp, &stripped)) {
1292 return map_nt_error_from_unix(errno);
1294 if (timestamp == 0) {
1295 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1296 mem_ctx, ppdesc);
1298 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1299 TALLOC_FREE(stripped);
1300 if (conv == NULL) {
1301 return map_nt_error_from_unix(errno);
1303 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1304 mem_ctx, ppdesc);
1305 TALLOC_FREE(conv);
1306 return status;
1309 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1310 const char *fname, mode_t mode)
1312 time_t timestamp;
1313 char *stripped;
1314 int ret, saved_errno;
1315 char *conv;
1317 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1318 &timestamp, &stripped)) {
1319 return -1;
1321 if (timestamp == 0) {
1322 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1324 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1325 TALLOC_FREE(stripped);
1326 if (conv == NULL) {
1327 return -1;
1329 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1330 saved_errno = errno;
1331 TALLOC_FREE(conv);
1332 errno = saved_errno;
1333 return ret;
1336 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1338 time_t timestamp;
1339 char *stripped;
1340 int ret, saved_errno;
1341 char *conv;
1343 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1344 &timestamp, &stripped)) {
1345 return -1;
1347 if (timestamp == 0) {
1348 return SMB_VFS_NEXT_RMDIR(handle, fname);
1350 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1351 TALLOC_FREE(stripped);
1352 if (conv == NULL) {
1353 return -1;
1355 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1356 saved_errno = errno;
1357 TALLOC_FREE(conv);
1358 errno = saved_errno;
1359 return ret;
1362 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1363 unsigned int flags)
1365 time_t timestamp;
1366 char *stripped;
1367 int ret, saved_errno;
1368 char *conv;
1370 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1371 &timestamp, &stripped)) {
1372 return -1;
1374 if (timestamp == 0) {
1375 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1377 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1378 TALLOC_FREE(stripped);
1379 if (conv == NULL) {
1380 return -1;
1382 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1383 saved_errno = errno;
1384 TALLOC_FREE(conv);
1385 errno = saved_errno;
1386 return ret;
1389 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1390 const char *fname, const char *aname,
1391 void *value, size_t size)
1393 time_t timestamp;
1394 char *stripped;
1395 ssize_t ret;
1396 int saved_errno;
1397 char *conv;
1399 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1400 &timestamp, &stripped)) {
1401 return -1;
1403 if (timestamp == 0) {
1404 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1405 size);
1407 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1408 TALLOC_FREE(stripped);
1409 if (conv == NULL) {
1410 return -1;
1412 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1413 saved_errno = errno;
1414 TALLOC_FREE(conv);
1415 errno = saved_errno;
1416 return ret;
1419 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1420 const char *fname,
1421 char *list, size_t size)
1423 time_t timestamp;
1424 char *stripped;
1425 ssize_t ret;
1426 int saved_errno;
1427 char *conv;
1429 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1430 &timestamp, &stripped)) {
1431 return -1;
1433 if (timestamp == 0) {
1434 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1436 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1437 TALLOC_FREE(stripped);
1438 if (conv == NULL) {
1439 return -1;
1441 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1442 saved_errno = errno;
1443 TALLOC_FREE(conv);
1444 errno = saved_errno;
1445 return ret;
1448 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1449 const char *fname, const char *aname)
1451 time_t timestamp;
1452 char *stripped;
1453 int ret, saved_errno;
1454 char *conv;
1456 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1457 &timestamp, &stripped)) {
1458 return -1;
1460 if (timestamp == 0) {
1461 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1463 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1464 TALLOC_FREE(stripped);
1465 if (conv == NULL) {
1466 return -1;
1468 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1469 saved_errno = errno;
1470 TALLOC_FREE(conv);
1471 errno = saved_errno;
1472 return ret;
1475 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1476 const char *fname,
1477 const char *aname, const void *value,
1478 size_t size, int flags)
1480 time_t timestamp;
1481 char *stripped;
1482 ssize_t ret;
1483 int saved_errno;
1484 char *conv;
1486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1487 &timestamp, &stripped)) {
1488 return -1;
1490 if (timestamp == 0) {
1491 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1492 flags);
1494 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1495 TALLOC_FREE(stripped);
1496 if (conv == NULL) {
1497 return -1;
1499 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1500 saved_errno = errno;
1501 TALLOC_FREE(conv);
1502 errno = saved_errno;
1503 return ret;
1506 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1507 const char *fname, mode_t mode)
1509 time_t timestamp;
1510 char *stripped;
1511 ssize_t ret;
1512 int saved_errno;
1513 char *conv;
1515 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1516 &timestamp, &stripped)) {
1517 return -1;
1519 if (timestamp == 0) {
1520 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1522 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1523 TALLOC_FREE(stripped);
1524 if (conv == NULL) {
1525 return -1;
1527 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1528 saved_errno = errno;
1529 TALLOC_FREE(conv);
1530 errno = saved_errno;
1531 return ret;
1534 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1535 const char *path,
1536 const char *name,
1537 TALLOC_CTX *mem_ctx,
1538 char **found_name)
1540 time_t timestamp;
1541 char *stripped;
1542 ssize_t ret;
1543 int saved_errno;
1544 char *conv;
1546 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1547 &timestamp, &stripped)) {
1548 return -1;
1550 if (timestamp == 0) {
1551 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1552 mem_ctx, found_name);
1554 if (stripped[0] == '\0') {
1555 *found_name = talloc_strdup(mem_ctx, name);
1556 if (*found_name == NULL) {
1557 errno = ENOMEM;
1558 return -1;
1560 return 0;
1562 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1563 TALLOC_FREE(stripped);
1564 if (conv == NULL) {
1565 return -1;
1567 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1568 mem_ctx, found_name);
1569 saved_errno = errno;
1570 TALLOC_FREE(conv);
1571 errno = saved_errno;
1572 return ret;
1575 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1576 const char *service, const char *user)
1578 struct shadow_copy2_config *config;
1579 int ret;
1580 const char *snapdir;
1581 const char *gmt_format;
1582 const char *sort_order;
1584 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1585 (unsigned)handle->conn->cnum,
1586 handle->conn->connectpath));
1588 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1589 if (ret < 0) {
1590 return ret;
1593 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1594 if (config == NULL) {
1595 DEBUG(0, ("talloc_zero() failed\n"));
1596 errno = ENOMEM;
1597 return -1;
1600 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1601 "shadow", "format",
1602 GMT_FORMAT);
1603 config->gmt_format = talloc_strdup(config, gmt_format);
1604 if (config->gmt_format == NULL) {
1605 DEBUG(0, ("talloc_strdup() failed\n"));
1606 errno = ENOMEM;
1607 return -1;
1610 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1611 "shadow", "sscanf", false);
1613 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1614 "shadow", "localtime",
1615 false);
1617 snapdir = lp_parm_const_string(SNUM(handle->conn),
1618 "shadow", "snapdir",
1619 ".snapshots");
1620 config->snapdir = talloc_strdup(config, snapdir);
1621 if (config->snapdir == NULL) {
1622 DEBUG(0, ("talloc_strdup() failed\n"));
1623 errno = ENOMEM;
1624 return -1;
1627 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1628 "shadow",
1629 "snapdirseverywhere",
1630 false);
1632 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1633 "shadow", "crossmountpoints",
1634 false);
1636 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1637 "shadow", "fixinodes",
1638 false);
1640 sort_order = lp_parm_const_string(SNUM(handle->conn),
1641 "shadow", "sort", "desc");
1642 config->sort_order = talloc_strdup(config, sort_order);
1643 if (config->sort_order == NULL) {
1644 DEBUG(0, ("talloc_strdup() failed\n"));
1645 errno = ENOMEM;
1646 return -1;
1649 SMB_VFS_HANDLE_SET_DATA(handle, config,
1650 NULL, struct shadow_copy2_config,
1651 return -1);
1653 return 0;
1656 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1657 .connect_fn = shadow_copy2_connect,
1658 .opendir_fn = shadow_copy2_opendir,
1659 .rename_fn = shadow_copy2_rename,
1660 .link_fn = shadow_copy2_link,
1661 .symlink_fn = shadow_copy2_symlink,
1662 .stat_fn = shadow_copy2_stat,
1663 .lstat_fn = shadow_copy2_lstat,
1664 .fstat_fn = shadow_copy2_fstat,
1665 .open_fn = shadow_copy2_open,
1666 .unlink_fn = shadow_copy2_unlink,
1667 .chmod_fn = shadow_copy2_chmod,
1668 .chown_fn = shadow_copy2_chown,
1669 .chdir_fn = shadow_copy2_chdir,
1670 .ntimes_fn = shadow_copy2_ntimes,
1671 .readlink_fn = shadow_copy2_readlink,
1672 .mknod_fn = shadow_copy2_mknod,
1673 .realpath_fn = shadow_copy2_realpath,
1674 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1675 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1676 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1677 .mkdir_fn = shadow_copy2_mkdir,
1678 .rmdir_fn = shadow_copy2_rmdir,
1679 .getxattr_fn = shadow_copy2_getxattr,
1680 .listxattr_fn = shadow_copy2_listxattr,
1681 .removexattr_fn = shadow_copy2_removexattr,
1682 .setxattr_fn = shadow_copy2_setxattr,
1683 .chmod_acl_fn = shadow_copy2_chmod_acl,
1684 .chflags_fn = shadow_copy2_chflags,
1685 .get_real_filename_fn = shadow_copy2_get_real_filename,
1688 NTSTATUS vfs_shadow_copy2_init(void);
1689 NTSTATUS vfs_shadow_copy2_init(void)
1691 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1692 "shadow_copy2", &vfs_shadow_copy2_fns);