shadow_copy2: fix shadow_copy2_convert() in the classical case.
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobcdab21e8924adf872f708ff0ca433a596338d8e2
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 DEBUG(10, ("converting '%s'\n", name));
495 if (!config->snapdirseverywhere) {
496 int ret;
497 char *snapshot_path;
499 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
500 handle,
501 timestamp);
502 if (snapshot_path == NULL) {
503 goto fail;
506 if (config->rel_connectpath == NULL) {
507 converted = talloc_asprintf(mem_ctx, "%s/%s",
508 snapshot_path, name);
509 } else {
510 converted = talloc_asprintf(mem_ctx, "%s/%s/%s",
511 snapshot_path,
512 config->rel_connectpath,
513 name);
515 if (converted == NULL) {
516 goto fail;
519 ZERO_STRUCT(converted_fname);
520 converted_fname.base_name = converted;
522 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
523 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
524 converted,
525 ret, ret == 0 ? "ok" : strerror(errno)));
526 if (ret == 0) {
527 DEBUG(10, ("Found %s\n", converted));
528 result = converted;
529 converted = NULL;
530 goto fail;
531 } else {
532 errno = ENOENT;
533 goto fail;
535 /* never reached ... */
538 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
539 name);
540 if (path == NULL) {
541 errno = ENOMEM;
542 goto fail;
544 pathlen = talloc_get_size(path)-1;
546 if (!shadow_copy2_find_slashes(talloc_tos(), path,
547 &slashes, &num_slashes)) {
548 goto fail;
551 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
552 if (insert == NULL) {
553 goto fail;
555 insertlen = talloc_get_size(insert)-1;
557 converted = talloc_zero_array(mem_ctx, char, pathlen + insertlen + 1);
558 if (converted == NULL) {
559 goto fail;
562 if (path[pathlen-1] != '/') {
564 * Append a fake slash to find the snapshot root
566 size_t *tmp;
567 tmp = talloc_realloc(talloc_tos(), slashes,
568 size_t, num_slashes+1);
569 if (tmp == NULL) {
570 goto fail;
572 slashes = tmp;
573 slashes[num_slashes] = pathlen;
574 num_slashes += 1;
577 min_offset = 0;
579 if (!config->crossmountpoints) {
580 char *mount_point;
582 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
583 handle);
584 if (mount_point == NULL) {
585 goto fail;
587 min_offset = strlen(mount_point);
588 TALLOC_FREE(mount_point);
591 memcpy(converted, path, pathlen+1);
592 converted[pathlen+insertlen] = '\0';
594 ZERO_STRUCT(converted_fname);
595 converted_fname.base_name = converted;
597 for (i = num_slashes-1; i>=0; i--) {
598 int ret;
599 size_t offset;
601 offset = slashes[i];
603 if (offset < min_offset) {
604 errno = ENOENT;
605 goto fail;
608 memcpy(converted+offset, insert, insertlen);
610 offset += insertlen;
611 memcpy(converted+offset, path + slashes[i],
612 pathlen - slashes[i]);
614 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
616 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
617 ret, ret == 0 ? "ok" : strerror(errno)));
618 if (ret == 0) {
619 /* success */
620 break;
622 if (errno == ENOTDIR) {
624 * This is a valid condition: We appended the
625 * .snaphots/@GMT.. to a file name. Just try
626 * with the upper levels.
628 continue;
630 if (errno != ENOENT) {
631 /* Other problem than "not found" */
632 goto fail;
636 if (i >= 0) {
638 * Found something
640 DEBUG(10, ("Found %s\n", converted));
641 result = converted;
642 converted = NULL;
643 } else {
644 errno = ENOENT;
646 fail:
647 saved_errno = errno;
648 TALLOC_FREE(converted);
649 TALLOC_FREE(insert);
650 TALLOC_FREE(slashes);
651 TALLOC_FREE(path);
652 errno = saved_errno;
653 return result;
657 modify a sbuf return to ensure that inodes in the shadow directory
658 are different from those in the main directory
660 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
661 SMB_STRUCT_STAT *sbuf)
663 struct shadow_copy2_config *config;
665 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
666 return);
668 if (config->fixinodes) {
669 /* some snapshot systems, like GPFS, return the name
670 device:inode for the snapshot files as the current
671 files. That breaks the 'restore' button in the shadow copy
672 GUI, as the client gets a sharing violation.
674 This is a crude way of allowing both files to be
675 open at once. It has a slight chance of inode
676 number collision, but I can't see a better approach
677 without significant VFS changes
679 uint32_t shash;
681 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
682 if (shash == 0) {
683 shash = 1;
685 sbuf->st_ex_ino ^= shash;
689 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
690 const char *fname,
691 const char *mask,
692 uint32 attr)
694 time_t timestamp;
695 char *stripped;
696 DIR *ret;
697 int saved_errno;
698 char *conv;
700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
701 &timestamp, &stripped)) {
702 return NULL;
704 if (timestamp == 0) {
705 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
707 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
708 TALLOC_FREE(stripped);
709 if (conv == NULL) {
710 return NULL;
712 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
713 saved_errno = errno;
714 TALLOC_FREE(conv);
715 errno = saved_errno;
716 return ret;
719 static int shadow_copy2_rename(vfs_handle_struct *handle,
720 const struct smb_filename *smb_fname_src,
721 const struct smb_filename *smb_fname_dst)
723 time_t timestamp_src, timestamp_dst;
725 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
726 smb_fname_src->base_name,
727 &timestamp_src, NULL)) {
728 return -1;
730 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
731 smb_fname_dst->base_name,
732 &timestamp_dst, NULL)) {
733 return -1;
735 if (timestamp_src != 0) {
736 errno = EXDEV;
737 return -1;
739 if (timestamp_dst != 0) {
740 errno = EROFS;
741 return -1;
743 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
746 static int shadow_copy2_symlink(vfs_handle_struct *handle,
747 const char *oldname, const char *newname)
749 time_t timestamp_old, timestamp_new;
751 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
752 &timestamp_old, NULL)) {
753 return -1;
755 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
756 &timestamp_new, NULL)) {
757 return -1;
759 if ((timestamp_old != 0) || (timestamp_new != 0)) {
760 errno = EROFS;
761 return -1;
763 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
766 static int shadow_copy2_link(vfs_handle_struct *handle,
767 const char *oldname, const char *newname)
769 time_t timestamp_old, timestamp_new;
771 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
772 &timestamp_old, NULL)) {
773 return -1;
775 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
776 &timestamp_new, NULL)) {
777 return -1;
779 if ((timestamp_old != 0) || (timestamp_new != 0)) {
780 errno = EROFS;
781 return -1;
783 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
786 static int shadow_copy2_stat(vfs_handle_struct *handle,
787 struct smb_filename *smb_fname)
789 time_t timestamp;
790 char *stripped, *tmp;
791 int ret, saved_errno;
793 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
794 smb_fname->base_name,
795 &timestamp, &stripped)) {
796 return -1;
798 if (timestamp == 0) {
799 return SMB_VFS_NEXT_STAT(handle, smb_fname);
802 tmp = smb_fname->base_name;
803 smb_fname->base_name = shadow_copy2_convert(
804 talloc_tos(), handle, stripped, timestamp);
805 TALLOC_FREE(stripped);
807 if (smb_fname->base_name == NULL) {
808 smb_fname->base_name = tmp;
809 return -1;
812 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
813 saved_errno = errno;
815 TALLOC_FREE(smb_fname->base_name);
816 smb_fname->base_name = tmp;
818 if (ret == 0) {
819 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
821 errno = saved_errno;
822 return ret;
825 static int shadow_copy2_lstat(vfs_handle_struct *handle,
826 struct smb_filename *smb_fname)
828 time_t timestamp;
829 char *stripped, *tmp;
830 int ret, saved_errno;
832 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
833 smb_fname->base_name,
834 &timestamp, &stripped)) {
835 return -1;
837 if (timestamp == 0) {
838 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
841 tmp = smb_fname->base_name;
842 smb_fname->base_name = shadow_copy2_convert(
843 talloc_tos(), handle, stripped, timestamp);
844 TALLOC_FREE(stripped);
846 if (smb_fname->base_name == NULL) {
847 smb_fname->base_name = tmp;
848 return -1;
851 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
852 saved_errno = errno;
854 TALLOC_FREE(smb_fname->base_name);
855 smb_fname->base_name = tmp;
857 if (ret == 0) {
858 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
860 errno = saved_errno;
861 return ret;
864 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
865 SMB_STRUCT_STAT *sbuf)
867 time_t timestamp;
868 int ret;
870 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
871 if (ret == -1) {
872 return ret;
874 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
875 fsp->fsp_name->base_name,
876 &timestamp, NULL)) {
877 return 0;
879 if (timestamp != 0) {
880 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
882 return 0;
885 static int shadow_copy2_open(vfs_handle_struct *handle,
886 struct smb_filename *smb_fname, files_struct *fsp,
887 int flags, mode_t mode)
889 time_t timestamp;
890 char *stripped, *tmp;
891 int ret, saved_errno;
893 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
894 smb_fname->base_name,
895 &timestamp, &stripped)) {
896 return -1;
898 if (timestamp == 0) {
899 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
902 tmp = smb_fname->base_name;
903 smb_fname->base_name = shadow_copy2_convert(
904 talloc_tos(), handle, stripped, timestamp);
905 TALLOC_FREE(stripped);
907 if (smb_fname->base_name == NULL) {
908 smb_fname->base_name = tmp;
909 return -1;
912 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
913 saved_errno = errno;
915 TALLOC_FREE(smb_fname->base_name);
916 smb_fname->base_name = tmp;
918 errno = saved_errno;
919 return ret;
922 static int shadow_copy2_unlink(vfs_handle_struct *handle,
923 const struct smb_filename *smb_fname)
925 time_t timestamp;
926 char *stripped;
927 int ret, saved_errno;
928 struct smb_filename *conv;
930 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
931 smb_fname->base_name,
932 &timestamp, &stripped)) {
933 return -1;
935 if (timestamp == 0) {
936 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
938 conv = cp_smb_filename(talloc_tos(), smb_fname);
939 if (conv == NULL) {
940 errno = ENOMEM;
941 return -1;
943 conv->base_name = shadow_copy2_convert(
944 conv, handle, stripped, timestamp);
945 TALLOC_FREE(stripped);
946 if (conv->base_name == NULL) {
947 return -1;
949 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
950 saved_errno = errno;
951 TALLOC_FREE(conv);
952 errno = saved_errno;
953 return ret;
956 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
957 mode_t mode)
959 time_t timestamp;
960 char *stripped;
961 int ret, saved_errno;
962 char *conv;
964 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
965 &timestamp, &stripped)) {
966 return -1;
968 if (timestamp == 0) {
969 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
971 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
972 TALLOC_FREE(stripped);
973 if (conv == NULL) {
974 return -1;
976 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
977 saved_errno = errno;
978 TALLOC_FREE(conv);
979 errno = saved_errno;
980 return ret;
983 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
984 uid_t uid, gid_t gid)
986 time_t timestamp;
987 char *stripped;
988 int ret, saved_errno;
989 char *conv;
991 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
992 &timestamp, &stripped)) {
993 return -1;
995 if (timestamp == 0) {
996 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
998 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
999 TALLOC_FREE(stripped);
1000 if (conv == NULL) {
1001 return -1;
1003 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1004 saved_errno = errno;
1005 TALLOC_FREE(conv);
1006 errno = saved_errno;
1007 return ret;
1010 static int shadow_copy2_chdir(vfs_handle_struct *handle,
1011 const char *fname)
1013 time_t timestamp;
1014 char *stripped;
1015 int ret, saved_errno;
1016 char *conv;
1018 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1019 &timestamp, &stripped)) {
1020 return -1;
1022 if (timestamp == 0) {
1023 return SMB_VFS_NEXT_CHDIR(handle, fname);
1025 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1026 TALLOC_FREE(stripped);
1027 if (conv == NULL) {
1028 return -1;
1030 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1031 saved_errno = errno;
1032 TALLOC_FREE(conv);
1033 errno = saved_errno;
1034 return ret;
1037 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
1038 const struct smb_filename *smb_fname,
1039 struct smb_file_time *ft)
1041 time_t timestamp;
1042 char *stripped;
1043 int ret, saved_errno;
1044 struct smb_filename *conv;
1046 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1047 smb_fname->base_name,
1048 &timestamp, &stripped)) {
1049 return -1;
1051 if (timestamp == 0) {
1052 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1054 conv = cp_smb_filename(talloc_tos(), smb_fname);
1055 if (conv == NULL) {
1056 errno = ENOMEM;
1057 return -1;
1059 conv->base_name = shadow_copy2_convert(
1060 conv, handle, stripped, timestamp);
1061 TALLOC_FREE(stripped);
1062 if (conv->base_name == NULL) {
1063 return -1;
1065 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1066 saved_errno = errno;
1067 TALLOC_FREE(conv);
1068 errno = saved_errno;
1069 return ret;
1072 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1073 const char *fname, char *buf, size_t bufsiz)
1075 time_t timestamp;
1076 char *stripped;
1077 int ret, saved_errno;
1078 char *conv;
1080 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1081 &timestamp, &stripped)) {
1082 return -1;
1084 if (timestamp == 0) {
1085 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1087 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1088 TALLOC_FREE(stripped);
1089 if (conv == NULL) {
1090 return -1;
1092 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1093 saved_errno = errno;
1094 TALLOC_FREE(conv);
1095 errno = saved_errno;
1096 return ret;
1099 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1100 const char *fname, mode_t mode, SMB_DEV_T dev)
1102 time_t timestamp;
1103 char *stripped;
1104 int ret, saved_errno;
1105 char *conv;
1107 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1108 &timestamp, &stripped)) {
1109 return -1;
1111 if (timestamp == 0) {
1112 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1114 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1115 TALLOC_FREE(stripped);
1116 if (conv == NULL) {
1117 return -1;
1119 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1120 saved_errno = errno;
1121 TALLOC_FREE(conv);
1122 errno = saved_errno;
1123 return ret;
1126 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1127 const char *fname)
1129 time_t timestamp;
1130 char *stripped = NULL;
1131 char *tmp = NULL;
1132 char *result = NULL;
1133 char *inserted = NULL;
1134 char *inserted_to, *inserted_end;
1135 int saved_errno;
1137 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1138 &timestamp, &stripped)) {
1139 goto done;
1141 if (timestamp == 0) {
1142 return SMB_VFS_NEXT_REALPATH(handle, fname);
1145 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1146 if (tmp == NULL) {
1147 goto done;
1150 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1151 if (result == NULL) {
1152 goto done;
1156 * Take away what we've inserted. This removes the @GMT-thingy
1157 * completely, but will give a path under the share root.
1159 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1160 if (inserted == NULL) {
1161 goto done;
1163 inserted_to = strstr_m(result, inserted);
1164 if (inserted_to == NULL) {
1165 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1166 goto done;
1168 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1169 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1171 done:
1172 saved_errno = errno;
1173 TALLOC_FREE(inserted);
1174 TALLOC_FREE(tmp);
1175 TALLOC_FREE(stripped);
1176 errno = saved_errno;
1177 return result;
1181 * Check whether a given directory contains a
1182 * snapshot directory as direct subdirectory.
1183 * If yes, return the path of the snapshot-subdir,
1184 * otherwise return NULL.
1186 static char *have_snapdir(struct vfs_handle_struct *handle,
1187 const char *path)
1189 struct smb_filename smb_fname;
1190 int ret;
1191 struct shadow_copy2_config *config;
1193 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1194 return NULL);
1196 ZERO_STRUCT(smb_fname);
1197 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1198 path, config->snapdir);
1199 if (smb_fname.base_name == NULL) {
1200 return NULL;
1203 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1204 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1205 return smb_fname.base_name;
1207 TALLOC_FREE(smb_fname.base_name);
1208 return NULL;
1212 * Find the snapshot directory (if any) for the given
1213 * filename (which is relative to the share).
1215 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1216 struct vfs_handle_struct *handle,
1217 struct smb_filename *smb_fname)
1219 char *path, *p;
1220 const char *snapdir;
1221 struct shadow_copy2_config *config;
1223 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1224 return NULL);
1227 * If the non-snapdisrseverywhere mode, we should not search!
1229 if (!config->snapdirseverywhere) {
1230 return config->snapshot_basepath;
1233 path = talloc_asprintf(mem_ctx, "%s/%s",
1234 handle->conn->connectpath,
1235 smb_fname->base_name);
1236 if (path == NULL) {
1237 return NULL;
1240 snapdir = have_snapdir(handle, path);
1241 if (snapdir != NULL) {
1242 TALLOC_FREE(path);
1243 return snapdir;
1246 while ((p = strrchr(path, '/')) && (p > path)) {
1248 p[0] = '\0';
1250 snapdir = have_snapdir(handle, path);
1251 if (snapdir != NULL) {
1252 TALLOC_FREE(path);
1253 return snapdir;
1256 TALLOC_FREE(path);
1257 return NULL;
1260 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1261 const char *name,
1262 char *gmt, size_t gmt_len)
1264 struct tm timestamp;
1265 time_t timestamp_t;
1266 unsigned long int timestamp_long;
1267 const char *fmt;
1268 struct shadow_copy2_config *config;
1270 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1271 return NULL);
1273 fmt = config->gmt_format;
1275 ZERO_STRUCT(timestamp);
1276 if (config->use_sscanf) {
1277 if (sscanf(name, fmt, &timestamp_long) != 1) {
1278 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1279 "no sscanf match %s: %s\n",
1280 fmt, name));
1281 return false;
1283 timestamp_t = timestamp_long;
1284 gmtime_r(&timestamp_t, &timestamp);
1285 } else {
1286 if (strptime(name, fmt, &timestamp) == NULL) {
1287 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1288 "no match %s: %s\n",
1289 fmt, name));
1290 return false;
1292 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1293 fmt, name));
1295 if (config->use_localtime) {
1296 timestamp.tm_isdst = -1;
1297 timestamp_t = mktime(&timestamp);
1298 gmtime_r(&timestamp_t, &timestamp);
1302 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1303 return true;
1306 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1308 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1311 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1313 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1317 sort the shadow copy data in ascending or descending order
1319 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1320 struct shadow_copy_data *shadow_copy2_data)
1322 int (*cmpfunc)(const void *, const void *);
1323 const char *sort;
1324 struct shadow_copy2_config *config;
1326 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1327 return);
1329 sort = config->sort_order;
1330 if (sort == NULL) {
1331 return;
1334 if (strcmp(sort, "asc") == 0) {
1335 cmpfunc = shadow_copy2_label_cmp_asc;
1336 } else if (strcmp(sort, "desc") == 0) {
1337 cmpfunc = shadow_copy2_label_cmp_desc;
1338 } else {
1339 return;
1342 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1343 shadow_copy2_data->labels)
1345 TYPESAFE_QSORT(shadow_copy2_data->labels,
1346 shadow_copy2_data->num_volumes,
1347 cmpfunc);
1351 static int shadow_copy2_get_shadow_copy_data(
1352 vfs_handle_struct *handle, files_struct *fsp,
1353 struct shadow_copy_data *shadow_copy2_data,
1354 bool labels)
1356 DIR *p;
1357 const char *snapdir;
1358 struct dirent *d;
1359 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1361 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1362 if (snapdir == NULL) {
1363 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1364 handle->conn->connectpath));
1365 errno = EINVAL;
1366 talloc_free(tmp_ctx);
1367 return -1;
1370 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1372 if (!p) {
1373 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1374 " - %s\n", snapdir, strerror(errno)));
1375 talloc_free(tmp_ctx);
1376 errno = ENOSYS;
1377 return -1;
1380 shadow_copy2_data->num_volumes = 0;
1381 shadow_copy2_data->labels = NULL;
1383 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1384 char snapshot[GMT_NAME_LEN+1];
1385 SHADOW_COPY_LABEL *tlabels;
1388 * ignore names not of the right form in the snapshot
1389 * directory
1391 if (!shadow_copy2_snapshot_to_gmt(
1392 handle, d->d_name,
1393 snapshot, sizeof(snapshot))) {
1395 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1396 "ignoring %s\n", d->d_name));
1397 continue;
1399 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1400 d->d_name, snapshot));
1402 if (!labels) {
1403 /* the caller doesn't want the labels */
1404 shadow_copy2_data->num_volumes++;
1405 continue;
1408 tlabels = talloc_realloc(shadow_copy2_data,
1409 shadow_copy2_data->labels,
1410 SHADOW_COPY_LABEL,
1411 shadow_copy2_data->num_volumes+1);
1412 if (tlabels == NULL) {
1413 DEBUG(0,("shadow_copy2: out of memory\n"));
1414 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1415 talloc_free(tmp_ctx);
1416 return -1;
1419 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1420 sizeof(*tlabels));
1422 shadow_copy2_data->num_volumes++;
1423 shadow_copy2_data->labels = tlabels;
1426 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1428 shadow_copy2_sort_data(handle, shadow_copy2_data);
1430 talloc_free(tmp_ctx);
1431 return 0;
1434 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1435 struct files_struct *fsp,
1436 uint32 security_info,
1437 TALLOC_CTX *mem_ctx,
1438 struct security_descriptor **ppdesc)
1440 time_t timestamp;
1441 char *stripped;
1442 NTSTATUS status;
1443 char *conv;
1445 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1446 fsp->fsp_name->base_name,
1447 &timestamp, &stripped)) {
1448 return map_nt_error_from_unix(errno);
1450 if (timestamp == 0) {
1451 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1452 mem_ctx,
1453 ppdesc);
1455 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1456 TALLOC_FREE(stripped);
1457 if (conv == NULL) {
1458 return map_nt_error_from_unix(errno);
1460 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1461 mem_ctx, ppdesc);
1462 TALLOC_FREE(conv);
1463 return status;
1466 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1467 const char *fname,
1468 uint32 security_info,
1469 TALLOC_CTX *mem_ctx,
1470 struct security_descriptor **ppdesc)
1472 time_t timestamp;
1473 char *stripped;
1474 NTSTATUS status;
1475 char *conv;
1477 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1478 &timestamp, &stripped)) {
1479 return map_nt_error_from_unix(errno);
1481 if (timestamp == 0) {
1482 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1483 mem_ctx, ppdesc);
1485 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1486 TALLOC_FREE(stripped);
1487 if (conv == NULL) {
1488 return map_nt_error_from_unix(errno);
1490 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1491 mem_ctx, ppdesc);
1492 TALLOC_FREE(conv);
1493 return status;
1496 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1497 const char *fname, mode_t mode)
1499 time_t timestamp;
1500 char *stripped;
1501 int ret, saved_errno;
1502 char *conv;
1504 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1505 &timestamp, &stripped)) {
1506 return -1;
1508 if (timestamp == 0) {
1509 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1511 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1512 TALLOC_FREE(stripped);
1513 if (conv == NULL) {
1514 return -1;
1516 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1517 saved_errno = errno;
1518 TALLOC_FREE(conv);
1519 errno = saved_errno;
1520 return ret;
1523 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1525 time_t timestamp;
1526 char *stripped;
1527 int ret, saved_errno;
1528 char *conv;
1530 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1531 &timestamp, &stripped)) {
1532 return -1;
1534 if (timestamp == 0) {
1535 return SMB_VFS_NEXT_RMDIR(handle, fname);
1537 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1538 TALLOC_FREE(stripped);
1539 if (conv == NULL) {
1540 return -1;
1542 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1543 saved_errno = errno;
1544 TALLOC_FREE(conv);
1545 errno = saved_errno;
1546 return ret;
1549 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1550 unsigned int flags)
1552 time_t timestamp;
1553 char *stripped;
1554 int ret, saved_errno;
1555 char *conv;
1557 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1558 &timestamp, &stripped)) {
1559 return -1;
1561 if (timestamp == 0) {
1562 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1564 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1565 TALLOC_FREE(stripped);
1566 if (conv == NULL) {
1567 return -1;
1569 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1570 saved_errno = errno;
1571 TALLOC_FREE(conv);
1572 errno = saved_errno;
1573 return ret;
1576 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1577 const char *fname, const char *aname,
1578 void *value, size_t size)
1580 time_t timestamp;
1581 char *stripped;
1582 ssize_t ret;
1583 int saved_errno;
1584 char *conv;
1586 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1587 &timestamp, &stripped)) {
1588 return -1;
1590 if (timestamp == 0) {
1591 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1592 size);
1594 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1595 TALLOC_FREE(stripped);
1596 if (conv == NULL) {
1597 return -1;
1599 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1600 saved_errno = errno;
1601 TALLOC_FREE(conv);
1602 errno = saved_errno;
1603 return ret;
1606 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1607 const char *fname,
1608 char *list, size_t size)
1610 time_t timestamp;
1611 char *stripped;
1612 ssize_t ret;
1613 int saved_errno;
1614 char *conv;
1616 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1617 &timestamp, &stripped)) {
1618 return -1;
1620 if (timestamp == 0) {
1621 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1623 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1624 TALLOC_FREE(stripped);
1625 if (conv == NULL) {
1626 return -1;
1628 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1629 saved_errno = errno;
1630 TALLOC_FREE(conv);
1631 errno = saved_errno;
1632 return ret;
1635 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1636 const char *fname, const char *aname)
1638 time_t timestamp;
1639 char *stripped;
1640 int ret, saved_errno;
1641 char *conv;
1643 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1644 &timestamp, &stripped)) {
1645 return -1;
1647 if (timestamp == 0) {
1648 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1650 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1651 TALLOC_FREE(stripped);
1652 if (conv == NULL) {
1653 return -1;
1655 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1656 saved_errno = errno;
1657 TALLOC_FREE(conv);
1658 errno = saved_errno;
1659 return ret;
1662 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1663 const char *fname,
1664 const char *aname, const void *value,
1665 size_t size, int flags)
1667 time_t timestamp;
1668 char *stripped;
1669 ssize_t ret;
1670 int saved_errno;
1671 char *conv;
1673 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1674 &timestamp, &stripped)) {
1675 return -1;
1677 if (timestamp == 0) {
1678 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1679 flags);
1681 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1682 TALLOC_FREE(stripped);
1683 if (conv == NULL) {
1684 return -1;
1686 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1687 saved_errno = errno;
1688 TALLOC_FREE(conv);
1689 errno = saved_errno;
1690 return ret;
1693 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1694 const char *fname, mode_t mode)
1696 time_t timestamp;
1697 char *stripped;
1698 ssize_t ret;
1699 int saved_errno;
1700 char *conv;
1702 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1703 &timestamp, &stripped)) {
1704 return -1;
1706 if (timestamp == 0) {
1707 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1709 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1710 TALLOC_FREE(stripped);
1711 if (conv == NULL) {
1712 return -1;
1714 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1715 saved_errno = errno;
1716 TALLOC_FREE(conv);
1717 errno = saved_errno;
1718 return ret;
1721 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1722 const char *path,
1723 const char *name,
1724 TALLOC_CTX *mem_ctx,
1725 char **found_name)
1727 time_t timestamp;
1728 char *stripped;
1729 ssize_t ret;
1730 int saved_errno;
1731 char *conv;
1733 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1734 &timestamp, &stripped)) {
1735 return -1;
1737 if (timestamp == 0) {
1738 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1739 mem_ctx, found_name);
1741 if (stripped[0] == '\0') {
1742 *found_name = talloc_strdup(mem_ctx, name);
1743 if (*found_name == NULL) {
1744 errno = ENOMEM;
1745 return -1;
1747 return 0;
1749 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1750 TALLOC_FREE(stripped);
1751 if (conv == NULL) {
1752 return -1;
1754 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1755 mem_ctx, found_name);
1756 saved_errno = errno;
1757 TALLOC_FREE(conv);
1758 errno = saved_errno;
1759 return ret;
1762 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1763 const char *path, bool small_query,
1764 uint64_t *bsize, uint64_t *dfree,
1765 uint64_t *dsize)
1767 time_t timestamp;
1768 char *stripped;
1769 ssize_t ret;
1770 int saved_errno;
1771 char *conv;
1773 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1774 &timestamp, &stripped)) {
1775 return -1;
1777 if (timestamp == 0) {
1778 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1779 bsize, dfree, dsize);
1782 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1783 TALLOC_FREE(stripped);
1784 if (conv == NULL) {
1785 return -1;
1788 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1789 dsize);
1791 saved_errno = errno;
1792 TALLOC_FREE(conv);
1793 errno = saved_errno;
1795 return ret;
1798 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1799 const char *service, const char *user)
1801 struct shadow_copy2_config *config;
1802 int ret;
1803 const char *snapdir;
1804 const char *gmt_format;
1805 const char *sort_order;
1806 const char *basedir;
1807 const char *mount_point;
1809 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1810 (unsigned)handle->conn->cnum,
1811 handle->conn->connectpath));
1813 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1814 if (ret < 0) {
1815 return ret;
1818 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1819 if (config == NULL) {
1820 DEBUG(0, ("talloc_zero() failed\n"));
1821 errno = ENOMEM;
1822 return -1;
1825 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1826 "shadow", "format",
1827 GMT_FORMAT);
1828 config->gmt_format = talloc_strdup(config, gmt_format);
1829 if (config->gmt_format == NULL) {
1830 DEBUG(0, ("talloc_strdup() failed\n"));
1831 errno = ENOMEM;
1832 return -1;
1835 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1836 "shadow", "sscanf", false);
1838 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1839 "shadow", "localtime",
1840 false);
1842 snapdir = lp_parm_const_string(SNUM(handle->conn),
1843 "shadow", "snapdir",
1844 ".snapshots");
1845 config->snapdir = talloc_strdup(config, snapdir);
1846 if (config->snapdir == NULL) {
1847 DEBUG(0, ("talloc_strdup() failed\n"));
1848 errno = ENOMEM;
1849 return -1;
1852 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1853 "shadow",
1854 "snapdirseverywhere",
1855 false);
1857 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1858 "shadow", "crossmountpoints",
1859 false);
1861 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1862 "shadow", "fixinodes",
1863 false);
1865 sort_order = lp_parm_const_string(SNUM(handle->conn),
1866 "shadow", "sort", "desc");
1867 config->sort_order = talloc_strdup(config, sort_order);
1868 if (config->sort_order == NULL) {
1869 DEBUG(0, ("talloc_strdup() failed\n"));
1870 errno = ENOMEM;
1871 return -1;
1874 mount_point = lp_parm_const_string(SNUM(handle->conn),
1875 "shadow", "mountpoint", NULL);
1876 if (mount_point != NULL) {
1877 if (mount_point[0] != '/') {
1878 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1879 "relative ('%s'), but it has to be an "
1880 "absolute path. Ignoring provided value.\n",
1881 mount_point));
1882 mount_point = NULL;
1883 } else {
1884 char *p;
1885 p = strstr(handle->conn->connectpath, mount_point);
1886 if (p != handle->conn->connectpath) {
1887 DEBUG(1, ("Warning: mount_point (%s) is not a "
1888 "subdirectory of the share root "
1889 "(%s). Ignoring provided value.\n",
1890 mount_point,
1891 handle->conn->connectpath));
1892 mount_point = NULL;
1897 if (mount_point != NULL) {
1898 config->mount_point = talloc_strdup(config, mount_point);
1899 if (config->mount_point == NULL) {
1900 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1901 return -1;
1903 } else {
1904 config->mount_point = shadow_copy2_find_mount_point(config,
1905 handle);
1906 if (config->mount_point == NULL) {
1907 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1908 " failed: %s\n", strerror(errno)));
1909 return -1;
1913 basedir = lp_parm_const_string(SNUM(handle->conn),
1914 "shadow", "basedir", NULL);
1916 if (basedir != NULL) {
1917 if (basedir[0] != '/') {
1918 DEBUG(1, (__location__ " Warning: 'basedir' is "
1919 "relative ('%s'), but it has to be an "
1920 "absolute path. Disabling basedir.\n",
1921 basedir));
1922 } else {
1923 char *p;
1924 p = strstr(basedir, config->mount_point);
1925 if (p != basedir) {
1926 DEBUG(1, ("Warning: basedir (%s) is not a "
1927 "subdirectory of the share root's "
1928 "mount point (%s). "
1929 "Disabling basedir\n",
1930 basedir, config->mount_point));
1931 } else {
1932 config->basedir = talloc_strdup(config,
1933 basedir);
1934 if (config->basedir == NULL) {
1935 DEBUG(0, ("talloc_strdup() failed\n"));
1936 errno = ENOMEM;
1937 return -1;
1943 if (config->snapdirseverywhere && config->basedir != NULL) {
1944 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1945 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1946 TALLOC_FREE(config->basedir);
1949 if (config->crossmountpoints && config->basedir != NULL) {
1950 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1951 "with 'crossmountpoints'. Disabling basedir.\n"));
1952 TALLOC_FREE(config->basedir);
1955 if (config->basedir == NULL) {
1956 config->basedir = config->mount_point;
1959 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1960 config->rel_connectpath = talloc_strdup(config,
1961 handle->conn->connectpath + strlen(config->basedir));
1962 if (config->rel_connectpath == NULL) {
1963 DEBUG(0, ("talloc_strdup() failed\n"));
1964 errno = ENOMEM;
1965 return -1;
1969 if (config->snapdir[0] == '/') {
1970 config->snapdir_absolute = true;
1972 if (config->snapdirseverywhere == true) {
1973 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1974 "is incompatible with 'snapdirseverywhere', "
1975 "setting 'snapdirseverywhere' to false.\n"));
1976 config->snapdirseverywhere = false;
1979 if (config->crossmountpoints == true) {
1980 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1981 "is not supported with an absolute snapdir. "
1982 "Disabling it.\n"));
1983 config->crossmountpoints = false;
1986 config->snapshot_basepath = config->snapdir;
1987 } else {
1988 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1989 config->mount_point, config->snapdir);
1990 if (config->snapshot_basepath == NULL) {
1991 DEBUG(0, ("talloc_asprintf() failed\n"));
1992 errno = ENOMEM;
1993 return -1;
1997 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1998 " share root: '%s'\n"
1999 " basedir: '%s'\n"
2000 " mountpoint: '%s'\n"
2001 " rel share root: '%s'\n"
2002 " snapdir: '%s'\n"
2003 " snapshot base path: '%s'\n"
2004 " format: '%s'\n"
2005 " use sscanf: %s\n"
2006 " snapdirs everywhere: %s\n"
2007 " cross mountpoints: %s\n"
2008 " fix inodes: %s\n"
2009 " sort order: %s\n"
2011 handle->conn->connectpath,
2012 config->basedir,
2013 config->mount_point,
2014 config->rel_connectpath,
2015 config->snapdir,
2016 config->snapshot_basepath,
2017 config->gmt_format,
2018 config->use_sscanf ? "yes" : "no",
2019 config->snapdirseverywhere ? "yes" : "no",
2020 config->crossmountpoints ? "yes" : "no",
2021 config->fixinodes ? "yes" : "no",
2022 config->sort_order
2026 SMB_VFS_HANDLE_SET_DATA(handle, config,
2027 NULL, struct shadow_copy2_config,
2028 return -1);
2030 return 0;
2033 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
2034 .connect_fn = shadow_copy2_connect,
2035 .opendir_fn = shadow_copy2_opendir,
2036 .disk_free_fn = shadow_copy2_disk_free,
2037 .rename_fn = shadow_copy2_rename,
2038 .link_fn = shadow_copy2_link,
2039 .symlink_fn = shadow_copy2_symlink,
2040 .stat_fn = shadow_copy2_stat,
2041 .lstat_fn = shadow_copy2_lstat,
2042 .fstat_fn = shadow_copy2_fstat,
2043 .open_fn = shadow_copy2_open,
2044 .unlink_fn = shadow_copy2_unlink,
2045 .chmod_fn = shadow_copy2_chmod,
2046 .chown_fn = shadow_copy2_chown,
2047 .chdir_fn = shadow_copy2_chdir,
2048 .ntimes_fn = shadow_copy2_ntimes,
2049 .readlink_fn = shadow_copy2_readlink,
2050 .mknod_fn = shadow_copy2_mknod,
2051 .realpath_fn = shadow_copy2_realpath,
2052 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2053 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2054 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2055 .mkdir_fn = shadow_copy2_mkdir,
2056 .rmdir_fn = shadow_copy2_rmdir,
2057 .getxattr_fn = shadow_copy2_getxattr,
2058 .listxattr_fn = shadow_copy2_listxattr,
2059 .removexattr_fn = shadow_copy2_removexattr,
2060 .setxattr_fn = shadow_copy2_setxattr,
2061 .chmod_acl_fn = shadow_copy2_chmod_acl,
2062 .chflags_fn = shadow_copy2_chflags,
2063 .get_real_filename_fn = shadow_copy2_get_real_filename,
2066 NTSTATUS vfs_shadow_copy2_init(void);
2067 NTSTATUS vfs_shadow_copy2_init(void)
2069 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2070 "shadow_copy2", &vfs_shadow_copy2_fns);