shadow_copy2: in the classical case, use configured path in shadow_copy2_find_snapdir()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobf17f90e725de17a198dcc540e66ec059658621b9
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;
122 char *rel_connectpath; /* share root, relative to the basedir */
123 char *snapshot_basepath; /* the absolute version of snapdir */
126 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
127 size_t **poffsets,
128 unsigned *pnum_offsets)
130 unsigned num_offsets;
131 size_t *offsets;
132 const char *p;
134 num_offsets = 0;
136 p = str;
137 while ((p = strchr(p, '/')) != NULL) {
138 num_offsets += 1;
139 p += 1;
142 offsets = talloc_array(mem_ctx, size_t, num_offsets);
143 if (offsets == NULL) {
144 return false;
147 p = str;
148 num_offsets = 0;
149 while ((p = strchr(p, '/')) != NULL) {
150 offsets[num_offsets] = p-str;
151 num_offsets += 1;
152 p += 1;
155 *poffsets = offsets;
156 *pnum_offsets = num_offsets;
157 return true;
161 * Given a timstamp, build the string to insert into a path
162 * as a path component for creating the local path to the
163 * snapshot at the given timestamp of the input path.
165 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
166 struct vfs_handle_struct *handle,
167 time_t snapshot)
169 struct tm snap_tm;
170 fstring snaptime_string;
171 size_t snaptime_len;
172 struct shadow_copy2_config *config;
174 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
175 return NULL);
177 if (config->use_sscanf) {
178 snaptime_len = snprintf(snaptime_string,
179 sizeof(snaptime_string),
180 config->gmt_format,
181 (unsigned long)snapshot);
182 if (snaptime_len <= 0) {
183 DEBUG(10, ("snprintf failed\n"));
184 return NULL;
186 } else {
187 if (config->use_localtime) {
188 if (localtime_r(&snapshot, &snap_tm) == 0) {
189 DEBUG(10, ("gmtime_r failed\n"));
190 return NULL;
192 } else {
193 if (gmtime_r(&snapshot, &snap_tm) == 0) {
194 DEBUG(10, ("gmtime_r failed\n"));
195 return NULL;
198 snaptime_len = strftime(snaptime_string,
199 sizeof(snaptime_string),
200 config->gmt_format,
201 &snap_tm);
202 if (snaptime_len == 0) {
203 DEBUG(10, ("strftime failed\n"));
204 return NULL;
207 return talloc_asprintf(mem_ctx, "/%s/%s",
208 config->snapdir, snaptime_string);
212 * Strip a snapshot component from an filename as
213 * handed in via the smb layer.
214 * Returns the parsed timestamp and the stripped filename.
216 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
217 struct vfs_handle_struct *handle,
218 const char *name,
219 time_t *ptimestamp,
220 char **pstripped)
222 struct tm tm;
223 time_t timestamp;
224 const char *p;
225 char *q;
226 char *stripped;
227 size_t rest_len, dst_len;
228 struct shadow_copy2_config *config;
230 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
231 return false);
233 p = strstr_m(name, "@GMT-");
234 if (p == NULL) {
235 goto no_snapshot;
237 if ((p > name) && (p[-1] != '/')) {
238 goto no_snapshot;
240 q = strptime(p, GMT_FORMAT, &tm);
241 if (q == NULL) {
242 goto no_snapshot;
244 tm.tm_isdst = -1;
245 timestamp = timegm(&tm);
246 if (timestamp == (time_t)-1) {
247 goto no_snapshot;
249 if ((p == name) && (q[0] == '\0')) {
250 if (pstripped != NULL) {
251 stripped = talloc_strdup(mem_ctx, "");
252 if (stripped == NULL) {
253 return false;
255 *pstripped = stripped;
257 *ptimestamp = timestamp;
258 return true;
260 if (q[0] != '/') {
261 goto no_snapshot;
263 q += 1;
265 rest_len = strlen(q);
266 dst_len = (p-name) + rest_len;
268 if (config->snapdirseverywhere) {
269 char *insert;
270 bool have_insert;
271 insert = shadow_copy2_insert_string(talloc_tos(), handle,
272 timestamp);
273 if (insert == NULL) {
274 errno = ENOMEM;
275 return false;
278 have_insert = (strstr(name, insert+1) != NULL);
279 TALLOC_FREE(insert);
280 if (have_insert) {
281 goto no_snapshot;
285 if (pstripped != NULL) {
286 stripped = talloc_array(mem_ctx, char, dst_len+1);
287 if (stripped == NULL) {
288 errno = ENOMEM;
289 return false;
291 if (p > name) {
292 memcpy(stripped, name, p-name);
294 if (rest_len > 0) {
295 memcpy(stripped + (p-name), q, rest_len);
297 stripped[dst_len] = '\0';
298 *pstripped = stripped;
300 *ptimestamp = timestamp;
301 return true;
302 no_snapshot:
303 *ptimestamp = 0;
304 return true;
307 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
308 vfs_handle_struct *handle)
310 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
311 dev_t dev;
312 struct stat st;
313 char *p;
315 if (stat(path, &st) != 0) {
316 talloc_free(path);
317 return NULL;
320 dev = st.st_dev;
322 while ((p = strrchr(path, '/')) && p > path) {
323 *p = 0;
324 if (stat(path, &st) != 0) {
325 talloc_free(path);
326 return NULL;
328 if (st.st_dev != dev) {
329 *p = '/';
330 break;
334 return path;
338 * Convert from a name as handed in via the SMB layer
339 * and a timestamp into the local path of the snapshot
340 * of the provided file at the provided time.
342 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
343 struct vfs_handle_struct *handle,
344 const char *name, time_t timestamp)
346 struct smb_filename converted_fname;
347 char *result = NULL;
348 size_t *slashes = NULL;
349 unsigned num_slashes;
350 char *path = NULL;
351 size_t pathlen;
352 char *insert = NULL;
353 char *converted = NULL;
354 size_t insertlen;
355 int i, saved_errno;
356 size_t min_offset;
357 struct shadow_copy2_config *config;
359 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
360 return NULL);
362 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
363 name);
364 if (path == NULL) {
365 errno = ENOMEM;
366 goto fail;
368 pathlen = talloc_get_size(path)-1;
370 DEBUG(10, ("converting %s\n", path));
372 if (!shadow_copy2_find_slashes(talloc_tos(), path,
373 &slashes, &num_slashes)) {
374 goto fail;
376 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
377 if (insert == NULL) {
378 goto fail;
380 insertlen = talloc_get_size(insert)-1;
381 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
382 if (converted == NULL) {
383 goto fail;
386 if (path[pathlen-1] != '/') {
388 * Append a fake slash to find the snapshot root
390 size_t *tmp;
391 tmp = talloc_realloc(talloc_tos(), slashes,
392 size_t, num_slashes+1);
393 if (tmp == NULL) {
394 goto fail;
396 slashes = tmp;
397 slashes[num_slashes] = pathlen;
398 num_slashes += 1;
401 min_offset = 0;
403 if (!config->crossmountpoints) {
404 char *mount_point;
406 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
407 handle);
408 if (mount_point == NULL) {
409 goto fail;
411 min_offset = strlen(mount_point);
412 TALLOC_FREE(mount_point);
415 memcpy(converted, path, pathlen+1);
416 converted[pathlen+insertlen] = '\0';
418 ZERO_STRUCT(converted_fname);
419 converted_fname.base_name = converted;
421 for (i = num_slashes-1; i>=0; i--) {
422 int ret;
423 size_t offset;
425 offset = slashes[i];
427 if (offset < min_offset) {
428 errno = ENOENT;
429 goto fail;
432 memcpy(converted+offset, insert, insertlen);
434 offset += insertlen;
435 memcpy(converted+offset, path + slashes[i],
436 pathlen - slashes[i]);
438 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
440 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
441 ret, ret == 0 ? "ok" : strerror(errno)));
442 if (ret == 0) {
443 /* success */
444 break;
446 if (errno == ENOTDIR) {
448 * This is a valid condition: We appended the
449 * .snaphots/@GMT.. to a file name. Just try
450 * with the upper levels.
452 continue;
454 if (errno != ENOENT) {
455 /* Other problem than "not found" */
456 goto fail;
460 if (i >= 0) {
462 * Found something
464 DEBUG(10, ("Found %s\n", converted));
465 result = converted;
466 converted = NULL;
467 } else {
468 errno = ENOENT;
470 fail:
471 saved_errno = errno;
472 TALLOC_FREE(converted);
473 TALLOC_FREE(insert);
474 TALLOC_FREE(slashes);
475 TALLOC_FREE(path);
476 errno = saved_errno;
477 return result;
481 modify a sbuf return to ensure that inodes in the shadow directory
482 are different from those in the main directory
484 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
485 SMB_STRUCT_STAT *sbuf)
487 struct shadow_copy2_config *config;
489 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
490 return);
492 if (config->fixinodes) {
493 /* some snapshot systems, like GPFS, return the name
494 device:inode for the snapshot files as the current
495 files. That breaks the 'restore' button in the shadow copy
496 GUI, as the client gets a sharing violation.
498 This is a crude way of allowing both files to be
499 open at once. It has a slight chance of inode
500 number collision, but I can't see a better approach
501 without significant VFS changes
503 uint32_t shash;
505 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
506 if (shash == 0) {
507 shash = 1;
509 sbuf->st_ex_ino ^= shash;
513 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
514 const char *fname,
515 const char *mask,
516 uint32 attr)
518 time_t timestamp;
519 char *stripped;
520 DIR *ret;
521 int saved_errno;
522 char *conv;
524 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
525 &timestamp, &stripped)) {
526 return NULL;
528 if (timestamp == 0) {
529 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
531 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
532 TALLOC_FREE(stripped);
533 if (conv == NULL) {
534 return NULL;
536 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
537 saved_errno = errno;
538 TALLOC_FREE(conv);
539 errno = saved_errno;
540 return ret;
543 static int shadow_copy2_rename(vfs_handle_struct *handle,
544 const struct smb_filename *smb_fname_src,
545 const struct smb_filename *smb_fname_dst)
547 time_t timestamp_src, timestamp_dst;
549 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
550 smb_fname_src->base_name,
551 &timestamp_src, NULL)) {
552 return -1;
554 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
555 smb_fname_dst->base_name,
556 &timestamp_dst, NULL)) {
557 return -1;
559 if (timestamp_src != 0) {
560 errno = EXDEV;
561 return -1;
563 if (timestamp_dst != 0) {
564 errno = EROFS;
565 return -1;
567 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
570 static int shadow_copy2_symlink(vfs_handle_struct *handle,
571 const char *oldname, const char *newname)
573 time_t timestamp_old, timestamp_new;
575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
576 &timestamp_old, NULL)) {
577 return -1;
579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
580 &timestamp_new, NULL)) {
581 return -1;
583 if ((timestamp_old != 0) || (timestamp_new != 0)) {
584 errno = EROFS;
585 return -1;
587 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
590 static int shadow_copy2_link(vfs_handle_struct *handle,
591 const char *oldname, const char *newname)
593 time_t timestamp_old, timestamp_new;
595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
596 &timestamp_old, NULL)) {
597 return -1;
599 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
600 &timestamp_new, NULL)) {
601 return -1;
603 if ((timestamp_old != 0) || (timestamp_new != 0)) {
604 errno = EROFS;
605 return -1;
607 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
610 static int shadow_copy2_stat(vfs_handle_struct *handle,
611 struct smb_filename *smb_fname)
613 time_t timestamp;
614 char *stripped, *tmp;
615 int ret, saved_errno;
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
618 smb_fname->base_name,
619 &timestamp, &stripped)) {
620 return -1;
622 if (timestamp == 0) {
623 return SMB_VFS_NEXT_STAT(handle, smb_fname);
626 tmp = smb_fname->base_name;
627 smb_fname->base_name = shadow_copy2_convert(
628 talloc_tos(), handle, stripped, timestamp);
629 TALLOC_FREE(stripped);
631 if (smb_fname->base_name == NULL) {
632 smb_fname->base_name = tmp;
633 return -1;
636 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
637 saved_errno = errno;
639 TALLOC_FREE(smb_fname->base_name);
640 smb_fname->base_name = tmp;
642 if (ret == 0) {
643 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
645 errno = saved_errno;
646 return ret;
649 static int shadow_copy2_lstat(vfs_handle_struct *handle,
650 struct smb_filename *smb_fname)
652 time_t timestamp;
653 char *stripped, *tmp;
654 int ret, saved_errno;
656 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
657 smb_fname->base_name,
658 &timestamp, &stripped)) {
659 return -1;
661 if (timestamp == 0) {
662 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
665 tmp = smb_fname->base_name;
666 smb_fname->base_name = shadow_copy2_convert(
667 talloc_tos(), handle, stripped, timestamp);
668 TALLOC_FREE(stripped);
670 if (smb_fname->base_name == NULL) {
671 smb_fname->base_name = tmp;
672 return -1;
675 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
676 saved_errno = errno;
678 TALLOC_FREE(smb_fname->base_name);
679 smb_fname->base_name = tmp;
681 if (ret == 0) {
682 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
684 errno = saved_errno;
685 return ret;
688 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
689 SMB_STRUCT_STAT *sbuf)
691 time_t timestamp;
692 int ret;
694 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
695 if (ret == -1) {
696 return ret;
698 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
699 fsp->fsp_name->base_name,
700 &timestamp, NULL)) {
701 return 0;
703 if (timestamp != 0) {
704 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
706 return 0;
709 static int shadow_copy2_open(vfs_handle_struct *handle,
710 struct smb_filename *smb_fname, files_struct *fsp,
711 int flags, mode_t mode)
713 time_t timestamp;
714 char *stripped, *tmp;
715 int ret, saved_errno;
717 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
718 smb_fname->base_name,
719 &timestamp, &stripped)) {
720 return -1;
722 if (timestamp == 0) {
723 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
726 tmp = smb_fname->base_name;
727 smb_fname->base_name = shadow_copy2_convert(
728 talloc_tos(), handle, stripped, timestamp);
729 TALLOC_FREE(stripped);
731 if (smb_fname->base_name == NULL) {
732 smb_fname->base_name = tmp;
733 return -1;
736 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
737 saved_errno = errno;
739 TALLOC_FREE(smb_fname->base_name);
740 smb_fname->base_name = tmp;
742 errno = saved_errno;
743 return ret;
746 static int shadow_copy2_unlink(vfs_handle_struct *handle,
747 const struct smb_filename *smb_fname)
749 time_t timestamp;
750 char *stripped;
751 int ret, saved_errno;
752 struct smb_filename *conv;
753 NTSTATUS status;
755 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
756 smb_fname->base_name,
757 &timestamp, &stripped)) {
758 return -1;
760 if (timestamp == 0) {
761 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
763 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
764 if (!NT_STATUS_IS_OK(status)) {
765 errno = ENOMEM;
766 return -1;
768 conv->base_name = shadow_copy2_convert(
769 conv, handle, stripped, timestamp);
770 TALLOC_FREE(stripped);
771 if (conv->base_name == NULL) {
772 return -1;
774 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
775 saved_errno = errno;
776 TALLOC_FREE(conv);
777 errno = saved_errno;
778 return ret;
781 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
782 mode_t mode)
784 time_t timestamp;
785 char *stripped;
786 int ret, saved_errno;
787 char *conv;
789 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
790 &timestamp, &stripped)) {
791 return -1;
793 if (timestamp == 0) {
794 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
796 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
797 TALLOC_FREE(stripped);
798 if (conv == NULL) {
799 return -1;
801 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
802 saved_errno = errno;
803 TALLOC_FREE(conv);
804 errno = saved_errno;
805 return ret;
808 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
809 uid_t uid, gid_t gid)
811 time_t timestamp;
812 char *stripped;
813 int ret, saved_errno;
814 char *conv;
816 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
817 &timestamp, &stripped)) {
818 return -1;
820 if (timestamp == 0) {
821 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
823 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
824 TALLOC_FREE(stripped);
825 if (conv == NULL) {
826 return -1;
828 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
829 saved_errno = errno;
830 TALLOC_FREE(conv);
831 errno = saved_errno;
832 return ret;
835 static int shadow_copy2_chdir(vfs_handle_struct *handle,
836 const char *fname)
838 time_t timestamp;
839 char *stripped;
840 int ret, saved_errno;
841 char *conv;
843 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
844 &timestamp, &stripped)) {
845 return -1;
847 if (timestamp == 0) {
848 return SMB_VFS_NEXT_CHDIR(handle, fname);
850 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
851 TALLOC_FREE(stripped);
852 if (conv == NULL) {
853 return -1;
855 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
856 saved_errno = errno;
857 TALLOC_FREE(conv);
858 errno = saved_errno;
859 return ret;
862 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
863 const struct smb_filename *smb_fname,
864 struct smb_file_time *ft)
866 time_t timestamp;
867 char *stripped;
868 int ret, saved_errno;
869 struct smb_filename *conv;
870 NTSTATUS status;
872 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
873 smb_fname->base_name,
874 &timestamp, &stripped)) {
875 return -1;
877 if (timestamp == 0) {
878 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
880 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
881 if (!NT_STATUS_IS_OK(status)) {
882 errno = ENOMEM;
883 return -1;
885 conv->base_name = shadow_copy2_convert(
886 conv, handle, stripped, timestamp);
887 TALLOC_FREE(stripped);
888 if (conv->base_name == NULL) {
889 return -1;
891 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
892 saved_errno = errno;
893 TALLOC_FREE(conv);
894 errno = saved_errno;
895 return ret;
898 static int shadow_copy2_readlink(vfs_handle_struct *handle,
899 const char *fname, char *buf, size_t bufsiz)
901 time_t timestamp;
902 char *stripped;
903 int ret, saved_errno;
904 char *conv;
906 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
907 &timestamp, &stripped)) {
908 return -1;
910 if (timestamp == 0) {
911 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
913 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
914 TALLOC_FREE(stripped);
915 if (conv == NULL) {
916 return -1;
918 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
919 saved_errno = errno;
920 TALLOC_FREE(conv);
921 errno = saved_errno;
922 return ret;
925 static int shadow_copy2_mknod(vfs_handle_struct *handle,
926 const char *fname, mode_t mode, SMB_DEV_T dev)
928 time_t timestamp;
929 char *stripped;
930 int ret, saved_errno;
931 char *conv;
933 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
934 &timestamp, &stripped)) {
935 return -1;
937 if (timestamp == 0) {
938 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
940 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
941 TALLOC_FREE(stripped);
942 if (conv == NULL) {
943 return -1;
945 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
946 saved_errno = errno;
947 TALLOC_FREE(conv);
948 errno = saved_errno;
949 return ret;
952 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
953 const char *fname)
955 time_t timestamp;
956 char *stripped = NULL;
957 char *tmp = NULL;
958 char *result = NULL;
959 char *inserted = NULL;
960 char *inserted_to, *inserted_end;
961 int saved_errno;
963 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
964 &timestamp, &stripped)) {
965 goto done;
967 if (timestamp == 0) {
968 return SMB_VFS_NEXT_REALPATH(handle, fname);
971 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
972 if (tmp == NULL) {
973 goto done;
976 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
977 if (result == NULL) {
978 goto done;
982 * Take away what we've inserted. This removes the @GMT-thingy
983 * completely, but will give a path under the share root.
985 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
986 if (inserted == NULL) {
987 goto done;
989 inserted_to = strstr_m(result, inserted);
990 if (inserted_to == NULL) {
991 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
992 goto done;
994 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
995 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
997 done:
998 saved_errno = errno;
999 TALLOC_FREE(inserted);
1000 TALLOC_FREE(tmp);
1001 TALLOC_FREE(stripped);
1002 errno = saved_errno;
1003 return result;
1007 * Check whether a given directory contains a
1008 * snapshot directory as direct subdirectory.
1009 * If yes, return the path of the snapshot-subdir,
1010 * otherwise return NULL.
1012 static char *have_snapdir(struct vfs_handle_struct *handle,
1013 const char *path)
1015 struct smb_filename smb_fname;
1016 int ret;
1017 struct shadow_copy2_config *config;
1019 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1020 return NULL);
1022 ZERO_STRUCT(smb_fname);
1023 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1024 path, config->snapdir);
1025 if (smb_fname.base_name == NULL) {
1026 return NULL;
1029 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1030 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1031 return smb_fname.base_name;
1033 TALLOC_FREE(smb_fname.base_name);
1034 return NULL;
1038 * Find the snapshot directory (if any) for the given
1039 * filename (which is relative to the share).
1041 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1042 struct vfs_handle_struct *handle,
1043 struct smb_filename *smb_fname)
1045 char *path, *p;
1046 char *snapdir;
1047 struct shadow_copy2_config *config;
1049 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1050 return NULL);
1053 * If the non-snapdisrseverywhere mode, we should not search!
1055 if (!config->snapdirseverywhere) {
1056 return config->snapshot_basepath;
1059 path = talloc_asprintf(mem_ctx, "%s/%s",
1060 handle->conn->connectpath,
1061 smb_fname->base_name);
1062 if (path == NULL) {
1063 return NULL;
1066 snapdir = have_snapdir(handle, path);
1067 if (snapdir != NULL) {
1068 TALLOC_FREE(path);
1069 return snapdir;
1072 while ((p = strrchr(path, '/')) && (p > path)) {
1074 p[0] = '\0';
1076 snapdir = have_snapdir(handle, path);
1077 if (snapdir != NULL) {
1078 TALLOC_FREE(path);
1079 return snapdir;
1082 TALLOC_FREE(path);
1083 return NULL;
1086 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1087 const char *name,
1088 char *gmt, size_t gmt_len)
1090 struct tm timestamp;
1091 time_t timestamp_t;
1092 unsigned long int timestamp_long;
1093 const char *fmt;
1094 struct shadow_copy2_config *config;
1096 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1097 return NULL);
1099 fmt = config->gmt_format;
1101 ZERO_STRUCT(timestamp);
1102 if (config->use_sscanf) {
1103 if (sscanf(name, fmt, &timestamp_long) != 1) {
1104 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1105 "no sscanf match %s: %s\n",
1106 fmt, name));
1107 return false;
1109 timestamp_t = timestamp_long;
1110 gmtime_r(&timestamp_t, &timestamp);
1111 } else {
1112 if (strptime(name, fmt, &timestamp) == NULL) {
1113 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1114 "no match %s: %s\n",
1115 fmt, name));
1116 return false;
1118 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1119 fmt, name));
1121 if (config->use_localtime) {
1122 timestamp.tm_isdst = -1;
1123 timestamp_t = mktime(&timestamp);
1124 gmtime_r(&timestamp_t, &timestamp);
1128 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1129 return true;
1132 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1134 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1137 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1139 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1143 sort the shadow copy data in ascending or descending order
1145 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1146 struct shadow_copy_data *shadow_copy2_data)
1148 int (*cmpfunc)(const void *, const void *);
1149 const char *sort;
1150 struct shadow_copy2_config *config;
1152 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1153 return);
1155 sort = config->sort_order;
1156 if (sort == NULL) {
1157 return;
1160 if (strcmp(sort, "asc") == 0) {
1161 cmpfunc = shadow_copy2_label_cmp_asc;
1162 } else if (strcmp(sort, "desc") == 0) {
1163 cmpfunc = shadow_copy2_label_cmp_desc;
1164 } else {
1165 return;
1168 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1169 shadow_copy2_data->labels)
1171 TYPESAFE_QSORT(shadow_copy2_data->labels,
1172 shadow_copy2_data->num_volumes,
1173 cmpfunc);
1177 static int shadow_copy2_get_shadow_copy_data(
1178 vfs_handle_struct *handle, files_struct *fsp,
1179 struct shadow_copy_data *shadow_copy2_data,
1180 bool labels)
1182 DIR *p;
1183 const char *snapdir;
1184 struct dirent *d;
1185 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1187 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1188 if (snapdir == NULL) {
1189 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1190 handle->conn->connectpath));
1191 errno = EINVAL;
1192 talloc_free(tmp_ctx);
1193 return -1;
1196 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1198 if (!p) {
1199 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1200 " - %s\n", snapdir, strerror(errno)));
1201 talloc_free(tmp_ctx);
1202 errno = ENOSYS;
1203 return -1;
1206 shadow_copy2_data->num_volumes = 0;
1207 shadow_copy2_data->labels = NULL;
1209 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1210 char snapshot[GMT_NAME_LEN+1];
1211 SHADOW_COPY_LABEL *tlabels;
1214 * ignore names not of the right form in the snapshot
1215 * directory
1217 if (!shadow_copy2_snapshot_to_gmt(
1218 handle, d->d_name,
1219 snapshot, sizeof(snapshot))) {
1221 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1222 "ignoring %s\n", d->d_name));
1223 continue;
1225 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1226 d->d_name, snapshot));
1228 if (!labels) {
1229 /* the caller doesn't want the labels */
1230 shadow_copy2_data->num_volumes++;
1231 continue;
1234 tlabels = talloc_realloc(shadow_copy2_data,
1235 shadow_copy2_data->labels,
1236 SHADOW_COPY_LABEL,
1237 shadow_copy2_data->num_volumes+1);
1238 if (tlabels == NULL) {
1239 DEBUG(0,("shadow_copy2: out of memory\n"));
1240 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1241 talloc_free(tmp_ctx);
1242 return -1;
1245 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1246 sizeof(*tlabels));
1248 shadow_copy2_data->num_volumes++;
1249 shadow_copy2_data->labels = tlabels;
1252 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1254 shadow_copy2_sort_data(handle, shadow_copy2_data);
1256 talloc_free(tmp_ctx);
1257 return 0;
1260 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1261 struct files_struct *fsp,
1262 uint32 security_info,
1263 TALLOC_CTX *mem_ctx,
1264 struct security_descriptor **ppdesc)
1266 time_t timestamp;
1267 char *stripped;
1268 NTSTATUS status;
1269 char *conv;
1271 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1272 fsp->fsp_name->base_name,
1273 &timestamp, &stripped)) {
1274 return map_nt_error_from_unix(errno);
1276 if (timestamp == 0) {
1277 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1278 mem_ctx,
1279 ppdesc);
1281 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1282 TALLOC_FREE(stripped);
1283 if (conv == NULL) {
1284 return map_nt_error_from_unix(errno);
1286 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1287 mem_ctx, ppdesc);
1288 TALLOC_FREE(conv);
1289 return status;
1292 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1293 const char *fname,
1294 uint32 security_info,
1295 TALLOC_CTX *mem_ctx,
1296 struct security_descriptor **ppdesc)
1298 time_t timestamp;
1299 char *stripped;
1300 NTSTATUS status;
1301 char *conv;
1303 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1304 &timestamp, &stripped)) {
1305 return map_nt_error_from_unix(errno);
1307 if (timestamp == 0) {
1308 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1309 mem_ctx, ppdesc);
1311 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1312 TALLOC_FREE(stripped);
1313 if (conv == NULL) {
1314 return map_nt_error_from_unix(errno);
1316 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1317 mem_ctx, ppdesc);
1318 TALLOC_FREE(conv);
1319 return status;
1322 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1323 const char *fname, mode_t mode)
1325 time_t timestamp;
1326 char *stripped;
1327 int ret, saved_errno;
1328 char *conv;
1330 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1331 &timestamp, &stripped)) {
1332 return -1;
1334 if (timestamp == 0) {
1335 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1337 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1338 TALLOC_FREE(stripped);
1339 if (conv == NULL) {
1340 return -1;
1342 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1343 saved_errno = errno;
1344 TALLOC_FREE(conv);
1345 errno = saved_errno;
1346 return ret;
1349 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1351 time_t timestamp;
1352 char *stripped;
1353 int ret, saved_errno;
1354 char *conv;
1356 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1357 &timestamp, &stripped)) {
1358 return -1;
1360 if (timestamp == 0) {
1361 return SMB_VFS_NEXT_RMDIR(handle, fname);
1363 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1364 TALLOC_FREE(stripped);
1365 if (conv == NULL) {
1366 return -1;
1368 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1369 saved_errno = errno;
1370 TALLOC_FREE(conv);
1371 errno = saved_errno;
1372 return ret;
1375 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1376 unsigned int flags)
1378 time_t timestamp;
1379 char *stripped;
1380 int ret, saved_errno;
1381 char *conv;
1383 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1384 &timestamp, &stripped)) {
1385 return -1;
1387 if (timestamp == 0) {
1388 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1390 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1391 TALLOC_FREE(stripped);
1392 if (conv == NULL) {
1393 return -1;
1395 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1396 saved_errno = errno;
1397 TALLOC_FREE(conv);
1398 errno = saved_errno;
1399 return ret;
1402 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1403 const char *fname, const char *aname,
1404 void *value, size_t size)
1406 time_t timestamp;
1407 char *stripped;
1408 ssize_t ret;
1409 int saved_errno;
1410 char *conv;
1412 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1413 &timestamp, &stripped)) {
1414 return -1;
1416 if (timestamp == 0) {
1417 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1418 size);
1420 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1421 TALLOC_FREE(stripped);
1422 if (conv == NULL) {
1423 return -1;
1425 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1426 saved_errno = errno;
1427 TALLOC_FREE(conv);
1428 errno = saved_errno;
1429 return ret;
1432 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1433 const char *fname,
1434 char *list, size_t size)
1436 time_t timestamp;
1437 char *stripped;
1438 ssize_t ret;
1439 int saved_errno;
1440 char *conv;
1442 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1443 &timestamp, &stripped)) {
1444 return -1;
1446 if (timestamp == 0) {
1447 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1449 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1450 TALLOC_FREE(stripped);
1451 if (conv == NULL) {
1452 return -1;
1454 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1455 saved_errno = errno;
1456 TALLOC_FREE(conv);
1457 errno = saved_errno;
1458 return ret;
1461 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1462 const char *fname, const char *aname)
1464 time_t timestamp;
1465 char *stripped;
1466 int ret, saved_errno;
1467 char *conv;
1469 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1470 &timestamp, &stripped)) {
1471 return -1;
1473 if (timestamp == 0) {
1474 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1476 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1477 TALLOC_FREE(stripped);
1478 if (conv == NULL) {
1479 return -1;
1481 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1482 saved_errno = errno;
1483 TALLOC_FREE(conv);
1484 errno = saved_errno;
1485 return ret;
1488 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1489 const char *fname,
1490 const char *aname, const void *value,
1491 size_t size, int flags)
1493 time_t timestamp;
1494 char *stripped;
1495 ssize_t ret;
1496 int saved_errno;
1497 char *conv;
1499 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1500 &timestamp, &stripped)) {
1501 return -1;
1503 if (timestamp == 0) {
1504 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1505 flags);
1507 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1508 TALLOC_FREE(stripped);
1509 if (conv == NULL) {
1510 return -1;
1512 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1513 saved_errno = errno;
1514 TALLOC_FREE(conv);
1515 errno = saved_errno;
1516 return ret;
1519 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1520 const char *fname, mode_t mode)
1522 time_t timestamp;
1523 char *stripped;
1524 ssize_t ret;
1525 int saved_errno;
1526 char *conv;
1528 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1529 &timestamp, &stripped)) {
1530 return -1;
1532 if (timestamp == 0) {
1533 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1535 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1536 TALLOC_FREE(stripped);
1537 if (conv == NULL) {
1538 return -1;
1540 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1541 saved_errno = errno;
1542 TALLOC_FREE(conv);
1543 errno = saved_errno;
1544 return ret;
1547 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1548 const char *path,
1549 const char *name,
1550 TALLOC_CTX *mem_ctx,
1551 char **found_name)
1553 time_t timestamp;
1554 char *stripped;
1555 ssize_t ret;
1556 int saved_errno;
1557 char *conv;
1559 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1560 &timestamp, &stripped)) {
1561 return -1;
1563 if (timestamp == 0) {
1564 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1565 mem_ctx, found_name);
1567 if (stripped[0] == '\0') {
1568 *found_name = talloc_strdup(mem_ctx, name);
1569 if (*found_name == NULL) {
1570 errno = ENOMEM;
1571 return -1;
1573 return 0;
1575 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1576 TALLOC_FREE(stripped);
1577 if (conv == NULL) {
1578 return -1;
1580 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1581 mem_ctx, found_name);
1582 saved_errno = errno;
1583 TALLOC_FREE(conv);
1584 errno = saved_errno;
1585 return ret;
1588 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1589 const char *path, bool small_query,
1590 uint64_t *bsize, uint64_t *dfree,
1591 uint64_t *dsize)
1593 time_t timestamp;
1594 char *stripped;
1595 ssize_t ret;
1596 int saved_errno;
1597 char *conv;
1599 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1600 &timestamp, &stripped)) {
1601 return -1;
1603 if (timestamp == 0) {
1604 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1605 bsize, dfree, dsize);
1608 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1609 TALLOC_FREE(stripped);
1610 if (conv == NULL) {
1611 return -1;
1614 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1615 dsize);
1617 saved_errno = errno;
1618 TALLOC_FREE(conv);
1619 errno = saved_errno;
1621 return ret;
1624 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1625 const char *service, const char *user)
1627 struct shadow_copy2_config *config;
1628 int ret;
1629 const char *snapdir;
1630 const char *gmt_format;
1631 const char *sort_order;
1632 const char *basedir;
1633 const char *mount_point;
1635 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1636 (unsigned)handle->conn->cnum,
1637 handle->conn->connectpath));
1639 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1640 if (ret < 0) {
1641 return ret;
1644 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1645 if (config == NULL) {
1646 DEBUG(0, ("talloc_zero() failed\n"));
1647 errno = ENOMEM;
1648 return -1;
1651 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1652 "shadow", "format",
1653 GMT_FORMAT);
1654 config->gmt_format = talloc_strdup(config, gmt_format);
1655 if (config->gmt_format == NULL) {
1656 DEBUG(0, ("talloc_strdup() failed\n"));
1657 errno = ENOMEM;
1658 return -1;
1661 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1662 "shadow", "sscanf", false);
1664 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1665 "shadow", "localtime",
1666 false);
1668 snapdir = lp_parm_const_string(SNUM(handle->conn),
1669 "shadow", "snapdir",
1670 ".snapshots");
1671 config->snapdir = talloc_strdup(config, snapdir);
1672 if (config->snapdir == NULL) {
1673 DEBUG(0, ("talloc_strdup() failed\n"));
1674 errno = ENOMEM;
1675 return -1;
1678 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1679 "shadow",
1680 "snapdirseverywhere",
1681 false);
1683 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1684 "shadow", "crossmountpoints",
1685 false);
1687 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1688 "shadow", "fixinodes",
1689 false);
1691 sort_order = lp_parm_const_string(SNUM(handle->conn),
1692 "shadow", "sort", "desc");
1693 config->sort_order = talloc_strdup(config, sort_order);
1694 if (config->sort_order == NULL) {
1695 DEBUG(0, ("talloc_strdup() failed\n"));
1696 errno = ENOMEM;
1697 return -1;
1700 mount_point = lp_parm_const_string(SNUM(handle->conn),
1701 "shadow", "mountpoint", NULL);
1702 if (mount_point != NULL) {
1703 if (mount_point[0] != '/') {
1704 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1705 "relative ('%s'), but it has to be an "
1706 "absolute path. Ignoring provided value.\n",
1707 mount_point));
1708 mount_point = NULL;
1709 } else {
1710 char *p;
1711 p = strstr(handle->conn->connectpath, mount_point);
1712 if (p != handle->conn->connectpath) {
1713 DEBUG(1, ("Warning: mount_point (%s) is not a "
1714 "subdirectory of the share root "
1715 "(%s). Ignoring provided value.\n",
1716 mount_point,
1717 handle->conn->connectpath));
1718 mount_point = NULL;
1723 if (mount_point != NULL) {
1724 config->mount_point = talloc_strdup(config, mount_point);
1725 if (config->mount_point == NULL) {
1726 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1727 return -1;
1729 } else {
1730 config->mount_point = shadow_copy2_find_mount_point(config,
1731 handle);
1732 if (config->mount_point == NULL) {
1733 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1734 " failed: %s\n", strerror(errno)));
1735 return -1;
1739 basedir = lp_parm_const_string(SNUM(handle->conn),
1740 "shadow", "basedir", NULL);
1742 if (basedir != NULL) {
1743 if (basedir[0] != '/') {
1744 DEBUG(1, (__location__ " Warning: 'basedir' is "
1745 "relative ('%s'), but it has to be an "
1746 "absolute path. Disabling basedir.\n",
1747 basedir));
1748 } else {
1749 char *p;
1750 p = strstr(basedir, config->mount_point);
1751 if (p != basedir) {
1752 DEBUG(1, ("Warning: basedir (%s) is not a "
1753 "subdirectory of the share root's "
1754 "mount point (%s). "
1755 "Disabling basedir\n",
1756 basedir, config->mount_point));
1757 } else {
1758 config->basedir = talloc_strdup(config,
1759 basedir);
1760 if (config->basedir == NULL) {
1761 DEBUG(0, ("talloc_strdup() failed\n"));
1762 errno = ENOMEM;
1763 return -1;
1769 if (config->snapdirseverywhere && config->basedir != NULL) {
1770 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1771 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1772 TALLOC_FREE(config->basedir);
1775 if (config->crossmountpoints && config->basedir != NULL) {
1776 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1777 "with 'crossmountpoints'. Disabling basedir.\n"));
1778 TALLOC_FREE(config->basedir);
1781 if (config->basedir == NULL) {
1782 config->basedir = config->mount_point;
1785 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1786 config->rel_connectpath = talloc_strdup(config,
1787 handle->conn->connectpath + strlen(config->basedir));
1788 if (config->rel_connectpath == NULL) {
1789 DEBUG(0, ("talloc_strdup() failed\n"));
1790 errno = ENOMEM;
1791 return -1;
1795 if (config->snapdir[0] == '/') {
1796 config->snapdir_absolute = true;
1798 if (config->snapdirseverywhere == true) {
1799 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1800 "is incompatible with 'snapdirseverywhere', "
1801 "setting 'snapdirseverywhere' to false.\n"));
1802 config->snapdirseverywhere = false;
1805 if (config->crossmountpoints == true) {
1806 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1807 "is not supported with an absolute snapdir. "
1808 "Disabling it.\n"));
1809 config->crossmountpoints = false;
1812 config->snapshot_basepath = config->snapdir;
1813 } else {
1814 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1815 config->mount_point, config->snapdir);
1816 if (config->snapshot_basepath == NULL) {
1817 DEBUG(0, ("talloc_asprintf() failed\n"));
1818 errno = ENOMEM;
1819 return -1;
1823 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1824 " share root: '%s'\n"
1825 " basedir: '%s'\n"
1826 " mountpoint: '%s'\n"
1827 " rel share root: '%s'\n"
1828 " snapdir: '%s'\n"
1829 " snapshot base path: '%s'\n"
1830 " format: '%s'\n"
1831 " use sscanf: %s\n"
1832 " snapdirs everywhere: %s\n"
1833 " cross mountpoints: %s\n"
1834 " fix inodes: %s\n"
1835 " sort order: %s\n"
1837 handle->conn->connectpath,
1838 config->basedir,
1839 config->mount_point,
1840 config->rel_connectpath,
1841 config->snapdir,
1842 config->snapshot_basepath,
1843 config->gmt_format,
1844 config->use_sscanf ? "yes" : "no",
1845 config->snapdirseverywhere ? "yes" : "no",
1846 config->crossmountpoints ? "yes" : "no",
1847 config->fixinodes ? "yes" : "no",
1848 config->sort_order
1852 SMB_VFS_HANDLE_SET_DATA(handle, config,
1853 NULL, struct shadow_copy2_config,
1854 return -1);
1856 return 0;
1859 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1860 .connect_fn = shadow_copy2_connect,
1861 .opendir_fn = shadow_copy2_opendir,
1862 .disk_free_fn = shadow_copy2_disk_free,
1863 .rename_fn = shadow_copy2_rename,
1864 .link_fn = shadow_copy2_link,
1865 .symlink_fn = shadow_copy2_symlink,
1866 .stat_fn = shadow_copy2_stat,
1867 .lstat_fn = shadow_copy2_lstat,
1868 .fstat_fn = shadow_copy2_fstat,
1869 .open_fn = shadow_copy2_open,
1870 .unlink_fn = shadow_copy2_unlink,
1871 .chmod_fn = shadow_copy2_chmod,
1872 .chown_fn = shadow_copy2_chown,
1873 .chdir_fn = shadow_copy2_chdir,
1874 .ntimes_fn = shadow_copy2_ntimes,
1875 .readlink_fn = shadow_copy2_readlink,
1876 .mknod_fn = shadow_copy2_mknod,
1877 .realpath_fn = shadow_copy2_realpath,
1878 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1879 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1880 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1881 .mkdir_fn = shadow_copy2_mkdir,
1882 .rmdir_fn = shadow_copy2_rmdir,
1883 .getxattr_fn = shadow_copy2_getxattr,
1884 .listxattr_fn = shadow_copy2_listxattr,
1885 .removexattr_fn = shadow_copy2_removexattr,
1886 .setxattr_fn = shadow_copy2_setxattr,
1887 .chmod_acl_fn = shadow_copy2_chmod_acl,
1888 .chflags_fn = shadow_copy2_chflags,
1889 .get_real_filename_fn = shadow_copy2_get_real_filename,
1892 NTSTATUS vfs_shadow_copy2_init(void);
1893 NTSTATUS vfs_shadow_copy2_init(void)
1895 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1896 "shadow_copy2", &vfs_shadow_copy2_fns);