shadow_copy2: introduce "shadow:mountpoint" option
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob1a5f211c5351bc02fc94bc15840771fc511276e9
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;
751 NTSTATUS status;
753 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
754 smb_fname->base_name,
755 &timestamp, &stripped)) {
756 return -1;
758 if (timestamp == 0) {
759 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
761 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
762 if (!NT_STATUS_IS_OK(status)) {
763 errno = ENOMEM;
764 return -1;
766 conv->base_name = shadow_copy2_convert(
767 conv, handle, stripped, timestamp);
768 TALLOC_FREE(stripped);
769 if (conv->base_name == NULL) {
770 return -1;
772 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
773 saved_errno = errno;
774 TALLOC_FREE(conv);
775 errno = saved_errno;
776 return ret;
779 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
780 mode_t mode)
782 time_t timestamp;
783 char *stripped;
784 int ret, saved_errno;
785 char *conv;
787 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
788 &timestamp, &stripped)) {
789 return -1;
791 if (timestamp == 0) {
792 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
794 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
795 TALLOC_FREE(stripped);
796 if (conv == NULL) {
797 return -1;
799 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
800 saved_errno = errno;
801 TALLOC_FREE(conv);
802 errno = saved_errno;
803 return ret;
806 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
807 uid_t uid, gid_t gid)
809 time_t timestamp;
810 char *stripped;
811 int ret, saved_errno;
812 char *conv;
814 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
815 &timestamp, &stripped)) {
816 return -1;
818 if (timestamp == 0) {
819 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
821 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
822 TALLOC_FREE(stripped);
823 if (conv == NULL) {
824 return -1;
826 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
827 saved_errno = errno;
828 TALLOC_FREE(conv);
829 errno = saved_errno;
830 return ret;
833 static int shadow_copy2_chdir(vfs_handle_struct *handle,
834 const char *fname)
836 time_t timestamp;
837 char *stripped;
838 int ret, saved_errno;
839 char *conv;
841 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
842 &timestamp, &stripped)) {
843 return -1;
845 if (timestamp == 0) {
846 return SMB_VFS_NEXT_CHDIR(handle, fname);
848 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
849 TALLOC_FREE(stripped);
850 if (conv == NULL) {
851 return -1;
853 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
854 saved_errno = errno;
855 TALLOC_FREE(conv);
856 errno = saved_errno;
857 return ret;
860 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
861 const struct smb_filename *smb_fname,
862 struct smb_file_time *ft)
864 time_t timestamp;
865 char *stripped;
866 int ret, saved_errno;
867 struct smb_filename *conv;
868 NTSTATUS status;
870 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
871 smb_fname->base_name,
872 &timestamp, &stripped)) {
873 return -1;
875 if (timestamp == 0) {
876 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
878 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
879 if (!NT_STATUS_IS_OK(status)) {
880 errno = ENOMEM;
881 return -1;
883 conv->base_name = shadow_copy2_convert(
884 conv, handle, stripped, timestamp);
885 TALLOC_FREE(stripped);
886 if (conv->base_name == NULL) {
887 return -1;
889 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
890 saved_errno = errno;
891 TALLOC_FREE(conv);
892 errno = saved_errno;
893 return ret;
896 static int shadow_copy2_readlink(vfs_handle_struct *handle,
897 const char *fname, char *buf, size_t bufsiz)
899 time_t timestamp;
900 char *stripped;
901 int ret, saved_errno;
902 char *conv;
904 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
905 &timestamp, &stripped)) {
906 return -1;
908 if (timestamp == 0) {
909 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
911 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
912 TALLOC_FREE(stripped);
913 if (conv == NULL) {
914 return -1;
916 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
917 saved_errno = errno;
918 TALLOC_FREE(conv);
919 errno = saved_errno;
920 return ret;
923 static int shadow_copy2_mknod(vfs_handle_struct *handle,
924 const char *fname, mode_t mode, SMB_DEV_T dev)
926 time_t timestamp;
927 char *stripped;
928 int ret, saved_errno;
929 char *conv;
931 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
932 &timestamp, &stripped)) {
933 return -1;
935 if (timestamp == 0) {
936 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
938 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
939 TALLOC_FREE(stripped);
940 if (conv == NULL) {
941 return -1;
943 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
944 saved_errno = errno;
945 TALLOC_FREE(conv);
946 errno = saved_errno;
947 return ret;
950 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
951 const char *fname)
953 time_t timestamp;
954 char *stripped = NULL;
955 char *tmp = NULL;
956 char *result = NULL;
957 char *inserted = NULL;
958 char *inserted_to, *inserted_end;
959 int saved_errno;
961 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
962 &timestamp, &stripped)) {
963 goto done;
965 if (timestamp == 0) {
966 return SMB_VFS_NEXT_REALPATH(handle, fname);
969 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
970 if (tmp == NULL) {
971 goto done;
974 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
975 if (result == NULL) {
976 goto done;
980 * Take away what we've inserted. This removes the @GMT-thingy
981 * completely, but will give a path under the share root.
983 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
984 if (inserted == NULL) {
985 goto done;
987 inserted_to = strstr_m(result, inserted);
988 if (inserted_to == NULL) {
989 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
990 goto done;
992 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
993 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
995 done:
996 saved_errno = errno;
997 TALLOC_FREE(inserted);
998 TALLOC_FREE(tmp);
999 TALLOC_FREE(stripped);
1000 errno = saved_errno;
1001 return result;
1005 * Check whether a given directory contains a
1006 * snapshot directory as direct subdirectory.
1007 * If yes, return the path of the snapshot-subdir,
1008 * otherwise return NULL.
1010 static char *have_snapdir(struct vfs_handle_struct *handle,
1011 const char *path)
1013 struct smb_filename smb_fname;
1014 int ret;
1015 struct shadow_copy2_config *config;
1017 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1018 return NULL);
1020 ZERO_STRUCT(smb_fname);
1021 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1022 path, config->snapdir);
1023 if (smb_fname.base_name == NULL) {
1024 return NULL;
1027 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1028 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1029 return smb_fname.base_name;
1031 TALLOC_FREE(smb_fname.base_name);
1032 return NULL;
1036 * Find the snapshot directory (if any) for the given
1037 * filename (which is relative to the share).
1039 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1040 struct vfs_handle_struct *handle,
1041 struct smb_filename *smb_fname)
1043 char *path, *p;
1044 char *snapdir;
1045 struct shadow_copy2_config *config;
1047 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1048 return NULL);
1050 path = talloc_asprintf(mem_ctx, "%s/%s",
1051 handle->conn->connectpath,
1052 smb_fname->base_name);
1053 if (path == NULL) {
1054 return NULL;
1057 snapdir = have_snapdir(handle, path);
1058 if (snapdir != NULL) {
1059 TALLOC_FREE(path);
1060 return snapdir;
1063 while ((p = strrchr(path, '/')) && (p > path)) {
1065 p[0] = '\0';
1067 snapdir = have_snapdir(handle, path);
1068 if (snapdir != NULL) {
1069 TALLOC_FREE(path);
1070 return snapdir;
1073 TALLOC_FREE(path);
1074 return NULL;
1077 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1078 const char *name,
1079 char *gmt, size_t gmt_len)
1081 struct tm timestamp;
1082 time_t timestamp_t;
1083 unsigned long int timestamp_long;
1084 const char *fmt;
1085 struct shadow_copy2_config *config;
1087 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1088 return NULL);
1090 fmt = config->gmt_format;
1092 ZERO_STRUCT(timestamp);
1093 if (config->use_sscanf) {
1094 if (sscanf(name, fmt, &timestamp_long) != 1) {
1095 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1096 "no sscanf match %s: %s\n",
1097 fmt, name));
1098 return false;
1100 timestamp_t = timestamp_long;
1101 gmtime_r(&timestamp_t, &timestamp);
1102 } else {
1103 if (strptime(name, fmt, &timestamp) == NULL) {
1104 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1105 "no match %s: %s\n",
1106 fmt, name));
1107 return false;
1109 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1110 fmt, name));
1112 if (config->use_localtime) {
1113 timestamp.tm_isdst = -1;
1114 timestamp_t = mktime(&timestamp);
1115 gmtime_r(&timestamp_t, &timestamp);
1119 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1120 return true;
1123 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1125 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1128 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1130 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1134 sort the shadow copy data in ascending or descending order
1136 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1137 struct shadow_copy_data *shadow_copy2_data)
1139 int (*cmpfunc)(const void *, const void *);
1140 const char *sort;
1141 struct shadow_copy2_config *config;
1143 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1144 return);
1146 sort = config->sort_order;
1147 if (sort == NULL) {
1148 return;
1151 if (strcmp(sort, "asc") == 0) {
1152 cmpfunc = shadow_copy2_label_cmp_asc;
1153 } else if (strcmp(sort, "desc") == 0) {
1154 cmpfunc = shadow_copy2_label_cmp_desc;
1155 } else {
1156 return;
1159 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1160 shadow_copy2_data->labels)
1162 TYPESAFE_QSORT(shadow_copy2_data->labels,
1163 shadow_copy2_data->num_volumes,
1164 cmpfunc);
1168 static int shadow_copy2_get_shadow_copy_data(
1169 vfs_handle_struct *handle, files_struct *fsp,
1170 struct shadow_copy_data *shadow_copy2_data,
1171 bool labels)
1173 DIR *p;
1174 const char *snapdir;
1175 struct dirent *d;
1176 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1178 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1179 if (snapdir == NULL) {
1180 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1181 handle->conn->connectpath));
1182 errno = EINVAL;
1183 talloc_free(tmp_ctx);
1184 return -1;
1187 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1189 if (!p) {
1190 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1191 " - %s\n", snapdir, strerror(errno)));
1192 talloc_free(tmp_ctx);
1193 errno = ENOSYS;
1194 return -1;
1197 shadow_copy2_data->num_volumes = 0;
1198 shadow_copy2_data->labels = NULL;
1200 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1201 char snapshot[GMT_NAME_LEN+1];
1202 SHADOW_COPY_LABEL *tlabels;
1205 * ignore names not of the right form in the snapshot
1206 * directory
1208 if (!shadow_copy2_snapshot_to_gmt(
1209 handle, d->d_name,
1210 snapshot, sizeof(snapshot))) {
1212 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1213 "ignoring %s\n", d->d_name));
1214 continue;
1216 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1217 d->d_name, snapshot));
1219 if (!labels) {
1220 /* the caller doesn't want the labels */
1221 shadow_copy2_data->num_volumes++;
1222 continue;
1225 tlabels = talloc_realloc(shadow_copy2_data,
1226 shadow_copy2_data->labels,
1227 SHADOW_COPY_LABEL,
1228 shadow_copy2_data->num_volumes+1);
1229 if (tlabels == NULL) {
1230 DEBUG(0,("shadow_copy2: out of memory\n"));
1231 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1232 talloc_free(tmp_ctx);
1233 return -1;
1236 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1237 sizeof(*tlabels));
1239 shadow_copy2_data->num_volumes++;
1240 shadow_copy2_data->labels = tlabels;
1243 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1245 shadow_copy2_sort_data(handle, shadow_copy2_data);
1247 talloc_free(tmp_ctx);
1248 return 0;
1251 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1252 struct files_struct *fsp,
1253 uint32 security_info,
1254 TALLOC_CTX *mem_ctx,
1255 struct security_descriptor **ppdesc)
1257 time_t timestamp;
1258 char *stripped;
1259 NTSTATUS status;
1260 char *conv;
1262 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1263 fsp->fsp_name->base_name,
1264 &timestamp, &stripped)) {
1265 return map_nt_error_from_unix(errno);
1267 if (timestamp == 0) {
1268 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1269 mem_ctx,
1270 ppdesc);
1272 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1273 TALLOC_FREE(stripped);
1274 if (conv == NULL) {
1275 return map_nt_error_from_unix(errno);
1277 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1278 mem_ctx, ppdesc);
1279 TALLOC_FREE(conv);
1280 return status;
1283 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1284 const char *fname,
1285 uint32 security_info,
1286 TALLOC_CTX *mem_ctx,
1287 struct security_descriptor **ppdesc)
1289 time_t timestamp;
1290 char *stripped;
1291 NTSTATUS status;
1292 char *conv;
1294 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1295 &timestamp, &stripped)) {
1296 return map_nt_error_from_unix(errno);
1298 if (timestamp == 0) {
1299 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1300 mem_ctx, ppdesc);
1302 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1303 TALLOC_FREE(stripped);
1304 if (conv == NULL) {
1305 return map_nt_error_from_unix(errno);
1307 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1308 mem_ctx, ppdesc);
1309 TALLOC_FREE(conv);
1310 return status;
1313 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1314 const char *fname, mode_t mode)
1316 time_t timestamp;
1317 char *stripped;
1318 int ret, saved_errno;
1319 char *conv;
1321 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1322 &timestamp, &stripped)) {
1323 return -1;
1325 if (timestamp == 0) {
1326 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1328 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1329 TALLOC_FREE(stripped);
1330 if (conv == NULL) {
1331 return -1;
1333 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1334 saved_errno = errno;
1335 TALLOC_FREE(conv);
1336 errno = saved_errno;
1337 return ret;
1340 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1342 time_t timestamp;
1343 char *stripped;
1344 int ret, saved_errno;
1345 char *conv;
1347 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1348 &timestamp, &stripped)) {
1349 return -1;
1351 if (timestamp == 0) {
1352 return SMB_VFS_NEXT_RMDIR(handle, fname);
1354 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1355 TALLOC_FREE(stripped);
1356 if (conv == NULL) {
1357 return -1;
1359 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1360 saved_errno = errno;
1361 TALLOC_FREE(conv);
1362 errno = saved_errno;
1363 return ret;
1366 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1367 unsigned int flags)
1369 time_t timestamp;
1370 char *stripped;
1371 int ret, saved_errno;
1372 char *conv;
1374 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1375 &timestamp, &stripped)) {
1376 return -1;
1378 if (timestamp == 0) {
1379 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1381 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1382 TALLOC_FREE(stripped);
1383 if (conv == NULL) {
1384 return -1;
1386 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1387 saved_errno = errno;
1388 TALLOC_FREE(conv);
1389 errno = saved_errno;
1390 return ret;
1393 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1394 const char *fname, const char *aname,
1395 void *value, size_t size)
1397 time_t timestamp;
1398 char *stripped;
1399 ssize_t ret;
1400 int saved_errno;
1401 char *conv;
1403 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1404 &timestamp, &stripped)) {
1405 return -1;
1407 if (timestamp == 0) {
1408 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1409 size);
1411 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1412 TALLOC_FREE(stripped);
1413 if (conv == NULL) {
1414 return -1;
1416 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1417 saved_errno = errno;
1418 TALLOC_FREE(conv);
1419 errno = saved_errno;
1420 return ret;
1423 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1424 const char *fname,
1425 char *list, size_t size)
1427 time_t timestamp;
1428 char *stripped;
1429 ssize_t ret;
1430 int saved_errno;
1431 char *conv;
1433 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1434 &timestamp, &stripped)) {
1435 return -1;
1437 if (timestamp == 0) {
1438 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1440 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1441 TALLOC_FREE(stripped);
1442 if (conv == NULL) {
1443 return -1;
1445 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1446 saved_errno = errno;
1447 TALLOC_FREE(conv);
1448 errno = saved_errno;
1449 return ret;
1452 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1453 const char *fname, const char *aname)
1455 time_t timestamp;
1456 char *stripped;
1457 int ret, saved_errno;
1458 char *conv;
1460 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1461 &timestamp, &stripped)) {
1462 return -1;
1464 if (timestamp == 0) {
1465 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1467 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1468 TALLOC_FREE(stripped);
1469 if (conv == NULL) {
1470 return -1;
1472 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1473 saved_errno = errno;
1474 TALLOC_FREE(conv);
1475 errno = saved_errno;
1476 return ret;
1479 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1480 const char *fname,
1481 const char *aname, const void *value,
1482 size_t size, int flags)
1484 time_t timestamp;
1485 char *stripped;
1486 ssize_t ret;
1487 int saved_errno;
1488 char *conv;
1490 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1491 &timestamp, &stripped)) {
1492 return -1;
1494 if (timestamp == 0) {
1495 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1496 flags);
1498 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1499 TALLOC_FREE(stripped);
1500 if (conv == NULL) {
1501 return -1;
1503 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1504 saved_errno = errno;
1505 TALLOC_FREE(conv);
1506 errno = saved_errno;
1507 return ret;
1510 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1511 const char *fname, mode_t mode)
1513 time_t timestamp;
1514 char *stripped;
1515 ssize_t ret;
1516 int saved_errno;
1517 char *conv;
1519 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1520 &timestamp, &stripped)) {
1521 return -1;
1523 if (timestamp == 0) {
1524 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1526 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1527 TALLOC_FREE(stripped);
1528 if (conv == NULL) {
1529 return -1;
1531 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1532 saved_errno = errno;
1533 TALLOC_FREE(conv);
1534 errno = saved_errno;
1535 return ret;
1538 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1539 const char *path,
1540 const char *name,
1541 TALLOC_CTX *mem_ctx,
1542 char **found_name)
1544 time_t timestamp;
1545 char *stripped;
1546 ssize_t ret;
1547 int saved_errno;
1548 char *conv;
1550 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1551 &timestamp, &stripped)) {
1552 return -1;
1554 if (timestamp == 0) {
1555 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1556 mem_ctx, found_name);
1558 if (stripped[0] == '\0') {
1559 *found_name = talloc_strdup(mem_ctx, name);
1560 if (*found_name == NULL) {
1561 errno = ENOMEM;
1562 return -1;
1564 return 0;
1566 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1567 TALLOC_FREE(stripped);
1568 if (conv == NULL) {
1569 return -1;
1571 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1572 mem_ctx, found_name);
1573 saved_errno = errno;
1574 TALLOC_FREE(conv);
1575 errno = saved_errno;
1576 return ret;
1579 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1580 const char *service, const char *user)
1582 struct shadow_copy2_config *config;
1583 int ret;
1584 const char *snapdir;
1585 const char *gmt_format;
1586 const char *sort_order;
1587 const char *basedir;
1588 const char *mount_point;
1590 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1591 (unsigned)handle->conn->cnum,
1592 handle->conn->connectpath));
1594 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1595 if (ret < 0) {
1596 return ret;
1599 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1600 if (config == NULL) {
1601 DEBUG(0, ("talloc_zero() failed\n"));
1602 errno = ENOMEM;
1603 return -1;
1606 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1607 "shadow", "format",
1608 GMT_FORMAT);
1609 config->gmt_format = talloc_strdup(config, gmt_format);
1610 if (config->gmt_format == NULL) {
1611 DEBUG(0, ("talloc_strdup() failed\n"));
1612 errno = ENOMEM;
1613 return -1;
1616 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1617 "shadow", "sscanf", false);
1619 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1620 "shadow", "localtime",
1621 false);
1623 snapdir = lp_parm_const_string(SNUM(handle->conn),
1624 "shadow", "snapdir",
1625 ".snapshots");
1626 config->snapdir = talloc_strdup(config, snapdir);
1627 if (config->snapdir == NULL) {
1628 DEBUG(0, ("talloc_strdup() failed\n"));
1629 errno = ENOMEM;
1630 return -1;
1633 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1634 "shadow",
1635 "snapdirseverywhere",
1636 false);
1638 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1639 "shadow", "crossmountpoints",
1640 false);
1642 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1643 "shadow", "fixinodes",
1644 false);
1646 sort_order = lp_parm_const_string(SNUM(handle->conn),
1647 "shadow", "sort", "desc");
1648 config->sort_order = talloc_strdup(config, sort_order);
1649 if (config->sort_order == NULL) {
1650 DEBUG(0, ("talloc_strdup() failed\n"));
1651 errno = ENOMEM;
1652 return -1;
1655 mount_point = lp_parm_const_string(SNUM(handle->conn),
1656 "shadow", "mountpoint", NULL);
1657 if (mount_point != NULL) {
1658 if (mount_point[0] != '/') {
1659 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1660 "relative ('%s'), but it has to be an "
1661 "absolute path. Ignoring provided value.\n",
1662 mount_point));
1663 mount_point = NULL;
1664 } else {
1665 char *p;
1666 p = strstr(handle->conn->connectpath, mount_point);
1667 if (p != handle->conn->connectpath) {
1668 DEBUG(1, ("Warning: mount_point (%s) is not a "
1669 "subdirectory of the share root "
1670 "(%s). Ignoring provided value.\n",
1671 mount_point,
1672 handle->conn->connectpath));
1673 mount_point = NULL;
1678 if (mount_point != NULL) {
1679 config->mount_point = talloc_strdup(config, mount_point);
1680 if (config->mount_point == NULL) {
1681 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1682 return -1;
1684 } else {
1685 config->mount_point = shadow_copy2_find_mount_point(config,
1686 handle);
1687 if (config->mount_point == NULL) {
1688 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1689 " failed: %s\n", strerror(errno)));
1690 return -1;
1694 basedir = lp_parm_const_string(SNUM(handle->conn),
1695 "shadow", "basedir", NULL);
1697 if (basedir != NULL) {
1698 if (basedir[0] != '/') {
1699 DEBUG(1, (__location__ " Warning: 'basedir' is "
1700 "relative ('%s'), but it has to be an "
1701 "absolute path. Disabling basedir.\n",
1702 basedir));
1703 } else {
1704 char *p;
1705 p = strstr(basedir, config->mount_point);
1706 if (p != basedir) {
1707 DEBUG(1, ("Warning: basedir (%s) is not a "
1708 "subdirectory of the share root's "
1709 "mount point (%s). "
1710 "Disabling basedir\n",
1711 basedir, config->mount_point));
1712 } else {
1713 config->basedir = talloc_strdup(config,
1714 basedir);
1715 if (config->basedir == NULL) {
1716 DEBUG(0, ("talloc_strdup() failed\n"));
1717 errno = ENOMEM;
1718 return -1;
1724 if (config->snapdirseverywhere && config->basedir != NULL) {
1725 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1726 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1727 TALLOC_FREE(config->basedir);
1730 if (config->crossmountpoints && config->basedir != NULL) {
1731 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1732 "with 'crossmountpoints'. Disabling basedir.\n"));
1733 TALLOC_FREE(config->basedir);
1736 if (config->basedir == NULL) {
1737 config->basedir = config->mount_point;
1740 if (config->snapdir[0] == '/') {
1741 config->snapdir_absolute = true;
1742 if (config->snapdirseverywhere == true) {
1743 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1744 "is incompatible with 'snapdirseverywhere', "
1745 "setting 'snapdirseverywhere' to false.\n"));
1746 config->snapdirseverywhere = false;
1749 if (config->crossmountpoints == true) {
1750 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1751 "is not supported with an absolute snapdir. "
1752 "Disabling it.\n"));
1753 config->crossmountpoints = false;
1757 SMB_VFS_HANDLE_SET_DATA(handle, config,
1758 NULL, struct shadow_copy2_config,
1759 return -1);
1761 return 0;
1764 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1765 .connect_fn = shadow_copy2_connect,
1766 .opendir_fn = shadow_copy2_opendir,
1767 .rename_fn = shadow_copy2_rename,
1768 .link_fn = shadow_copy2_link,
1769 .symlink_fn = shadow_copy2_symlink,
1770 .stat_fn = shadow_copy2_stat,
1771 .lstat_fn = shadow_copy2_lstat,
1772 .fstat_fn = shadow_copy2_fstat,
1773 .open_fn = shadow_copy2_open,
1774 .unlink_fn = shadow_copy2_unlink,
1775 .chmod_fn = shadow_copy2_chmod,
1776 .chown_fn = shadow_copy2_chown,
1777 .chdir_fn = shadow_copy2_chdir,
1778 .ntimes_fn = shadow_copy2_ntimes,
1779 .readlink_fn = shadow_copy2_readlink,
1780 .mknod_fn = shadow_copy2_mknod,
1781 .realpath_fn = shadow_copy2_realpath,
1782 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1783 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1784 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1785 .mkdir_fn = shadow_copy2_mkdir,
1786 .rmdir_fn = shadow_copy2_rmdir,
1787 .getxattr_fn = shadow_copy2_getxattr,
1788 .listxattr_fn = shadow_copy2_listxattr,
1789 .removexattr_fn = shadow_copy2_removexattr,
1790 .setxattr_fn = shadow_copy2_setxattr,
1791 .chmod_acl_fn = shadow_copy2_chmod_acl,
1792 .chflags_fn = shadow_copy2_chflags,
1793 .get_real_filename_fn = shadow_copy2_get_real_filename,
1796 NTSTATUS vfs_shadow_copy2_init(void);
1797 NTSTATUS vfs_shadow_copy2_init(void)
1799 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1800 "shadow_copy2", &vfs_shadow_copy2_fns);