shadow_copy2: add some debug to shadow_copy2_strip_snapshot()
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob528245ae81b8e77b6ad197f66da3977577811cbd
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);
386 if (pstripped != NULL) {
387 stripped = talloc_array(mem_ctx, char, dst_len+1);
388 if (stripped == NULL) {
389 errno = ENOMEM;
390 return false;
392 if (p > name) {
393 memcpy(stripped, name, p-name);
395 if (rest_len > 0) {
396 memcpy(stripped + (p-name), q, rest_len);
398 stripped[dst_len] = '\0';
399 *pstripped = stripped;
401 *ptimestamp = timestamp;
402 return true;
403 no_snapshot:
404 *ptimestamp = 0;
405 return true;
408 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
409 vfs_handle_struct *handle)
411 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
412 dev_t dev;
413 struct stat st;
414 char *p;
416 if (stat(path, &st) != 0) {
417 talloc_free(path);
418 return NULL;
421 dev = st.st_dev;
423 while ((p = strrchr(path, '/')) && p > path) {
424 *p = 0;
425 if (stat(path, &st) != 0) {
426 talloc_free(path);
427 return NULL;
429 if (st.st_dev != dev) {
430 *p = '/';
431 break;
435 return path;
439 * Convert from a name as handed in via the SMB layer
440 * and a timestamp into the local path of the snapshot
441 * of the provided file at the provided time.
443 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
444 struct vfs_handle_struct *handle,
445 const char *name, time_t timestamp)
447 struct smb_filename converted_fname;
448 char *result = NULL;
449 size_t *slashes = NULL;
450 unsigned num_slashes;
451 char *path = NULL;
452 size_t pathlen;
453 char *insert = NULL;
454 char *converted = NULL;
455 size_t insertlen;
456 int i, saved_errno;
457 size_t min_offset;
458 struct shadow_copy2_config *config;
460 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
461 return NULL);
463 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
464 name);
465 if (path == NULL) {
466 errno = ENOMEM;
467 goto fail;
469 pathlen = talloc_get_size(path)-1;
471 DEBUG(10, ("converting %s\n", path));
473 if (!shadow_copy2_find_slashes(talloc_tos(), path,
474 &slashes, &num_slashes)) {
475 goto fail;
477 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
478 if (insert == NULL) {
479 goto fail;
481 insertlen = talloc_get_size(insert)-1;
482 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
483 if (converted == NULL) {
484 goto fail;
487 if (path[pathlen-1] != '/') {
489 * Append a fake slash to find the snapshot root
491 size_t *tmp;
492 tmp = talloc_realloc(talloc_tos(), slashes,
493 size_t, num_slashes+1);
494 if (tmp == NULL) {
495 goto fail;
497 slashes = tmp;
498 slashes[num_slashes] = pathlen;
499 num_slashes += 1;
502 min_offset = 0;
504 if (!config->crossmountpoints) {
505 char *mount_point;
507 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
508 handle);
509 if (mount_point == NULL) {
510 goto fail;
512 min_offset = strlen(mount_point);
513 TALLOC_FREE(mount_point);
516 memcpy(converted, path, pathlen+1);
517 converted[pathlen+insertlen] = '\0';
519 ZERO_STRUCT(converted_fname);
520 converted_fname.base_name = converted;
522 for (i = num_slashes-1; i>=0; i--) {
523 int ret;
524 size_t offset;
526 offset = slashes[i];
528 if (offset < min_offset) {
529 errno = ENOENT;
530 goto fail;
533 memcpy(converted+offset, insert, insertlen);
535 offset += insertlen;
536 memcpy(converted+offset, path + slashes[i],
537 pathlen - slashes[i]);
539 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
541 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
542 ret, ret == 0 ? "ok" : strerror(errno)));
543 if (ret == 0) {
544 /* success */
545 break;
547 if (errno == ENOTDIR) {
549 * This is a valid condition: We appended the
550 * .snaphots/@GMT.. to a file name. Just try
551 * with the upper levels.
553 continue;
555 if (errno != ENOENT) {
556 /* Other problem than "not found" */
557 goto fail;
561 if (i >= 0) {
563 * Found something
565 DEBUG(10, ("Found %s\n", converted));
566 result = converted;
567 converted = NULL;
568 } else {
569 errno = ENOENT;
571 fail:
572 saved_errno = errno;
573 TALLOC_FREE(converted);
574 TALLOC_FREE(insert);
575 TALLOC_FREE(slashes);
576 TALLOC_FREE(path);
577 errno = saved_errno;
578 return result;
582 modify a sbuf return to ensure that inodes in the shadow directory
583 are different from those in the main directory
585 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
586 SMB_STRUCT_STAT *sbuf)
588 struct shadow_copy2_config *config;
590 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
591 return);
593 if (config->fixinodes) {
594 /* some snapshot systems, like GPFS, return the name
595 device:inode for the snapshot files as the current
596 files. That breaks the 'restore' button in the shadow copy
597 GUI, as the client gets a sharing violation.
599 This is a crude way of allowing both files to be
600 open at once. It has a slight chance of inode
601 number collision, but I can't see a better approach
602 without significant VFS changes
604 uint32_t shash;
606 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
607 if (shash == 0) {
608 shash = 1;
610 sbuf->st_ex_ino ^= shash;
614 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
615 const char *fname,
616 const char *mask,
617 uint32 attr)
619 time_t timestamp;
620 char *stripped;
621 DIR *ret;
622 int saved_errno;
623 char *conv;
625 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
626 &timestamp, &stripped)) {
627 return NULL;
629 if (timestamp == 0) {
630 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
632 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
633 TALLOC_FREE(stripped);
634 if (conv == NULL) {
635 return NULL;
637 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
638 saved_errno = errno;
639 TALLOC_FREE(conv);
640 errno = saved_errno;
641 return ret;
644 static int shadow_copy2_rename(vfs_handle_struct *handle,
645 const struct smb_filename *smb_fname_src,
646 const struct smb_filename *smb_fname_dst)
648 time_t timestamp_src, timestamp_dst;
650 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
651 smb_fname_src->base_name,
652 &timestamp_src, NULL)) {
653 return -1;
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
656 smb_fname_dst->base_name,
657 &timestamp_dst, NULL)) {
658 return -1;
660 if (timestamp_src != 0) {
661 errno = EXDEV;
662 return -1;
664 if (timestamp_dst != 0) {
665 errno = EROFS;
666 return -1;
668 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
671 static int shadow_copy2_symlink(vfs_handle_struct *handle,
672 const char *oldname, const char *newname)
674 time_t timestamp_old, timestamp_new;
676 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
677 &timestamp_old, NULL)) {
678 return -1;
680 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
681 &timestamp_new, NULL)) {
682 return -1;
684 if ((timestamp_old != 0) || (timestamp_new != 0)) {
685 errno = EROFS;
686 return -1;
688 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
691 static int shadow_copy2_link(vfs_handle_struct *handle,
692 const char *oldname, const char *newname)
694 time_t timestamp_old, timestamp_new;
696 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
697 &timestamp_old, NULL)) {
698 return -1;
700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
701 &timestamp_new, NULL)) {
702 return -1;
704 if ((timestamp_old != 0) || (timestamp_new != 0)) {
705 errno = EROFS;
706 return -1;
708 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
711 static int shadow_copy2_stat(vfs_handle_struct *handle,
712 struct smb_filename *smb_fname)
714 time_t timestamp;
715 char *stripped, *tmp;
716 int ret, saved_errno;
718 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
719 smb_fname->base_name,
720 &timestamp, &stripped)) {
721 return -1;
723 if (timestamp == 0) {
724 return SMB_VFS_NEXT_STAT(handle, smb_fname);
727 tmp = smb_fname->base_name;
728 smb_fname->base_name = shadow_copy2_convert(
729 talloc_tos(), handle, stripped, timestamp);
730 TALLOC_FREE(stripped);
732 if (smb_fname->base_name == NULL) {
733 smb_fname->base_name = tmp;
734 return -1;
737 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
738 saved_errno = errno;
740 TALLOC_FREE(smb_fname->base_name);
741 smb_fname->base_name = tmp;
743 if (ret == 0) {
744 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
746 errno = saved_errno;
747 return ret;
750 static int shadow_copy2_lstat(vfs_handle_struct *handle,
751 struct smb_filename *smb_fname)
753 time_t timestamp;
754 char *stripped, *tmp;
755 int ret, saved_errno;
757 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
758 smb_fname->base_name,
759 &timestamp, &stripped)) {
760 return -1;
762 if (timestamp == 0) {
763 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
766 tmp = smb_fname->base_name;
767 smb_fname->base_name = shadow_copy2_convert(
768 talloc_tos(), handle, stripped, timestamp);
769 TALLOC_FREE(stripped);
771 if (smb_fname->base_name == NULL) {
772 smb_fname->base_name = tmp;
773 return -1;
776 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
777 saved_errno = errno;
779 TALLOC_FREE(smb_fname->base_name);
780 smb_fname->base_name = tmp;
782 if (ret == 0) {
783 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
785 errno = saved_errno;
786 return ret;
789 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
790 SMB_STRUCT_STAT *sbuf)
792 time_t timestamp;
793 int ret;
795 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
796 if (ret == -1) {
797 return ret;
799 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
800 fsp->fsp_name->base_name,
801 &timestamp, NULL)) {
802 return 0;
804 if (timestamp != 0) {
805 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
807 return 0;
810 static int shadow_copy2_open(vfs_handle_struct *handle,
811 struct smb_filename *smb_fname, files_struct *fsp,
812 int flags, mode_t mode)
814 time_t timestamp;
815 char *stripped, *tmp;
816 int ret, saved_errno;
818 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
819 smb_fname->base_name,
820 &timestamp, &stripped)) {
821 return -1;
823 if (timestamp == 0) {
824 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
827 tmp = smb_fname->base_name;
828 smb_fname->base_name = shadow_copy2_convert(
829 talloc_tos(), handle, stripped, timestamp);
830 TALLOC_FREE(stripped);
832 if (smb_fname->base_name == NULL) {
833 smb_fname->base_name = tmp;
834 return -1;
837 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
838 saved_errno = errno;
840 TALLOC_FREE(smb_fname->base_name);
841 smb_fname->base_name = tmp;
843 errno = saved_errno;
844 return ret;
847 static int shadow_copy2_unlink(vfs_handle_struct *handle,
848 const struct smb_filename *smb_fname)
850 time_t timestamp;
851 char *stripped;
852 int ret, saved_errno;
853 struct smb_filename *conv;
854 NTSTATUS status;
856 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
857 smb_fname->base_name,
858 &timestamp, &stripped)) {
859 return -1;
861 if (timestamp == 0) {
862 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
864 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
865 if (!NT_STATUS_IS_OK(status)) {
866 errno = ENOMEM;
867 return -1;
869 conv->base_name = shadow_copy2_convert(
870 conv, handle, stripped, timestamp);
871 TALLOC_FREE(stripped);
872 if (conv->base_name == NULL) {
873 return -1;
875 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
876 saved_errno = errno;
877 TALLOC_FREE(conv);
878 errno = saved_errno;
879 return ret;
882 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
883 mode_t mode)
885 time_t timestamp;
886 char *stripped;
887 int ret, saved_errno;
888 char *conv;
890 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
891 &timestamp, &stripped)) {
892 return -1;
894 if (timestamp == 0) {
895 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
897 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
898 TALLOC_FREE(stripped);
899 if (conv == NULL) {
900 return -1;
902 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
903 saved_errno = errno;
904 TALLOC_FREE(conv);
905 errno = saved_errno;
906 return ret;
909 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
910 uid_t uid, gid_t gid)
912 time_t timestamp;
913 char *stripped;
914 int ret, saved_errno;
915 char *conv;
917 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
918 &timestamp, &stripped)) {
919 return -1;
921 if (timestamp == 0) {
922 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
924 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
925 TALLOC_FREE(stripped);
926 if (conv == NULL) {
927 return -1;
929 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
930 saved_errno = errno;
931 TALLOC_FREE(conv);
932 errno = saved_errno;
933 return ret;
936 static int shadow_copy2_chdir(vfs_handle_struct *handle,
937 const char *fname)
939 time_t timestamp;
940 char *stripped;
941 int ret, saved_errno;
942 char *conv;
944 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
945 &timestamp, &stripped)) {
946 return -1;
948 if (timestamp == 0) {
949 return SMB_VFS_NEXT_CHDIR(handle, fname);
951 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
952 TALLOC_FREE(stripped);
953 if (conv == NULL) {
954 return -1;
956 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
957 saved_errno = errno;
958 TALLOC_FREE(conv);
959 errno = saved_errno;
960 return ret;
963 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
964 const struct smb_filename *smb_fname,
965 struct smb_file_time *ft)
967 time_t timestamp;
968 char *stripped;
969 int ret, saved_errno;
970 struct smb_filename *conv;
971 NTSTATUS status;
973 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
974 smb_fname->base_name,
975 &timestamp, &stripped)) {
976 return -1;
978 if (timestamp == 0) {
979 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
981 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
982 if (!NT_STATUS_IS_OK(status)) {
983 errno = ENOMEM;
984 return -1;
986 conv->base_name = shadow_copy2_convert(
987 conv, handle, stripped, timestamp);
988 TALLOC_FREE(stripped);
989 if (conv->base_name == NULL) {
990 return -1;
992 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
993 saved_errno = errno;
994 TALLOC_FREE(conv);
995 errno = saved_errno;
996 return ret;
999 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1000 const char *fname, char *buf, size_t bufsiz)
1002 time_t timestamp;
1003 char *stripped;
1004 int ret, saved_errno;
1005 char *conv;
1007 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1008 &timestamp, &stripped)) {
1009 return -1;
1011 if (timestamp == 0) {
1012 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1014 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1015 TALLOC_FREE(stripped);
1016 if (conv == NULL) {
1017 return -1;
1019 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1020 saved_errno = errno;
1021 TALLOC_FREE(conv);
1022 errno = saved_errno;
1023 return ret;
1026 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1027 const char *fname, mode_t mode, SMB_DEV_T dev)
1029 time_t timestamp;
1030 char *stripped;
1031 int ret, saved_errno;
1032 char *conv;
1034 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1035 &timestamp, &stripped)) {
1036 return -1;
1038 if (timestamp == 0) {
1039 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1041 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1042 TALLOC_FREE(stripped);
1043 if (conv == NULL) {
1044 return -1;
1046 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1047 saved_errno = errno;
1048 TALLOC_FREE(conv);
1049 errno = saved_errno;
1050 return ret;
1053 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1054 const char *fname)
1056 time_t timestamp;
1057 char *stripped = NULL;
1058 char *tmp = NULL;
1059 char *result = NULL;
1060 char *inserted = NULL;
1061 char *inserted_to, *inserted_end;
1062 int saved_errno;
1064 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1065 &timestamp, &stripped)) {
1066 goto done;
1068 if (timestamp == 0) {
1069 return SMB_VFS_NEXT_REALPATH(handle, fname);
1072 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1073 if (tmp == NULL) {
1074 goto done;
1077 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1078 if (result == NULL) {
1079 goto done;
1083 * Take away what we've inserted. This removes the @GMT-thingy
1084 * completely, but will give a path under the share root.
1086 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1087 if (inserted == NULL) {
1088 goto done;
1090 inserted_to = strstr_m(result, inserted);
1091 if (inserted_to == NULL) {
1092 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1093 goto done;
1095 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1096 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1098 done:
1099 saved_errno = errno;
1100 TALLOC_FREE(inserted);
1101 TALLOC_FREE(tmp);
1102 TALLOC_FREE(stripped);
1103 errno = saved_errno;
1104 return result;
1108 * Check whether a given directory contains a
1109 * snapshot directory as direct subdirectory.
1110 * If yes, return the path of the snapshot-subdir,
1111 * otherwise return NULL.
1113 static char *have_snapdir(struct vfs_handle_struct *handle,
1114 const char *path)
1116 struct smb_filename smb_fname;
1117 int ret;
1118 struct shadow_copy2_config *config;
1120 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1121 return NULL);
1123 ZERO_STRUCT(smb_fname);
1124 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1125 path, config->snapdir);
1126 if (smb_fname.base_name == NULL) {
1127 return NULL;
1130 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1131 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1132 return smb_fname.base_name;
1134 TALLOC_FREE(smb_fname.base_name);
1135 return NULL;
1139 * Find the snapshot directory (if any) for the given
1140 * filename (which is relative to the share).
1142 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1143 struct vfs_handle_struct *handle,
1144 struct smb_filename *smb_fname)
1146 char *path, *p;
1147 const char *snapdir;
1148 struct shadow_copy2_config *config;
1150 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1151 return NULL);
1154 * If the non-snapdisrseverywhere mode, we should not search!
1156 if (!config->snapdirseverywhere) {
1157 return config->snapshot_basepath;
1160 path = talloc_asprintf(mem_ctx, "%s/%s",
1161 handle->conn->connectpath,
1162 smb_fname->base_name);
1163 if (path == NULL) {
1164 return NULL;
1167 snapdir = have_snapdir(handle, path);
1168 if (snapdir != NULL) {
1169 TALLOC_FREE(path);
1170 return snapdir;
1173 while ((p = strrchr(path, '/')) && (p > path)) {
1175 p[0] = '\0';
1177 snapdir = have_snapdir(handle, path);
1178 if (snapdir != NULL) {
1179 TALLOC_FREE(path);
1180 return snapdir;
1183 TALLOC_FREE(path);
1184 return NULL;
1187 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1188 const char *name,
1189 char *gmt, size_t gmt_len)
1191 struct tm timestamp;
1192 time_t timestamp_t;
1193 unsigned long int timestamp_long;
1194 const char *fmt;
1195 struct shadow_copy2_config *config;
1197 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1198 return NULL);
1200 fmt = config->gmt_format;
1202 ZERO_STRUCT(timestamp);
1203 if (config->use_sscanf) {
1204 if (sscanf(name, fmt, &timestamp_long) != 1) {
1205 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1206 "no sscanf match %s: %s\n",
1207 fmt, name));
1208 return false;
1210 timestamp_t = timestamp_long;
1211 gmtime_r(&timestamp_t, &timestamp);
1212 } else {
1213 if (strptime(name, fmt, &timestamp) == NULL) {
1214 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1215 "no match %s: %s\n",
1216 fmt, name));
1217 return false;
1219 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1220 fmt, name));
1222 if (config->use_localtime) {
1223 timestamp.tm_isdst = -1;
1224 timestamp_t = mktime(&timestamp);
1225 gmtime_r(&timestamp_t, &timestamp);
1229 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1230 return true;
1233 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1235 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1238 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1240 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1244 sort the shadow copy data in ascending or descending order
1246 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1247 struct shadow_copy_data *shadow_copy2_data)
1249 int (*cmpfunc)(const void *, const void *);
1250 const char *sort;
1251 struct shadow_copy2_config *config;
1253 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1254 return);
1256 sort = config->sort_order;
1257 if (sort == NULL) {
1258 return;
1261 if (strcmp(sort, "asc") == 0) {
1262 cmpfunc = shadow_copy2_label_cmp_asc;
1263 } else if (strcmp(sort, "desc") == 0) {
1264 cmpfunc = shadow_copy2_label_cmp_desc;
1265 } else {
1266 return;
1269 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1270 shadow_copy2_data->labels)
1272 TYPESAFE_QSORT(shadow_copy2_data->labels,
1273 shadow_copy2_data->num_volumes,
1274 cmpfunc);
1278 static int shadow_copy2_get_shadow_copy_data(
1279 vfs_handle_struct *handle, files_struct *fsp,
1280 struct shadow_copy_data *shadow_copy2_data,
1281 bool labels)
1283 DIR *p;
1284 const char *snapdir;
1285 struct dirent *d;
1286 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1288 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1289 if (snapdir == NULL) {
1290 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1291 handle->conn->connectpath));
1292 errno = EINVAL;
1293 talloc_free(tmp_ctx);
1294 return -1;
1297 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1299 if (!p) {
1300 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1301 " - %s\n", snapdir, strerror(errno)));
1302 talloc_free(tmp_ctx);
1303 errno = ENOSYS;
1304 return -1;
1307 shadow_copy2_data->num_volumes = 0;
1308 shadow_copy2_data->labels = NULL;
1310 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1311 char snapshot[GMT_NAME_LEN+1];
1312 SHADOW_COPY_LABEL *tlabels;
1315 * ignore names not of the right form in the snapshot
1316 * directory
1318 if (!shadow_copy2_snapshot_to_gmt(
1319 handle, d->d_name,
1320 snapshot, sizeof(snapshot))) {
1322 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1323 "ignoring %s\n", d->d_name));
1324 continue;
1326 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1327 d->d_name, snapshot));
1329 if (!labels) {
1330 /* the caller doesn't want the labels */
1331 shadow_copy2_data->num_volumes++;
1332 continue;
1335 tlabels = talloc_realloc(shadow_copy2_data,
1336 shadow_copy2_data->labels,
1337 SHADOW_COPY_LABEL,
1338 shadow_copy2_data->num_volumes+1);
1339 if (tlabels == NULL) {
1340 DEBUG(0,("shadow_copy2: out of memory\n"));
1341 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1342 talloc_free(tmp_ctx);
1343 return -1;
1346 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1347 sizeof(*tlabels));
1349 shadow_copy2_data->num_volumes++;
1350 shadow_copy2_data->labels = tlabels;
1353 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1355 shadow_copy2_sort_data(handle, shadow_copy2_data);
1357 talloc_free(tmp_ctx);
1358 return 0;
1361 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1362 struct files_struct *fsp,
1363 uint32 security_info,
1364 TALLOC_CTX *mem_ctx,
1365 struct security_descriptor **ppdesc)
1367 time_t timestamp;
1368 char *stripped;
1369 NTSTATUS status;
1370 char *conv;
1372 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1373 fsp->fsp_name->base_name,
1374 &timestamp, &stripped)) {
1375 return map_nt_error_from_unix(errno);
1377 if (timestamp == 0) {
1378 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1379 mem_ctx,
1380 ppdesc);
1382 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1383 TALLOC_FREE(stripped);
1384 if (conv == NULL) {
1385 return map_nt_error_from_unix(errno);
1387 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1388 mem_ctx, ppdesc);
1389 TALLOC_FREE(conv);
1390 return status;
1393 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1394 const char *fname,
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, fname,
1405 &timestamp, &stripped)) {
1406 return map_nt_error_from_unix(errno);
1408 if (timestamp == 0) {
1409 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1410 mem_ctx, ppdesc);
1412 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1413 TALLOC_FREE(stripped);
1414 if (conv == NULL) {
1415 return map_nt_error_from_unix(errno);
1417 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1418 mem_ctx, ppdesc);
1419 TALLOC_FREE(conv);
1420 return status;
1423 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1424 const char *fname, mode_t mode)
1426 time_t timestamp;
1427 char *stripped;
1428 int ret, saved_errno;
1429 char *conv;
1431 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1432 &timestamp, &stripped)) {
1433 return -1;
1435 if (timestamp == 0) {
1436 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1438 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1439 TALLOC_FREE(stripped);
1440 if (conv == NULL) {
1441 return -1;
1443 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1444 saved_errno = errno;
1445 TALLOC_FREE(conv);
1446 errno = saved_errno;
1447 return ret;
1450 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1452 time_t timestamp;
1453 char *stripped;
1454 int ret, saved_errno;
1455 char *conv;
1457 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1458 &timestamp, &stripped)) {
1459 return -1;
1461 if (timestamp == 0) {
1462 return SMB_VFS_NEXT_RMDIR(handle, fname);
1464 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1465 TALLOC_FREE(stripped);
1466 if (conv == NULL) {
1467 return -1;
1469 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1470 saved_errno = errno;
1471 TALLOC_FREE(conv);
1472 errno = saved_errno;
1473 return ret;
1476 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1477 unsigned int flags)
1479 time_t timestamp;
1480 char *stripped;
1481 int ret, saved_errno;
1482 char *conv;
1484 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1485 &timestamp, &stripped)) {
1486 return -1;
1488 if (timestamp == 0) {
1489 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1491 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1492 TALLOC_FREE(stripped);
1493 if (conv == NULL) {
1494 return -1;
1496 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1497 saved_errno = errno;
1498 TALLOC_FREE(conv);
1499 errno = saved_errno;
1500 return ret;
1503 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1504 const char *fname, const char *aname,
1505 void *value, size_t size)
1507 time_t timestamp;
1508 char *stripped;
1509 ssize_t ret;
1510 int saved_errno;
1511 char *conv;
1513 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1514 &timestamp, &stripped)) {
1515 return -1;
1517 if (timestamp == 0) {
1518 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1519 size);
1521 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1522 TALLOC_FREE(stripped);
1523 if (conv == NULL) {
1524 return -1;
1526 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1527 saved_errno = errno;
1528 TALLOC_FREE(conv);
1529 errno = saved_errno;
1530 return ret;
1533 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1534 const char *fname,
1535 char *list, size_t size)
1537 time_t timestamp;
1538 char *stripped;
1539 ssize_t ret;
1540 int saved_errno;
1541 char *conv;
1543 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1544 &timestamp, &stripped)) {
1545 return -1;
1547 if (timestamp == 0) {
1548 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1550 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1551 TALLOC_FREE(stripped);
1552 if (conv == NULL) {
1553 return -1;
1555 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1556 saved_errno = errno;
1557 TALLOC_FREE(conv);
1558 errno = saved_errno;
1559 return ret;
1562 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1563 const char *fname, const char *aname)
1565 time_t timestamp;
1566 char *stripped;
1567 int ret, saved_errno;
1568 char *conv;
1570 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1571 &timestamp, &stripped)) {
1572 return -1;
1574 if (timestamp == 0) {
1575 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1577 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1578 TALLOC_FREE(stripped);
1579 if (conv == NULL) {
1580 return -1;
1582 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1583 saved_errno = errno;
1584 TALLOC_FREE(conv);
1585 errno = saved_errno;
1586 return ret;
1589 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1590 const char *fname,
1591 const char *aname, const void *value,
1592 size_t size, int flags)
1594 time_t timestamp;
1595 char *stripped;
1596 ssize_t ret;
1597 int saved_errno;
1598 char *conv;
1600 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1601 &timestamp, &stripped)) {
1602 return -1;
1604 if (timestamp == 0) {
1605 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1606 flags);
1608 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1609 TALLOC_FREE(stripped);
1610 if (conv == NULL) {
1611 return -1;
1613 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1614 saved_errno = errno;
1615 TALLOC_FREE(conv);
1616 errno = saved_errno;
1617 return ret;
1620 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1621 const char *fname, mode_t mode)
1623 time_t timestamp;
1624 char *stripped;
1625 ssize_t ret;
1626 int saved_errno;
1627 char *conv;
1629 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1630 &timestamp, &stripped)) {
1631 return -1;
1633 if (timestamp == 0) {
1634 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1636 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1637 TALLOC_FREE(stripped);
1638 if (conv == NULL) {
1639 return -1;
1641 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1642 saved_errno = errno;
1643 TALLOC_FREE(conv);
1644 errno = saved_errno;
1645 return ret;
1648 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1649 const char *path,
1650 const char *name,
1651 TALLOC_CTX *mem_ctx,
1652 char **found_name)
1654 time_t timestamp;
1655 char *stripped;
1656 ssize_t ret;
1657 int saved_errno;
1658 char *conv;
1660 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1661 &timestamp, &stripped)) {
1662 return -1;
1664 if (timestamp == 0) {
1665 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1666 mem_ctx, found_name);
1668 if (stripped[0] == '\0') {
1669 *found_name = talloc_strdup(mem_ctx, name);
1670 if (*found_name == NULL) {
1671 errno = ENOMEM;
1672 return -1;
1674 return 0;
1676 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1677 TALLOC_FREE(stripped);
1678 if (conv == NULL) {
1679 return -1;
1681 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1682 mem_ctx, found_name);
1683 saved_errno = errno;
1684 TALLOC_FREE(conv);
1685 errno = saved_errno;
1686 return ret;
1689 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1690 const char *path, bool small_query,
1691 uint64_t *bsize, uint64_t *dfree,
1692 uint64_t *dsize)
1694 time_t timestamp;
1695 char *stripped;
1696 ssize_t ret;
1697 int saved_errno;
1698 char *conv;
1700 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1701 &timestamp, &stripped)) {
1702 return -1;
1704 if (timestamp == 0) {
1705 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1706 bsize, dfree, dsize);
1709 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1710 TALLOC_FREE(stripped);
1711 if (conv == NULL) {
1712 return -1;
1715 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1716 dsize);
1718 saved_errno = errno;
1719 TALLOC_FREE(conv);
1720 errno = saved_errno;
1722 return ret;
1725 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1726 const char *service, const char *user)
1728 struct shadow_copy2_config *config;
1729 int ret;
1730 const char *snapdir;
1731 const char *gmt_format;
1732 const char *sort_order;
1733 const char *basedir;
1734 const char *mount_point;
1736 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1737 (unsigned)handle->conn->cnum,
1738 handle->conn->connectpath));
1740 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1741 if (ret < 0) {
1742 return ret;
1745 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1746 if (config == NULL) {
1747 DEBUG(0, ("talloc_zero() failed\n"));
1748 errno = ENOMEM;
1749 return -1;
1752 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1753 "shadow", "format",
1754 GMT_FORMAT);
1755 config->gmt_format = talloc_strdup(config, gmt_format);
1756 if (config->gmt_format == NULL) {
1757 DEBUG(0, ("talloc_strdup() failed\n"));
1758 errno = ENOMEM;
1759 return -1;
1762 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1763 "shadow", "sscanf", false);
1765 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1766 "shadow", "localtime",
1767 false);
1769 snapdir = lp_parm_const_string(SNUM(handle->conn),
1770 "shadow", "snapdir",
1771 ".snapshots");
1772 config->snapdir = talloc_strdup(config, snapdir);
1773 if (config->snapdir == NULL) {
1774 DEBUG(0, ("talloc_strdup() failed\n"));
1775 errno = ENOMEM;
1776 return -1;
1779 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1780 "shadow",
1781 "snapdirseverywhere",
1782 false);
1784 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1785 "shadow", "crossmountpoints",
1786 false);
1788 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1789 "shadow", "fixinodes",
1790 false);
1792 sort_order = lp_parm_const_string(SNUM(handle->conn),
1793 "shadow", "sort", "desc");
1794 config->sort_order = talloc_strdup(config, sort_order);
1795 if (config->sort_order == NULL) {
1796 DEBUG(0, ("talloc_strdup() failed\n"));
1797 errno = ENOMEM;
1798 return -1;
1801 mount_point = lp_parm_const_string(SNUM(handle->conn),
1802 "shadow", "mountpoint", NULL);
1803 if (mount_point != NULL) {
1804 if (mount_point[0] != '/') {
1805 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1806 "relative ('%s'), but it has to be an "
1807 "absolute path. Ignoring provided value.\n",
1808 mount_point));
1809 mount_point = NULL;
1810 } else {
1811 char *p;
1812 p = strstr(handle->conn->connectpath, mount_point);
1813 if (p != handle->conn->connectpath) {
1814 DEBUG(1, ("Warning: mount_point (%s) is not a "
1815 "subdirectory of the share root "
1816 "(%s). Ignoring provided value.\n",
1817 mount_point,
1818 handle->conn->connectpath));
1819 mount_point = NULL;
1824 if (mount_point != NULL) {
1825 config->mount_point = talloc_strdup(config, mount_point);
1826 if (config->mount_point == NULL) {
1827 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1828 return -1;
1830 } else {
1831 config->mount_point = shadow_copy2_find_mount_point(config,
1832 handle);
1833 if (config->mount_point == NULL) {
1834 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1835 " failed: %s\n", strerror(errno)));
1836 return -1;
1840 basedir = lp_parm_const_string(SNUM(handle->conn),
1841 "shadow", "basedir", NULL);
1843 if (basedir != NULL) {
1844 if (basedir[0] != '/') {
1845 DEBUG(1, (__location__ " Warning: 'basedir' is "
1846 "relative ('%s'), but it has to be an "
1847 "absolute path. Disabling basedir.\n",
1848 basedir));
1849 } else {
1850 char *p;
1851 p = strstr(basedir, config->mount_point);
1852 if (p != basedir) {
1853 DEBUG(1, ("Warning: basedir (%s) is not a "
1854 "subdirectory of the share root's "
1855 "mount point (%s). "
1856 "Disabling basedir\n",
1857 basedir, config->mount_point));
1858 } else {
1859 config->basedir = talloc_strdup(config,
1860 basedir);
1861 if (config->basedir == NULL) {
1862 DEBUG(0, ("talloc_strdup() failed\n"));
1863 errno = ENOMEM;
1864 return -1;
1870 if (config->snapdirseverywhere && config->basedir != NULL) {
1871 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1872 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1873 TALLOC_FREE(config->basedir);
1876 if (config->crossmountpoints && config->basedir != NULL) {
1877 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1878 "with 'crossmountpoints'. Disabling basedir.\n"));
1879 TALLOC_FREE(config->basedir);
1882 if (config->basedir == NULL) {
1883 config->basedir = config->mount_point;
1886 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1887 config->rel_connectpath = talloc_strdup(config,
1888 handle->conn->connectpath + strlen(config->basedir));
1889 if (config->rel_connectpath == NULL) {
1890 DEBUG(0, ("talloc_strdup() failed\n"));
1891 errno = ENOMEM;
1892 return -1;
1896 if (config->snapdir[0] == '/') {
1897 config->snapdir_absolute = true;
1899 if (config->snapdirseverywhere == true) {
1900 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1901 "is incompatible with 'snapdirseverywhere', "
1902 "setting 'snapdirseverywhere' to false.\n"));
1903 config->snapdirseverywhere = false;
1906 if (config->crossmountpoints == true) {
1907 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1908 "is not supported with an absolute snapdir. "
1909 "Disabling it.\n"));
1910 config->crossmountpoints = false;
1913 config->snapshot_basepath = config->snapdir;
1914 } else {
1915 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1916 config->mount_point, config->snapdir);
1917 if (config->snapshot_basepath == NULL) {
1918 DEBUG(0, ("talloc_asprintf() failed\n"));
1919 errno = ENOMEM;
1920 return -1;
1924 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1925 " share root: '%s'\n"
1926 " basedir: '%s'\n"
1927 " mountpoint: '%s'\n"
1928 " rel share root: '%s'\n"
1929 " snapdir: '%s'\n"
1930 " snapshot base path: '%s'\n"
1931 " format: '%s'\n"
1932 " use sscanf: %s\n"
1933 " snapdirs everywhere: %s\n"
1934 " cross mountpoints: %s\n"
1935 " fix inodes: %s\n"
1936 " sort order: %s\n"
1938 handle->conn->connectpath,
1939 config->basedir,
1940 config->mount_point,
1941 config->rel_connectpath,
1942 config->snapdir,
1943 config->snapshot_basepath,
1944 config->gmt_format,
1945 config->use_sscanf ? "yes" : "no",
1946 config->snapdirseverywhere ? "yes" : "no",
1947 config->crossmountpoints ? "yes" : "no",
1948 config->fixinodes ? "yes" : "no",
1949 config->sort_order
1953 SMB_VFS_HANDLE_SET_DATA(handle, config,
1954 NULL, struct shadow_copy2_config,
1955 return -1);
1957 return 0;
1960 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1961 .connect_fn = shadow_copy2_connect,
1962 .opendir_fn = shadow_copy2_opendir,
1963 .disk_free_fn = shadow_copy2_disk_free,
1964 .rename_fn = shadow_copy2_rename,
1965 .link_fn = shadow_copy2_link,
1966 .symlink_fn = shadow_copy2_symlink,
1967 .stat_fn = shadow_copy2_stat,
1968 .lstat_fn = shadow_copy2_lstat,
1969 .fstat_fn = shadow_copy2_fstat,
1970 .open_fn = shadow_copy2_open,
1971 .unlink_fn = shadow_copy2_unlink,
1972 .chmod_fn = shadow_copy2_chmod,
1973 .chown_fn = shadow_copy2_chown,
1974 .chdir_fn = shadow_copy2_chdir,
1975 .ntimes_fn = shadow_copy2_ntimes,
1976 .readlink_fn = shadow_copy2_readlink,
1977 .mknod_fn = shadow_copy2_mknod,
1978 .realpath_fn = shadow_copy2_realpath,
1979 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1980 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1981 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1982 .mkdir_fn = shadow_copy2_mkdir,
1983 .rmdir_fn = shadow_copy2_rmdir,
1984 .getxattr_fn = shadow_copy2_getxattr,
1985 .listxattr_fn = shadow_copy2_listxattr,
1986 .removexattr_fn = shadow_copy2_removexattr,
1987 .setxattr_fn = shadow_copy2_setxattr,
1988 .chmod_acl_fn = shadow_copy2_chmod_acl,
1989 .chflags_fn = shadow_copy2_chflags,
1990 .get_real_filename_fn = shadow_copy2_get_real_filename,
1993 NTSTATUS vfs_shadow_copy2_init(void);
1994 NTSTATUS vfs_shadow_copy2_init(void)
1996 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1997 "shadow_copy2", &vfs_shadow_copy2_fns);