shadow_copy2: add some blank lines for visual separation to shadow_copy2_convert()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob95249b2eac9339cc33786b7bfc57d14d24931a41
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 posix level GTM-tag string
162 * based on the configurable format.
164 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct *handle,
165 time_t snapshot,
166 char *snaptime_string,
167 size_t len)
169 struct tm snap_tm;
170 size_t snaptime_len;
171 struct shadow_copy2_config *config;
173 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
174 return 0);
176 if (config->use_sscanf) {
177 snaptime_len = snprintf(snaptime_string,
178 len,
179 config->gmt_format,
180 (unsigned long)snapshot);
181 if (snaptime_len <= 0) {
182 DEBUG(10, ("snprintf failed\n"));
183 return snaptime_len;
185 } else {
186 if (config->use_localtime) {
187 if (localtime_r(&snapshot, &snap_tm) == 0) {
188 DEBUG(10, ("gmtime_r failed\n"));
189 return -1;
191 } else {
192 if (gmtime_r(&snapshot, &snap_tm) == 0) {
193 DEBUG(10, ("gmtime_r failed\n"));
194 return -1;
197 snaptime_len = strftime(snaptime_string,
198 len,
199 config->gmt_format,
200 &snap_tm);
201 if (snaptime_len == 0) {
202 DEBUG(10, ("strftime failed\n"));
203 return 0;
207 return snaptime_len;
211 * Given a timstamp, build the string to insert into a path
212 * as a path component for creating the local path to the
213 * snapshot at the given timestamp of the input path.
215 * In the case of a parallel snapdir (specified with an
216 * absolute path), this is the inital portion of the
217 * local path of any snapshot file. The complete path is
218 * obtained by appending the portion of the file's path
219 * below the share root's mountpoint.
221 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
222 struct vfs_handle_struct *handle,
223 time_t snapshot)
225 fstring snaptime_string;
226 size_t snaptime_len = 0;
227 char *result = NULL;
228 struct shadow_copy2_config *config;
230 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
231 return NULL);
233 snaptime_len = shadow_copy2_posix_gmt_string(handle,
234 snapshot,
235 snaptime_string,
236 sizeof(snaptime_string));
237 if (snaptime_len <= 0) {
238 return NULL;
241 if (config->snapdir_absolute) {
242 result = talloc_asprintf(mem_ctx, "%s/%s",
243 config->snapdir, snaptime_string);
244 } else {
245 result = talloc_asprintf(mem_ctx, "/%s/%s",
246 config->snapdir, snaptime_string);
248 if (result == NULL) {
249 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
252 return result;
256 * Build the posix snapshot path for the connection
257 * at the given timestamp, i.e. the absolute posix path
258 * that contains the snapshot for this file system.
260 * This only applies to classical case, i.e. not
261 * to the "snapdirseverywhere" mode.
263 static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx,
264 struct vfs_handle_struct *handle,
265 time_t snapshot)
267 fstring snaptime_string;
268 size_t snaptime_len = 0;
269 char *result = NULL;
270 struct shadow_copy2_config *config;
272 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
273 return NULL);
275 snaptime_len = shadow_copy2_posix_gmt_string(handle,
276 snapshot,
277 snaptime_string,
278 sizeof(snaptime_string));
279 if (snaptime_len <= 0) {
280 return NULL;
283 result = talloc_asprintf(mem_ctx, "%s/%s",
284 config->snapshot_basepath, snaptime_string);
285 if (result == NULL) {
286 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
289 return result;
293 * Strip a snapshot component from an filename as
294 * handed in via the smb layer.
295 * Returns the parsed timestamp and the stripped filename.
297 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
298 struct vfs_handle_struct *handle,
299 const char *name,
300 time_t *ptimestamp,
301 char **pstripped)
303 struct tm tm;
304 time_t timestamp;
305 const char *p;
306 char *q;
307 char *stripped;
308 size_t rest_len, dst_len;
309 struct shadow_copy2_config *config;
311 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
312 return false);
314 DEBUG(10, (__location__ ": enter path '%s'\n", name));
316 p = strstr_m(name, "@GMT-");
317 if (p == NULL) {
318 goto no_snapshot;
320 if ((p > name) && (p[-1] != '/')) {
321 /* the GMT-token does not start a path-component */
322 goto no_snapshot;
324 q = strptime(p, GMT_FORMAT, &tm);
325 if (q == NULL) {
326 goto no_snapshot;
328 tm.tm_isdst = -1;
329 timestamp = timegm(&tm);
330 if (timestamp == (time_t)-1) {
331 goto no_snapshot;
333 if ((p == name) && (q[0] == '\0')) {
334 /* the name consists of only the GMT token */
335 if (pstripped != NULL) {
336 stripped = talloc_strdup(mem_ctx, "");
337 if (stripped == NULL) {
338 return false;
340 *pstripped = stripped;
342 *ptimestamp = timestamp;
343 return true;
345 if (q[0] != '/') {
347 * The GMT token is either at the end of the path
348 * or it is not a complete path component, i.e. the
349 * path component continues after the gmt-token.
351 * TODO: Is this correct? Or would the GMT tag as the
352 * last component be a valid input?
354 goto no_snapshot;
356 q += 1;
358 rest_len = strlen(q);
359 dst_len = (p-name) + rest_len;
361 if (config->snapdirseverywhere) {
362 char *insert;
363 bool have_insert;
364 insert = shadow_copy2_insert_string(talloc_tos(), handle,
365 timestamp);
366 if (insert == NULL) {
367 errno = ENOMEM;
368 return false;
371 DEBUG(10, (__location__ ": snapdirseverywhere mode.\n"
372 "path '%s'.\n"
373 "insert string '%s'\n", name, insert));
375 have_insert = (strstr(name, insert+1) != NULL);
376 if (have_insert) {
377 DEBUG(10, (__location__ ": insert string '%s' found in "
378 "path '%s' found in snapdirseverywhere mode "
379 "==> already converted\n", insert, name));
380 TALLOC_FREE(insert);
381 goto no_snapshot;
383 TALLOC_FREE(insert);
384 } else {
385 char *snapshot_path;
386 char *s;
388 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
389 handle,
390 timestamp);
391 if (snapshot_path == NULL) {
392 errno = ENOMEM;
393 return false;
396 DEBUG(10, (__location__ " path: '%s'.\n"
397 "snapshot path: '%s'\n", name, snapshot_path));
399 s = strstr(name, snapshot_path);
400 if (s == name) {
402 * this starts with "snapshot_basepath/GMT-Token"
403 * so it is already a converted absolute
404 * path. Don't process further.
406 DEBUG(10, (__location__ ": path '%s' starts with "
407 "snapshot path '%s' (not in "
408 "snapdirseverywhere mode) ==> "
409 "already converted\n", name, snapshot_path));
410 talloc_free(snapshot_path);
411 goto no_snapshot;
413 talloc_free(snapshot_path);
416 if (pstripped != NULL) {
417 stripped = talloc_array(mem_ctx, char, dst_len+1);
418 if (stripped == NULL) {
419 errno = ENOMEM;
420 return false;
422 if (p > name) {
423 memcpy(stripped, name, p-name);
425 if (rest_len > 0) {
426 memcpy(stripped + (p-name), q, rest_len);
428 stripped[dst_len] = '\0';
429 *pstripped = stripped;
431 *ptimestamp = timestamp;
432 return true;
433 no_snapshot:
434 *ptimestamp = 0;
435 return true;
438 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
439 vfs_handle_struct *handle)
441 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
442 dev_t dev;
443 struct stat st;
444 char *p;
446 if (stat(path, &st) != 0) {
447 talloc_free(path);
448 return NULL;
451 dev = st.st_dev;
453 while ((p = strrchr(path, '/')) && p > path) {
454 *p = 0;
455 if (stat(path, &st) != 0) {
456 talloc_free(path);
457 return NULL;
459 if (st.st_dev != dev) {
460 *p = '/';
461 break;
465 return path;
469 * Convert from a name as handed in via the SMB layer
470 * and a timestamp into the local path of the snapshot
471 * of the provided file at the provided time.
473 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
474 struct vfs_handle_struct *handle,
475 const char *name, time_t timestamp)
477 struct smb_filename converted_fname;
478 char *result = NULL;
479 size_t *slashes = NULL;
480 unsigned num_slashes;
481 char *path = NULL;
482 size_t pathlen;
483 char *insert = NULL;
484 char *converted = NULL;
485 size_t insertlen;
486 int i, saved_errno;
487 size_t min_offset;
488 struct shadow_copy2_config *config;
490 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
491 return NULL);
493 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
494 name);
495 if (path == NULL) {
496 errno = ENOMEM;
497 goto fail;
499 pathlen = talloc_get_size(path)-1;
501 DEBUG(10, ("converting %s\n", path));
503 if (!shadow_copy2_find_slashes(talloc_tos(), path,
504 &slashes, &num_slashes)) {
505 goto fail;
508 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
509 if (insert == NULL) {
510 goto fail;
512 insertlen = talloc_get_size(insert)-1;
514 converted = talloc_zero_array(mem_ctx, char, pathlen + insertlen + 1);
515 if (converted == NULL) {
516 goto fail;
519 if (path[pathlen-1] != '/') {
521 * Append a fake slash to find the snapshot root
523 size_t *tmp;
524 tmp = talloc_realloc(talloc_tos(), slashes,
525 size_t, num_slashes+1);
526 if (tmp == NULL) {
527 goto fail;
529 slashes = tmp;
530 slashes[num_slashes] = pathlen;
531 num_slashes += 1;
534 min_offset = 0;
536 if (!config->crossmountpoints) {
537 char *mount_point;
539 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
540 handle);
541 if (mount_point == NULL) {
542 goto fail;
544 min_offset = strlen(mount_point);
545 TALLOC_FREE(mount_point);
548 memcpy(converted, path, pathlen+1);
549 converted[pathlen+insertlen] = '\0';
551 ZERO_STRUCT(converted_fname);
552 converted_fname.base_name = converted;
554 for (i = num_slashes-1; i>=0; i--) {
555 int ret;
556 size_t offset;
558 offset = slashes[i];
560 if (offset < min_offset) {
561 errno = ENOENT;
562 goto fail;
565 memcpy(converted+offset, insert, insertlen);
567 offset += insertlen;
568 memcpy(converted+offset, path + slashes[i],
569 pathlen - slashes[i]);
571 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
573 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
574 ret, ret == 0 ? "ok" : strerror(errno)));
575 if (ret == 0) {
576 /* success */
577 break;
579 if (errno == ENOTDIR) {
581 * This is a valid condition: We appended the
582 * .snaphots/@GMT.. to a file name. Just try
583 * with the upper levels.
585 continue;
587 if (errno != ENOENT) {
588 /* Other problem than "not found" */
589 goto fail;
593 if (i >= 0) {
595 * Found something
597 DEBUG(10, ("Found %s\n", converted));
598 result = converted;
599 converted = NULL;
600 } else {
601 errno = ENOENT;
603 fail:
604 saved_errno = errno;
605 TALLOC_FREE(converted);
606 TALLOC_FREE(insert);
607 TALLOC_FREE(slashes);
608 TALLOC_FREE(path);
609 errno = saved_errno;
610 return result;
614 modify a sbuf return to ensure that inodes in the shadow directory
615 are different from those in the main directory
617 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
618 SMB_STRUCT_STAT *sbuf)
620 struct shadow_copy2_config *config;
622 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
623 return);
625 if (config->fixinodes) {
626 /* some snapshot systems, like GPFS, return the name
627 device:inode for the snapshot files as the current
628 files. That breaks the 'restore' button in the shadow copy
629 GUI, as the client gets a sharing violation.
631 This is a crude way of allowing both files to be
632 open at once. It has a slight chance of inode
633 number collision, but I can't see a better approach
634 without significant VFS changes
636 uint32_t shash;
638 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
639 if (shash == 0) {
640 shash = 1;
642 sbuf->st_ex_ino ^= shash;
646 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
647 const char *fname,
648 const char *mask,
649 uint32 attr)
651 time_t timestamp;
652 char *stripped;
653 DIR *ret;
654 int saved_errno;
655 char *conv;
657 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
658 &timestamp, &stripped)) {
659 return NULL;
661 if (timestamp == 0) {
662 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
664 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
665 TALLOC_FREE(stripped);
666 if (conv == NULL) {
667 return NULL;
669 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
670 saved_errno = errno;
671 TALLOC_FREE(conv);
672 errno = saved_errno;
673 return ret;
676 static int shadow_copy2_rename(vfs_handle_struct *handle,
677 const struct smb_filename *smb_fname_src,
678 const struct smb_filename *smb_fname_dst)
680 time_t timestamp_src, timestamp_dst;
682 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
683 smb_fname_src->base_name,
684 &timestamp_src, NULL)) {
685 return -1;
687 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
688 smb_fname_dst->base_name,
689 &timestamp_dst, NULL)) {
690 return -1;
692 if (timestamp_src != 0) {
693 errno = EXDEV;
694 return -1;
696 if (timestamp_dst != 0) {
697 errno = EROFS;
698 return -1;
700 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
703 static int shadow_copy2_symlink(vfs_handle_struct *handle,
704 const char *oldname, const char *newname)
706 time_t timestamp_old, timestamp_new;
708 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
709 &timestamp_old, NULL)) {
710 return -1;
712 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
713 &timestamp_new, NULL)) {
714 return -1;
716 if ((timestamp_old != 0) || (timestamp_new != 0)) {
717 errno = EROFS;
718 return -1;
720 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
723 static int shadow_copy2_link(vfs_handle_struct *handle,
724 const char *oldname, const char *newname)
726 time_t timestamp_old, timestamp_new;
728 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
729 &timestamp_old, NULL)) {
730 return -1;
732 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
733 &timestamp_new, NULL)) {
734 return -1;
736 if ((timestamp_old != 0) || (timestamp_new != 0)) {
737 errno = EROFS;
738 return -1;
740 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
743 static int shadow_copy2_stat(vfs_handle_struct *handle,
744 struct smb_filename *smb_fname)
746 time_t timestamp;
747 char *stripped, *tmp;
748 int ret, saved_errno;
750 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
751 smb_fname->base_name,
752 &timestamp, &stripped)) {
753 return -1;
755 if (timestamp == 0) {
756 return SMB_VFS_NEXT_STAT(handle, smb_fname);
759 tmp = smb_fname->base_name;
760 smb_fname->base_name = shadow_copy2_convert(
761 talloc_tos(), handle, stripped, timestamp);
762 TALLOC_FREE(stripped);
764 if (smb_fname->base_name == NULL) {
765 smb_fname->base_name = tmp;
766 return -1;
769 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
770 saved_errno = errno;
772 TALLOC_FREE(smb_fname->base_name);
773 smb_fname->base_name = tmp;
775 if (ret == 0) {
776 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
778 errno = saved_errno;
779 return ret;
782 static int shadow_copy2_lstat(vfs_handle_struct *handle,
783 struct smb_filename *smb_fname)
785 time_t timestamp;
786 char *stripped, *tmp;
787 int ret, saved_errno;
789 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
790 smb_fname->base_name,
791 &timestamp, &stripped)) {
792 return -1;
794 if (timestamp == 0) {
795 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
798 tmp = smb_fname->base_name;
799 smb_fname->base_name = shadow_copy2_convert(
800 talloc_tos(), handle, stripped, timestamp);
801 TALLOC_FREE(stripped);
803 if (smb_fname->base_name == NULL) {
804 smb_fname->base_name = tmp;
805 return -1;
808 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
809 saved_errno = errno;
811 TALLOC_FREE(smb_fname->base_name);
812 smb_fname->base_name = tmp;
814 if (ret == 0) {
815 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
817 errno = saved_errno;
818 return ret;
821 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
822 SMB_STRUCT_STAT *sbuf)
824 time_t timestamp;
825 int ret;
827 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
828 if (ret == -1) {
829 return ret;
831 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
832 fsp->fsp_name->base_name,
833 &timestamp, NULL)) {
834 return 0;
836 if (timestamp != 0) {
837 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
839 return 0;
842 static int shadow_copy2_open(vfs_handle_struct *handle,
843 struct smb_filename *smb_fname, files_struct *fsp,
844 int flags, mode_t mode)
846 time_t timestamp;
847 char *stripped, *tmp;
848 int ret, saved_errno;
850 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
851 smb_fname->base_name,
852 &timestamp, &stripped)) {
853 return -1;
855 if (timestamp == 0) {
856 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
859 tmp = smb_fname->base_name;
860 smb_fname->base_name = shadow_copy2_convert(
861 talloc_tos(), handle, stripped, timestamp);
862 TALLOC_FREE(stripped);
864 if (smb_fname->base_name == NULL) {
865 smb_fname->base_name = tmp;
866 return -1;
869 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
870 saved_errno = errno;
872 TALLOC_FREE(smb_fname->base_name);
873 smb_fname->base_name = tmp;
875 errno = saved_errno;
876 return ret;
879 static int shadow_copy2_unlink(vfs_handle_struct *handle,
880 const struct smb_filename *smb_fname)
882 time_t timestamp;
883 char *stripped;
884 int ret, saved_errno;
885 struct smb_filename *conv;
887 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
888 smb_fname->base_name,
889 &timestamp, &stripped)) {
890 return -1;
892 if (timestamp == 0) {
893 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
895 conv = cp_smb_filename(talloc_tos(), smb_fname);
896 if (conv == NULL) {
897 errno = ENOMEM;
898 return -1;
900 conv->base_name = shadow_copy2_convert(
901 conv, handle, stripped, timestamp);
902 TALLOC_FREE(stripped);
903 if (conv->base_name == NULL) {
904 return -1;
906 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
907 saved_errno = errno;
908 TALLOC_FREE(conv);
909 errno = saved_errno;
910 return ret;
913 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
914 mode_t mode)
916 time_t timestamp;
917 char *stripped;
918 int ret, saved_errno;
919 char *conv;
921 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
922 &timestamp, &stripped)) {
923 return -1;
925 if (timestamp == 0) {
926 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
928 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
929 TALLOC_FREE(stripped);
930 if (conv == NULL) {
931 return -1;
933 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
934 saved_errno = errno;
935 TALLOC_FREE(conv);
936 errno = saved_errno;
937 return ret;
940 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
941 uid_t uid, gid_t gid)
943 time_t timestamp;
944 char *stripped;
945 int ret, saved_errno;
946 char *conv;
948 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
949 &timestamp, &stripped)) {
950 return -1;
952 if (timestamp == 0) {
953 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
955 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
956 TALLOC_FREE(stripped);
957 if (conv == NULL) {
958 return -1;
960 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
961 saved_errno = errno;
962 TALLOC_FREE(conv);
963 errno = saved_errno;
964 return ret;
967 static int shadow_copy2_chdir(vfs_handle_struct *handle,
968 const char *fname)
970 time_t timestamp;
971 char *stripped;
972 int ret, saved_errno;
973 char *conv;
975 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
976 &timestamp, &stripped)) {
977 return -1;
979 if (timestamp == 0) {
980 return SMB_VFS_NEXT_CHDIR(handle, fname);
982 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
983 TALLOC_FREE(stripped);
984 if (conv == NULL) {
985 return -1;
987 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
988 saved_errno = errno;
989 TALLOC_FREE(conv);
990 errno = saved_errno;
991 return ret;
994 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
995 const struct smb_filename *smb_fname,
996 struct smb_file_time *ft)
998 time_t timestamp;
999 char *stripped;
1000 int ret, saved_errno;
1001 struct smb_filename *conv;
1003 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1004 smb_fname->base_name,
1005 &timestamp, &stripped)) {
1006 return -1;
1008 if (timestamp == 0) {
1009 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1011 conv = cp_smb_filename(talloc_tos(), smb_fname);
1012 if (conv == NULL) {
1013 errno = ENOMEM;
1014 return -1;
1016 conv->base_name = shadow_copy2_convert(
1017 conv, handle, stripped, timestamp);
1018 TALLOC_FREE(stripped);
1019 if (conv->base_name == NULL) {
1020 return -1;
1022 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1023 saved_errno = errno;
1024 TALLOC_FREE(conv);
1025 errno = saved_errno;
1026 return ret;
1029 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1030 const char *fname, char *buf, size_t bufsiz)
1032 time_t timestamp;
1033 char *stripped;
1034 int ret, saved_errno;
1035 char *conv;
1037 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1038 &timestamp, &stripped)) {
1039 return -1;
1041 if (timestamp == 0) {
1042 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1044 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1045 TALLOC_FREE(stripped);
1046 if (conv == NULL) {
1047 return -1;
1049 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1050 saved_errno = errno;
1051 TALLOC_FREE(conv);
1052 errno = saved_errno;
1053 return ret;
1056 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1057 const char *fname, mode_t mode, SMB_DEV_T dev)
1059 time_t timestamp;
1060 char *stripped;
1061 int ret, saved_errno;
1062 char *conv;
1064 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1065 &timestamp, &stripped)) {
1066 return -1;
1068 if (timestamp == 0) {
1069 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1071 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1072 TALLOC_FREE(stripped);
1073 if (conv == NULL) {
1074 return -1;
1076 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1077 saved_errno = errno;
1078 TALLOC_FREE(conv);
1079 errno = saved_errno;
1080 return ret;
1083 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1084 const char *fname)
1086 time_t timestamp;
1087 char *stripped = NULL;
1088 char *tmp = NULL;
1089 char *result = NULL;
1090 char *inserted = NULL;
1091 char *inserted_to, *inserted_end;
1092 int saved_errno;
1094 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1095 &timestamp, &stripped)) {
1096 goto done;
1098 if (timestamp == 0) {
1099 return SMB_VFS_NEXT_REALPATH(handle, fname);
1102 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1103 if (tmp == NULL) {
1104 goto done;
1107 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1108 if (result == NULL) {
1109 goto done;
1113 * Take away what we've inserted. This removes the @GMT-thingy
1114 * completely, but will give a path under the share root.
1116 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1117 if (inserted == NULL) {
1118 goto done;
1120 inserted_to = strstr_m(result, inserted);
1121 if (inserted_to == NULL) {
1122 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1123 goto done;
1125 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1126 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1128 done:
1129 saved_errno = errno;
1130 TALLOC_FREE(inserted);
1131 TALLOC_FREE(tmp);
1132 TALLOC_FREE(stripped);
1133 errno = saved_errno;
1134 return result;
1138 * Check whether a given directory contains a
1139 * snapshot directory as direct subdirectory.
1140 * If yes, return the path of the snapshot-subdir,
1141 * otherwise return NULL.
1143 static char *have_snapdir(struct vfs_handle_struct *handle,
1144 const char *path)
1146 struct smb_filename smb_fname;
1147 int ret;
1148 struct shadow_copy2_config *config;
1150 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1151 return NULL);
1153 ZERO_STRUCT(smb_fname);
1154 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1155 path, config->snapdir);
1156 if (smb_fname.base_name == NULL) {
1157 return NULL;
1160 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1161 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1162 return smb_fname.base_name;
1164 TALLOC_FREE(smb_fname.base_name);
1165 return NULL;
1169 * Find the snapshot directory (if any) for the given
1170 * filename (which is relative to the share).
1172 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1173 struct vfs_handle_struct *handle,
1174 struct smb_filename *smb_fname)
1176 char *path, *p;
1177 const char *snapdir;
1178 struct shadow_copy2_config *config;
1180 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1181 return NULL);
1184 * If the non-snapdisrseverywhere mode, we should not search!
1186 if (!config->snapdirseverywhere) {
1187 return config->snapshot_basepath;
1190 path = talloc_asprintf(mem_ctx, "%s/%s",
1191 handle->conn->connectpath,
1192 smb_fname->base_name);
1193 if (path == NULL) {
1194 return NULL;
1197 snapdir = have_snapdir(handle, path);
1198 if (snapdir != NULL) {
1199 TALLOC_FREE(path);
1200 return snapdir;
1203 while ((p = strrchr(path, '/')) && (p > path)) {
1205 p[0] = '\0';
1207 snapdir = have_snapdir(handle, path);
1208 if (snapdir != NULL) {
1209 TALLOC_FREE(path);
1210 return snapdir;
1213 TALLOC_FREE(path);
1214 return NULL;
1217 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1218 const char *name,
1219 char *gmt, size_t gmt_len)
1221 struct tm timestamp;
1222 time_t timestamp_t;
1223 unsigned long int timestamp_long;
1224 const char *fmt;
1225 struct shadow_copy2_config *config;
1227 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1228 return NULL);
1230 fmt = config->gmt_format;
1232 ZERO_STRUCT(timestamp);
1233 if (config->use_sscanf) {
1234 if (sscanf(name, fmt, &timestamp_long) != 1) {
1235 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1236 "no sscanf match %s: %s\n",
1237 fmt, name));
1238 return false;
1240 timestamp_t = timestamp_long;
1241 gmtime_r(&timestamp_t, &timestamp);
1242 } else {
1243 if (strptime(name, fmt, &timestamp) == NULL) {
1244 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1245 "no match %s: %s\n",
1246 fmt, name));
1247 return false;
1249 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1250 fmt, name));
1252 if (config->use_localtime) {
1253 timestamp.tm_isdst = -1;
1254 timestamp_t = mktime(&timestamp);
1255 gmtime_r(&timestamp_t, &timestamp);
1259 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1260 return true;
1263 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1265 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1268 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1270 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1274 sort the shadow copy data in ascending or descending order
1276 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1277 struct shadow_copy_data *shadow_copy2_data)
1279 int (*cmpfunc)(const void *, const void *);
1280 const char *sort;
1281 struct shadow_copy2_config *config;
1283 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1284 return);
1286 sort = config->sort_order;
1287 if (sort == NULL) {
1288 return;
1291 if (strcmp(sort, "asc") == 0) {
1292 cmpfunc = shadow_copy2_label_cmp_asc;
1293 } else if (strcmp(sort, "desc") == 0) {
1294 cmpfunc = shadow_copy2_label_cmp_desc;
1295 } else {
1296 return;
1299 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1300 shadow_copy2_data->labels)
1302 TYPESAFE_QSORT(shadow_copy2_data->labels,
1303 shadow_copy2_data->num_volumes,
1304 cmpfunc);
1308 static int shadow_copy2_get_shadow_copy_data(
1309 vfs_handle_struct *handle, files_struct *fsp,
1310 struct shadow_copy_data *shadow_copy2_data,
1311 bool labels)
1313 DIR *p;
1314 const char *snapdir;
1315 struct dirent *d;
1316 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1318 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1319 if (snapdir == NULL) {
1320 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1321 handle->conn->connectpath));
1322 errno = EINVAL;
1323 talloc_free(tmp_ctx);
1324 return -1;
1327 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1329 if (!p) {
1330 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1331 " - %s\n", snapdir, strerror(errno)));
1332 talloc_free(tmp_ctx);
1333 errno = ENOSYS;
1334 return -1;
1337 shadow_copy2_data->num_volumes = 0;
1338 shadow_copy2_data->labels = NULL;
1340 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1341 char snapshot[GMT_NAME_LEN+1];
1342 SHADOW_COPY_LABEL *tlabels;
1345 * ignore names not of the right form in the snapshot
1346 * directory
1348 if (!shadow_copy2_snapshot_to_gmt(
1349 handle, d->d_name,
1350 snapshot, sizeof(snapshot))) {
1352 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1353 "ignoring %s\n", d->d_name));
1354 continue;
1356 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1357 d->d_name, snapshot));
1359 if (!labels) {
1360 /* the caller doesn't want the labels */
1361 shadow_copy2_data->num_volumes++;
1362 continue;
1365 tlabels = talloc_realloc(shadow_copy2_data,
1366 shadow_copy2_data->labels,
1367 SHADOW_COPY_LABEL,
1368 shadow_copy2_data->num_volumes+1);
1369 if (tlabels == NULL) {
1370 DEBUG(0,("shadow_copy2: out of memory\n"));
1371 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1372 talloc_free(tmp_ctx);
1373 return -1;
1376 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1377 sizeof(*tlabels));
1379 shadow_copy2_data->num_volumes++;
1380 shadow_copy2_data->labels = tlabels;
1383 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1385 shadow_copy2_sort_data(handle, shadow_copy2_data);
1387 talloc_free(tmp_ctx);
1388 return 0;
1391 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1392 struct files_struct *fsp,
1393 uint32 security_info,
1394 TALLOC_CTX *mem_ctx,
1395 struct security_descriptor **ppdesc)
1397 time_t timestamp;
1398 char *stripped;
1399 NTSTATUS status;
1400 char *conv;
1402 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1403 fsp->fsp_name->base_name,
1404 &timestamp, &stripped)) {
1405 return map_nt_error_from_unix(errno);
1407 if (timestamp == 0) {
1408 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1409 mem_ctx,
1410 ppdesc);
1412 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1413 TALLOC_FREE(stripped);
1414 if (conv == NULL) {
1415 return map_nt_error_from_unix(errno);
1417 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1418 mem_ctx, ppdesc);
1419 TALLOC_FREE(conv);
1420 return status;
1423 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1424 const char *fname,
1425 uint32 security_info,
1426 TALLOC_CTX *mem_ctx,
1427 struct security_descriptor **ppdesc)
1429 time_t timestamp;
1430 char *stripped;
1431 NTSTATUS status;
1432 char *conv;
1434 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1435 &timestamp, &stripped)) {
1436 return map_nt_error_from_unix(errno);
1438 if (timestamp == 0) {
1439 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1440 mem_ctx, ppdesc);
1442 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1443 TALLOC_FREE(stripped);
1444 if (conv == NULL) {
1445 return map_nt_error_from_unix(errno);
1447 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1448 mem_ctx, ppdesc);
1449 TALLOC_FREE(conv);
1450 return status;
1453 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1454 const char *fname, mode_t mode)
1456 time_t timestamp;
1457 char *stripped;
1458 int ret, saved_errno;
1459 char *conv;
1461 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1462 &timestamp, &stripped)) {
1463 return -1;
1465 if (timestamp == 0) {
1466 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1468 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1469 TALLOC_FREE(stripped);
1470 if (conv == NULL) {
1471 return -1;
1473 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1474 saved_errno = errno;
1475 TALLOC_FREE(conv);
1476 errno = saved_errno;
1477 return ret;
1480 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
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_RMDIR(handle, fname);
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_RMDIR(handle, conv);
1500 saved_errno = errno;
1501 TALLOC_FREE(conv);
1502 errno = saved_errno;
1503 return ret;
1506 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1507 unsigned int flags)
1509 time_t timestamp;
1510 char *stripped;
1511 int ret, saved_errno;
1512 char *conv;
1514 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1515 &timestamp, &stripped)) {
1516 return -1;
1518 if (timestamp == 0) {
1519 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1521 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1522 TALLOC_FREE(stripped);
1523 if (conv == NULL) {
1524 return -1;
1526 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1527 saved_errno = errno;
1528 TALLOC_FREE(conv);
1529 errno = saved_errno;
1530 return ret;
1533 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1534 const char *fname, const char *aname,
1535 void *value, size_t size)
1537 time_t timestamp;
1538 char *stripped;
1539 ssize_t ret;
1540 int saved_errno;
1541 char *conv;
1543 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1544 &timestamp, &stripped)) {
1545 return -1;
1547 if (timestamp == 0) {
1548 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1549 size);
1551 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1552 TALLOC_FREE(stripped);
1553 if (conv == NULL) {
1554 return -1;
1556 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1557 saved_errno = errno;
1558 TALLOC_FREE(conv);
1559 errno = saved_errno;
1560 return ret;
1563 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1564 const char *fname,
1565 char *list, size_t size)
1567 time_t timestamp;
1568 char *stripped;
1569 ssize_t ret;
1570 int saved_errno;
1571 char *conv;
1573 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1574 &timestamp, &stripped)) {
1575 return -1;
1577 if (timestamp == 0) {
1578 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1580 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1581 TALLOC_FREE(stripped);
1582 if (conv == NULL) {
1583 return -1;
1585 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1586 saved_errno = errno;
1587 TALLOC_FREE(conv);
1588 errno = saved_errno;
1589 return ret;
1592 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1593 const char *fname, const char *aname)
1595 time_t timestamp;
1596 char *stripped;
1597 int ret, saved_errno;
1598 char *conv;
1600 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1601 &timestamp, &stripped)) {
1602 return -1;
1604 if (timestamp == 0) {
1605 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1607 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1608 TALLOC_FREE(stripped);
1609 if (conv == NULL) {
1610 return -1;
1612 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1613 saved_errno = errno;
1614 TALLOC_FREE(conv);
1615 errno = saved_errno;
1616 return ret;
1619 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1620 const char *fname,
1621 const char *aname, const void *value,
1622 size_t size, int flags)
1624 time_t timestamp;
1625 char *stripped;
1626 ssize_t ret;
1627 int saved_errno;
1628 char *conv;
1630 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1631 &timestamp, &stripped)) {
1632 return -1;
1634 if (timestamp == 0) {
1635 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1636 flags);
1638 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1639 TALLOC_FREE(stripped);
1640 if (conv == NULL) {
1641 return -1;
1643 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1644 saved_errno = errno;
1645 TALLOC_FREE(conv);
1646 errno = saved_errno;
1647 return ret;
1650 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1651 const char *fname, mode_t mode)
1653 time_t timestamp;
1654 char *stripped;
1655 ssize_t ret;
1656 int saved_errno;
1657 char *conv;
1659 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1660 &timestamp, &stripped)) {
1661 return -1;
1663 if (timestamp == 0) {
1664 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1666 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1667 TALLOC_FREE(stripped);
1668 if (conv == NULL) {
1669 return -1;
1671 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1672 saved_errno = errno;
1673 TALLOC_FREE(conv);
1674 errno = saved_errno;
1675 return ret;
1678 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1679 const char *path,
1680 const char *name,
1681 TALLOC_CTX *mem_ctx,
1682 char **found_name)
1684 time_t timestamp;
1685 char *stripped;
1686 ssize_t ret;
1687 int saved_errno;
1688 char *conv;
1690 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1691 &timestamp, &stripped)) {
1692 return -1;
1694 if (timestamp == 0) {
1695 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1696 mem_ctx, found_name);
1698 if (stripped[0] == '\0') {
1699 *found_name = talloc_strdup(mem_ctx, name);
1700 if (*found_name == NULL) {
1701 errno = ENOMEM;
1702 return -1;
1704 return 0;
1706 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1707 TALLOC_FREE(stripped);
1708 if (conv == NULL) {
1709 return -1;
1711 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1712 mem_ctx, found_name);
1713 saved_errno = errno;
1714 TALLOC_FREE(conv);
1715 errno = saved_errno;
1716 return ret;
1719 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1720 const char *path, bool small_query,
1721 uint64_t *bsize, uint64_t *dfree,
1722 uint64_t *dsize)
1724 time_t timestamp;
1725 char *stripped;
1726 ssize_t ret;
1727 int saved_errno;
1728 char *conv;
1730 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1731 &timestamp, &stripped)) {
1732 return -1;
1734 if (timestamp == 0) {
1735 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1736 bsize, dfree, dsize);
1739 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1740 TALLOC_FREE(stripped);
1741 if (conv == NULL) {
1742 return -1;
1745 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1746 dsize);
1748 saved_errno = errno;
1749 TALLOC_FREE(conv);
1750 errno = saved_errno;
1752 return ret;
1755 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1756 const char *service, const char *user)
1758 struct shadow_copy2_config *config;
1759 int ret;
1760 const char *snapdir;
1761 const char *gmt_format;
1762 const char *sort_order;
1763 const char *basedir;
1764 const char *mount_point;
1766 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1767 (unsigned)handle->conn->cnum,
1768 handle->conn->connectpath));
1770 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1771 if (ret < 0) {
1772 return ret;
1775 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1776 if (config == NULL) {
1777 DEBUG(0, ("talloc_zero() failed\n"));
1778 errno = ENOMEM;
1779 return -1;
1782 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1783 "shadow", "format",
1784 GMT_FORMAT);
1785 config->gmt_format = talloc_strdup(config, gmt_format);
1786 if (config->gmt_format == NULL) {
1787 DEBUG(0, ("talloc_strdup() failed\n"));
1788 errno = ENOMEM;
1789 return -1;
1792 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1793 "shadow", "sscanf", false);
1795 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1796 "shadow", "localtime",
1797 false);
1799 snapdir = lp_parm_const_string(SNUM(handle->conn),
1800 "shadow", "snapdir",
1801 ".snapshots");
1802 config->snapdir = talloc_strdup(config, snapdir);
1803 if (config->snapdir == NULL) {
1804 DEBUG(0, ("talloc_strdup() failed\n"));
1805 errno = ENOMEM;
1806 return -1;
1809 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1810 "shadow",
1811 "snapdirseverywhere",
1812 false);
1814 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1815 "shadow", "crossmountpoints",
1816 false);
1818 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1819 "shadow", "fixinodes",
1820 false);
1822 sort_order = lp_parm_const_string(SNUM(handle->conn),
1823 "shadow", "sort", "desc");
1824 config->sort_order = talloc_strdup(config, sort_order);
1825 if (config->sort_order == NULL) {
1826 DEBUG(0, ("talloc_strdup() failed\n"));
1827 errno = ENOMEM;
1828 return -1;
1831 mount_point = lp_parm_const_string(SNUM(handle->conn),
1832 "shadow", "mountpoint", NULL);
1833 if (mount_point != NULL) {
1834 if (mount_point[0] != '/') {
1835 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1836 "relative ('%s'), but it has to be an "
1837 "absolute path. Ignoring provided value.\n",
1838 mount_point));
1839 mount_point = NULL;
1840 } else {
1841 char *p;
1842 p = strstr(handle->conn->connectpath, mount_point);
1843 if (p != handle->conn->connectpath) {
1844 DEBUG(1, ("Warning: mount_point (%s) is not a "
1845 "subdirectory of the share root "
1846 "(%s). Ignoring provided value.\n",
1847 mount_point,
1848 handle->conn->connectpath));
1849 mount_point = NULL;
1854 if (mount_point != NULL) {
1855 config->mount_point = talloc_strdup(config, mount_point);
1856 if (config->mount_point == NULL) {
1857 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1858 return -1;
1860 } else {
1861 config->mount_point = shadow_copy2_find_mount_point(config,
1862 handle);
1863 if (config->mount_point == NULL) {
1864 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1865 " failed: %s\n", strerror(errno)));
1866 return -1;
1870 basedir = lp_parm_const_string(SNUM(handle->conn),
1871 "shadow", "basedir", NULL);
1873 if (basedir != NULL) {
1874 if (basedir[0] != '/') {
1875 DEBUG(1, (__location__ " Warning: 'basedir' is "
1876 "relative ('%s'), but it has to be an "
1877 "absolute path. Disabling basedir.\n",
1878 basedir));
1879 } else {
1880 char *p;
1881 p = strstr(basedir, config->mount_point);
1882 if (p != basedir) {
1883 DEBUG(1, ("Warning: basedir (%s) is not a "
1884 "subdirectory of the share root's "
1885 "mount point (%s). "
1886 "Disabling basedir\n",
1887 basedir, config->mount_point));
1888 } else {
1889 config->basedir = talloc_strdup(config,
1890 basedir);
1891 if (config->basedir == NULL) {
1892 DEBUG(0, ("talloc_strdup() failed\n"));
1893 errno = ENOMEM;
1894 return -1;
1900 if (config->snapdirseverywhere && config->basedir != NULL) {
1901 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1902 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1903 TALLOC_FREE(config->basedir);
1906 if (config->crossmountpoints && config->basedir != NULL) {
1907 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1908 "with 'crossmountpoints'. Disabling basedir.\n"));
1909 TALLOC_FREE(config->basedir);
1912 if (config->basedir == NULL) {
1913 config->basedir = config->mount_point;
1916 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1917 config->rel_connectpath = talloc_strdup(config,
1918 handle->conn->connectpath + strlen(config->basedir));
1919 if (config->rel_connectpath == NULL) {
1920 DEBUG(0, ("talloc_strdup() failed\n"));
1921 errno = ENOMEM;
1922 return -1;
1926 if (config->snapdir[0] == '/') {
1927 config->snapdir_absolute = true;
1929 if (config->snapdirseverywhere == true) {
1930 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1931 "is incompatible with 'snapdirseverywhere', "
1932 "setting 'snapdirseverywhere' to false.\n"));
1933 config->snapdirseverywhere = false;
1936 if (config->crossmountpoints == true) {
1937 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1938 "is not supported with an absolute snapdir. "
1939 "Disabling it.\n"));
1940 config->crossmountpoints = false;
1943 config->snapshot_basepath = config->snapdir;
1944 } else {
1945 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1946 config->mount_point, config->snapdir);
1947 if (config->snapshot_basepath == NULL) {
1948 DEBUG(0, ("talloc_asprintf() failed\n"));
1949 errno = ENOMEM;
1950 return -1;
1954 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1955 " share root: '%s'\n"
1956 " basedir: '%s'\n"
1957 " mountpoint: '%s'\n"
1958 " rel share root: '%s'\n"
1959 " snapdir: '%s'\n"
1960 " snapshot base path: '%s'\n"
1961 " format: '%s'\n"
1962 " use sscanf: %s\n"
1963 " snapdirs everywhere: %s\n"
1964 " cross mountpoints: %s\n"
1965 " fix inodes: %s\n"
1966 " sort order: %s\n"
1968 handle->conn->connectpath,
1969 config->basedir,
1970 config->mount_point,
1971 config->rel_connectpath,
1972 config->snapdir,
1973 config->snapshot_basepath,
1974 config->gmt_format,
1975 config->use_sscanf ? "yes" : "no",
1976 config->snapdirseverywhere ? "yes" : "no",
1977 config->crossmountpoints ? "yes" : "no",
1978 config->fixinodes ? "yes" : "no",
1979 config->sort_order
1983 SMB_VFS_HANDLE_SET_DATA(handle, config,
1984 NULL, struct shadow_copy2_config,
1985 return -1);
1987 return 0;
1990 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1991 .connect_fn = shadow_copy2_connect,
1992 .opendir_fn = shadow_copy2_opendir,
1993 .disk_free_fn = shadow_copy2_disk_free,
1994 .rename_fn = shadow_copy2_rename,
1995 .link_fn = shadow_copy2_link,
1996 .symlink_fn = shadow_copy2_symlink,
1997 .stat_fn = shadow_copy2_stat,
1998 .lstat_fn = shadow_copy2_lstat,
1999 .fstat_fn = shadow_copy2_fstat,
2000 .open_fn = shadow_copy2_open,
2001 .unlink_fn = shadow_copy2_unlink,
2002 .chmod_fn = shadow_copy2_chmod,
2003 .chown_fn = shadow_copy2_chown,
2004 .chdir_fn = shadow_copy2_chdir,
2005 .ntimes_fn = shadow_copy2_ntimes,
2006 .readlink_fn = shadow_copy2_readlink,
2007 .mknod_fn = shadow_copy2_mknod,
2008 .realpath_fn = shadow_copy2_realpath,
2009 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2010 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2011 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2012 .mkdir_fn = shadow_copy2_mkdir,
2013 .rmdir_fn = shadow_copy2_rmdir,
2014 .getxattr_fn = shadow_copy2_getxattr,
2015 .listxattr_fn = shadow_copy2_listxattr,
2016 .removexattr_fn = shadow_copy2_removexattr,
2017 .setxattr_fn = shadow_copy2_setxattr,
2018 .chmod_acl_fn = shadow_copy2_chmod_acl,
2019 .chflags_fn = shadow_copy2_chflags,
2020 .get_real_filename_fn = shadow_copy2_get_real_filename,
2023 NTSTATUS vfs_shadow_copy2_init(void);
2024 NTSTATUS vfs_shadow_copy2_init(void)
2026 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2027 "shadow_copy2", &vfs_shadow_copy2_fns);