shadow_copy2: add some blank lines for visual separation to shadow_copy2_convert()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobda1db42696c184344b1f97a76e647bd5bfb7a639
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;
886 NTSTATUS status;
888 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
889 smb_fname->base_name,
890 &timestamp, &stripped)) {
891 return -1;
893 if (timestamp == 0) {
894 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
896 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
897 if (!NT_STATUS_IS_OK(status)) {
898 errno = ENOMEM;
899 return -1;
901 conv->base_name = shadow_copy2_convert(
902 conv, handle, stripped, timestamp);
903 TALLOC_FREE(stripped);
904 if (conv->base_name == NULL) {
905 return -1;
907 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
908 saved_errno = errno;
909 TALLOC_FREE(conv);
910 errno = saved_errno;
911 return ret;
914 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
915 mode_t mode)
917 time_t timestamp;
918 char *stripped;
919 int ret, saved_errno;
920 char *conv;
922 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
923 &timestamp, &stripped)) {
924 return -1;
926 if (timestamp == 0) {
927 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
929 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
930 TALLOC_FREE(stripped);
931 if (conv == NULL) {
932 return -1;
934 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
935 saved_errno = errno;
936 TALLOC_FREE(conv);
937 errno = saved_errno;
938 return ret;
941 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
942 uid_t uid, gid_t gid)
944 time_t timestamp;
945 char *stripped;
946 int ret, saved_errno;
947 char *conv;
949 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
950 &timestamp, &stripped)) {
951 return -1;
953 if (timestamp == 0) {
954 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
956 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
957 TALLOC_FREE(stripped);
958 if (conv == NULL) {
959 return -1;
961 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
962 saved_errno = errno;
963 TALLOC_FREE(conv);
964 errno = saved_errno;
965 return ret;
968 static int shadow_copy2_chdir(vfs_handle_struct *handle,
969 const char *fname)
971 time_t timestamp;
972 char *stripped;
973 int ret, saved_errno;
974 char *conv;
976 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
977 &timestamp, &stripped)) {
978 return -1;
980 if (timestamp == 0) {
981 return SMB_VFS_NEXT_CHDIR(handle, fname);
983 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
984 TALLOC_FREE(stripped);
985 if (conv == NULL) {
986 return -1;
988 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
989 saved_errno = errno;
990 TALLOC_FREE(conv);
991 errno = saved_errno;
992 return ret;
995 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
996 const struct smb_filename *smb_fname,
997 struct smb_file_time *ft)
999 time_t timestamp;
1000 char *stripped;
1001 int ret, saved_errno;
1002 struct smb_filename *conv;
1003 NTSTATUS status;
1005 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1006 smb_fname->base_name,
1007 &timestamp, &stripped)) {
1008 return -1;
1010 if (timestamp == 0) {
1011 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1013 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 errno = ENOMEM;
1016 return -1;
1018 conv->base_name = shadow_copy2_convert(
1019 conv, handle, stripped, timestamp);
1020 TALLOC_FREE(stripped);
1021 if (conv->base_name == NULL) {
1022 return -1;
1024 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1025 saved_errno = errno;
1026 TALLOC_FREE(conv);
1027 errno = saved_errno;
1028 return ret;
1031 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1032 const char *fname, char *buf, size_t bufsiz)
1034 time_t timestamp;
1035 char *stripped;
1036 int ret, saved_errno;
1037 char *conv;
1039 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1040 &timestamp, &stripped)) {
1041 return -1;
1043 if (timestamp == 0) {
1044 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1046 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1047 TALLOC_FREE(stripped);
1048 if (conv == NULL) {
1049 return -1;
1051 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1052 saved_errno = errno;
1053 TALLOC_FREE(conv);
1054 errno = saved_errno;
1055 return ret;
1058 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1059 const char *fname, mode_t mode, SMB_DEV_T dev)
1061 time_t timestamp;
1062 char *stripped;
1063 int ret, saved_errno;
1064 char *conv;
1066 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1067 &timestamp, &stripped)) {
1068 return -1;
1070 if (timestamp == 0) {
1071 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1073 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1074 TALLOC_FREE(stripped);
1075 if (conv == NULL) {
1076 return -1;
1078 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1079 saved_errno = errno;
1080 TALLOC_FREE(conv);
1081 errno = saved_errno;
1082 return ret;
1085 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1086 const char *fname)
1088 time_t timestamp;
1089 char *stripped = NULL;
1090 char *tmp = NULL;
1091 char *result = NULL;
1092 char *inserted = NULL;
1093 char *inserted_to, *inserted_end;
1094 int saved_errno;
1096 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1097 &timestamp, &stripped)) {
1098 goto done;
1100 if (timestamp == 0) {
1101 return SMB_VFS_NEXT_REALPATH(handle, fname);
1104 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1105 if (tmp == NULL) {
1106 goto done;
1109 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1110 if (result == NULL) {
1111 goto done;
1115 * Take away what we've inserted. This removes the @GMT-thingy
1116 * completely, but will give a path under the share root.
1118 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1119 if (inserted == NULL) {
1120 goto done;
1122 inserted_to = strstr_m(result, inserted);
1123 if (inserted_to == NULL) {
1124 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1125 goto done;
1127 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1128 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1130 done:
1131 saved_errno = errno;
1132 TALLOC_FREE(inserted);
1133 TALLOC_FREE(tmp);
1134 TALLOC_FREE(stripped);
1135 errno = saved_errno;
1136 return result;
1140 * Check whether a given directory contains a
1141 * snapshot directory as direct subdirectory.
1142 * If yes, return the path of the snapshot-subdir,
1143 * otherwise return NULL.
1145 static char *have_snapdir(struct vfs_handle_struct *handle,
1146 const char *path)
1148 struct smb_filename smb_fname;
1149 int ret;
1150 struct shadow_copy2_config *config;
1152 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1153 return NULL);
1155 ZERO_STRUCT(smb_fname);
1156 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1157 path, config->snapdir);
1158 if (smb_fname.base_name == NULL) {
1159 return NULL;
1162 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1163 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1164 return smb_fname.base_name;
1166 TALLOC_FREE(smb_fname.base_name);
1167 return NULL;
1171 * Find the snapshot directory (if any) for the given
1172 * filename (which is relative to the share).
1174 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1175 struct vfs_handle_struct *handle,
1176 struct smb_filename *smb_fname)
1178 char *path, *p;
1179 const char *snapdir;
1180 struct shadow_copy2_config *config;
1182 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1183 return NULL);
1186 * If the non-snapdisrseverywhere mode, we should not search!
1188 if (!config->snapdirseverywhere) {
1189 return config->snapshot_basepath;
1192 path = talloc_asprintf(mem_ctx, "%s/%s",
1193 handle->conn->connectpath,
1194 smb_fname->base_name);
1195 if (path == NULL) {
1196 return NULL;
1199 snapdir = have_snapdir(handle, path);
1200 if (snapdir != NULL) {
1201 TALLOC_FREE(path);
1202 return snapdir;
1205 while ((p = strrchr(path, '/')) && (p > path)) {
1207 p[0] = '\0';
1209 snapdir = have_snapdir(handle, path);
1210 if (snapdir != NULL) {
1211 TALLOC_FREE(path);
1212 return snapdir;
1215 TALLOC_FREE(path);
1216 return NULL;
1219 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1220 const char *name,
1221 char *gmt, size_t gmt_len)
1223 struct tm timestamp;
1224 time_t timestamp_t;
1225 unsigned long int timestamp_long;
1226 const char *fmt;
1227 struct shadow_copy2_config *config;
1229 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1230 return NULL);
1232 fmt = config->gmt_format;
1234 ZERO_STRUCT(timestamp);
1235 if (config->use_sscanf) {
1236 if (sscanf(name, fmt, &timestamp_long) != 1) {
1237 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1238 "no sscanf match %s: %s\n",
1239 fmt, name));
1240 return false;
1242 timestamp_t = timestamp_long;
1243 gmtime_r(&timestamp_t, &timestamp);
1244 } else {
1245 if (strptime(name, fmt, &timestamp) == NULL) {
1246 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1247 "no match %s: %s\n",
1248 fmt, name));
1249 return false;
1251 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1252 fmt, name));
1254 if (config->use_localtime) {
1255 timestamp.tm_isdst = -1;
1256 timestamp_t = mktime(&timestamp);
1257 gmtime_r(&timestamp_t, &timestamp);
1261 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1262 return true;
1265 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1267 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1270 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1272 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1276 sort the shadow copy data in ascending or descending order
1278 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1279 struct shadow_copy_data *shadow_copy2_data)
1281 int (*cmpfunc)(const void *, const void *);
1282 const char *sort;
1283 struct shadow_copy2_config *config;
1285 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1286 return);
1288 sort = config->sort_order;
1289 if (sort == NULL) {
1290 return;
1293 if (strcmp(sort, "asc") == 0) {
1294 cmpfunc = shadow_copy2_label_cmp_asc;
1295 } else if (strcmp(sort, "desc") == 0) {
1296 cmpfunc = shadow_copy2_label_cmp_desc;
1297 } else {
1298 return;
1301 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1302 shadow_copy2_data->labels)
1304 TYPESAFE_QSORT(shadow_copy2_data->labels,
1305 shadow_copy2_data->num_volumes,
1306 cmpfunc);
1310 static int shadow_copy2_get_shadow_copy_data(
1311 vfs_handle_struct *handle, files_struct *fsp,
1312 struct shadow_copy_data *shadow_copy2_data,
1313 bool labels)
1315 DIR *p;
1316 const char *snapdir;
1317 struct dirent *d;
1318 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1320 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1321 if (snapdir == NULL) {
1322 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1323 handle->conn->connectpath));
1324 errno = EINVAL;
1325 talloc_free(tmp_ctx);
1326 return -1;
1329 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1331 if (!p) {
1332 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1333 " - %s\n", snapdir, strerror(errno)));
1334 talloc_free(tmp_ctx);
1335 errno = ENOSYS;
1336 return -1;
1339 shadow_copy2_data->num_volumes = 0;
1340 shadow_copy2_data->labels = NULL;
1342 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1343 char snapshot[GMT_NAME_LEN+1];
1344 SHADOW_COPY_LABEL *tlabels;
1347 * ignore names not of the right form in the snapshot
1348 * directory
1350 if (!shadow_copy2_snapshot_to_gmt(
1351 handle, d->d_name,
1352 snapshot, sizeof(snapshot))) {
1354 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1355 "ignoring %s\n", d->d_name));
1356 continue;
1358 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1359 d->d_name, snapshot));
1361 if (!labels) {
1362 /* the caller doesn't want the labels */
1363 shadow_copy2_data->num_volumes++;
1364 continue;
1367 tlabels = talloc_realloc(shadow_copy2_data,
1368 shadow_copy2_data->labels,
1369 SHADOW_COPY_LABEL,
1370 shadow_copy2_data->num_volumes+1);
1371 if (tlabels == NULL) {
1372 DEBUG(0,("shadow_copy2: out of memory\n"));
1373 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1374 talloc_free(tmp_ctx);
1375 return -1;
1378 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1379 sizeof(*tlabels));
1381 shadow_copy2_data->num_volumes++;
1382 shadow_copy2_data->labels = tlabels;
1385 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1387 shadow_copy2_sort_data(handle, shadow_copy2_data);
1389 talloc_free(tmp_ctx);
1390 return 0;
1393 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1394 struct files_struct *fsp,
1395 uint32 security_info,
1396 TALLOC_CTX *mem_ctx,
1397 struct security_descriptor **ppdesc)
1399 time_t timestamp;
1400 char *stripped;
1401 NTSTATUS status;
1402 char *conv;
1404 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1405 fsp->fsp_name->base_name,
1406 &timestamp, &stripped)) {
1407 return map_nt_error_from_unix(errno);
1409 if (timestamp == 0) {
1410 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1411 mem_ctx,
1412 ppdesc);
1414 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1415 TALLOC_FREE(stripped);
1416 if (conv == NULL) {
1417 return map_nt_error_from_unix(errno);
1419 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1420 mem_ctx, ppdesc);
1421 TALLOC_FREE(conv);
1422 return status;
1425 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1426 const char *fname,
1427 uint32 security_info,
1428 TALLOC_CTX *mem_ctx,
1429 struct security_descriptor **ppdesc)
1431 time_t timestamp;
1432 char *stripped;
1433 NTSTATUS status;
1434 char *conv;
1436 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1437 &timestamp, &stripped)) {
1438 return map_nt_error_from_unix(errno);
1440 if (timestamp == 0) {
1441 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1442 mem_ctx, ppdesc);
1444 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1445 TALLOC_FREE(stripped);
1446 if (conv == NULL) {
1447 return map_nt_error_from_unix(errno);
1449 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1450 mem_ctx, ppdesc);
1451 TALLOC_FREE(conv);
1452 return status;
1455 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1456 const char *fname, mode_t mode)
1458 time_t timestamp;
1459 char *stripped;
1460 int ret, saved_errno;
1461 char *conv;
1463 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1464 &timestamp, &stripped)) {
1465 return -1;
1467 if (timestamp == 0) {
1468 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1470 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1471 TALLOC_FREE(stripped);
1472 if (conv == NULL) {
1473 return -1;
1475 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1476 saved_errno = errno;
1477 TALLOC_FREE(conv);
1478 errno = saved_errno;
1479 return ret;
1482 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1484 time_t timestamp;
1485 char *stripped;
1486 int ret, saved_errno;
1487 char *conv;
1489 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1490 &timestamp, &stripped)) {
1491 return -1;
1493 if (timestamp == 0) {
1494 return SMB_VFS_NEXT_RMDIR(handle, fname);
1496 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1497 TALLOC_FREE(stripped);
1498 if (conv == NULL) {
1499 return -1;
1501 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1502 saved_errno = errno;
1503 TALLOC_FREE(conv);
1504 errno = saved_errno;
1505 return ret;
1508 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1509 unsigned int flags)
1511 time_t timestamp;
1512 char *stripped;
1513 int ret, saved_errno;
1514 char *conv;
1516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1517 &timestamp, &stripped)) {
1518 return -1;
1520 if (timestamp == 0) {
1521 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1523 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1524 TALLOC_FREE(stripped);
1525 if (conv == NULL) {
1526 return -1;
1528 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1529 saved_errno = errno;
1530 TALLOC_FREE(conv);
1531 errno = saved_errno;
1532 return ret;
1535 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1536 const char *fname, const char *aname,
1537 void *value, size_t size)
1539 time_t timestamp;
1540 char *stripped;
1541 ssize_t ret;
1542 int saved_errno;
1543 char *conv;
1545 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1546 &timestamp, &stripped)) {
1547 return -1;
1549 if (timestamp == 0) {
1550 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1551 size);
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_GETXATTR(handle, conv, aname, value, size);
1559 saved_errno = errno;
1560 TALLOC_FREE(conv);
1561 errno = saved_errno;
1562 return ret;
1565 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1566 const char *fname,
1567 char *list, size_t size)
1569 time_t timestamp;
1570 char *stripped;
1571 ssize_t ret;
1572 int saved_errno;
1573 char *conv;
1575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1576 &timestamp, &stripped)) {
1577 return -1;
1579 if (timestamp == 0) {
1580 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1582 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1583 TALLOC_FREE(stripped);
1584 if (conv == NULL) {
1585 return -1;
1587 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1588 saved_errno = errno;
1589 TALLOC_FREE(conv);
1590 errno = saved_errno;
1591 return ret;
1594 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1595 const char *fname, const char *aname)
1597 time_t timestamp;
1598 char *stripped;
1599 int ret, saved_errno;
1600 char *conv;
1602 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1603 &timestamp, &stripped)) {
1604 return -1;
1606 if (timestamp == 0) {
1607 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1609 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1610 TALLOC_FREE(stripped);
1611 if (conv == NULL) {
1612 return -1;
1614 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1615 saved_errno = errno;
1616 TALLOC_FREE(conv);
1617 errno = saved_errno;
1618 return ret;
1621 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1622 const char *fname,
1623 const char *aname, const void *value,
1624 size_t size, int flags)
1626 time_t timestamp;
1627 char *stripped;
1628 ssize_t ret;
1629 int saved_errno;
1630 char *conv;
1632 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1633 &timestamp, &stripped)) {
1634 return -1;
1636 if (timestamp == 0) {
1637 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1638 flags);
1640 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1641 TALLOC_FREE(stripped);
1642 if (conv == NULL) {
1643 return -1;
1645 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1646 saved_errno = errno;
1647 TALLOC_FREE(conv);
1648 errno = saved_errno;
1649 return ret;
1652 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1653 const char *fname, mode_t mode)
1655 time_t timestamp;
1656 char *stripped;
1657 ssize_t ret;
1658 int saved_errno;
1659 char *conv;
1661 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1662 &timestamp, &stripped)) {
1663 return -1;
1665 if (timestamp == 0) {
1666 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1668 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1669 TALLOC_FREE(stripped);
1670 if (conv == NULL) {
1671 return -1;
1673 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1674 saved_errno = errno;
1675 TALLOC_FREE(conv);
1676 errno = saved_errno;
1677 return ret;
1680 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1681 const char *path,
1682 const char *name,
1683 TALLOC_CTX *mem_ctx,
1684 char **found_name)
1686 time_t timestamp;
1687 char *stripped;
1688 ssize_t ret;
1689 int saved_errno;
1690 char *conv;
1692 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1693 &timestamp, &stripped)) {
1694 return -1;
1696 if (timestamp == 0) {
1697 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1698 mem_ctx, found_name);
1700 if (stripped[0] == '\0') {
1701 *found_name = talloc_strdup(mem_ctx, name);
1702 if (*found_name == NULL) {
1703 errno = ENOMEM;
1704 return -1;
1706 return 0;
1708 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1709 TALLOC_FREE(stripped);
1710 if (conv == NULL) {
1711 return -1;
1713 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1714 mem_ctx, found_name);
1715 saved_errno = errno;
1716 TALLOC_FREE(conv);
1717 errno = saved_errno;
1718 return ret;
1721 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1722 const char *path, bool small_query,
1723 uint64_t *bsize, uint64_t *dfree,
1724 uint64_t *dsize)
1726 time_t timestamp;
1727 char *stripped;
1728 ssize_t ret;
1729 int saved_errno;
1730 char *conv;
1732 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1733 &timestamp, &stripped)) {
1734 return -1;
1736 if (timestamp == 0) {
1737 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1738 bsize, dfree, dsize);
1741 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1742 TALLOC_FREE(stripped);
1743 if (conv == NULL) {
1744 return -1;
1747 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1748 dsize);
1750 saved_errno = errno;
1751 TALLOC_FREE(conv);
1752 errno = saved_errno;
1754 return ret;
1757 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1758 const char *service, const char *user)
1760 struct shadow_copy2_config *config;
1761 int ret;
1762 const char *snapdir;
1763 const char *gmt_format;
1764 const char *sort_order;
1765 const char *basedir;
1766 const char *mount_point;
1768 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1769 (unsigned)handle->conn->cnum,
1770 handle->conn->connectpath));
1772 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1773 if (ret < 0) {
1774 return ret;
1777 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1778 if (config == NULL) {
1779 DEBUG(0, ("talloc_zero() failed\n"));
1780 errno = ENOMEM;
1781 return -1;
1784 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1785 "shadow", "format",
1786 GMT_FORMAT);
1787 config->gmt_format = talloc_strdup(config, gmt_format);
1788 if (config->gmt_format == NULL) {
1789 DEBUG(0, ("talloc_strdup() failed\n"));
1790 errno = ENOMEM;
1791 return -1;
1794 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1795 "shadow", "sscanf", false);
1797 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1798 "shadow", "localtime",
1799 false);
1801 snapdir = lp_parm_const_string(SNUM(handle->conn),
1802 "shadow", "snapdir",
1803 ".snapshots");
1804 config->snapdir = talloc_strdup(config, snapdir);
1805 if (config->snapdir == NULL) {
1806 DEBUG(0, ("talloc_strdup() failed\n"));
1807 errno = ENOMEM;
1808 return -1;
1811 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1812 "shadow",
1813 "snapdirseverywhere",
1814 false);
1816 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1817 "shadow", "crossmountpoints",
1818 false);
1820 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1821 "shadow", "fixinodes",
1822 false);
1824 sort_order = lp_parm_const_string(SNUM(handle->conn),
1825 "shadow", "sort", "desc");
1826 config->sort_order = talloc_strdup(config, sort_order);
1827 if (config->sort_order == NULL) {
1828 DEBUG(0, ("talloc_strdup() failed\n"));
1829 errno = ENOMEM;
1830 return -1;
1833 mount_point = lp_parm_const_string(SNUM(handle->conn),
1834 "shadow", "mountpoint", NULL);
1835 if (mount_point != NULL) {
1836 if (mount_point[0] != '/') {
1837 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1838 "relative ('%s'), but it has to be an "
1839 "absolute path. Ignoring provided value.\n",
1840 mount_point));
1841 mount_point = NULL;
1842 } else {
1843 char *p;
1844 p = strstr(handle->conn->connectpath, mount_point);
1845 if (p != handle->conn->connectpath) {
1846 DEBUG(1, ("Warning: mount_point (%s) is not a "
1847 "subdirectory of the share root "
1848 "(%s). Ignoring provided value.\n",
1849 mount_point,
1850 handle->conn->connectpath));
1851 mount_point = NULL;
1856 if (mount_point != NULL) {
1857 config->mount_point = talloc_strdup(config, mount_point);
1858 if (config->mount_point == NULL) {
1859 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1860 return -1;
1862 } else {
1863 config->mount_point = shadow_copy2_find_mount_point(config,
1864 handle);
1865 if (config->mount_point == NULL) {
1866 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1867 " failed: %s\n", strerror(errno)));
1868 return -1;
1872 basedir = lp_parm_const_string(SNUM(handle->conn),
1873 "shadow", "basedir", NULL);
1875 if (basedir != NULL) {
1876 if (basedir[0] != '/') {
1877 DEBUG(1, (__location__ " Warning: 'basedir' is "
1878 "relative ('%s'), but it has to be an "
1879 "absolute path. Disabling basedir.\n",
1880 basedir));
1881 } else {
1882 char *p;
1883 p = strstr(basedir, config->mount_point);
1884 if (p != basedir) {
1885 DEBUG(1, ("Warning: basedir (%s) is not a "
1886 "subdirectory of the share root's "
1887 "mount point (%s). "
1888 "Disabling basedir\n",
1889 basedir, config->mount_point));
1890 } else {
1891 config->basedir = talloc_strdup(config,
1892 basedir);
1893 if (config->basedir == NULL) {
1894 DEBUG(0, ("talloc_strdup() failed\n"));
1895 errno = ENOMEM;
1896 return -1;
1902 if (config->snapdirseverywhere && config->basedir != NULL) {
1903 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1904 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1905 TALLOC_FREE(config->basedir);
1908 if (config->crossmountpoints && config->basedir != NULL) {
1909 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1910 "with 'crossmountpoints'. Disabling basedir.\n"));
1911 TALLOC_FREE(config->basedir);
1914 if (config->basedir == NULL) {
1915 config->basedir = config->mount_point;
1918 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1919 config->rel_connectpath = talloc_strdup(config,
1920 handle->conn->connectpath + strlen(config->basedir));
1921 if (config->rel_connectpath == NULL) {
1922 DEBUG(0, ("talloc_strdup() failed\n"));
1923 errno = ENOMEM;
1924 return -1;
1928 if (config->snapdir[0] == '/') {
1929 config->snapdir_absolute = true;
1931 if (config->snapdirseverywhere == true) {
1932 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1933 "is incompatible with 'snapdirseverywhere', "
1934 "setting 'snapdirseverywhere' to false.\n"));
1935 config->snapdirseverywhere = false;
1938 if (config->crossmountpoints == true) {
1939 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1940 "is not supported with an absolute snapdir. "
1941 "Disabling it.\n"));
1942 config->crossmountpoints = false;
1945 config->snapshot_basepath = config->snapdir;
1946 } else {
1947 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1948 config->mount_point, config->snapdir);
1949 if (config->snapshot_basepath == NULL) {
1950 DEBUG(0, ("talloc_asprintf() failed\n"));
1951 errno = ENOMEM;
1952 return -1;
1956 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1957 " share root: '%s'\n"
1958 " basedir: '%s'\n"
1959 " mountpoint: '%s'\n"
1960 " rel share root: '%s'\n"
1961 " snapdir: '%s'\n"
1962 " snapshot base path: '%s'\n"
1963 " format: '%s'\n"
1964 " use sscanf: %s\n"
1965 " snapdirs everywhere: %s\n"
1966 " cross mountpoints: %s\n"
1967 " fix inodes: %s\n"
1968 " sort order: %s\n"
1970 handle->conn->connectpath,
1971 config->basedir,
1972 config->mount_point,
1973 config->rel_connectpath,
1974 config->snapdir,
1975 config->snapshot_basepath,
1976 config->gmt_format,
1977 config->use_sscanf ? "yes" : "no",
1978 config->snapdirseverywhere ? "yes" : "no",
1979 config->crossmountpoints ? "yes" : "no",
1980 config->fixinodes ? "yes" : "no",
1981 config->sort_order
1985 SMB_VFS_HANDLE_SET_DATA(handle, config,
1986 NULL, struct shadow_copy2_config,
1987 return -1);
1989 return 0;
1992 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1993 .connect_fn = shadow_copy2_connect,
1994 .opendir_fn = shadow_copy2_opendir,
1995 .disk_free_fn = shadow_copy2_disk_free,
1996 .rename_fn = shadow_copy2_rename,
1997 .link_fn = shadow_copy2_link,
1998 .symlink_fn = shadow_copy2_symlink,
1999 .stat_fn = shadow_copy2_stat,
2000 .lstat_fn = shadow_copy2_lstat,
2001 .fstat_fn = shadow_copy2_fstat,
2002 .open_fn = shadow_copy2_open,
2003 .unlink_fn = shadow_copy2_unlink,
2004 .chmod_fn = shadow_copy2_chmod,
2005 .chown_fn = shadow_copy2_chown,
2006 .chdir_fn = shadow_copy2_chdir,
2007 .ntimes_fn = shadow_copy2_ntimes,
2008 .readlink_fn = shadow_copy2_readlink,
2009 .mknod_fn = shadow_copy2_mknod,
2010 .realpath_fn = shadow_copy2_realpath,
2011 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2012 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2013 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2014 .mkdir_fn = shadow_copy2_mkdir,
2015 .rmdir_fn = shadow_copy2_rmdir,
2016 .getxattr_fn = shadow_copy2_getxattr,
2017 .listxattr_fn = shadow_copy2_listxattr,
2018 .removexattr_fn = shadow_copy2_removexattr,
2019 .setxattr_fn = shadow_copy2_setxattr,
2020 .chmod_acl_fn = shadow_copy2_chmod_acl,
2021 .chflags_fn = shadow_copy2_chflags,
2022 .get_real_filename_fn = shadow_copy2_get_real_filename,
2025 NTSTATUS vfs_shadow_copy2_init(void);
2026 NTSTATUS vfs_shadow_copy2_init(void)
2028 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2029 "shadow_copy2", &vfs_shadow_copy2_fns);