shadow_copy2: shadow_copy2_insert_string(): do not prepend a "/" in absolute mode
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob5db0811e06d9cbddab60d9cb255d5a062e97eeb1
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 * In the case of a parallel snapdir (specified with an
166 * absolute path), this is the inital portion of the
167 * local path of any snapshot file. The complete path is
168 * obtained by appending the portion of the file's path
169 * below the share root's mountpoint.
171 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
172 struct vfs_handle_struct *handle,
173 time_t snapshot)
175 struct tm snap_tm;
176 fstring snaptime_string;
177 size_t snaptime_len;
178 struct shadow_copy2_config *config;
179 char *result = NULL;
181 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
182 return NULL);
184 if (config->use_sscanf) {
185 snaptime_len = snprintf(snaptime_string,
186 sizeof(snaptime_string),
187 config->gmt_format,
188 (unsigned long)snapshot);
189 if (snaptime_len <= 0) {
190 DEBUG(10, ("snprintf failed\n"));
191 return NULL;
193 } else {
194 if (config->use_localtime) {
195 if (localtime_r(&snapshot, &snap_tm) == 0) {
196 DEBUG(10, ("gmtime_r failed\n"));
197 return NULL;
199 } else {
200 if (gmtime_r(&snapshot, &snap_tm) == 0) {
201 DEBUG(10, ("gmtime_r failed\n"));
202 return NULL;
205 snaptime_len = strftime(snaptime_string,
206 sizeof(snaptime_string),
207 config->gmt_format,
208 &snap_tm);
209 if (snaptime_len == 0) {
210 DEBUG(10, ("strftime failed\n"));
211 return NULL;
215 if (config->snapdir_absolute) {
216 result = talloc_asprintf(mem_ctx, "%s/%s",
217 config->snapdir, snaptime_string);
218 } else {
219 result = talloc_asprintf(mem_ctx, "/%s/%s",
220 config->snapdir, snaptime_string);
222 if (result == NULL) {
223 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
226 return result;
230 * Strip a snapshot component from an filename as
231 * handed in via the smb layer.
232 * Returns the parsed timestamp and the stripped filename.
234 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
235 struct vfs_handle_struct *handle,
236 const char *name,
237 time_t *ptimestamp,
238 char **pstripped)
240 struct tm tm;
241 time_t timestamp;
242 const char *p;
243 char *q;
244 char *stripped;
245 size_t rest_len, dst_len;
246 struct shadow_copy2_config *config;
248 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
249 return false);
251 p = strstr_m(name, "@GMT-");
252 if (p == NULL) {
253 goto no_snapshot;
255 if ((p > name) && (p[-1] != '/')) {
256 goto no_snapshot;
258 q = strptime(p, GMT_FORMAT, &tm);
259 if (q == NULL) {
260 goto no_snapshot;
262 tm.tm_isdst = -1;
263 timestamp = timegm(&tm);
264 if (timestamp == (time_t)-1) {
265 goto no_snapshot;
267 if ((p == name) && (q[0] == '\0')) {
268 if (pstripped != NULL) {
269 stripped = talloc_strdup(mem_ctx, "");
270 if (stripped == NULL) {
271 return false;
273 *pstripped = stripped;
275 *ptimestamp = timestamp;
276 return true;
278 if (q[0] != '/') {
279 goto no_snapshot;
281 q += 1;
283 rest_len = strlen(q);
284 dst_len = (p-name) + rest_len;
286 if (config->snapdirseverywhere) {
287 char *insert;
288 bool have_insert;
289 insert = shadow_copy2_insert_string(talloc_tos(), handle,
290 timestamp);
291 if (insert == NULL) {
292 errno = ENOMEM;
293 return false;
296 have_insert = (strstr(name, insert+1) != NULL);
297 TALLOC_FREE(insert);
298 if (have_insert) {
299 goto no_snapshot;
303 if (pstripped != NULL) {
304 stripped = talloc_array(mem_ctx, char, dst_len+1);
305 if (stripped == NULL) {
306 errno = ENOMEM;
307 return false;
309 if (p > name) {
310 memcpy(stripped, name, p-name);
312 if (rest_len > 0) {
313 memcpy(stripped + (p-name), q, rest_len);
315 stripped[dst_len] = '\0';
316 *pstripped = stripped;
318 *ptimestamp = timestamp;
319 return true;
320 no_snapshot:
321 *ptimestamp = 0;
322 return true;
325 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
326 vfs_handle_struct *handle)
328 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
329 dev_t dev;
330 struct stat st;
331 char *p;
333 if (stat(path, &st) != 0) {
334 talloc_free(path);
335 return NULL;
338 dev = st.st_dev;
340 while ((p = strrchr(path, '/')) && p > path) {
341 *p = 0;
342 if (stat(path, &st) != 0) {
343 talloc_free(path);
344 return NULL;
346 if (st.st_dev != dev) {
347 *p = '/';
348 break;
352 return path;
356 * Convert from a name as handed in via the SMB layer
357 * and a timestamp into the local path of the snapshot
358 * of the provided file at the provided time.
360 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
361 struct vfs_handle_struct *handle,
362 const char *name, time_t timestamp)
364 struct smb_filename converted_fname;
365 char *result = NULL;
366 size_t *slashes = NULL;
367 unsigned num_slashes;
368 char *path = NULL;
369 size_t pathlen;
370 char *insert = NULL;
371 char *converted = NULL;
372 size_t insertlen;
373 int i, saved_errno;
374 size_t min_offset;
375 struct shadow_copy2_config *config;
377 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
378 return NULL);
380 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
381 name);
382 if (path == NULL) {
383 errno = ENOMEM;
384 goto fail;
386 pathlen = talloc_get_size(path)-1;
388 DEBUG(10, ("converting %s\n", path));
390 if (!shadow_copy2_find_slashes(talloc_tos(), path,
391 &slashes, &num_slashes)) {
392 goto fail;
394 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
395 if (insert == NULL) {
396 goto fail;
398 insertlen = talloc_get_size(insert)-1;
399 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
400 if (converted == NULL) {
401 goto fail;
404 if (path[pathlen-1] != '/') {
406 * Append a fake slash to find the snapshot root
408 size_t *tmp;
409 tmp = talloc_realloc(talloc_tos(), slashes,
410 size_t, num_slashes+1);
411 if (tmp == NULL) {
412 goto fail;
414 slashes = tmp;
415 slashes[num_slashes] = pathlen;
416 num_slashes += 1;
419 min_offset = 0;
421 if (!config->crossmountpoints) {
422 char *mount_point;
424 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
425 handle);
426 if (mount_point == NULL) {
427 goto fail;
429 min_offset = strlen(mount_point);
430 TALLOC_FREE(mount_point);
433 memcpy(converted, path, pathlen+1);
434 converted[pathlen+insertlen] = '\0';
436 ZERO_STRUCT(converted_fname);
437 converted_fname.base_name = converted;
439 for (i = num_slashes-1; i>=0; i--) {
440 int ret;
441 size_t offset;
443 offset = slashes[i];
445 if (offset < min_offset) {
446 errno = ENOENT;
447 goto fail;
450 memcpy(converted+offset, insert, insertlen);
452 offset += insertlen;
453 memcpy(converted+offset, path + slashes[i],
454 pathlen - slashes[i]);
456 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
458 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
459 ret, ret == 0 ? "ok" : strerror(errno)));
460 if (ret == 0) {
461 /* success */
462 break;
464 if (errno == ENOTDIR) {
466 * This is a valid condition: We appended the
467 * .snaphots/@GMT.. to a file name. Just try
468 * with the upper levels.
470 continue;
472 if (errno != ENOENT) {
473 /* Other problem than "not found" */
474 goto fail;
478 if (i >= 0) {
480 * Found something
482 DEBUG(10, ("Found %s\n", converted));
483 result = converted;
484 converted = NULL;
485 } else {
486 errno = ENOENT;
488 fail:
489 saved_errno = errno;
490 TALLOC_FREE(converted);
491 TALLOC_FREE(insert);
492 TALLOC_FREE(slashes);
493 TALLOC_FREE(path);
494 errno = saved_errno;
495 return result;
499 modify a sbuf return to ensure that inodes in the shadow directory
500 are different from those in the main directory
502 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
503 SMB_STRUCT_STAT *sbuf)
505 struct shadow_copy2_config *config;
507 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
508 return);
510 if (config->fixinodes) {
511 /* some snapshot systems, like GPFS, return the name
512 device:inode for the snapshot files as the current
513 files. That breaks the 'restore' button in the shadow copy
514 GUI, as the client gets a sharing violation.
516 This is a crude way of allowing both files to be
517 open at once. It has a slight chance of inode
518 number collision, but I can't see a better approach
519 without significant VFS changes
521 uint32_t shash;
523 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
524 if (shash == 0) {
525 shash = 1;
527 sbuf->st_ex_ino ^= shash;
531 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
532 const char *fname,
533 const char *mask,
534 uint32 attr)
536 time_t timestamp;
537 char *stripped;
538 DIR *ret;
539 int saved_errno;
540 char *conv;
542 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
543 &timestamp, &stripped)) {
544 return NULL;
546 if (timestamp == 0) {
547 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
549 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
550 TALLOC_FREE(stripped);
551 if (conv == NULL) {
552 return NULL;
554 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
555 saved_errno = errno;
556 TALLOC_FREE(conv);
557 errno = saved_errno;
558 return ret;
561 static int shadow_copy2_rename(vfs_handle_struct *handle,
562 const struct smb_filename *smb_fname_src,
563 const struct smb_filename *smb_fname_dst)
565 time_t timestamp_src, timestamp_dst;
567 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
568 smb_fname_src->base_name,
569 &timestamp_src, NULL)) {
570 return -1;
572 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
573 smb_fname_dst->base_name,
574 &timestamp_dst, NULL)) {
575 return -1;
577 if (timestamp_src != 0) {
578 errno = EXDEV;
579 return -1;
581 if (timestamp_dst != 0) {
582 errno = EROFS;
583 return -1;
585 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
588 static int shadow_copy2_symlink(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_SYMLINK(handle, oldname, newname);
608 static int shadow_copy2_link(vfs_handle_struct *handle,
609 const char *oldname, const char *newname)
611 time_t timestamp_old, timestamp_new;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
614 &timestamp_old, NULL)) {
615 return -1;
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
618 &timestamp_new, NULL)) {
619 return -1;
621 if ((timestamp_old != 0) || (timestamp_new != 0)) {
622 errno = EROFS;
623 return -1;
625 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
628 static int shadow_copy2_stat(vfs_handle_struct *handle,
629 struct smb_filename *smb_fname)
631 time_t timestamp;
632 char *stripped, *tmp;
633 int ret, saved_errno;
635 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
636 smb_fname->base_name,
637 &timestamp, &stripped)) {
638 return -1;
640 if (timestamp == 0) {
641 return SMB_VFS_NEXT_STAT(handle, smb_fname);
644 tmp = smb_fname->base_name;
645 smb_fname->base_name = shadow_copy2_convert(
646 talloc_tos(), handle, stripped, timestamp);
647 TALLOC_FREE(stripped);
649 if (smb_fname->base_name == NULL) {
650 smb_fname->base_name = tmp;
651 return -1;
654 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
655 saved_errno = errno;
657 TALLOC_FREE(smb_fname->base_name);
658 smb_fname->base_name = tmp;
660 if (ret == 0) {
661 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
663 errno = saved_errno;
664 return ret;
667 static int shadow_copy2_lstat(vfs_handle_struct *handle,
668 struct smb_filename *smb_fname)
670 time_t timestamp;
671 char *stripped, *tmp;
672 int ret, saved_errno;
674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
675 smb_fname->base_name,
676 &timestamp, &stripped)) {
677 return -1;
679 if (timestamp == 0) {
680 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
683 tmp = smb_fname->base_name;
684 smb_fname->base_name = shadow_copy2_convert(
685 talloc_tos(), handle, stripped, timestamp);
686 TALLOC_FREE(stripped);
688 if (smb_fname->base_name == NULL) {
689 smb_fname->base_name = tmp;
690 return -1;
693 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
694 saved_errno = errno;
696 TALLOC_FREE(smb_fname->base_name);
697 smb_fname->base_name = tmp;
699 if (ret == 0) {
700 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
702 errno = saved_errno;
703 return ret;
706 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
707 SMB_STRUCT_STAT *sbuf)
709 time_t timestamp;
710 int ret;
712 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
713 if (ret == -1) {
714 return ret;
716 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
717 fsp->fsp_name->base_name,
718 &timestamp, NULL)) {
719 return 0;
721 if (timestamp != 0) {
722 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
724 return 0;
727 static int shadow_copy2_open(vfs_handle_struct *handle,
728 struct smb_filename *smb_fname, files_struct *fsp,
729 int flags, mode_t mode)
731 time_t timestamp;
732 char *stripped, *tmp;
733 int ret, saved_errno;
735 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
736 smb_fname->base_name,
737 &timestamp, &stripped)) {
738 return -1;
740 if (timestamp == 0) {
741 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
744 tmp = smb_fname->base_name;
745 smb_fname->base_name = shadow_copy2_convert(
746 talloc_tos(), handle, stripped, timestamp);
747 TALLOC_FREE(stripped);
749 if (smb_fname->base_name == NULL) {
750 smb_fname->base_name = tmp;
751 return -1;
754 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
755 saved_errno = errno;
757 TALLOC_FREE(smb_fname->base_name);
758 smb_fname->base_name = tmp;
760 errno = saved_errno;
761 return ret;
764 static int shadow_copy2_unlink(vfs_handle_struct *handle,
765 const struct smb_filename *smb_fname)
767 time_t timestamp;
768 char *stripped;
769 int ret, saved_errno;
770 struct smb_filename *conv;
771 NTSTATUS status;
773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
774 smb_fname->base_name,
775 &timestamp, &stripped)) {
776 return -1;
778 if (timestamp == 0) {
779 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
781 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
782 if (!NT_STATUS_IS_OK(status)) {
783 errno = ENOMEM;
784 return -1;
786 conv->base_name = shadow_copy2_convert(
787 conv, handle, stripped, timestamp);
788 TALLOC_FREE(stripped);
789 if (conv->base_name == NULL) {
790 return -1;
792 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
793 saved_errno = errno;
794 TALLOC_FREE(conv);
795 errno = saved_errno;
796 return ret;
799 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
800 mode_t mode)
802 time_t timestamp;
803 char *stripped;
804 int ret, saved_errno;
805 char *conv;
807 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
808 &timestamp, &stripped)) {
809 return -1;
811 if (timestamp == 0) {
812 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
814 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
815 TALLOC_FREE(stripped);
816 if (conv == NULL) {
817 return -1;
819 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
820 saved_errno = errno;
821 TALLOC_FREE(conv);
822 errno = saved_errno;
823 return ret;
826 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
827 uid_t uid, gid_t gid)
829 time_t timestamp;
830 char *stripped;
831 int ret, saved_errno;
832 char *conv;
834 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
835 &timestamp, &stripped)) {
836 return -1;
838 if (timestamp == 0) {
839 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
841 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
842 TALLOC_FREE(stripped);
843 if (conv == NULL) {
844 return -1;
846 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
847 saved_errno = errno;
848 TALLOC_FREE(conv);
849 errno = saved_errno;
850 return ret;
853 static int shadow_copy2_chdir(vfs_handle_struct *handle,
854 const char *fname)
856 time_t timestamp;
857 char *stripped;
858 int ret, saved_errno;
859 char *conv;
861 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
862 &timestamp, &stripped)) {
863 return -1;
865 if (timestamp == 0) {
866 return SMB_VFS_NEXT_CHDIR(handle, fname);
868 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
869 TALLOC_FREE(stripped);
870 if (conv == NULL) {
871 return -1;
873 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
874 saved_errno = errno;
875 TALLOC_FREE(conv);
876 errno = saved_errno;
877 return ret;
880 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
881 const struct smb_filename *smb_fname,
882 struct smb_file_time *ft)
884 time_t timestamp;
885 char *stripped;
886 int ret, saved_errno;
887 struct smb_filename *conv;
888 NTSTATUS status;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
891 smb_fname->base_name,
892 &timestamp, &stripped)) {
893 return -1;
895 if (timestamp == 0) {
896 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
898 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
899 if (!NT_STATUS_IS_OK(status)) {
900 errno = ENOMEM;
901 return -1;
903 conv->base_name = shadow_copy2_convert(
904 conv, handle, stripped, timestamp);
905 TALLOC_FREE(stripped);
906 if (conv->base_name == NULL) {
907 return -1;
909 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
910 saved_errno = errno;
911 TALLOC_FREE(conv);
912 errno = saved_errno;
913 return ret;
916 static int shadow_copy2_readlink(vfs_handle_struct *handle,
917 const char *fname, char *buf, size_t bufsiz)
919 time_t timestamp;
920 char *stripped;
921 int ret, saved_errno;
922 char *conv;
924 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
925 &timestamp, &stripped)) {
926 return -1;
928 if (timestamp == 0) {
929 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
931 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
932 TALLOC_FREE(stripped);
933 if (conv == NULL) {
934 return -1;
936 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
937 saved_errno = errno;
938 TALLOC_FREE(conv);
939 errno = saved_errno;
940 return ret;
943 static int shadow_copy2_mknod(vfs_handle_struct *handle,
944 const char *fname, mode_t mode, SMB_DEV_T dev)
946 time_t timestamp;
947 char *stripped;
948 int ret, saved_errno;
949 char *conv;
951 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
952 &timestamp, &stripped)) {
953 return -1;
955 if (timestamp == 0) {
956 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
958 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
959 TALLOC_FREE(stripped);
960 if (conv == NULL) {
961 return -1;
963 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
964 saved_errno = errno;
965 TALLOC_FREE(conv);
966 errno = saved_errno;
967 return ret;
970 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
971 const char *fname)
973 time_t timestamp;
974 char *stripped = NULL;
975 char *tmp = NULL;
976 char *result = NULL;
977 char *inserted = NULL;
978 char *inserted_to, *inserted_end;
979 int saved_errno;
981 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
982 &timestamp, &stripped)) {
983 goto done;
985 if (timestamp == 0) {
986 return SMB_VFS_NEXT_REALPATH(handle, fname);
989 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
990 if (tmp == NULL) {
991 goto done;
994 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
995 if (result == NULL) {
996 goto done;
1000 * Take away what we've inserted. This removes the @GMT-thingy
1001 * completely, but will give a path under the share root.
1003 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1004 if (inserted == NULL) {
1005 goto done;
1007 inserted_to = strstr_m(result, inserted);
1008 if (inserted_to == NULL) {
1009 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1010 goto done;
1012 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1013 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1015 done:
1016 saved_errno = errno;
1017 TALLOC_FREE(inserted);
1018 TALLOC_FREE(tmp);
1019 TALLOC_FREE(stripped);
1020 errno = saved_errno;
1021 return result;
1025 * Check whether a given directory contains a
1026 * snapshot directory as direct subdirectory.
1027 * If yes, return the path of the snapshot-subdir,
1028 * otherwise return NULL.
1030 static char *have_snapdir(struct vfs_handle_struct *handle,
1031 const char *path)
1033 struct smb_filename smb_fname;
1034 int ret;
1035 struct shadow_copy2_config *config;
1037 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1038 return NULL);
1040 ZERO_STRUCT(smb_fname);
1041 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1042 path, config->snapdir);
1043 if (smb_fname.base_name == NULL) {
1044 return NULL;
1047 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1048 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1049 return smb_fname.base_name;
1051 TALLOC_FREE(smb_fname.base_name);
1052 return NULL;
1056 * Find the snapshot directory (if any) for the given
1057 * filename (which is relative to the share).
1059 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1060 struct vfs_handle_struct *handle,
1061 struct smb_filename *smb_fname)
1063 char *path, *p;
1064 const char *snapdir;
1065 struct shadow_copy2_config *config;
1067 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1068 return NULL);
1071 * If the non-snapdisrseverywhere mode, we should not search!
1073 if (!config->snapdirseverywhere) {
1074 return config->snapshot_basepath;
1077 path = talloc_asprintf(mem_ctx, "%s/%s",
1078 handle->conn->connectpath,
1079 smb_fname->base_name);
1080 if (path == NULL) {
1081 return NULL;
1084 snapdir = have_snapdir(handle, path);
1085 if (snapdir != NULL) {
1086 TALLOC_FREE(path);
1087 return snapdir;
1090 while ((p = strrchr(path, '/')) && (p > path)) {
1092 p[0] = '\0';
1094 snapdir = have_snapdir(handle, path);
1095 if (snapdir != NULL) {
1096 TALLOC_FREE(path);
1097 return snapdir;
1100 TALLOC_FREE(path);
1101 return NULL;
1104 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1105 const char *name,
1106 char *gmt, size_t gmt_len)
1108 struct tm timestamp;
1109 time_t timestamp_t;
1110 unsigned long int timestamp_long;
1111 const char *fmt;
1112 struct shadow_copy2_config *config;
1114 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1115 return NULL);
1117 fmt = config->gmt_format;
1119 ZERO_STRUCT(timestamp);
1120 if (config->use_sscanf) {
1121 if (sscanf(name, fmt, &timestamp_long) != 1) {
1122 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1123 "no sscanf match %s: %s\n",
1124 fmt, name));
1125 return false;
1127 timestamp_t = timestamp_long;
1128 gmtime_r(&timestamp_t, &timestamp);
1129 } else {
1130 if (strptime(name, fmt, &timestamp) == NULL) {
1131 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1132 "no match %s: %s\n",
1133 fmt, name));
1134 return false;
1136 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1137 fmt, name));
1139 if (config->use_localtime) {
1140 timestamp.tm_isdst = -1;
1141 timestamp_t = mktime(&timestamp);
1142 gmtime_r(&timestamp_t, &timestamp);
1146 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1147 return true;
1150 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1152 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1155 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1157 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1161 sort the shadow copy data in ascending or descending order
1163 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1164 struct shadow_copy_data *shadow_copy2_data)
1166 int (*cmpfunc)(const void *, const void *);
1167 const char *sort;
1168 struct shadow_copy2_config *config;
1170 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1171 return);
1173 sort = config->sort_order;
1174 if (sort == NULL) {
1175 return;
1178 if (strcmp(sort, "asc") == 0) {
1179 cmpfunc = shadow_copy2_label_cmp_asc;
1180 } else if (strcmp(sort, "desc") == 0) {
1181 cmpfunc = shadow_copy2_label_cmp_desc;
1182 } else {
1183 return;
1186 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1187 shadow_copy2_data->labels)
1189 TYPESAFE_QSORT(shadow_copy2_data->labels,
1190 shadow_copy2_data->num_volumes,
1191 cmpfunc);
1195 static int shadow_copy2_get_shadow_copy_data(
1196 vfs_handle_struct *handle, files_struct *fsp,
1197 struct shadow_copy_data *shadow_copy2_data,
1198 bool labels)
1200 DIR *p;
1201 const char *snapdir;
1202 struct dirent *d;
1203 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1205 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1206 if (snapdir == NULL) {
1207 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1208 handle->conn->connectpath));
1209 errno = EINVAL;
1210 talloc_free(tmp_ctx);
1211 return -1;
1214 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1216 if (!p) {
1217 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1218 " - %s\n", snapdir, strerror(errno)));
1219 talloc_free(tmp_ctx);
1220 errno = ENOSYS;
1221 return -1;
1224 shadow_copy2_data->num_volumes = 0;
1225 shadow_copy2_data->labels = NULL;
1227 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1228 char snapshot[GMT_NAME_LEN+1];
1229 SHADOW_COPY_LABEL *tlabels;
1232 * ignore names not of the right form in the snapshot
1233 * directory
1235 if (!shadow_copy2_snapshot_to_gmt(
1236 handle, d->d_name,
1237 snapshot, sizeof(snapshot))) {
1239 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1240 "ignoring %s\n", d->d_name));
1241 continue;
1243 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1244 d->d_name, snapshot));
1246 if (!labels) {
1247 /* the caller doesn't want the labels */
1248 shadow_copy2_data->num_volumes++;
1249 continue;
1252 tlabels = talloc_realloc(shadow_copy2_data,
1253 shadow_copy2_data->labels,
1254 SHADOW_COPY_LABEL,
1255 shadow_copy2_data->num_volumes+1);
1256 if (tlabels == NULL) {
1257 DEBUG(0,("shadow_copy2: out of memory\n"));
1258 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1259 talloc_free(tmp_ctx);
1260 return -1;
1263 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1264 sizeof(*tlabels));
1266 shadow_copy2_data->num_volumes++;
1267 shadow_copy2_data->labels = tlabels;
1270 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1272 shadow_copy2_sort_data(handle, shadow_copy2_data);
1274 talloc_free(tmp_ctx);
1275 return 0;
1278 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1279 struct files_struct *fsp,
1280 uint32 security_info,
1281 TALLOC_CTX *mem_ctx,
1282 struct security_descriptor **ppdesc)
1284 time_t timestamp;
1285 char *stripped;
1286 NTSTATUS status;
1287 char *conv;
1289 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1290 fsp->fsp_name->base_name,
1291 &timestamp, &stripped)) {
1292 return map_nt_error_from_unix(errno);
1294 if (timestamp == 0) {
1295 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1296 mem_ctx,
1297 ppdesc);
1299 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1300 TALLOC_FREE(stripped);
1301 if (conv == NULL) {
1302 return map_nt_error_from_unix(errno);
1304 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1305 mem_ctx, ppdesc);
1306 TALLOC_FREE(conv);
1307 return status;
1310 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1311 const char *fname,
1312 uint32 security_info,
1313 TALLOC_CTX *mem_ctx,
1314 struct security_descriptor **ppdesc)
1316 time_t timestamp;
1317 char *stripped;
1318 NTSTATUS status;
1319 char *conv;
1321 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1322 &timestamp, &stripped)) {
1323 return map_nt_error_from_unix(errno);
1325 if (timestamp == 0) {
1326 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1327 mem_ctx, ppdesc);
1329 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1330 TALLOC_FREE(stripped);
1331 if (conv == NULL) {
1332 return map_nt_error_from_unix(errno);
1334 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1335 mem_ctx, ppdesc);
1336 TALLOC_FREE(conv);
1337 return status;
1340 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1341 const char *fname, mode_t mode)
1343 time_t timestamp;
1344 char *stripped;
1345 int ret, saved_errno;
1346 char *conv;
1348 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1349 &timestamp, &stripped)) {
1350 return -1;
1352 if (timestamp == 0) {
1353 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1355 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1356 TALLOC_FREE(stripped);
1357 if (conv == NULL) {
1358 return -1;
1360 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1361 saved_errno = errno;
1362 TALLOC_FREE(conv);
1363 errno = saved_errno;
1364 return ret;
1367 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
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_RMDIR(handle, fname);
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_RMDIR(handle, conv);
1387 saved_errno = errno;
1388 TALLOC_FREE(conv);
1389 errno = saved_errno;
1390 return ret;
1393 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1394 unsigned int flags)
1396 time_t timestamp;
1397 char *stripped;
1398 int ret, saved_errno;
1399 char *conv;
1401 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1402 &timestamp, &stripped)) {
1403 return -1;
1405 if (timestamp == 0) {
1406 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1408 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1409 TALLOC_FREE(stripped);
1410 if (conv == NULL) {
1411 return -1;
1413 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1414 saved_errno = errno;
1415 TALLOC_FREE(conv);
1416 errno = saved_errno;
1417 return ret;
1420 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1421 const char *fname, const char *aname,
1422 void *value, size_t size)
1424 time_t timestamp;
1425 char *stripped;
1426 ssize_t ret;
1427 int saved_errno;
1428 char *conv;
1430 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1431 &timestamp, &stripped)) {
1432 return -1;
1434 if (timestamp == 0) {
1435 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1436 size);
1438 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1439 TALLOC_FREE(stripped);
1440 if (conv == NULL) {
1441 return -1;
1443 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1444 saved_errno = errno;
1445 TALLOC_FREE(conv);
1446 errno = saved_errno;
1447 return ret;
1450 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1451 const char *fname,
1452 char *list, size_t size)
1454 time_t timestamp;
1455 char *stripped;
1456 ssize_t ret;
1457 int 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_LISTXATTR(handle, fname, list, size);
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_LISTXATTR(handle, conv, list, size);
1473 saved_errno = errno;
1474 TALLOC_FREE(conv);
1475 errno = saved_errno;
1476 return ret;
1479 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1480 const char *fname, const char *aname)
1482 time_t timestamp;
1483 char *stripped;
1484 int ret, saved_errno;
1485 char *conv;
1487 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1488 &timestamp, &stripped)) {
1489 return -1;
1491 if (timestamp == 0) {
1492 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1494 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1495 TALLOC_FREE(stripped);
1496 if (conv == NULL) {
1497 return -1;
1499 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1500 saved_errno = errno;
1501 TALLOC_FREE(conv);
1502 errno = saved_errno;
1503 return ret;
1506 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1507 const char *fname,
1508 const char *aname, const void *value,
1509 size_t size, int flags)
1511 time_t timestamp;
1512 char *stripped;
1513 ssize_t ret;
1514 int saved_errno;
1515 char *conv;
1517 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1518 &timestamp, &stripped)) {
1519 return -1;
1521 if (timestamp == 0) {
1522 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1523 flags);
1525 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1526 TALLOC_FREE(stripped);
1527 if (conv == NULL) {
1528 return -1;
1530 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1531 saved_errno = errno;
1532 TALLOC_FREE(conv);
1533 errno = saved_errno;
1534 return ret;
1537 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1538 const char *fname, mode_t mode)
1540 time_t timestamp;
1541 char *stripped;
1542 ssize_t ret;
1543 int saved_errno;
1544 char *conv;
1546 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1547 &timestamp, &stripped)) {
1548 return -1;
1550 if (timestamp == 0) {
1551 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1553 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1554 TALLOC_FREE(stripped);
1555 if (conv == NULL) {
1556 return -1;
1558 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1559 saved_errno = errno;
1560 TALLOC_FREE(conv);
1561 errno = saved_errno;
1562 return ret;
1565 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1566 const char *path,
1567 const char *name,
1568 TALLOC_CTX *mem_ctx,
1569 char **found_name)
1571 time_t timestamp;
1572 char *stripped;
1573 ssize_t ret;
1574 int saved_errno;
1575 char *conv;
1577 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1578 &timestamp, &stripped)) {
1579 return -1;
1581 if (timestamp == 0) {
1582 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1583 mem_ctx, found_name);
1585 if (stripped[0] == '\0') {
1586 *found_name = talloc_strdup(mem_ctx, name);
1587 if (*found_name == NULL) {
1588 errno = ENOMEM;
1589 return -1;
1591 return 0;
1593 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1594 TALLOC_FREE(stripped);
1595 if (conv == NULL) {
1596 return -1;
1598 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1599 mem_ctx, found_name);
1600 saved_errno = errno;
1601 TALLOC_FREE(conv);
1602 errno = saved_errno;
1603 return ret;
1606 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1607 const char *path, bool small_query,
1608 uint64_t *bsize, uint64_t *dfree,
1609 uint64_t *dsize)
1611 time_t timestamp;
1612 char *stripped;
1613 ssize_t ret;
1614 int saved_errno;
1615 char *conv;
1617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1618 &timestamp, &stripped)) {
1619 return -1;
1621 if (timestamp == 0) {
1622 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1623 bsize, dfree, dsize);
1626 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1627 TALLOC_FREE(stripped);
1628 if (conv == NULL) {
1629 return -1;
1632 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1633 dsize);
1635 saved_errno = errno;
1636 TALLOC_FREE(conv);
1637 errno = saved_errno;
1639 return ret;
1642 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1643 const char *service, const char *user)
1645 struct shadow_copy2_config *config;
1646 int ret;
1647 const char *snapdir;
1648 const char *gmt_format;
1649 const char *sort_order;
1650 const char *basedir;
1651 const char *mount_point;
1653 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1654 (unsigned)handle->conn->cnum,
1655 handle->conn->connectpath));
1657 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1658 if (ret < 0) {
1659 return ret;
1662 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1663 if (config == NULL) {
1664 DEBUG(0, ("talloc_zero() failed\n"));
1665 errno = ENOMEM;
1666 return -1;
1669 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1670 "shadow", "format",
1671 GMT_FORMAT);
1672 config->gmt_format = talloc_strdup(config, gmt_format);
1673 if (config->gmt_format == NULL) {
1674 DEBUG(0, ("talloc_strdup() failed\n"));
1675 errno = ENOMEM;
1676 return -1;
1679 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1680 "shadow", "sscanf", false);
1682 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1683 "shadow", "localtime",
1684 false);
1686 snapdir = lp_parm_const_string(SNUM(handle->conn),
1687 "shadow", "snapdir",
1688 ".snapshots");
1689 config->snapdir = talloc_strdup(config, snapdir);
1690 if (config->snapdir == NULL) {
1691 DEBUG(0, ("talloc_strdup() failed\n"));
1692 errno = ENOMEM;
1693 return -1;
1696 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1697 "shadow",
1698 "snapdirseverywhere",
1699 false);
1701 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1702 "shadow", "crossmountpoints",
1703 false);
1705 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1706 "shadow", "fixinodes",
1707 false);
1709 sort_order = lp_parm_const_string(SNUM(handle->conn),
1710 "shadow", "sort", "desc");
1711 config->sort_order = talloc_strdup(config, sort_order);
1712 if (config->sort_order == NULL) {
1713 DEBUG(0, ("talloc_strdup() failed\n"));
1714 errno = ENOMEM;
1715 return -1;
1718 mount_point = lp_parm_const_string(SNUM(handle->conn),
1719 "shadow", "mountpoint", NULL);
1720 if (mount_point != NULL) {
1721 if (mount_point[0] != '/') {
1722 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1723 "relative ('%s'), but it has to be an "
1724 "absolute path. Ignoring provided value.\n",
1725 mount_point));
1726 mount_point = NULL;
1727 } else {
1728 char *p;
1729 p = strstr(handle->conn->connectpath, mount_point);
1730 if (p != handle->conn->connectpath) {
1731 DEBUG(1, ("Warning: mount_point (%s) is not a "
1732 "subdirectory of the share root "
1733 "(%s). Ignoring provided value.\n",
1734 mount_point,
1735 handle->conn->connectpath));
1736 mount_point = NULL;
1741 if (mount_point != NULL) {
1742 config->mount_point = talloc_strdup(config, mount_point);
1743 if (config->mount_point == NULL) {
1744 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1745 return -1;
1747 } else {
1748 config->mount_point = shadow_copy2_find_mount_point(config,
1749 handle);
1750 if (config->mount_point == NULL) {
1751 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1752 " failed: %s\n", strerror(errno)));
1753 return -1;
1757 basedir = lp_parm_const_string(SNUM(handle->conn),
1758 "shadow", "basedir", NULL);
1760 if (basedir != NULL) {
1761 if (basedir[0] != '/') {
1762 DEBUG(1, (__location__ " Warning: 'basedir' is "
1763 "relative ('%s'), but it has to be an "
1764 "absolute path. Disabling basedir.\n",
1765 basedir));
1766 } else {
1767 char *p;
1768 p = strstr(basedir, config->mount_point);
1769 if (p != basedir) {
1770 DEBUG(1, ("Warning: basedir (%s) is not a "
1771 "subdirectory of the share root's "
1772 "mount point (%s). "
1773 "Disabling basedir\n",
1774 basedir, config->mount_point));
1775 } else {
1776 config->basedir = talloc_strdup(config,
1777 basedir);
1778 if (config->basedir == NULL) {
1779 DEBUG(0, ("talloc_strdup() failed\n"));
1780 errno = ENOMEM;
1781 return -1;
1787 if (config->snapdirseverywhere && config->basedir != NULL) {
1788 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1789 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1790 TALLOC_FREE(config->basedir);
1793 if (config->crossmountpoints && config->basedir != NULL) {
1794 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1795 "with 'crossmountpoints'. Disabling basedir.\n"));
1796 TALLOC_FREE(config->basedir);
1799 if (config->basedir == NULL) {
1800 config->basedir = config->mount_point;
1803 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1804 config->rel_connectpath = talloc_strdup(config,
1805 handle->conn->connectpath + strlen(config->basedir));
1806 if (config->rel_connectpath == NULL) {
1807 DEBUG(0, ("talloc_strdup() failed\n"));
1808 errno = ENOMEM;
1809 return -1;
1813 if (config->snapdir[0] == '/') {
1814 config->snapdir_absolute = true;
1816 if (config->snapdirseverywhere == true) {
1817 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1818 "is incompatible with 'snapdirseverywhere', "
1819 "setting 'snapdirseverywhere' to false.\n"));
1820 config->snapdirseverywhere = false;
1823 if (config->crossmountpoints == true) {
1824 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1825 "is not supported with an absolute snapdir. "
1826 "Disabling it.\n"));
1827 config->crossmountpoints = false;
1830 config->snapshot_basepath = config->snapdir;
1831 } else {
1832 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1833 config->mount_point, config->snapdir);
1834 if (config->snapshot_basepath == NULL) {
1835 DEBUG(0, ("talloc_asprintf() failed\n"));
1836 errno = ENOMEM;
1837 return -1;
1841 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1842 " share root: '%s'\n"
1843 " basedir: '%s'\n"
1844 " mountpoint: '%s'\n"
1845 " rel share root: '%s'\n"
1846 " snapdir: '%s'\n"
1847 " snapshot base path: '%s'\n"
1848 " format: '%s'\n"
1849 " use sscanf: %s\n"
1850 " snapdirs everywhere: %s\n"
1851 " cross mountpoints: %s\n"
1852 " fix inodes: %s\n"
1853 " sort order: %s\n"
1855 handle->conn->connectpath,
1856 config->basedir,
1857 config->mount_point,
1858 config->rel_connectpath,
1859 config->snapdir,
1860 config->snapshot_basepath,
1861 config->gmt_format,
1862 config->use_sscanf ? "yes" : "no",
1863 config->snapdirseverywhere ? "yes" : "no",
1864 config->crossmountpoints ? "yes" : "no",
1865 config->fixinodes ? "yes" : "no",
1866 config->sort_order
1870 SMB_VFS_HANDLE_SET_DATA(handle, config,
1871 NULL, struct shadow_copy2_config,
1872 return -1);
1874 return 0;
1877 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1878 .connect_fn = shadow_copy2_connect,
1879 .opendir_fn = shadow_copy2_opendir,
1880 .disk_free_fn = shadow_copy2_disk_free,
1881 .rename_fn = shadow_copy2_rename,
1882 .link_fn = shadow_copy2_link,
1883 .symlink_fn = shadow_copy2_symlink,
1884 .stat_fn = shadow_copy2_stat,
1885 .lstat_fn = shadow_copy2_lstat,
1886 .fstat_fn = shadow_copy2_fstat,
1887 .open_fn = shadow_copy2_open,
1888 .unlink_fn = shadow_copy2_unlink,
1889 .chmod_fn = shadow_copy2_chmod,
1890 .chown_fn = shadow_copy2_chown,
1891 .chdir_fn = shadow_copy2_chdir,
1892 .ntimes_fn = shadow_copy2_ntimes,
1893 .readlink_fn = shadow_copy2_readlink,
1894 .mknod_fn = shadow_copy2_mknod,
1895 .realpath_fn = shadow_copy2_realpath,
1896 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1897 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1898 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1899 .mkdir_fn = shadow_copy2_mkdir,
1900 .rmdir_fn = shadow_copy2_rmdir,
1901 .getxattr_fn = shadow_copy2_getxattr,
1902 .listxattr_fn = shadow_copy2_listxattr,
1903 .removexattr_fn = shadow_copy2_removexattr,
1904 .setxattr_fn = shadow_copy2_setxattr,
1905 .chmod_acl_fn = shadow_copy2_chmod_acl,
1906 .chflags_fn = shadow_copy2_chflags,
1907 .get_real_filename_fn = shadow_copy2_get_real_filename,
1910 NTSTATUS vfs_shadow_copy2_init(void);
1911 NTSTATUS vfs_shadow_copy2_init(void)
1913 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1914 "shadow_copy2", &vfs_shadow_copy2_fns);