s3:modules:shadow_copy2: improve headline comment
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob711ef864d110592588bf610d5f8b3b2cfad825d4
1 /*
2 * shadow_copy2: a shadow copy module (second implementation)
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
8 * Copyright (C) Michael Adam 2013
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 This is a 3rd implemetation of a shadow copy module for exposing
28 snapshots to windows clients as shadow copies. This version has the
29 following features:
31 1) you don't need to populate your shares with symlinks to the
32 snapshots. This can be very important when you have thousands of
33 shares, or use [homes]
35 2) the inode number of the files is altered so it is different
36 from the original. This allows the 'restore' button to work
37 without a sharing violation
39 3) shadow copy results can be sorted before being sent to the
40 client. This is beneficial for filesystems that don't read
41 directories alphabetically (the default unix).
43 4) vanity naming for snapshots. Snapshots can be named in any
44 format compatible with str[fp]time conversions.
46 5) time stamps in snapshot names can be represented in localtime
47 rather than UTC.
49 Module options:
51 shadow:snapdir = <directory where snapshots are kept>
53 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
54 path it is used as-is. If it is a relative path, then it is taken relative to the mount
55 point of the filesystem that the root of this share is on
57 shadow:basedir = <base directory that snapshots are from>
59 This is an optional parameter that specifies the directory that
60 the snapshots are relative to. It defaults to the filesystem
61 mount point
63 shadow:fixinodes = yes/no
65 If you enable shadow:fixinodes then this module will modify the
66 apparent inode number of files in the snapshot directories using
67 a hash of the files path. This is needed for snapshot systems
68 where the snapshots have the same device:inode number as the
69 original files (such as happens with GPFS snapshots). If you
70 don't set this option then the 'restore' button in the shadow
71 copy UI will fail with a sharing violation.
73 shadow:sort = asc/desc, or not specified for unsorted (default)
75 This is an optional parameter that specifies that the shadow
76 copy directories should be sorted before sending them to the
77 client. This can be beneficial as unix filesystems are usually
78 not listed alphabetically sorted. If enabled, you typically
79 want to specify descending order.
81 shadow:format = <format specification for snapshot names>
83 This is an optional parameter that specifies the format
84 specification for the naming of snapshots. The format must
85 be compatible with the conversion specifications recognized
86 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
88 shadow:sscanf = yes/no (default is no)
90 The time is the unsigned long integer (%lu) in the format string
91 rather than a time strptime() can parse. The result must be a unix time_t
92 time.
94 shadow:localtime = yes/no (default is no)
96 This is an optional parameter that indicates whether the
97 snapshot names are in UTC/GMT or the local time.
100 The following command would generate a correctly formatted directory name
101 for use with the default parameters:
102 date -u +@GMT-%Y.%m.%d-%H.%M.%S
105 #include "includes.h"
106 #include "system/filesys.h"
107 #include "include/ntioctl.h"
108 #include <ccan/hash/hash.h>
109 #include "util_tdb.h"
111 struct shadow_copy2_config {
112 char *gmt_format;
113 bool use_sscanf;
114 bool use_localtime;
115 char *snapdir;
116 bool snapdirseverywhere;
117 bool crossmountpoints;
118 bool fixinodes;
119 char *sort_order;
120 bool snapdir_absolute;
121 char *basedir;
122 char *mount_point;
123 char *rel_connectpath; /* share root, relative to the basedir */
124 char *snapshot_basepath; /* the absolute version of snapdir */
127 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
128 size_t **poffsets,
129 unsigned *pnum_offsets)
131 unsigned num_offsets;
132 size_t *offsets;
133 const char *p;
135 num_offsets = 0;
137 p = str;
138 while ((p = strchr(p, '/')) != NULL) {
139 num_offsets += 1;
140 p += 1;
143 offsets = talloc_array(mem_ctx, size_t, num_offsets);
144 if (offsets == NULL) {
145 return false;
148 p = str;
149 num_offsets = 0;
150 while ((p = strchr(p, '/')) != NULL) {
151 offsets[num_offsets] = p-str;
152 num_offsets += 1;
153 p += 1;
156 *poffsets = offsets;
157 *pnum_offsets = num_offsets;
158 return true;
162 * Given a timstamp, build the posix level GTM-tag string
163 * based on the configurable format.
165 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct *handle,
166 time_t snapshot,
167 char *snaptime_string,
168 size_t len)
170 struct tm snap_tm;
171 size_t snaptime_len;
172 struct shadow_copy2_config *config;
174 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
175 return 0);
177 if (config->use_sscanf) {
178 snaptime_len = snprintf(snaptime_string,
179 len,
180 config->gmt_format,
181 (unsigned long)snapshot);
182 if (snaptime_len <= 0) {
183 DEBUG(10, ("snprintf failed\n"));
184 return snaptime_len;
186 } else {
187 if (config->use_localtime) {
188 if (localtime_r(&snapshot, &snap_tm) == 0) {
189 DEBUG(10, ("gmtime_r failed\n"));
190 return -1;
192 } else {
193 if (gmtime_r(&snapshot, &snap_tm) == 0) {
194 DEBUG(10, ("gmtime_r failed\n"));
195 return -1;
198 snaptime_len = strftime(snaptime_string,
199 len,
200 config->gmt_format,
201 &snap_tm);
202 if (snaptime_len == 0) {
203 DEBUG(10, ("strftime failed\n"));
204 return 0;
208 return snaptime_len;
212 * Given a timstamp, build the string to insert into a path
213 * as a path component for creating the local path to the
214 * snapshot at the given timestamp of the input path.
216 * In the case of a parallel snapdir (specified with an
217 * absolute path), this is the inital portion of the
218 * local path of any snapshot file. The complete path is
219 * obtained by appending the portion of the file's path
220 * below the share root's mountpoint.
222 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
223 struct vfs_handle_struct *handle,
224 time_t snapshot)
226 fstring snaptime_string;
227 size_t snaptime_len = 0;
228 char *result = NULL;
229 struct shadow_copy2_config *config;
231 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
232 return NULL);
234 snaptime_len = shadow_copy2_posix_gmt_string(handle,
235 snapshot,
236 snaptime_string,
237 sizeof(snaptime_string));
238 if (snaptime_len <= 0) {
239 return NULL;
242 if (config->snapdir_absolute) {
243 result = talloc_asprintf(mem_ctx, "%s/%s",
244 config->snapdir, snaptime_string);
245 } else {
246 result = talloc_asprintf(mem_ctx, "/%s/%s",
247 config->snapdir, snaptime_string);
249 if (result == NULL) {
250 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
253 return result;
257 * Build the posix snapshot path for the connection
258 * at the given timestamp, i.e. the absolute posix path
259 * that contains the snapshot for this file system.
261 * This only applies to classical case, i.e. not
262 * to the "snapdirseverywhere" mode.
264 static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx,
265 struct vfs_handle_struct *handle,
266 time_t snapshot)
268 fstring snaptime_string;
269 size_t snaptime_len = 0;
270 char *result = NULL;
271 struct shadow_copy2_config *config;
273 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
274 return NULL);
276 snaptime_len = shadow_copy2_posix_gmt_string(handle,
277 snapshot,
278 snaptime_string,
279 sizeof(snaptime_string));
280 if (snaptime_len <= 0) {
281 return NULL;
284 result = talloc_asprintf(mem_ctx, "%s/%s",
285 config->snapshot_basepath, snaptime_string);
286 if (result == NULL) {
287 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
290 return result;
294 * Strip a snapshot component from an filename as
295 * handed in via the smb layer.
296 * Returns the parsed timestamp and the stripped filename.
298 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
299 struct vfs_handle_struct *handle,
300 const char *name,
301 time_t *ptimestamp,
302 char **pstripped)
304 struct tm tm;
305 time_t timestamp;
306 const char *p;
307 char *q;
308 char *stripped;
309 size_t rest_len, dst_len;
310 struct shadow_copy2_config *config;
312 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
313 return false);
315 DEBUG(10, (__location__ ": enter path '%s'\n", name));
317 p = strstr_m(name, "@GMT-");
318 if (p == NULL) {
319 goto no_snapshot;
321 if ((p > name) && (p[-1] != '/')) {
322 /* the GMT-token does not start a path-component */
323 goto no_snapshot;
325 q = strptime(p, GMT_FORMAT, &tm);
326 if (q == NULL) {
327 goto no_snapshot;
329 tm.tm_isdst = -1;
330 timestamp = timegm(&tm);
331 if (timestamp == (time_t)-1) {
332 goto no_snapshot;
334 if ((p == name) && (q[0] == '\0')) {
335 /* the name consists of only the GMT token */
336 if (pstripped != NULL) {
337 stripped = talloc_strdup(mem_ctx, "");
338 if (stripped == NULL) {
339 return false;
341 *pstripped = stripped;
343 *ptimestamp = timestamp;
344 return true;
346 if (q[0] != '/') {
348 * The GMT token is either at the end of the path
349 * or it is not a complete path component, i.e. the
350 * path component continues after the gmt-token.
352 * TODO: Is this correct? Or would the GMT tag as the
353 * last component be a valid input?
355 goto no_snapshot;
357 q += 1;
359 rest_len = strlen(q);
360 dst_len = (p-name) + rest_len;
362 if (config->snapdirseverywhere) {
363 char *insert;
364 bool have_insert;
365 insert = shadow_copy2_insert_string(talloc_tos(), handle,
366 timestamp);
367 if (insert == NULL) {
368 errno = ENOMEM;
369 return false;
372 DEBUG(10, (__location__ ": snapdirseverywhere mode.\n"
373 "path '%s'.\n"
374 "insert string '%s'\n", name, insert));
376 have_insert = (strstr(name, insert+1) != NULL);
377 if (have_insert) {
378 DEBUG(10, (__location__ ": insert string '%s' found in "
379 "path '%s' found in snapdirseverywhere mode "
380 "==> already converted\n", insert, name));
381 TALLOC_FREE(insert);
382 goto no_snapshot;
384 TALLOC_FREE(insert);
385 } else {
386 char *snapshot_path;
387 char *s;
389 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
390 handle,
391 timestamp);
392 if (snapshot_path == NULL) {
393 errno = ENOMEM;
394 return false;
397 DEBUG(10, (__location__ " path: '%s'.\n"
398 "snapshot path: '%s'\n", name, snapshot_path));
400 s = strstr(name, snapshot_path);
401 if (s == name) {
403 * this starts with "snapshot_basepath/GMT-Token"
404 * so it is already a converted absolute
405 * path. Don't process further.
407 DEBUG(10, (__location__ ": path '%s' starts with "
408 "snapshot path '%s' (not in "
409 "snapdirseverywhere mode) ==> "
410 "already converted\n", name, snapshot_path));
411 talloc_free(snapshot_path);
412 goto no_snapshot;
414 talloc_free(snapshot_path);
417 if (pstripped != NULL) {
418 stripped = talloc_array(mem_ctx, char, dst_len+1);
419 if (stripped == NULL) {
420 errno = ENOMEM;
421 return false;
423 if (p > name) {
424 memcpy(stripped, name, p-name);
426 if (rest_len > 0) {
427 memcpy(stripped + (p-name), q, rest_len);
429 stripped[dst_len] = '\0';
430 *pstripped = stripped;
432 *ptimestamp = timestamp;
433 return true;
434 no_snapshot:
435 *ptimestamp = 0;
436 return true;
439 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
440 vfs_handle_struct *handle)
442 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
443 dev_t dev;
444 struct stat st;
445 char *p;
447 if (stat(path, &st) != 0) {
448 talloc_free(path);
449 return NULL;
452 dev = st.st_dev;
454 while ((p = strrchr(path, '/')) && p > path) {
455 *p = 0;
456 if (stat(path, &st) != 0) {
457 talloc_free(path);
458 return NULL;
460 if (st.st_dev != dev) {
461 *p = '/';
462 break;
466 return path;
470 * Convert from a name as handed in via the SMB layer
471 * and a timestamp into the local path of the snapshot
472 * of the provided file at the provided time.
474 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
475 struct vfs_handle_struct *handle,
476 const char *name, time_t timestamp)
478 struct smb_filename converted_fname;
479 char *result = NULL;
480 size_t *slashes = NULL;
481 unsigned num_slashes;
482 char *path = NULL;
483 size_t pathlen;
484 char *insert = NULL;
485 char *converted = NULL;
486 size_t insertlen;
487 int i, saved_errno;
488 size_t min_offset;
489 struct shadow_copy2_config *config;
491 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
492 return NULL);
494 DEBUG(10, ("converting '%s'\n", name));
496 if (!config->snapdirseverywhere) {
497 int ret;
498 char *snapshot_path;
500 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
501 handle,
502 timestamp);
503 if (snapshot_path == NULL) {
504 goto fail;
507 if (config->rel_connectpath == NULL) {
508 converted = talloc_asprintf(mem_ctx, "%s/%s",
509 snapshot_path, name);
510 } else {
511 converted = talloc_asprintf(mem_ctx, "%s/%s/%s",
512 snapshot_path,
513 config->rel_connectpath,
514 name);
516 if (converted == NULL) {
517 goto fail;
520 ZERO_STRUCT(converted_fname);
521 converted_fname.base_name = converted;
523 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
524 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
525 converted,
526 ret, ret == 0 ? "ok" : strerror(errno)));
527 if (ret == 0) {
528 DEBUG(10, ("Found %s\n", converted));
529 result = converted;
530 converted = NULL;
531 goto fail;
532 } else {
533 errno = ENOENT;
534 goto fail;
536 /* never reached ... */
539 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
540 name);
541 if (path == NULL) {
542 errno = ENOMEM;
543 goto fail;
545 pathlen = talloc_get_size(path)-1;
547 if (!shadow_copy2_find_slashes(talloc_tos(), path,
548 &slashes, &num_slashes)) {
549 goto fail;
552 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
553 if (insert == NULL) {
554 goto fail;
556 insertlen = talloc_get_size(insert)-1;
558 converted = talloc_zero_array(mem_ctx, char, pathlen + insertlen + 1);
559 if (converted == NULL) {
560 goto fail;
563 if (path[pathlen-1] != '/') {
565 * Append a fake slash to find the snapshot root
567 size_t *tmp;
568 tmp = talloc_realloc(talloc_tos(), slashes,
569 size_t, num_slashes+1);
570 if (tmp == NULL) {
571 goto fail;
573 slashes = tmp;
574 slashes[num_slashes] = pathlen;
575 num_slashes += 1;
578 min_offset = 0;
580 if (!config->crossmountpoints) {
581 min_offset = strlen(config->mount_point);
584 memcpy(converted, path, pathlen+1);
585 converted[pathlen+insertlen] = '\0';
587 ZERO_STRUCT(converted_fname);
588 converted_fname.base_name = converted;
590 for (i = num_slashes-1; i>=0; i--) {
591 int ret;
592 size_t offset;
594 offset = slashes[i];
596 if (offset < min_offset) {
597 errno = ENOENT;
598 goto fail;
601 memcpy(converted+offset, insert, insertlen);
603 offset += insertlen;
604 memcpy(converted+offset, path + slashes[i],
605 pathlen - slashes[i]);
607 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
609 DEBUG(10, ("Trying[snapdirseverywhere] %s: %d (%s)\n",
610 converted,
611 ret, ret == 0 ? "ok" : strerror(errno)));
612 if (ret == 0) {
613 /* success */
614 break;
616 if (errno == ENOTDIR) {
618 * This is a valid condition: We appended the
619 * .snaphots/@GMT.. to a file name. Just try
620 * with the upper levels.
622 continue;
624 if (errno != ENOENT) {
625 /* Other problem than "not found" */
626 goto fail;
630 if (i >= 0) {
632 * Found something
634 DEBUG(10, ("Found %s\n", converted));
635 result = converted;
636 converted = NULL;
637 } else {
638 errno = ENOENT;
640 fail:
641 saved_errno = errno;
642 TALLOC_FREE(converted);
643 TALLOC_FREE(insert);
644 TALLOC_FREE(slashes);
645 TALLOC_FREE(path);
646 errno = saved_errno;
647 return result;
651 modify a sbuf return to ensure that inodes in the shadow directory
652 are different from those in the main directory
654 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
655 SMB_STRUCT_STAT *sbuf)
657 struct shadow_copy2_config *config;
659 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
660 return);
662 if (config->fixinodes) {
663 /* some snapshot systems, like GPFS, return the name
664 device:inode for the snapshot files as the current
665 files. That breaks the 'restore' button in the shadow copy
666 GUI, as the client gets a sharing violation.
668 This is a crude way of allowing both files to be
669 open at once. It has a slight chance of inode
670 number collision, but I can't see a better approach
671 without significant VFS changes
673 uint32_t shash;
675 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
676 if (shash == 0) {
677 shash = 1;
679 sbuf->st_ex_ino ^= shash;
683 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
684 const char *fname,
685 const char *mask,
686 uint32 attr)
688 time_t timestamp;
689 char *stripped;
690 DIR *ret;
691 int saved_errno;
692 char *conv;
694 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
695 &timestamp, &stripped)) {
696 return NULL;
698 if (timestamp == 0) {
699 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
701 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
702 TALLOC_FREE(stripped);
703 if (conv == NULL) {
704 return NULL;
706 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
707 saved_errno = errno;
708 TALLOC_FREE(conv);
709 errno = saved_errno;
710 return ret;
713 static int shadow_copy2_rename(vfs_handle_struct *handle,
714 const struct smb_filename *smb_fname_src,
715 const struct smb_filename *smb_fname_dst)
717 time_t timestamp_src, timestamp_dst;
719 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
720 smb_fname_src->base_name,
721 &timestamp_src, NULL)) {
722 return -1;
724 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
725 smb_fname_dst->base_name,
726 &timestamp_dst, NULL)) {
727 return -1;
729 if (timestamp_src != 0) {
730 errno = EXDEV;
731 return -1;
733 if (timestamp_dst != 0) {
734 errno = EROFS;
735 return -1;
737 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
740 static int shadow_copy2_symlink(vfs_handle_struct *handle,
741 const char *oldname, const char *newname)
743 time_t timestamp_old, timestamp_new;
745 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
746 &timestamp_old, NULL)) {
747 return -1;
749 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
750 &timestamp_new, NULL)) {
751 return -1;
753 if ((timestamp_old != 0) || (timestamp_new != 0)) {
754 errno = EROFS;
755 return -1;
757 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
760 static int shadow_copy2_link(vfs_handle_struct *handle,
761 const char *oldname, const char *newname)
763 time_t timestamp_old, timestamp_new;
765 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
766 &timestamp_old, NULL)) {
767 return -1;
769 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
770 &timestamp_new, NULL)) {
771 return -1;
773 if ((timestamp_old != 0) || (timestamp_new != 0)) {
774 errno = EROFS;
775 return -1;
777 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
780 static int shadow_copy2_stat(vfs_handle_struct *handle,
781 struct smb_filename *smb_fname)
783 time_t timestamp;
784 char *stripped, *tmp;
785 int ret, saved_errno;
787 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
788 smb_fname->base_name,
789 &timestamp, &stripped)) {
790 return -1;
792 if (timestamp == 0) {
793 return SMB_VFS_NEXT_STAT(handle, smb_fname);
796 tmp = smb_fname->base_name;
797 smb_fname->base_name = shadow_copy2_convert(
798 talloc_tos(), handle, stripped, timestamp);
799 TALLOC_FREE(stripped);
801 if (smb_fname->base_name == NULL) {
802 smb_fname->base_name = tmp;
803 return -1;
806 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
807 saved_errno = errno;
809 TALLOC_FREE(smb_fname->base_name);
810 smb_fname->base_name = tmp;
812 if (ret == 0) {
813 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
815 errno = saved_errno;
816 return ret;
819 static int shadow_copy2_lstat(vfs_handle_struct *handle,
820 struct smb_filename *smb_fname)
822 time_t timestamp;
823 char *stripped, *tmp;
824 int ret, saved_errno;
826 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
827 smb_fname->base_name,
828 &timestamp, &stripped)) {
829 return -1;
831 if (timestamp == 0) {
832 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
835 tmp = smb_fname->base_name;
836 smb_fname->base_name = shadow_copy2_convert(
837 talloc_tos(), handle, stripped, timestamp);
838 TALLOC_FREE(stripped);
840 if (smb_fname->base_name == NULL) {
841 smb_fname->base_name = tmp;
842 return -1;
845 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
846 saved_errno = errno;
848 TALLOC_FREE(smb_fname->base_name);
849 smb_fname->base_name = tmp;
851 if (ret == 0) {
852 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
854 errno = saved_errno;
855 return ret;
858 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
859 SMB_STRUCT_STAT *sbuf)
861 time_t timestamp;
862 int ret;
864 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
865 if (ret == -1) {
866 return ret;
868 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
869 fsp->fsp_name->base_name,
870 &timestamp, NULL)) {
871 return 0;
873 if (timestamp != 0) {
874 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
876 return 0;
879 static int shadow_copy2_open(vfs_handle_struct *handle,
880 struct smb_filename *smb_fname, files_struct *fsp,
881 int flags, mode_t mode)
883 time_t timestamp;
884 char *stripped, *tmp;
885 int ret, saved_errno;
887 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
888 smb_fname->base_name,
889 &timestamp, &stripped)) {
890 return -1;
892 if (timestamp == 0) {
893 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
896 tmp = smb_fname->base_name;
897 smb_fname->base_name = shadow_copy2_convert(
898 talloc_tos(), handle, stripped, timestamp);
899 TALLOC_FREE(stripped);
901 if (smb_fname->base_name == NULL) {
902 smb_fname->base_name = tmp;
903 return -1;
906 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
907 saved_errno = errno;
909 TALLOC_FREE(smb_fname->base_name);
910 smb_fname->base_name = tmp;
912 errno = saved_errno;
913 return ret;
916 static int shadow_copy2_unlink(vfs_handle_struct *handle,
917 const struct smb_filename *smb_fname)
919 time_t timestamp;
920 char *stripped;
921 int ret, saved_errno;
922 struct smb_filename *conv;
923 NTSTATUS status;
925 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
926 smb_fname->base_name,
927 &timestamp, &stripped)) {
928 return -1;
930 if (timestamp == 0) {
931 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
933 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
934 if (!NT_STATUS_IS_OK(status)) {
935 errno = ENOMEM;
936 return -1;
938 conv->base_name = shadow_copy2_convert(
939 conv, handle, stripped, timestamp);
940 TALLOC_FREE(stripped);
941 if (conv->base_name == NULL) {
942 return -1;
944 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
945 saved_errno = errno;
946 TALLOC_FREE(conv);
947 errno = saved_errno;
948 return ret;
951 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
952 mode_t mode)
954 time_t timestamp;
955 char *stripped;
956 int ret, saved_errno;
957 char *conv;
959 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
960 &timestamp, &stripped)) {
961 return -1;
963 if (timestamp == 0) {
964 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
966 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
967 TALLOC_FREE(stripped);
968 if (conv == NULL) {
969 return -1;
971 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
972 saved_errno = errno;
973 TALLOC_FREE(conv);
974 errno = saved_errno;
975 return ret;
978 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
979 uid_t uid, gid_t gid)
981 time_t timestamp;
982 char *stripped;
983 int ret, saved_errno;
984 char *conv;
986 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
987 &timestamp, &stripped)) {
988 return -1;
990 if (timestamp == 0) {
991 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
993 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
994 TALLOC_FREE(stripped);
995 if (conv == NULL) {
996 return -1;
998 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
999 saved_errno = errno;
1000 TALLOC_FREE(conv);
1001 errno = saved_errno;
1002 return ret;
1005 static int shadow_copy2_chdir(vfs_handle_struct *handle,
1006 const char *fname)
1008 time_t timestamp;
1009 char *stripped;
1010 int ret, saved_errno;
1011 char *conv;
1013 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1014 &timestamp, &stripped)) {
1015 return -1;
1017 if (timestamp == 0) {
1018 return SMB_VFS_NEXT_CHDIR(handle, fname);
1020 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1021 TALLOC_FREE(stripped);
1022 if (conv == NULL) {
1023 return -1;
1025 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1026 saved_errno = errno;
1027 TALLOC_FREE(conv);
1028 errno = saved_errno;
1029 return ret;
1032 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
1033 const struct smb_filename *smb_fname,
1034 struct smb_file_time *ft)
1036 time_t timestamp;
1037 char *stripped;
1038 int ret, saved_errno;
1039 struct smb_filename *conv;
1040 NTSTATUS status;
1042 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1043 smb_fname->base_name,
1044 &timestamp, &stripped)) {
1045 return -1;
1047 if (timestamp == 0) {
1048 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1050 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
1051 if (!NT_STATUS_IS_OK(status)) {
1052 errno = ENOMEM;
1053 return -1;
1055 conv->base_name = shadow_copy2_convert(
1056 conv, handle, stripped, timestamp);
1057 TALLOC_FREE(stripped);
1058 if (conv->base_name == NULL) {
1059 return -1;
1061 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1062 saved_errno = errno;
1063 TALLOC_FREE(conv);
1064 errno = saved_errno;
1065 return ret;
1068 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1069 const char *fname, char *buf, size_t bufsiz)
1071 time_t timestamp;
1072 char *stripped;
1073 int ret, saved_errno;
1074 char *conv;
1076 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1077 &timestamp, &stripped)) {
1078 return -1;
1080 if (timestamp == 0) {
1081 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1083 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1084 TALLOC_FREE(stripped);
1085 if (conv == NULL) {
1086 return -1;
1088 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1089 saved_errno = errno;
1090 TALLOC_FREE(conv);
1091 errno = saved_errno;
1092 return ret;
1095 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1096 const char *fname, mode_t mode, SMB_DEV_T dev)
1098 time_t timestamp;
1099 char *stripped;
1100 int ret, saved_errno;
1101 char *conv;
1103 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1104 &timestamp, &stripped)) {
1105 return -1;
1107 if (timestamp == 0) {
1108 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1110 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1111 TALLOC_FREE(stripped);
1112 if (conv == NULL) {
1113 return -1;
1115 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1116 saved_errno = errno;
1117 TALLOC_FREE(conv);
1118 errno = saved_errno;
1119 return ret;
1122 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1123 const char *fname)
1125 time_t timestamp;
1126 char *stripped = NULL;
1127 char *tmp = NULL;
1128 char *result = NULL;
1129 char *inserted = NULL;
1130 char *inserted_to, *inserted_end;
1131 int saved_errno;
1133 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1134 &timestamp, &stripped)) {
1135 goto done;
1137 if (timestamp == 0) {
1138 return SMB_VFS_NEXT_REALPATH(handle, fname);
1141 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1142 if (tmp == NULL) {
1143 goto done;
1146 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1147 if (result == NULL) {
1148 goto done;
1152 * Take away what we've inserted. This removes the @GMT-thingy
1153 * completely, but will give a path under the share root.
1155 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
1156 if (inserted == NULL) {
1157 goto done;
1159 inserted_to = strstr_m(result, inserted);
1160 if (inserted_to == NULL) {
1161 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
1162 goto done;
1164 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
1165 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
1167 done:
1168 saved_errno = errno;
1169 TALLOC_FREE(inserted);
1170 TALLOC_FREE(tmp);
1171 TALLOC_FREE(stripped);
1172 errno = saved_errno;
1173 return result;
1177 * Check whether a given directory contains a
1178 * snapshot directory as direct subdirectory.
1179 * If yes, return the path of the snapshot-subdir,
1180 * otherwise return NULL.
1182 static char *have_snapdir(struct vfs_handle_struct *handle,
1183 const char *path)
1185 struct smb_filename smb_fname;
1186 int ret;
1187 struct shadow_copy2_config *config;
1189 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1190 return NULL);
1192 ZERO_STRUCT(smb_fname);
1193 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1194 path, config->snapdir);
1195 if (smb_fname.base_name == NULL) {
1196 return NULL;
1199 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1200 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1201 return smb_fname.base_name;
1203 TALLOC_FREE(smb_fname.base_name);
1204 return NULL;
1208 * Find the snapshot directory (if any) for the given
1209 * filename (which is relative to the share).
1211 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1212 struct vfs_handle_struct *handle,
1213 struct smb_filename *smb_fname)
1215 char *path, *p;
1216 const char *snapdir;
1217 struct shadow_copy2_config *config;
1219 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1220 return NULL);
1223 * If the non-snapdisrseverywhere mode, we should not search!
1225 if (!config->snapdirseverywhere) {
1226 return config->snapshot_basepath;
1229 path = talloc_asprintf(mem_ctx, "%s/%s",
1230 handle->conn->connectpath,
1231 smb_fname->base_name);
1232 if (path == NULL) {
1233 return NULL;
1236 snapdir = have_snapdir(handle, path);
1237 if (snapdir != NULL) {
1238 TALLOC_FREE(path);
1239 return snapdir;
1242 while ((p = strrchr(path, '/')) && (p > path)) {
1244 p[0] = '\0';
1246 snapdir = have_snapdir(handle, path);
1247 if (snapdir != NULL) {
1248 TALLOC_FREE(path);
1249 return snapdir;
1252 TALLOC_FREE(path);
1253 return NULL;
1256 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1257 const char *name,
1258 char *gmt, size_t gmt_len)
1260 struct tm timestamp;
1261 time_t timestamp_t;
1262 unsigned long int timestamp_long;
1263 const char *fmt;
1264 struct shadow_copy2_config *config;
1266 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1267 return NULL);
1269 fmt = config->gmt_format;
1271 ZERO_STRUCT(timestamp);
1272 if (config->use_sscanf) {
1273 if (sscanf(name, fmt, &timestamp_long) != 1) {
1274 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1275 "no sscanf match %s: %s\n",
1276 fmt, name));
1277 return false;
1279 timestamp_t = timestamp_long;
1280 gmtime_r(&timestamp_t, &timestamp);
1281 } else {
1282 if (strptime(name, fmt, &timestamp) == NULL) {
1283 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1284 "no match %s: %s\n",
1285 fmt, name));
1286 return false;
1288 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1289 fmt, name));
1291 if (config->use_localtime) {
1292 timestamp.tm_isdst = -1;
1293 timestamp_t = mktime(&timestamp);
1294 gmtime_r(&timestamp_t, &timestamp);
1298 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1299 return true;
1302 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1304 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1307 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1309 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1313 sort the shadow copy data in ascending or descending order
1315 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1316 struct shadow_copy_data *shadow_copy2_data)
1318 int (*cmpfunc)(const void *, const void *);
1319 const char *sort;
1320 struct shadow_copy2_config *config;
1322 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1323 return);
1325 sort = config->sort_order;
1326 if (sort == NULL) {
1327 return;
1330 if (strcmp(sort, "asc") == 0) {
1331 cmpfunc = shadow_copy2_label_cmp_asc;
1332 } else if (strcmp(sort, "desc") == 0) {
1333 cmpfunc = shadow_copy2_label_cmp_desc;
1334 } else {
1335 return;
1338 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1339 shadow_copy2_data->labels)
1341 TYPESAFE_QSORT(shadow_copy2_data->labels,
1342 shadow_copy2_data->num_volumes,
1343 cmpfunc);
1347 static int shadow_copy2_get_shadow_copy_data(
1348 vfs_handle_struct *handle, files_struct *fsp,
1349 struct shadow_copy_data *shadow_copy2_data,
1350 bool labels)
1352 DIR *p;
1353 const char *snapdir;
1354 struct dirent *d;
1355 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1357 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1358 if (snapdir == NULL) {
1359 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1360 handle->conn->connectpath));
1361 errno = EINVAL;
1362 talloc_free(tmp_ctx);
1363 return -1;
1366 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1368 if (!p) {
1369 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1370 " - %s\n", snapdir, strerror(errno)));
1371 talloc_free(tmp_ctx);
1372 errno = ENOSYS;
1373 return -1;
1376 shadow_copy2_data->num_volumes = 0;
1377 shadow_copy2_data->labels = NULL;
1379 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1380 char snapshot[GMT_NAME_LEN+1];
1381 SHADOW_COPY_LABEL *tlabels;
1384 * ignore names not of the right form in the snapshot
1385 * directory
1387 if (!shadow_copy2_snapshot_to_gmt(
1388 handle, d->d_name,
1389 snapshot, sizeof(snapshot))) {
1391 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1392 "ignoring %s\n", d->d_name));
1393 continue;
1395 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1396 d->d_name, snapshot));
1398 if (!labels) {
1399 /* the caller doesn't want the labels */
1400 shadow_copy2_data->num_volumes++;
1401 continue;
1404 tlabels = talloc_realloc(shadow_copy2_data,
1405 shadow_copy2_data->labels,
1406 SHADOW_COPY_LABEL,
1407 shadow_copy2_data->num_volumes+1);
1408 if (tlabels == NULL) {
1409 DEBUG(0,("shadow_copy2: out of memory\n"));
1410 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1411 talloc_free(tmp_ctx);
1412 return -1;
1415 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1416 sizeof(*tlabels));
1418 shadow_copy2_data->num_volumes++;
1419 shadow_copy2_data->labels = tlabels;
1422 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1424 shadow_copy2_sort_data(handle, shadow_copy2_data);
1426 talloc_free(tmp_ctx);
1427 return 0;
1430 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1431 struct files_struct *fsp,
1432 uint32 security_info,
1433 TALLOC_CTX *mem_ctx,
1434 struct security_descriptor **ppdesc)
1436 time_t timestamp;
1437 char *stripped;
1438 NTSTATUS status;
1439 char *conv;
1441 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1442 fsp->fsp_name->base_name,
1443 &timestamp, &stripped)) {
1444 return map_nt_error_from_unix(errno);
1446 if (timestamp == 0) {
1447 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1448 mem_ctx,
1449 ppdesc);
1451 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1452 TALLOC_FREE(stripped);
1453 if (conv == NULL) {
1454 return map_nt_error_from_unix(errno);
1456 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1457 mem_ctx, ppdesc);
1458 TALLOC_FREE(conv);
1459 return status;
1462 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1463 const char *fname,
1464 uint32 security_info,
1465 TALLOC_CTX *mem_ctx,
1466 struct security_descriptor **ppdesc)
1468 time_t timestamp;
1469 char *stripped;
1470 NTSTATUS status;
1471 char *conv;
1473 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1474 &timestamp, &stripped)) {
1475 return map_nt_error_from_unix(errno);
1477 if (timestamp == 0) {
1478 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1479 mem_ctx, ppdesc);
1481 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1482 TALLOC_FREE(stripped);
1483 if (conv == NULL) {
1484 return map_nt_error_from_unix(errno);
1486 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1487 mem_ctx, ppdesc);
1488 TALLOC_FREE(conv);
1489 return status;
1492 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1493 const char *fname, mode_t mode)
1495 time_t timestamp;
1496 char *stripped;
1497 int ret, saved_errno;
1498 char *conv;
1500 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1501 &timestamp, &stripped)) {
1502 return -1;
1504 if (timestamp == 0) {
1505 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1507 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1508 TALLOC_FREE(stripped);
1509 if (conv == NULL) {
1510 return -1;
1512 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1513 saved_errno = errno;
1514 TALLOC_FREE(conv);
1515 errno = saved_errno;
1516 return ret;
1519 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1521 time_t timestamp;
1522 char *stripped;
1523 int ret, saved_errno;
1524 char *conv;
1526 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1527 &timestamp, &stripped)) {
1528 return -1;
1530 if (timestamp == 0) {
1531 return SMB_VFS_NEXT_RMDIR(handle, fname);
1533 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1534 TALLOC_FREE(stripped);
1535 if (conv == NULL) {
1536 return -1;
1538 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1539 saved_errno = errno;
1540 TALLOC_FREE(conv);
1541 errno = saved_errno;
1542 return ret;
1545 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1546 unsigned int flags)
1548 time_t timestamp;
1549 char *stripped;
1550 int ret, saved_errno;
1551 char *conv;
1553 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1554 &timestamp, &stripped)) {
1555 return -1;
1557 if (timestamp == 0) {
1558 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1560 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1561 TALLOC_FREE(stripped);
1562 if (conv == NULL) {
1563 return -1;
1565 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1566 saved_errno = errno;
1567 TALLOC_FREE(conv);
1568 errno = saved_errno;
1569 return ret;
1572 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1573 const char *fname, const char *aname,
1574 void *value, size_t size)
1576 time_t timestamp;
1577 char *stripped;
1578 ssize_t ret;
1579 int saved_errno;
1580 char *conv;
1582 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1583 &timestamp, &stripped)) {
1584 return -1;
1586 if (timestamp == 0) {
1587 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1588 size);
1590 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1591 TALLOC_FREE(stripped);
1592 if (conv == NULL) {
1593 return -1;
1595 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1596 saved_errno = errno;
1597 TALLOC_FREE(conv);
1598 errno = saved_errno;
1599 return ret;
1602 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1603 const char *fname,
1604 char *list, size_t size)
1606 time_t timestamp;
1607 char *stripped;
1608 ssize_t ret;
1609 int saved_errno;
1610 char *conv;
1612 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1613 &timestamp, &stripped)) {
1614 return -1;
1616 if (timestamp == 0) {
1617 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1619 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1620 TALLOC_FREE(stripped);
1621 if (conv == NULL) {
1622 return -1;
1624 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1625 saved_errno = errno;
1626 TALLOC_FREE(conv);
1627 errno = saved_errno;
1628 return ret;
1631 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1632 const char *fname, const char *aname)
1634 time_t timestamp;
1635 char *stripped;
1636 int ret, saved_errno;
1637 char *conv;
1639 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1640 &timestamp, &stripped)) {
1641 return -1;
1643 if (timestamp == 0) {
1644 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1646 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1647 TALLOC_FREE(stripped);
1648 if (conv == NULL) {
1649 return -1;
1651 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1652 saved_errno = errno;
1653 TALLOC_FREE(conv);
1654 errno = saved_errno;
1655 return ret;
1658 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1659 const char *fname,
1660 const char *aname, const void *value,
1661 size_t size, int flags)
1663 time_t timestamp;
1664 char *stripped;
1665 ssize_t ret;
1666 int saved_errno;
1667 char *conv;
1669 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1670 &timestamp, &stripped)) {
1671 return -1;
1673 if (timestamp == 0) {
1674 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1675 flags);
1677 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1678 TALLOC_FREE(stripped);
1679 if (conv == NULL) {
1680 return -1;
1682 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1683 saved_errno = errno;
1684 TALLOC_FREE(conv);
1685 errno = saved_errno;
1686 return ret;
1689 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1690 const char *fname, mode_t mode)
1692 time_t timestamp;
1693 char *stripped;
1694 ssize_t ret;
1695 int saved_errno;
1696 char *conv;
1698 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1699 &timestamp, &stripped)) {
1700 return -1;
1702 if (timestamp == 0) {
1703 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1705 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1706 TALLOC_FREE(stripped);
1707 if (conv == NULL) {
1708 return -1;
1710 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1711 saved_errno = errno;
1712 TALLOC_FREE(conv);
1713 errno = saved_errno;
1714 return ret;
1717 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1718 const char *path,
1719 const char *name,
1720 TALLOC_CTX *mem_ctx,
1721 char **found_name)
1723 time_t timestamp;
1724 char *stripped;
1725 ssize_t ret;
1726 int saved_errno;
1727 char *conv;
1729 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1730 &timestamp, &stripped)) {
1731 return -1;
1733 if (timestamp == 0) {
1734 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1735 mem_ctx, found_name);
1737 if (stripped[0] == '\0') {
1738 *found_name = talloc_strdup(mem_ctx, name);
1739 if (*found_name == NULL) {
1740 errno = ENOMEM;
1741 return -1;
1743 return 0;
1745 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1746 TALLOC_FREE(stripped);
1747 if (conv == NULL) {
1748 return -1;
1750 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1751 mem_ctx, found_name);
1752 saved_errno = errno;
1753 TALLOC_FREE(conv);
1754 errno = saved_errno;
1755 return ret;
1758 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1759 const char *path, bool small_query,
1760 uint64_t *bsize, uint64_t *dfree,
1761 uint64_t *dsize)
1763 time_t timestamp;
1764 char *stripped;
1765 ssize_t ret;
1766 int saved_errno;
1767 char *conv;
1769 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1770 &timestamp, &stripped)) {
1771 return -1;
1773 if (timestamp == 0) {
1774 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1775 bsize, dfree, dsize);
1778 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1779 TALLOC_FREE(stripped);
1780 if (conv == NULL) {
1781 return -1;
1784 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1785 dsize);
1787 saved_errno = errno;
1788 TALLOC_FREE(conv);
1789 errno = saved_errno;
1791 return ret;
1794 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1795 const char *service, const char *user)
1797 struct shadow_copy2_config *config;
1798 int ret;
1799 const char *snapdir;
1800 const char *gmt_format;
1801 const char *sort_order;
1802 const char *basedir;
1803 const char *mount_point;
1805 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1806 (unsigned)handle->conn->cnum,
1807 handle->conn->connectpath));
1809 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1810 if (ret < 0) {
1811 return ret;
1814 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1815 if (config == NULL) {
1816 DEBUG(0, ("talloc_zero() failed\n"));
1817 errno = ENOMEM;
1818 return -1;
1821 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1822 "shadow", "format",
1823 GMT_FORMAT);
1824 config->gmt_format = talloc_strdup(config, gmt_format);
1825 if (config->gmt_format == NULL) {
1826 DEBUG(0, ("talloc_strdup() failed\n"));
1827 errno = ENOMEM;
1828 return -1;
1831 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1832 "shadow", "sscanf", false);
1834 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1835 "shadow", "localtime",
1836 false);
1838 snapdir = lp_parm_const_string(SNUM(handle->conn),
1839 "shadow", "snapdir",
1840 ".snapshots");
1841 config->snapdir = talloc_strdup(config, snapdir);
1842 if (config->snapdir == NULL) {
1843 DEBUG(0, ("talloc_strdup() failed\n"));
1844 errno = ENOMEM;
1845 return -1;
1848 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1849 "shadow",
1850 "snapdirseverywhere",
1851 false);
1853 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1854 "shadow", "crossmountpoints",
1855 false);
1857 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1858 "shadow", "fixinodes",
1859 false);
1861 sort_order = lp_parm_const_string(SNUM(handle->conn),
1862 "shadow", "sort", "desc");
1863 config->sort_order = talloc_strdup(config, sort_order);
1864 if (config->sort_order == NULL) {
1865 DEBUG(0, ("talloc_strdup() failed\n"));
1866 errno = ENOMEM;
1867 return -1;
1870 mount_point = lp_parm_const_string(SNUM(handle->conn),
1871 "shadow", "mountpoint", NULL);
1872 if (mount_point != NULL) {
1873 if (mount_point[0] != '/') {
1874 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1875 "relative ('%s'), but it has to be an "
1876 "absolute path. Ignoring provided value.\n",
1877 mount_point));
1878 mount_point = NULL;
1879 } else {
1880 char *p;
1881 p = strstr(handle->conn->connectpath, mount_point);
1882 if (p != handle->conn->connectpath) {
1883 DEBUG(1, ("Warning: mount_point (%s) is not a "
1884 "subdirectory of the share root "
1885 "(%s). Ignoring provided value.\n",
1886 mount_point,
1887 handle->conn->connectpath));
1888 mount_point = NULL;
1893 if (mount_point != NULL) {
1894 config->mount_point = talloc_strdup(config, mount_point);
1895 if (config->mount_point == NULL) {
1896 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1897 return -1;
1899 } else {
1900 config->mount_point = shadow_copy2_find_mount_point(config,
1901 handle);
1902 if (config->mount_point == NULL) {
1903 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1904 " failed: %s\n", strerror(errno)));
1905 return -1;
1909 basedir = lp_parm_const_string(SNUM(handle->conn),
1910 "shadow", "basedir", NULL);
1912 if (basedir != NULL) {
1913 if (basedir[0] != '/') {
1914 DEBUG(1, (__location__ " Warning: 'basedir' is "
1915 "relative ('%s'), but it has to be an "
1916 "absolute path. Disabling basedir.\n",
1917 basedir));
1918 } else {
1919 char *p;
1920 p = strstr(basedir, config->mount_point);
1921 if (p != basedir) {
1922 DEBUG(1, ("Warning: basedir (%s) is not a "
1923 "subdirectory of the share root's "
1924 "mount point (%s). "
1925 "Disabling basedir\n",
1926 basedir, config->mount_point));
1927 } else {
1928 config->basedir = talloc_strdup(config,
1929 basedir);
1930 if (config->basedir == NULL) {
1931 DEBUG(0, ("talloc_strdup() failed\n"));
1932 errno = ENOMEM;
1933 return -1;
1939 if (config->snapdirseverywhere && config->basedir != NULL) {
1940 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1941 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1942 TALLOC_FREE(config->basedir);
1945 if (config->crossmountpoints && config->basedir != NULL) {
1946 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1947 "with 'crossmountpoints'. Disabling basedir.\n"));
1948 TALLOC_FREE(config->basedir);
1951 if (config->basedir == NULL) {
1952 config->basedir = config->mount_point;
1955 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1956 config->rel_connectpath = talloc_strdup(config,
1957 handle->conn->connectpath + strlen(config->basedir));
1958 if (config->rel_connectpath == NULL) {
1959 DEBUG(0, ("talloc_strdup() failed\n"));
1960 errno = ENOMEM;
1961 return -1;
1965 if (config->snapdir[0] == '/') {
1966 config->snapdir_absolute = true;
1968 if (config->snapdirseverywhere == true) {
1969 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1970 "is incompatible with 'snapdirseverywhere', "
1971 "setting 'snapdirseverywhere' to false.\n"));
1972 config->snapdirseverywhere = false;
1975 if (config->crossmountpoints == true) {
1976 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1977 "is not supported with an absolute snapdir. "
1978 "Disabling it.\n"));
1979 config->crossmountpoints = false;
1982 config->snapshot_basepath = config->snapdir;
1983 } else {
1984 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1985 config->mount_point, config->snapdir);
1986 if (config->snapshot_basepath == NULL) {
1987 DEBUG(0, ("talloc_asprintf() failed\n"));
1988 errno = ENOMEM;
1989 return -1;
1993 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1994 " share root: '%s'\n"
1995 " basedir: '%s'\n"
1996 " mountpoint: '%s'\n"
1997 " rel share root: '%s'\n"
1998 " snapdir: '%s'\n"
1999 " snapshot base path: '%s'\n"
2000 " format: '%s'\n"
2001 " use sscanf: %s\n"
2002 " snapdirs everywhere: %s\n"
2003 " cross mountpoints: %s\n"
2004 " fix inodes: %s\n"
2005 " sort order: %s\n"
2007 handle->conn->connectpath,
2008 config->basedir,
2009 config->mount_point,
2010 config->rel_connectpath,
2011 config->snapdir,
2012 config->snapshot_basepath,
2013 config->gmt_format,
2014 config->use_sscanf ? "yes" : "no",
2015 config->snapdirseverywhere ? "yes" : "no",
2016 config->crossmountpoints ? "yes" : "no",
2017 config->fixinodes ? "yes" : "no",
2018 config->sort_order
2022 SMB_VFS_HANDLE_SET_DATA(handle, config,
2023 NULL, struct shadow_copy2_config,
2024 return -1);
2026 return 0;
2029 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
2030 .connect_fn = shadow_copy2_connect,
2031 .opendir_fn = shadow_copy2_opendir,
2032 .disk_free_fn = shadow_copy2_disk_free,
2033 .rename_fn = shadow_copy2_rename,
2034 .link_fn = shadow_copy2_link,
2035 .symlink_fn = shadow_copy2_symlink,
2036 .stat_fn = shadow_copy2_stat,
2037 .lstat_fn = shadow_copy2_lstat,
2038 .fstat_fn = shadow_copy2_fstat,
2039 .open_fn = shadow_copy2_open,
2040 .unlink_fn = shadow_copy2_unlink,
2041 .chmod_fn = shadow_copy2_chmod,
2042 .chown_fn = shadow_copy2_chown,
2043 .chdir_fn = shadow_copy2_chdir,
2044 .ntimes_fn = shadow_copy2_ntimes,
2045 .readlink_fn = shadow_copy2_readlink,
2046 .mknod_fn = shadow_copy2_mknod,
2047 .realpath_fn = shadow_copy2_realpath,
2048 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2049 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2050 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2051 .mkdir_fn = shadow_copy2_mkdir,
2052 .rmdir_fn = shadow_copy2_rmdir,
2053 .getxattr_fn = shadow_copy2_getxattr,
2054 .listxattr_fn = shadow_copy2_listxattr,
2055 .removexattr_fn = shadow_copy2_removexattr,
2056 .setxattr_fn = shadow_copy2_setxattr,
2057 .chmod_acl_fn = shadow_copy2_chmod_acl,
2058 .chflags_fn = shadow_copy2_chflags,
2059 .get_real_filename_fn = shadow_copy2_get_real_filename,
2062 NTSTATUS vfs_shadow_copy2_init(void);
2063 NTSTATUS vfs_shadow_copy2_init(void)
2065 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2066 "shadow_copy2", &vfs_shadow_copy2_fns);