shadow_copy2: re-add the basedir option.
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobd584fa92af1ce9ab5f7a856279c4ef8e67a52749
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;
120 char *basedir;
121 char *mount_point;
124 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
125 size_t **poffsets,
126 unsigned *pnum_offsets)
128 unsigned num_offsets;
129 size_t *offsets;
130 const char *p;
132 num_offsets = 0;
134 p = str;
135 while ((p = strchr(p, '/')) != NULL) {
136 num_offsets += 1;
137 p += 1;
140 offsets = talloc_array(mem_ctx, size_t, num_offsets);
141 if (offsets == NULL) {
142 return false;
145 p = str;
146 num_offsets = 0;
147 while ((p = strchr(p, '/')) != NULL) {
148 offsets[num_offsets] = p-str;
149 num_offsets += 1;
150 p += 1;
153 *poffsets = offsets;
154 *pnum_offsets = num_offsets;
155 return true;
159 * Given a timstamp, build the string to insert into a path
160 * as a path component for creating the local path to the
161 * snapshot at the given timestamp of the input path.
163 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
164 struct vfs_handle_struct *handle,
165 time_t snapshot)
167 struct tm snap_tm;
168 fstring snaptime_string;
169 size_t snaptime_len;
170 struct shadow_copy2_config *config;
172 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
173 return NULL);
175 if (config->use_sscanf) {
176 snaptime_len = snprintf(snaptime_string,
177 sizeof(snaptime_string),
178 config->gmt_format,
179 (unsigned long)snapshot);
180 if (snaptime_len <= 0) {
181 DEBUG(10, ("snprintf failed\n"));
182 return NULL;
184 } else {
185 if (config->use_localtime) {
186 if (localtime_r(&snapshot, &snap_tm) == 0) {
187 DEBUG(10, ("gmtime_r failed\n"));
188 return NULL;
190 } else {
191 if (gmtime_r(&snapshot, &snap_tm) == 0) {
192 DEBUG(10, ("gmtime_r failed\n"));
193 return NULL;
196 snaptime_len = strftime(snaptime_string,
197 sizeof(snaptime_string),
198 config->gmt_format,
199 &snap_tm);
200 if (snaptime_len == 0) {
201 DEBUG(10, ("strftime failed\n"));
202 return NULL;
205 return talloc_asprintf(mem_ctx, "/%s/%s",
206 config->snapdir, snaptime_string);
210 * Strip a snapshot component from an filename as
211 * handed in via the smb layer.
212 * Returns the parsed timestamp and the stripped filename.
214 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
215 struct vfs_handle_struct *handle,
216 const char *name,
217 time_t *ptimestamp,
218 char **pstripped)
220 struct tm tm;
221 time_t timestamp;
222 const char *p;
223 char *q;
224 char *stripped;
225 size_t rest_len, dst_len;
226 struct shadow_copy2_config *config;
228 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
229 return false);
231 p = strstr_m(name, "@GMT-");
232 if (p == NULL) {
233 goto no_snapshot;
235 if ((p > name) && (p[-1] != '/')) {
236 goto no_snapshot;
238 q = strptime(p, GMT_FORMAT, &tm);
239 if (q == NULL) {
240 goto no_snapshot;
242 tm.tm_isdst = -1;
243 timestamp = timegm(&tm);
244 if (timestamp == (time_t)-1) {
245 goto no_snapshot;
247 if ((p == name) && (q[0] == '\0')) {
248 if (pstripped != NULL) {
249 stripped = talloc_strdup(mem_ctx, "");
250 if (stripped == NULL) {
251 return false;
253 *pstripped = stripped;
255 *ptimestamp = timestamp;
256 return true;
258 if (q[0] != '/') {
259 goto no_snapshot;
261 q += 1;
263 rest_len = strlen(q);
264 dst_len = (p-name) + rest_len;
266 if (config->snapdirseverywhere) {
267 char *insert;
268 bool have_insert;
269 insert = shadow_copy2_insert_string(talloc_tos(), handle,
270 timestamp);
271 if (insert == NULL) {
272 errno = ENOMEM;
273 return false;
276 have_insert = (strstr(name, insert+1) != NULL);
277 TALLOC_FREE(insert);
278 if (have_insert) {
279 goto no_snapshot;
283 if (pstripped != NULL) {
284 stripped = talloc_array(mem_ctx, char, dst_len+1);
285 if (stripped == NULL) {
286 errno = ENOMEM;
287 return false;
289 if (p > name) {
290 memcpy(stripped, name, p-name);
292 if (rest_len > 0) {
293 memcpy(stripped + (p-name), q, rest_len);
295 stripped[dst_len] = '\0';
296 *pstripped = stripped;
298 *ptimestamp = timestamp;
299 return true;
300 no_snapshot:
301 *ptimestamp = 0;
302 return true;
305 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
306 vfs_handle_struct *handle)
308 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
309 dev_t dev;
310 struct stat st;
311 char *p;
313 if (stat(path, &st) != 0) {
314 talloc_free(path);
315 return NULL;
318 dev = st.st_dev;
320 while ((p = strrchr(path, '/')) && p > path) {
321 *p = 0;
322 if (stat(path, &st) != 0) {
323 talloc_free(path);
324 return NULL;
326 if (st.st_dev != dev) {
327 *p = '/';
328 break;
332 return path;
336 * Convert from a name as handed in via the SMB layer
337 * and a timestamp into the local path of the snapshot
338 * of the provided file at the provided time.
340 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
341 struct vfs_handle_struct *handle,
342 const char *name, time_t timestamp)
344 struct smb_filename converted_fname;
345 char *result = NULL;
346 size_t *slashes = NULL;
347 unsigned num_slashes;
348 char *path = NULL;
349 size_t pathlen;
350 char *insert = NULL;
351 char *converted = NULL;
352 size_t insertlen;
353 int i, saved_errno;
354 size_t min_offset;
355 struct shadow_copy2_config *config;
357 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
358 return NULL);
360 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
361 name);
362 if (path == NULL) {
363 errno = ENOMEM;
364 goto fail;
366 pathlen = talloc_get_size(path)-1;
368 DEBUG(10, ("converting %s\n", path));
370 if (!shadow_copy2_find_slashes(talloc_tos(), path,
371 &slashes, &num_slashes)) {
372 goto fail;
374 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
375 if (insert == NULL) {
376 goto fail;
378 insertlen = talloc_get_size(insert)-1;
379 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
380 if (converted == NULL) {
381 goto fail;
384 if (path[pathlen-1] != '/') {
386 * Append a fake slash to find the snapshot root
388 size_t *tmp;
389 tmp = talloc_realloc(talloc_tos(), slashes,
390 size_t, num_slashes+1);
391 if (tmp == NULL) {
392 goto fail;
394 slashes = tmp;
395 slashes[num_slashes] = pathlen;
396 num_slashes += 1;
399 min_offset = 0;
401 if (!config->crossmountpoints) {
402 char *mount_point;
404 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
405 handle);
406 if (mount_point == NULL) {
407 goto fail;
409 min_offset = strlen(mount_point);
410 TALLOC_FREE(mount_point);
413 memcpy(converted, path, pathlen+1);
414 converted[pathlen+insertlen] = '\0';
416 ZERO_STRUCT(converted_fname);
417 converted_fname.base_name = converted;
419 for (i = num_slashes-1; i>=0; i--) {
420 int ret;
421 size_t offset;
423 offset = slashes[i];
425 if (offset < min_offset) {
426 errno = ENOENT;
427 goto fail;
430 memcpy(converted+offset, insert, insertlen);
432 offset += insertlen;
433 memcpy(converted+offset, path + slashes[i],
434 pathlen - slashes[i]);
436 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
438 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
439 ret, ret == 0 ? "ok" : strerror(errno)));
440 if (ret == 0) {
441 /* success */
442 break;
444 if (errno == ENOTDIR) {
446 * This is a valid condition: We appended the
447 * .snaphots/@GMT.. to a file name. Just try
448 * with the upper levels.
450 continue;
452 if (errno != ENOENT) {
453 /* Other problem than "not found" */
454 goto fail;
458 if (i >= 0) {
460 * Found something
462 DEBUG(10, ("Found %s\n", converted));
463 result = converted;
464 converted = NULL;
465 } else {
466 errno = ENOENT;
468 fail:
469 saved_errno = errno;
470 TALLOC_FREE(converted);
471 TALLOC_FREE(insert);
472 TALLOC_FREE(slashes);
473 TALLOC_FREE(path);
474 errno = saved_errno;
475 return result;
479 modify a sbuf return to ensure that inodes in the shadow directory
480 are different from those in the main directory
482 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
483 SMB_STRUCT_STAT *sbuf)
485 struct shadow_copy2_config *config;
487 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
488 return);
490 if (config->fixinodes) {
491 /* some snapshot systems, like GPFS, return the name
492 device:inode for the snapshot files as the current
493 files. That breaks the 'restore' button in the shadow copy
494 GUI, as the client gets a sharing violation.
496 This is a crude way of allowing both files to be
497 open at once. It has a slight chance of inode
498 number collision, but I can't see a better approach
499 without significant VFS changes
501 uint32_t shash;
503 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
504 if (shash == 0) {
505 shash = 1;
507 sbuf->st_ex_ino ^= shash;
511 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
512 const char *fname,
513 const char *mask,
514 uint32 attr)
516 time_t timestamp;
517 char *stripped;
518 DIR *ret;
519 int saved_errno;
520 char *conv;
522 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
523 &timestamp, &stripped)) {
524 return NULL;
526 if (timestamp == 0) {
527 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
529 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
530 TALLOC_FREE(stripped);
531 if (conv == NULL) {
532 return NULL;
534 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
535 saved_errno = errno;
536 TALLOC_FREE(conv);
537 errno = saved_errno;
538 return ret;
541 static int shadow_copy2_rename(vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname_src,
543 const struct smb_filename *smb_fname_dst)
545 time_t timestamp_src, timestamp_dst;
547 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
548 smb_fname_src->base_name,
549 &timestamp_src, NULL)) {
550 return -1;
552 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
553 smb_fname_dst->base_name,
554 &timestamp_dst, NULL)) {
555 return -1;
557 if (timestamp_src != 0) {
558 errno = EXDEV;
559 return -1;
561 if (timestamp_dst != 0) {
562 errno = EROFS;
563 return -1;
565 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
568 static int shadow_copy2_symlink(vfs_handle_struct *handle,
569 const char *oldname, const char *newname)
571 time_t timestamp_old, timestamp_new;
573 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
574 &timestamp_old, NULL)) {
575 return -1;
577 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
578 &timestamp_new, NULL)) {
579 return -1;
581 if ((timestamp_old != 0) || (timestamp_new != 0)) {
582 errno = EROFS;
583 return -1;
585 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
588 static int shadow_copy2_link(vfs_handle_struct *handle,
589 const char *oldname, const char *newname)
591 time_t timestamp_old, timestamp_new;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
594 &timestamp_old, NULL)) {
595 return -1;
597 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
598 &timestamp_new, NULL)) {
599 return -1;
601 if ((timestamp_old != 0) || (timestamp_new != 0)) {
602 errno = EROFS;
603 return -1;
605 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
608 static int shadow_copy2_stat(vfs_handle_struct *handle,
609 struct smb_filename *smb_fname)
611 time_t timestamp;
612 char *stripped, *tmp;
613 int ret, saved_errno;
615 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
616 smb_fname->base_name,
617 &timestamp, &stripped)) {
618 return -1;
620 if (timestamp == 0) {
621 return SMB_VFS_NEXT_STAT(handle, smb_fname);
624 tmp = smb_fname->base_name;
625 smb_fname->base_name = shadow_copy2_convert(
626 talloc_tos(), handle, stripped, timestamp);
627 TALLOC_FREE(stripped);
629 if (smb_fname->base_name == NULL) {
630 smb_fname->base_name = tmp;
631 return -1;
634 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
635 saved_errno = errno;
637 TALLOC_FREE(smb_fname->base_name);
638 smb_fname->base_name = tmp;
640 if (ret == 0) {
641 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
643 errno = saved_errno;
644 return ret;
647 static int shadow_copy2_lstat(vfs_handle_struct *handle,
648 struct smb_filename *smb_fname)
650 time_t timestamp;
651 char *stripped, *tmp;
652 int ret, saved_errno;
654 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
655 smb_fname->base_name,
656 &timestamp, &stripped)) {
657 return -1;
659 if (timestamp == 0) {
660 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
663 tmp = smb_fname->base_name;
664 smb_fname->base_name = shadow_copy2_convert(
665 talloc_tos(), handle, stripped, timestamp);
666 TALLOC_FREE(stripped);
668 if (smb_fname->base_name == NULL) {
669 smb_fname->base_name = tmp;
670 return -1;
673 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
674 saved_errno = errno;
676 TALLOC_FREE(smb_fname->base_name);
677 smb_fname->base_name = tmp;
679 if (ret == 0) {
680 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
682 errno = saved_errno;
683 return ret;
686 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
687 SMB_STRUCT_STAT *sbuf)
689 time_t timestamp;
690 int ret;
692 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
693 if (ret == -1) {
694 return ret;
696 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
697 fsp->fsp_name->base_name,
698 &timestamp, NULL)) {
699 return 0;
701 if (timestamp != 0) {
702 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
704 return 0;
707 static int shadow_copy2_open(vfs_handle_struct *handle,
708 struct smb_filename *smb_fname, files_struct *fsp,
709 int flags, mode_t mode)
711 time_t timestamp;
712 char *stripped, *tmp;
713 int ret, saved_errno;
715 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
716 smb_fname->base_name,
717 &timestamp, &stripped)) {
718 return -1;
720 if (timestamp == 0) {
721 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
724 tmp = smb_fname->base_name;
725 smb_fname->base_name = shadow_copy2_convert(
726 talloc_tos(), handle, stripped, timestamp);
727 TALLOC_FREE(stripped);
729 if (smb_fname->base_name == NULL) {
730 smb_fname->base_name = tmp;
731 return -1;
734 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
735 saved_errno = errno;
737 TALLOC_FREE(smb_fname->base_name);
738 smb_fname->base_name = tmp;
740 errno = saved_errno;
741 return ret;
744 static int shadow_copy2_unlink(vfs_handle_struct *handle,
745 const struct smb_filename *smb_fname)
747 time_t timestamp;
748 char *stripped;
749 int ret, saved_errno;
750 struct smb_filename *conv;
752 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
753 smb_fname->base_name,
754 &timestamp, &stripped)) {
755 return -1;
757 if (timestamp == 0) {
758 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
760 conv = cp_smb_filename(talloc_tos(), smb_fname);
761 if (conv == NULL) {
762 errno = ENOMEM;
763 return -1;
765 conv->base_name = shadow_copy2_convert(
766 conv, handle, stripped, timestamp);
767 TALLOC_FREE(stripped);
768 if (conv->base_name == NULL) {
769 return -1;
771 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
772 saved_errno = errno;
773 TALLOC_FREE(conv);
774 errno = saved_errno;
775 return ret;
778 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
779 mode_t mode)
781 time_t timestamp;
782 char *stripped;
783 int ret, saved_errno;
784 char *conv;
786 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
787 &timestamp, &stripped)) {
788 return -1;
790 if (timestamp == 0) {
791 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
793 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
794 TALLOC_FREE(stripped);
795 if (conv == NULL) {
796 return -1;
798 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
799 saved_errno = errno;
800 TALLOC_FREE(conv);
801 errno = saved_errno;
802 return ret;
805 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
806 uid_t uid, gid_t gid)
808 time_t timestamp;
809 char *stripped;
810 int ret, saved_errno;
811 char *conv;
813 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
814 &timestamp, &stripped)) {
815 return -1;
817 if (timestamp == 0) {
818 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
820 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
821 TALLOC_FREE(stripped);
822 if (conv == NULL) {
823 return -1;
825 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
826 saved_errno = errno;
827 TALLOC_FREE(conv);
828 errno = saved_errno;
829 return ret;
832 static int shadow_copy2_chdir(vfs_handle_struct *handle,
833 const char *fname)
835 time_t timestamp;
836 char *stripped;
837 int ret, saved_errno;
838 char *conv;
840 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
841 &timestamp, &stripped)) {
842 return -1;
844 if (timestamp == 0) {
845 return SMB_VFS_NEXT_CHDIR(handle, fname);
847 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
848 TALLOC_FREE(stripped);
849 if (conv == NULL) {
850 return -1;
852 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
853 saved_errno = errno;
854 TALLOC_FREE(conv);
855 errno = saved_errno;
856 return ret;
859 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
860 const struct smb_filename *smb_fname,
861 struct smb_file_time *ft)
863 time_t timestamp;
864 char *stripped;
865 int ret, saved_errno;
866 struct smb_filename *conv;
868 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
869 smb_fname->base_name,
870 &timestamp, &stripped)) {
871 return -1;
873 if (timestamp == 0) {
874 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
876 conv = cp_smb_filename(talloc_tos(), smb_fname);
877 if (conv == NULL) {
878 errno = ENOMEM;
879 return -1;
881 conv->base_name = shadow_copy2_convert(
882 conv, handle, stripped, timestamp);
883 TALLOC_FREE(stripped);
884 if (conv->base_name == NULL) {
885 return -1;
887 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
888 saved_errno = errno;
889 TALLOC_FREE(conv);
890 errno = saved_errno;
891 return ret;
894 static int shadow_copy2_readlink(vfs_handle_struct *handle,
895 const char *fname, char *buf, size_t bufsiz)
897 time_t timestamp;
898 char *stripped;
899 int ret, saved_errno;
900 char *conv;
902 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
903 &timestamp, &stripped)) {
904 return -1;
906 if (timestamp == 0) {
907 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
909 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
910 TALLOC_FREE(stripped);
911 if (conv == NULL) {
912 return -1;
914 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
915 saved_errno = errno;
916 TALLOC_FREE(conv);
917 errno = saved_errno;
918 return ret;
921 static int shadow_copy2_mknod(vfs_handle_struct *handle,
922 const char *fname, mode_t mode, SMB_DEV_T dev)
924 time_t timestamp;
925 char *stripped;
926 int ret, saved_errno;
927 char *conv;
929 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
930 &timestamp, &stripped)) {
931 return -1;
933 if (timestamp == 0) {
934 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
936 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
937 TALLOC_FREE(stripped);
938 if (conv == NULL) {
939 return -1;
941 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
942 saved_errno = errno;
943 TALLOC_FREE(conv);
944 errno = saved_errno;
945 return ret;
948 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
949 const char *fname)
951 time_t timestamp;
952 char *stripped = NULL;
953 char *tmp = NULL;
954 char *result = NULL;
955 char *inserted = NULL;
956 char *inserted_to, *inserted_end;
957 int saved_errno;
959 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
960 &timestamp, &stripped)) {
961 goto done;
963 if (timestamp == 0) {
964 return SMB_VFS_NEXT_REALPATH(handle, fname);
967 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
968 if (tmp == NULL) {
969 goto done;
972 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
973 if (result == NULL) {
974 goto done;
978 * Take away what we've inserted. This removes the @GMT-thingy
979 * completely, but will give a path under the share root.
981 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
982 if (inserted == NULL) {
983 goto done;
985 inserted_to = strstr_m(result, inserted);
986 if (inserted_to == NULL) {
987 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
988 goto done;
990 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
991 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
993 done:
994 saved_errno = errno;
995 TALLOC_FREE(inserted);
996 TALLOC_FREE(tmp);
997 TALLOC_FREE(stripped);
998 errno = saved_errno;
999 return result;
1003 * Check whether a given directory contains a
1004 * snapshot directory as direct subdirectory.
1005 * If yes, return the path of the snapshot-subdir,
1006 * otherwise return NULL.
1008 static char *have_snapdir(struct vfs_handle_struct *handle,
1009 const char *path)
1011 struct smb_filename smb_fname;
1012 int ret;
1013 struct shadow_copy2_config *config;
1015 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1016 return NULL);
1018 ZERO_STRUCT(smb_fname);
1019 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1020 path, config->snapdir);
1021 if (smb_fname.base_name == NULL) {
1022 return NULL;
1025 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1026 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1027 return smb_fname.base_name;
1029 TALLOC_FREE(smb_fname.base_name);
1030 return NULL;
1034 * Find the snapshot directory (if any) for the given
1035 * filename (which is relative to the share).
1037 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1038 struct vfs_handle_struct *handle,
1039 struct smb_filename *smb_fname)
1041 char *path, *p;
1042 char *snapdir;
1043 struct shadow_copy2_config *config;
1045 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1046 return NULL);
1048 path = talloc_asprintf(mem_ctx, "%s/%s",
1049 handle->conn->connectpath,
1050 smb_fname->base_name);
1051 if (path == NULL) {
1052 return NULL;
1055 snapdir = have_snapdir(handle, path);
1056 if (snapdir != NULL) {
1057 TALLOC_FREE(path);
1058 return snapdir;
1061 while ((p = strrchr(path, '/')) && (p > path)) {
1063 p[0] = '\0';
1065 snapdir = have_snapdir(handle, path);
1066 if (snapdir != NULL) {
1067 TALLOC_FREE(path);
1068 return snapdir;
1071 TALLOC_FREE(path);
1072 return NULL;
1075 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1076 const char *name,
1077 char *gmt, size_t gmt_len)
1079 struct tm timestamp;
1080 time_t timestamp_t;
1081 unsigned long int timestamp_long;
1082 const char *fmt;
1083 struct shadow_copy2_config *config;
1085 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1086 return NULL);
1088 fmt = config->gmt_format;
1090 ZERO_STRUCT(timestamp);
1091 if (config->use_sscanf) {
1092 if (sscanf(name, fmt, &timestamp_long) != 1) {
1093 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1094 "no sscanf match %s: %s\n",
1095 fmt, name));
1096 return false;
1098 timestamp_t = timestamp_long;
1099 gmtime_r(&timestamp_t, &timestamp);
1100 } else {
1101 if (strptime(name, fmt, &timestamp) == NULL) {
1102 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1103 "no match %s: %s\n",
1104 fmt, name));
1105 return false;
1107 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1108 fmt, name));
1110 if (config->use_localtime) {
1111 timestamp.tm_isdst = -1;
1112 timestamp_t = mktime(&timestamp);
1113 gmtime_r(&timestamp_t, &timestamp);
1117 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1118 return true;
1121 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1123 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1126 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1128 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1132 sort the shadow copy data in ascending or descending order
1134 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1135 struct shadow_copy_data *shadow_copy2_data)
1137 int (*cmpfunc)(const void *, const void *);
1138 const char *sort;
1139 struct shadow_copy2_config *config;
1141 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1142 return);
1144 sort = config->sort_order;
1145 if (sort == NULL) {
1146 return;
1149 if (strcmp(sort, "asc") == 0) {
1150 cmpfunc = shadow_copy2_label_cmp_asc;
1151 } else if (strcmp(sort, "desc") == 0) {
1152 cmpfunc = shadow_copy2_label_cmp_desc;
1153 } else {
1154 return;
1157 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1158 shadow_copy2_data->labels)
1160 TYPESAFE_QSORT(shadow_copy2_data->labels,
1161 shadow_copy2_data->num_volumes,
1162 cmpfunc);
1166 static int shadow_copy2_get_shadow_copy_data(
1167 vfs_handle_struct *handle, files_struct *fsp,
1168 struct shadow_copy_data *shadow_copy2_data,
1169 bool labels)
1171 DIR *p;
1172 const char *snapdir;
1173 struct dirent *d;
1174 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1176 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1177 if (snapdir == NULL) {
1178 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1179 handle->conn->connectpath));
1180 errno = EINVAL;
1181 talloc_free(tmp_ctx);
1182 return -1;
1185 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1187 if (!p) {
1188 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1189 " - %s\n", snapdir, strerror(errno)));
1190 talloc_free(tmp_ctx);
1191 errno = ENOSYS;
1192 return -1;
1195 shadow_copy2_data->num_volumes = 0;
1196 shadow_copy2_data->labels = NULL;
1198 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1199 char snapshot[GMT_NAME_LEN+1];
1200 SHADOW_COPY_LABEL *tlabels;
1203 * ignore names not of the right form in the snapshot
1204 * directory
1206 if (!shadow_copy2_snapshot_to_gmt(
1207 handle, d->d_name,
1208 snapshot, sizeof(snapshot))) {
1210 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1211 "ignoring %s\n", d->d_name));
1212 continue;
1214 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1215 d->d_name, snapshot));
1217 if (!labels) {
1218 /* the caller doesn't want the labels */
1219 shadow_copy2_data->num_volumes++;
1220 continue;
1223 tlabels = talloc_realloc(shadow_copy2_data,
1224 shadow_copy2_data->labels,
1225 SHADOW_COPY_LABEL,
1226 shadow_copy2_data->num_volumes+1);
1227 if (tlabels == NULL) {
1228 DEBUG(0,("shadow_copy2: out of memory\n"));
1229 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1230 talloc_free(tmp_ctx);
1231 return -1;
1234 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1235 sizeof(*tlabels));
1237 shadow_copy2_data->num_volumes++;
1238 shadow_copy2_data->labels = tlabels;
1241 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1243 shadow_copy2_sort_data(handle, shadow_copy2_data);
1245 talloc_free(tmp_ctx);
1246 return 0;
1249 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1250 struct files_struct *fsp,
1251 uint32 security_info,
1252 TALLOC_CTX *mem_ctx,
1253 struct security_descriptor **ppdesc)
1255 time_t timestamp;
1256 char *stripped;
1257 NTSTATUS status;
1258 char *conv;
1260 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1261 fsp->fsp_name->base_name,
1262 &timestamp, &stripped)) {
1263 return map_nt_error_from_unix(errno);
1265 if (timestamp == 0) {
1266 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1267 mem_ctx,
1268 ppdesc);
1270 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1271 TALLOC_FREE(stripped);
1272 if (conv == NULL) {
1273 return map_nt_error_from_unix(errno);
1275 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1276 mem_ctx, ppdesc);
1277 TALLOC_FREE(conv);
1278 return status;
1281 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1282 const char *fname,
1283 uint32 security_info,
1284 TALLOC_CTX *mem_ctx,
1285 struct security_descriptor **ppdesc)
1287 time_t timestamp;
1288 char *stripped;
1289 NTSTATUS status;
1290 char *conv;
1292 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1293 &timestamp, &stripped)) {
1294 return map_nt_error_from_unix(errno);
1296 if (timestamp == 0) {
1297 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1298 mem_ctx, ppdesc);
1300 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1301 TALLOC_FREE(stripped);
1302 if (conv == NULL) {
1303 return map_nt_error_from_unix(errno);
1305 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1306 mem_ctx, ppdesc);
1307 TALLOC_FREE(conv);
1308 return status;
1311 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1312 const char *fname, mode_t mode)
1314 time_t timestamp;
1315 char *stripped;
1316 int ret, saved_errno;
1317 char *conv;
1319 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1320 &timestamp, &stripped)) {
1321 return -1;
1323 if (timestamp == 0) {
1324 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1326 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1327 TALLOC_FREE(stripped);
1328 if (conv == NULL) {
1329 return -1;
1331 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1332 saved_errno = errno;
1333 TALLOC_FREE(conv);
1334 errno = saved_errno;
1335 return ret;
1338 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1340 time_t timestamp;
1341 char *stripped;
1342 int ret, saved_errno;
1343 char *conv;
1345 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1346 &timestamp, &stripped)) {
1347 return -1;
1349 if (timestamp == 0) {
1350 return SMB_VFS_NEXT_RMDIR(handle, fname);
1352 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1353 TALLOC_FREE(stripped);
1354 if (conv == NULL) {
1355 return -1;
1357 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1358 saved_errno = errno;
1359 TALLOC_FREE(conv);
1360 errno = saved_errno;
1361 return ret;
1364 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1365 unsigned int flags)
1367 time_t timestamp;
1368 char *stripped;
1369 int ret, saved_errno;
1370 char *conv;
1372 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1373 &timestamp, &stripped)) {
1374 return -1;
1376 if (timestamp == 0) {
1377 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1379 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1380 TALLOC_FREE(stripped);
1381 if (conv == NULL) {
1382 return -1;
1384 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1385 saved_errno = errno;
1386 TALLOC_FREE(conv);
1387 errno = saved_errno;
1388 return ret;
1391 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1392 const char *fname, const char *aname,
1393 void *value, size_t size)
1395 time_t timestamp;
1396 char *stripped;
1397 ssize_t ret;
1398 int saved_errno;
1399 char *conv;
1401 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1402 &timestamp, &stripped)) {
1403 return -1;
1405 if (timestamp == 0) {
1406 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1407 size);
1409 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1410 TALLOC_FREE(stripped);
1411 if (conv == NULL) {
1412 return -1;
1414 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1415 saved_errno = errno;
1416 TALLOC_FREE(conv);
1417 errno = saved_errno;
1418 return ret;
1421 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1422 const char *fname,
1423 char *list, size_t size)
1425 time_t timestamp;
1426 char *stripped;
1427 ssize_t ret;
1428 int saved_errno;
1429 char *conv;
1431 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1432 &timestamp, &stripped)) {
1433 return -1;
1435 if (timestamp == 0) {
1436 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1438 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1439 TALLOC_FREE(stripped);
1440 if (conv == NULL) {
1441 return -1;
1443 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1444 saved_errno = errno;
1445 TALLOC_FREE(conv);
1446 errno = saved_errno;
1447 return ret;
1450 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1451 const char *fname, const char *aname)
1453 time_t timestamp;
1454 char *stripped;
1455 int ret, saved_errno;
1456 char *conv;
1458 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1459 &timestamp, &stripped)) {
1460 return -1;
1462 if (timestamp == 0) {
1463 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1465 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1466 TALLOC_FREE(stripped);
1467 if (conv == NULL) {
1468 return -1;
1470 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1471 saved_errno = errno;
1472 TALLOC_FREE(conv);
1473 errno = saved_errno;
1474 return ret;
1477 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1478 const char *fname,
1479 const char *aname, const void *value,
1480 size_t size, int flags)
1482 time_t timestamp;
1483 char *stripped;
1484 ssize_t ret;
1485 int saved_errno;
1486 char *conv;
1488 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1489 &timestamp, &stripped)) {
1490 return -1;
1492 if (timestamp == 0) {
1493 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1494 flags);
1496 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1497 TALLOC_FREE(stripped);
1498 if (conv == NULL) {
1499 return -1;
1501 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1502 saved_errno = errno;
1503 TALLOC_FREE(conv);
1504 errno = saved_errno;
1505 return ret;
1508 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1509 const char *fname, mode_t mode)
1511 time_t timestamp;
1512 char *stripped;
1513 ssize_t ret;
1514 int saved_errno;
1515 char *conv;
1517 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1518 &timestamp, &stripped)) {
1519 return -1;
1521 if (timestamp == 0) {
1522 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1524 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1525 TALLOC_FREE(stripped);
1526 if (conv == NULL) {
1527 return -1;
1529 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1530 saved_errno = errno;
1531 TALLOC_FREE(conv);
1532 errno = saved_errno;
1533 return ret;
1536 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1537 const char *path,
1538 const char *name,
1539 TALLOC_CTX *mem_ctx,
1540 char **found_name)
1542 time_t timestamp;
1543 char *stripped;
1544 ssize_t ret;
1545 int saved_errno;
1546 char *conv;
1548 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1549 &timestamp, &stripped)) {
1550 return -1;
1552 if (timestamp == 0) {
1553 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1554 mem_ctx, found_name);
1556 if (stripped[0] == '\0') {
1557 *found_name = talloc_strdup(mem_ctx, name);
1558 if (*found_name == NULL) {
1559 errno = ENOMEM;
1560 return -1;
1562 return 0;
1564 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1565 TALLOC_FREE(stripped);
1566 if (conv == NULL) {
1567 return -1;
1569 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1570 mem_ctx, found_name);
1571 saved_errno = errno;
1572 TALLOC_FREE(conv);
1573 errno = saved_errno;
1574 return ret;
1577 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1578 const char *service, const char *user)
1580 struct shadow_copy2_config *config;
1581 int ret;
1582 const char *snapdir;
1583 const char *gmt_format;
1584 const char *sort_order;
1585 const char *basedir;
1587 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1588 (unsigned)handle->conn->cnum,
1589 handle->conn->connectpath));
1591 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1592 if (ret < 0) {
1593 return ret;
1596 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1597 if (config == NULL) {
1598 DEBUG(0, ("talloc_zero() failed\n"));
1599 errno = ENOMEM;
1600 return -1;
1603 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1604 "shadow", "format",
1605 GMT_FORMAT);
1606 config->gmt_format = talloc_strdup(config, gmt_format);
1607 if (config->gmt_format == NULL) {
1608 DEBUG(0, ("talloc_strdup() failed\n"));
1609 errno = ENOMEM;
1610 return -1;
1613 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1614 "shadow", "sscanf", false);
1616 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1617 "shadow", "localtime",
1618 false);
1620 snapdir = lp_parm_const_string(SNUM(handle->conn),
1621 "shadow", "snapdir",
1622 ".snapshots");
1623 config->snapdir = talloc_strdup(config, snapdir);
1624 if (config->snapdir == NULL) {
1625 DEBUG(0, ("talloc_strdup() failed\n"));
1626 errno = ENOMEM;
1627 return -1;
1630 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1631 "shadow",
1632 "snapdirseverywhere",
1633 false);
1635 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1636 "shadow", "crossmountpoints",
1637 false);
1639 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1640 "shadow", "fixinodes",
1641 false);
1643 sort_order = lp_parm_const_string(SNUM(handle->conn),
1644 "shadow", "sort", "desc");
1645 config->sort_order = talloc_strdup(config, sort_order);
1646 if (config->sort_order == NULL) {
1647 DEBUG(0, ("talloc_strdup() failed\n"));
1648 errno = ENOMEM;
1649 return -1;
1652 config->mount_point = shadow_copy2_find_mount_point(config, handle);
1653 if (config->mount_point == NULL) {
1654 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point "
1655 "failed: %s\n", strerror(errno)));
1656 return -1;
1659 basedir = lp_parm_const_string(SNUM(handle->conn),
1660 "shadow", "basedir", NULL);
1662 if (basedir != NULL) {
1663 if (basedir[0] != '/') {
1664 DEBUG(1, (__location__ " Warning: 'basedir' is "
1665 "relative ('%s'), but it has to be an "
1666 "absolute path. Disabling basedir.\n",
1667 basedir));
1668 } else {
1669 char *p;
1670 p = strstr(basedir, config->mount_point);
1671 if (p != basedir) {
1672 DEBUG(1, ("Warning: basedir (%s) is not a "
1673 "subdirectory of the share root's "
1674 "mount point (%s). "
1675 "Disabling basedir\n",
1676 basedir, config->mount_point));
1677 } else {
1678 config->basedir = talloc_strdup(config,
1679 basedir);
1680 if (config->basedir == NULL) {
1681 DEBUG(0, ("talloc_strdup() failed\n"));
1682 errno = ENOMEM;
1683 return -1;
1689 if (config->snapdirseverywhere && config->basedir != NULL) {
1690 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1691 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1692 TALLOC_FREE(config->basedir);
1695 if (config->crossmountpoints && config->basedir != NULL) {
1696 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1697 "with 'crossmountpoints'. Disabling basedir.\n"));
1698 TALLOC_FREE(config->basedir);
1701 if (config->basedir == NULL) {
1702 config->basedir = config->mount_point;
1705 if (config->snapdir[0] == '/') {
1706 config->snapdir_absolute = true;
1707 if (config->snapdirseverywhere == true) {
1708 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1709 "is incompatible with 'snapdirseverywhere', "
1710 "setting 'snapdirseverywhere' to false.\n"));
1711 config->snapdirseverywhere = false;
1714 if (config->crossmountpoints == true) {
1715 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1716 "is not supported with an absolute snapdir. "
1717 "Disabling it.\n"));
1718 config->crossmountpoints = false;
1722 SMB_VFS_HANDLE_SET_DATA(handle, config,
1723 NULL, struct shadow_copy2_config,
1724 return -1);
1726 return 0;
1729 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1730 .connect_fn = shadow_copy2_connect,
1731 .opendir_fn = shadow_copy2_opendir,
1732 .rename_fn = shadow_copy2_rename,
1733 .link_fn = shadow_copy2_link,
1734 .symlink_fn = shadow_copy2_symlink,
1735 .stat_fn = shadow_copy2_stat,
1736 .lstat_fn = shadow_copy2_lstat,
1737 .fstat_fn = shadow_copy2_fstat,
1738 .open_fn = shadow_copy2_open,
1739 .unlink_fn = shadow_copy2_unlink,
1740 .chmod_fn = shadow_copy2_chmod,
1741 .chown_fn = shadow_copy2_chown,
1742 .chdir_fn = shadow_copy2_chdir,
1743 .ntimes_fn = shadow_copy2_ntimes,
1744 .readlink_fn = shadow_copy2_readlink,
1745 .mknod_fn = shadow_copy2_mknod,
1746 .realpath_fn = shadow_copy2_realpath,
1747 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1748 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1749 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1750 .mkdir_fn = shadow_copy2_mkdir,
1751 .rmdir_fn = shadow_copy2_rmdir,
1752 .getxattr_fn = shadow_copy2_getxattr,
1753 .listxattr_fn = shadow_copy2_listxattr,
1754 .removexattr_fn = shadow_copy2_removexattr,
1755 .setxattr_fn = shadow_copy2_setxattr,
1756 .chmod_acl_fn = shadow_copy2_chmod_acl,
1757 .chflags_fn = shadow_copy2_chflags,
1758 .get_real_filename_fn = shadow_copy2_get_real_filename,
1761 NTSTATUS vfs_shadow_copy2_init(void);
1762 NTSTATUS vfs_shadow_copy2_init(void)
1764 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1765 "shadow_copy2", &vfs_shadow_copy2_fns);