shadow_copy2: make shadow_copy2_find_snapdir() return const char *
[Samba/wip.git] / source3 / modules / vfs_shadow_copy2.c
blobe227b1074e6d609f58f137471952be391229b0a3
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 string to insert into a path
162 * as a path component for creating the local path to the
163 * snapshot at the given timestamp of the input path.
165 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
166 struct vfs_handle_struct *handle,
167 time_t snapshot)
169 struct tm snap_tm;
170 fstring snaptime_string;
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 NULL);
177 if (config->use_sscanf) {
178 snaptime_len = snprintf(snaptime_string,
179 sizeof(snaptime_string),
180 config->gmt_format,
181 (unsigned long)snapshot);
182 if (snaptime_len <= 0) {
183 DEBUG(10, ("snprintf failed\n"));
184 return NULL;
186 } else {
187 if (config->use_localtime) {
188 if (localtime_r(&snapshot, &snap_tm) == 0) {
189 DEBUG(10, ("gmtime_r failed\n"));
190 return NULL;
192 } else {
193 if (gmtime_r(&snapshot, &snap_tm) == 0) {
194 DEBUG(10, ("gmtime_r failed\n"));
195 return NULL;
198 snaptime_len = strftime(snaptime_string,
199 sizeof(snaptime_string),
200 config->gmt_format,
201 &snap_tm);
202 if (snaptime_len == 0) {
203 DEBUG(10, ("strftime failed\n"));
204 return NULL;
207 return talloc_asprintf(mem_ctx, "/%s/%s",
208 config->snapdir, snaptime_string);
212 * Strip a snapshot component from an filename as
213 * handed in via the smb layer.
214 * Returns the parsed timestamp and the stripped filename.
216 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
217 struct vfs_handle_struct *handle,
218 const char *name,
219 time_t *ptimestamp,
220 char **pstripped)
222 struct tm tm;
223 time_t timestamp;
224 const char *p;
225 char *q;
226 char *stripped;
227 size_t rest_len, dst_len;
228 struct shadow_copy2_config *config;
230 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
231 return false);
233 p = strstr_m(name, "@GMT-");
234 if (p == NULL) {
235 goto no_snapshot;
237 if ((p > name) && (p[-1] != '/')) {
238 goto no_snapshot;
240 q = strptime(p, GMT_FORMAT, &tm);
241 if (q == NULL) {
242 goto no_snapshot;
244 tm.tm_isdst = -1;
245 timestamp = timegm(&tm);
246 if (timestamp == (time_t)-1) {
247 goto no_snapshot;
249 if ((p == name) && (q[0] == '\0')) {
250 if (pstripped != NULL) {
251 stripped = talloc_strdup(mem_ctx, "");
252 if (stripped == NULL) {
253 return false;
255 *pstripped = stripped;
257 *ptimestamp = timestamp;
258 return true;
260 if (q[0] != '/') {
261 goto no_snapshot;
263 q += 1;
265 rest_len = strlen(q);
266 dst_len = (p-name) + rest_len;
268 if (config->snapdirseverywhere) {
269 char *insert;
270 bool have_insert;
271 insert = shadow_copy2_insert_string(talloc_tos(), handle,
272 timestamp);
273 if (insert == NULL) {
274 errno = ENOMEM;
275 return false;
278 have_insert = (strstr(name, insert+1) != NULL);
279 TALLOC_FREE(insert);
280 if (have_insert) {
281 goto no_snapshot;
285 if (pstripped != NULL) {
286 stripped = talloc_array(mem_ctx, char, dst_len+1);
287 if (stripped == NULL) {
288 errno = ENOMEM;
289 return false;
291 if (p > name) {
292 memcpy(stripped, name, p-name);
294 if (rest_len > 0) {
295 memcpy(stripped + (p-name), q, rest_len);
297 stripped[dst_len] = '\0';
298 *pstripped = stripped;
300 *ptimestamp = timestamp;
301 return true;
302 no_snapshot:
303 *ptimestamp = 0;
304 return true;
307 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
308 vfs_handle_struct *handle)
310 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
311 dev_t dev;
312 struct stat st;
313 char *p;
315 if (stat(path, &st) != 0) {
316 talloc_free(path);
317 return NULL;
320 dev = st.st_dev;
322 while ((p = strrchr(path, '/')) && p > path) {
323 *p = 0;
324 if (stat(path, &st) != 0) {
325 talloc_free(path);
326 return NULL;
328 if (st.st_dev != dev) {
329 *p = '/';
330 break;
334 return path;
338 * Convert from a name as handed in via the SMB layer
339 * and a timestamp into the local path of the snapshot
340 * of the provided file at the provided time.
342 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
343 struct vfs_handle_struct *handle,
344 const char *name, time_t timestamp)
346 struct smb_filename converted_fname;
347 char *result = NULL;
348 size_t *slashes = NULL;
349 unsigned num_slashes;
350 char *path = NULL;
351 size_t pathlen;
352 char *insert = NULL;
353 char *converted = NULL;
354 size_t insertlen;
355 int i, saved_errno;
356 size_t min_offset;
357 struct shadow_copy2_config *config;
359 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
360 return NULL);
362 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
363 name);
364 if (path == NULL) {
365 errno = ENOMEM;
366 goto fail;
368 pathlen = talloc_get_size(path)-1;
370 DEBUG(10, ("converting %s\n", path));
372 if (!shadow_copy2_find_slashes(talloc_tos(), path,
373 &slashes, &num_slashes)) {
374 goto fail;
376 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
377 if (insert == NULL) {
378 goto fail;
380 insertlen = talloc_get_size(insert)-1;
381 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
382 if (converted == NULL) {
383 goto fail;
386 if (path[pathlen-1] != '/') {
388 * Append a fake slash to find the snapshot root
390 size_t *tmp;
391 tmp = talloc_realloc(talloc_tos(), slashes,
392 size_t, num_slashes+1);
393 if (tmp == NULL) {
394 goto fail;
396 slashes = tmp;
397 slashes[num_slashes] = pathlen;
398 num_slashes += 1;
401 min_offset = 0;
403 if (!config->crossmountpoints) {
404 char *mount_point;
406 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
407 handle);
408 if (mount_point == NULL) {
409 goto fail;
411 min_offset = strlen(mount_point);
412 TALLOC_FREE(mount_point);
415 memcpy(converted, path, pathlen+1);
416 converted[pathlen+insertlen] = '\0';
418 ZERO_STRUCT(converted_fname);
419 converted_fname.base_name = converted;
421 for (i = num_slashes-1; i>=0; i--) {
422 int ret;
423 size_t offset;
425 offset = slashes[i];
427 if (offset < min_offset) {
428 errno = ENOENT;
429 goto fail;
432 memcpy(converted+offset, insert, insertlen);
434 offset += insertlen;
435 memcpy(converted+offset, path + slashes[i],
436 pathlen - slashes[i]);
438 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
440 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
441 ret, ret == 0 ? "ok" : strerror(errno)));
442 if (ret == 0) {
443 /* success */
444 break;
446 if (errno == ENOTDIR) {
448 * This is a valid condition: We appended the
449 * .snaphots/@GMT.. to a file name. Just try
450 * with the upper levels.
452 continue;
454 if (errno != ENOENT) {
455 /* Other problem than "not found" */
456 goto fail;
460 if (i >= 0) {
462 * Found something
464 DEBUG(10, ("Found %s\n", converted));
465 result = converted;
466 converted = NULL;
467 } else {
468 errno = ENOENT;
470 fail:
471 saved_errno = errno;
472 TALLOC_FREE(converted);
473 TALLOC_FREE(insert);
474 TALLOC_FREE(slashes);
475 TALLOC_FREE(path);
476 errno = saved_errno;
477 return result;
481 modify a sbuf return to ensure that inodes in the shadow directory
482 are different from those in the main directory
484 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
485 SMB_STRUCT_STAT *sbuf)
487 struct shadow_copy2_config *config;
489 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
490 return);
492 if (config->fixinodes) {
493 /* some snapshot systems, like GPFS, return the name
494 device:inode for the snapshot files as the current
495 files. That breaks the 'restore' button in the shadow copy
496 GUI, as the client gets a sharing violation.
498 This is a crude way of allowing both files to be
499 open at once. It has a slight chance of inode
500 number collision, but I can't see a better approach
501 without significant VFS changes
503 uint32_t shash;
505 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
506 if (shash == 0) {
507 shash = 1;
509 sbuf->st_ex_ino ^= shash;
513 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
514 const char *fname,
515 const char *mask,
516 uint32 attr)
518 time_t timestamp;
519 char *stripped;
520 DIR *ret;
521 int saved_errno;
522 char *conv;
524 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
525 &timestamp, &stripped)) {
526 return NULL;
528 if (timestamp == 0) {
529 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
531 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
532 TALLOC_FREE(stripped);
533 if (conv == NULL) {
534 return NULL;
536 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
537 saved_errno = errno;
538 TALLOC_FREE(conv);
539 errno = saved_errno;
540 return ret;
543 static int shadow_copy2_rename(vfs_handle_struct *handle,
544 const struct smb_filename *smb_fname_src,
545 const struct smb_filename *smb_fname_dst)
547 time_t timestamp_src, timestamp_dst;
549 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
550 smb_fname_src->base_name,
551 &timestamp_src, NULL)) {
552 return -1;
554 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
555 smb_fname_dst->base_name,
556 &timestamp_dst, NULL)) {
557 return -1;
559 if (timestamp_src != 0) {
560 errno = EXDEV;
561 return -1;
563 if (timestamp_dst != 0) {
564 errno = EROFS;
565 return -1;
567 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
570 static int shadow_copy2_symlink(vfs_handle_struct *handle,
571 const char *oldname, const char *newname)
573 time_t timestamp_old, timestamp_new;
575 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
576 &timestamp_old, NULL)) {
577 return -1;
579 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
580 &timestamp_new, NULL)) {
581 return -1;
583 if ((timestamp_old != 0) || (timestamp_new != 0)) {
584 errno = EROFS;
585 return -1;
587 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
590 static int shadow_copy2_link(vfs_handle_struct *handle,
591 const char *oldname, const char *newname)
593 time_t timestamp_old, timestamp_new;
595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
596 &timestamp_old, NULL)) {
597 return -1;
599 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
600 &timestamp_new, NULL)) {
601 return -1;
603 if ((timestamp_old != 0) || (timestamp_new != 0)) {
604 errno = EROFS;
605 return -1;
607 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
610 static int shadow_copy2_stat(vfs_handle_struct *handle,
611 struct smb_filename *smb_fname)
613 time_t timestamp;
614 char *stripped, *tmp;
615 int ret, saved_errno;
617 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
618 smb_fname->base_name,
619 &timestamp, &stripped)) {
620 return -1;
622 if (timestamp == 0) {
623 return SMB_VFS_NEXT_STAT(handle, smb_fname);
626 tmp = smb_fname->base_name;
627 smb_fname->base_name = shadow_copy2_convert(
628 talloc_tos(), handle, stripped, timestamp);
629 TALLOC_FREE(stripped);
631 if (smb_fname->base_name == NULL) {
632 smb_fname->base_name = tmp;
633 return -1;
636 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
637 saved_errno = errno;
639 TALLOC_FREE(smb_fname->base_name);
640 smb_fname->base_name = tmp;
642 if (ret == 0) {
643 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
645 errno = saved_errno;
646 return ret;
649 static int shadow_copy2_lstat(vfs_handle_struct *handle,
650 struct smb_filename *smb_fname)
652 time_t timestamp;
653 char *stripped, *tmp;
654 int ret, saved_errno;
656 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
657 smb_fname->base_name,
658 &timestamp, &stripped)) {
659 return -1;
661 if (timestamp == 0) {
662 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
665 tmp = smb_fname->base_name;
666 smb_fname->base_name = shadow_copy2_convert(
667 talloc_tos(), handle, stripped, timestamp);
668 TALLOC_FREE(stripped);
670 if (smb_fname->base_name == NULL) {
671 smb_fname->base_name = tmp;
672 return -1;
675 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
676 saved_errno = errno;
678 TALLOC_FREE(smb_fname->base_name);
679 smb_fname->base_name = tmp;
681 if (ret == 0) {
682 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
684 errno = saved_errno;
685 return ret;
688 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
689 SMB_STRUCT_STAT *sbuf)
691 time_t timestamp;
692 int ret;
694 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
695 if (ret == -1) {
696 return ret;
698 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
699 fsp->fsp_name->base_name,
700 &timestamp, NULL)) {
701 return 0;
703 if (timestamp != 0) {
704 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
706 return 0;
709 static int shadow_copy2_open(vfs_handle_struct *handle,
710 struct smb_filename *smb_fname, files_struct *fsp,
711 int flags, mode_t mode)
713 time_t timestamp;
714 char *stripped, *tmp;
715 int ret, saved_errno;
717 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
718 smb_fname->base_name,
719 &timestamp, &stripped)) {
720 return -1;
722 if (timestamp == 0) {
723 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
726 tmp = smb_fname->base_name;
727 smb_fname->base_name = shadow_copy2_convert(
728 talloc_tos(), handle, stripped, timestamp);
729 TALLOC_FREE(stripped);
731 if (smb_fname->base_name == NULL) {
732 smb_fname->base_name = tmp;
733 return -1;
736 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
737 saved_errno = errno;
739 TALLOC_FREE(smb_fname->base_name);
740 smb_fname->base_name = tmp;
742 errno = saved_errno;
743 return ret;
746 static int shadow_copy2_unlink(vfs_handle_struct *handle,
747 const struct smb_filename *smb_fname)
749 time_t timestamp;
750 char *stripped;
751 int ret, saved_errno;
752 struct smb_filename *conv;
754 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
755 smb_fname->base_name,
756 &timestamp, &stripped)) {
757 return -1;
759 if (timestamp == 0) {
760 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
762 conv = cp_smb_filename(talloc_tos(), smb_fname);
763 if (conv == NULL) {
764 errno = ENOMEM;
765 return -1;
767 conv->base_name = shadow_copy2_convert(
768 conv, handle, stripped, timestamp);
769 TALLOC_FREE(stripped);
770 if (conv->base_name == NULL) {
771 return -1;
773 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
774 saved_errno = errno;
775 TALLOC_FREE(conv);
776 errno = saved_errno;
777 return ret;
780 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
781 mode_t mode)
783 time_t timestamp;
784 char *stripped;
785 int ret, saved_errno;
786 char *conv;
788 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
789 &timestamp, &stripped)) {
790 return -1;
792 if (timestamp == 0) {
793 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
795 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
796 TALLOC_FREE(stripped);
797 if (conv == NULL) {
798 return -1;
800 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
801 saved_errno = errno;
802 TALLOC_FREE(conv);
803 errno = saved_errno;
804 return ret;
807 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
808 uid_t uid, gid_t gid)
810 time_t timestamp;
811 char *stripped;
812 int ret, saved_errno;
813 char *conv;
815 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
816 &timestamp, &stripped)) {
817 return -1;
819 if (timestamp == 0) {
820 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
822 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
823 TALLOC_FREE(stripped);
824 if (conv == NULL) {
825 return -1;
827 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
828 saved_errno = errno;
829 TALLOC_FREE(conv);
830 errno = saved_errno;
831 return ret;
834 static int shadow_copy2_chdir(vfs_handle_struct *handle,
835 const char *fname)
837 time_t timestamp;
838 char *stripped;
839 int ret, saved_errno;
840 char *conv;
842 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
843 &timestamp, &stripped)) {
844 return -1;
846 if (timestamp == 0) {
847 return SMB_VFS_NEXT_CHDIR(handle, fname);
849 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
850 TALLOC_FREE(stripped);
851 if (conv == NULL) {
852 return -1;
854 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
855 saved_errno = errno;
856 TALLOC_FREE(conv);
857 errno = saved_errno;
858 return ret;
861 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
862 const struct smb_filename *smb_fname,
863 struct smb_file_time *ft)
865 time_t timestamp;
866 char *stripped;
867 int ret, saved_errno;
868 struct smb_filename *conv;
870 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
871 smb_fname->base_name,
872 &timestamp, &stripped)) {
873 return -1;
875 if (timestamp == 0) {
876 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
878 conv = cp_smb_filename(talloc_tos(), smb_fname);
879 if (conv == NULL) {
880 errno = ENOMEM;
881 return -1;
883 conv->base_name = shadow_copy2_convert(
884 conv, handle, stripped, timestamp);
885 TALLOC_FREE(stripped);
886 if (conv->base_name == NULL) {
887 return -1;
889 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
890 saved_errno = errno;
891 TALLOC_FREE(conv);
892 errno = saved_errno;
893 return ret;
896 static int shadow_copy2_readlink(vfs_handle_struct *handle,
897 const char *fname, char *buf, size_t bufsiz)
899 time_t timestamp;
900 char *stripped;
901 int ret, saved_errno;
902 char *conv;
904 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
905 &timestamp, &stripped)) {
906 return -1;
908 if (timestamp == 0) {
909 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
911 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
912 TALLOC_FREE(stripped);
913 if (conv == NULL) {
914 return -1;
916 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
917 saved_errno = errno;
918 TALLOC_FREE(conv);
919 errno = saved_errno;
920 return ret;
923 static int shadow_copy2_mknod(vfs_handle_struct *handle,
924 const char *fname, mode_t mode, SMB_DEV_T dev)
926 time_t timestamp;
927 char *stripped;
928 int ret, saved_errno;
929 char *conv;
931 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
932 &timestamp, &stripped)) {
933 return -1;
935 if (timestamp == 0) {
936 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
938 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
939 TALLOC_FREE(stripped);
940 if (conv == NULL) {
941 return -1;
943 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
944 saved_errno = errno;
945 TALLOC_FREE(conv);
946 errno = saved_errno;
947 return ret;
950 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
951 const char *fname)
953 time_t timestamp;
954 char *stripped = NULL;
955 char *tmp = NULL;
956 char *result = NULL;
957 char *inserted = NULL;
958 char *inserted_to, *inserted_end;
959 int saved_errno;
961 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
962 &timestamp, &stripped)) {
963 goto done;
965 if (timestamp == 0) {
966 return SMB_VFS_NEXT_REALPATH(handle, fname);
969 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
970 if (tmp == NULL) {
971 goto done;
974 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
975 if (result == NULL) {
976 goto done;
980 * Take away what we've inserted. This removes the @GMT-thingy
981 * completely, but will give a path under the share root.
983 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
984 if (inserted == NULL) {
985 goto done;
987 inserted_to = strstr_m(result, inserted);
988 if (inserted_to == NULL) {
989 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
990 goto done;
992 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
993 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
995 done:
996 saved_errno = errno;
997 TALLOC_FREE(inserted);
998 TALLOC_FREE(tmp);
999 TALLOC_FREE(stripped);
1000 errno = saved_errno;
1001 return result;
1005 * Check whether a given directory contains a
1006 * snapshot directory as direct subdirectory.
1007 * If yes, return the path of the snapshot-subdir,
1008 * otherwise return NULL.
1010 static char *have_snapdir(struct vfs_handle_struct *handle,
1011 const char *path)
1013 struct smb_filename smb_fname;
1014 int ret;
1015 struct shadow_copy2_config *config;
1017 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1018 return NULL);
1020 ZERO_STRUCT(smb_fname);
1021 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1022 path, config->snapdir);
1023 if (smb_fname.base_name == NULL) {
1024 return NULL;
1027 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1028 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1029 return smb_fname.base_name;
1031 TALLOC_FREE(smb_fname.base_name);
1032 return NULL;
1036 * Find the snapshot directory (if any) for the given
1037 * filename (which is relative to the share).
1039 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1040 struct vfs_handle_struct *handle,
1041 struct smb_filename *smb_fname)
1043 char *path, *p;
1044 const char *snapdir;
1045 struct shadow_copy2_config *config;
1047 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1048 return NULL);
1051 * If the non-snapdisrseverywhere mode, we should not search!
1053 if (!config->snapdirseverywhere) {
1054 return config->snapshot_basepath;
1057 path = talloc_asprintf(mem_ctx, "%s/%s",
1058 handle->conn->connectpath,
1059 smb_fname->base_name);
1060 if (path == NULL) {
1061 return NULL;
1064 snapdir = have_snapdir(handle, path);
1065 if (snapdir != NULL) {
1066 TALLOC_FREE(path);
1067 return snapdir;
1070 while ((p = strrchr(path, '/')) && (p > path)) {
1072 p[0] = '\0';
1074 snapdir = have_snapdir(handle, path);
1075 if (snapdir != NULL) {
1076 TALLOC_FREE(path);
1077 return snapdir;
1080 TALLOC_FREE(path);
1081 return NULL;
1084 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1085 const char *name,
1086 char *gmt, size_t gmt_len)
1088 struct tm timestamp;
1089 time_t timestamp_t;
1090 unsigned long int timestamp_long;
1091 const char *fmt;
1092 struct shadow_copy2_config *config;
1094 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1095 return NULL);
1097 fmt = config->gmt_format;
1099 ZERO_STRUCT(timestamp);
1100 if (config->use_sscanf) {
1101 if (sscanf(name, fmt, &timestamp_long) != 1) {
1102 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1103 "no sscanf match %s: %s\n",
1104 fmt, name));
1105 return false;
1107 timestamp_t = timestamp_long;
1108 gmtime_r(&timestamp_t, &timestamp);
1109 } else {
1110 if (strptime(name, fmt, &timestamp) == NULL) {
1111 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1112 "no match %s: %s\n",
1113 fmt, name));
1114 return false;
1116 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1117 fmt, name));
1119 if (config->use_localtime) {
1120 timestamp.tm_isdst = -1;
1121 timestamp_t = mktime(&timestamp);
1122 gmtime_r(&timestamp_t, &timestamp);
1126 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1127 return true;
1130 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1132 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1135 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1137 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1141 sort the shadow copy data in ascending or descending order
1143 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1144 struct shadow_copy_data *shadow_copy2_data)
1146 int (*cmpfunc)(const void *, const void *);
1147 const char *sort;
1148 struct shadow_copy2_config *config;
1150 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1151 return);
1153 sort = config->sort_order;
1154 if (sort == NULL) {
1155 return;
1158 if (strcmp(sort, "asc") == 0) {
1159 cmpfunc = shadow_copy2_label_cmp_asc;
1160 } else if (strcmp(sort, "desc") == 0) {
1161 cmpfunc = shadow_copy2_label_cmp_desc;
1162 } else {
1163 return;
1166 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1167 shadow_copy2_data->labels)
1169 TYPESAFE_QSORT(shadow_copy2_data->labels,
1170 shadow_copy2_data->num_volumes,
1171 cmpfunc);
1175 static int shadow_copy2_get_shadow_copy_data(
1176 vfs_handle_struct *handle, files_struct *fsp,
1177 struct shadow_copy_data *shadow_copy2_data,
1178 bool labels)
1180 DIR *p;
1181 const char *snapdir;
1182 struct dirent *d;
1183 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1185 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1186 if (snapdir == NULL) {
1187 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1188 handle->conn->connectpath));
1189 errno = EINVAL;
1190 talloc_free(tmp_ctx);
1191 return -1;
1194 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1196 if (!p) {
1197 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1198 " - %s\n", snapdir, strerror(errno)));
1199 talloc_free(tmp_ctx);
1200 errno = ENOSYS;
1201 return -1;
1204 shadow_copy2_data->num_volumes = 0;
1205 shadow_copy2_data->labels = NULL;
1207 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1208 char snapshot[GMT_NAME_LEN+1];
1209 SHADOW_COPY_LABEL *tlabels;
1212 * ignore names not of the right form in the snapshot
1213 * directory
1215 if (!shadow_copy2_snapshot_to_gmt(
1216 handle, d->d_name,
1217 snapshot, sizeof(snapshot))) {
1219 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1220 "ignoring %s\n", d->d_name));
1221 continue;
1223 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1224 d->d_name, snapshot));
1226 if (!labels) {
1227 /* the caller doesn't want the labels */
1228 shadow_copy2_data->num_volumes++;
1229 continue;
1232 tlabels = talloc_realloc(shadow_copy2_data,
1233 shadow_copy2_data->labels,
1234 SHADOW_COPY_LABEL,
1235 shadow_copy2_data->num_volumes+1);
1236 if (tlabels == NULL) {
1237 DEBUG(0,("shadow_copy2: out of memory\n"));
1238 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1239 talloc_free(tmp_ctx);
1240 return -1;
1243 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1244 sizeof(*tlabels));
1246 shadow_copy2_data->num_volumes++;
1247 shadow_copy2_data->labels = tlabels;
1250 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1252 shadow_copy2_sort_data(handle, shadow_copy2_data);
1254 talloc_free(tmp_ctx);
1255 return 0;
1258 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1259 struct files_struct *fsp,
1260 uint32 security_info,
1261 TALLOC_CTX *mem_ctx,
1262 struct security_descriptor **ppdesc)
1264 time_t timestamp;
1265 char *stripped;
1266 NTSTATUS status;
1267 char *conv;
1269 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1270 fsp->fsp_name->base_name,
1271 &timestamp, &stripped)) {
1272 return map_nt_error_from_unix(errno);
1274 if (timestamp == 0) {
1275 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1276 mem_ctx,
1277 ppdesc);
1279 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1280 TALLOC_FREE(stripped);
1281 if (conv == NULL) {
1282 return map_nt_error_from_unix(errno);
1284 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1285 mem_ctx, ppdesc);
1286 TALLOC_FREE(conv);
1287 return status;
1290 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1291 const char *fname,
1292 uint32 security_info,
1293 TALLOC_CTX *mem_ctx,
1294 struct security_descriptor **ppdesc)
1296 time_t timestamp;
1297 char *stripped;
1298 NTSTATUS status;
1299 char *conv;
1301 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1302 &timestamp, &stripped)) {
1303 return map_nt_error_from_unix(errno);
1305 if (timestamp == 0) {
1306 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1307 mem_ctx, ppdesc);
1309 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1310 TALLOC_FREE(stripped);
1311 if (conv == NULL) {
1312 return map_nt_error_from_unix(errno);
1314 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1315 mem_ctx, ppdesc);
1316 TALLOC_FREE(conv);
1317 return status;
1320 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1321 const char *fname, mode_t mode)
1323 time_t timestamp;
1324 char *stripped;
1325 int ret, saved_errno;
1326 char *conv;
1328 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1329 &timestamp, &stripped)) {
1330 return -1;
1332 if (timestamp == 0) {
1333 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1335 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1336 TALLOC_FREE(stripped);
1337 if (conv == NULL) {
1338 return -1;
1340 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1341 saved_errno = errno;
1342 TALLOC_FREE(conv);
1343 errno = saved_errno;
1344 return ret;
1347 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1349 time_t timestamp;
1350 char *stripped;
1351 int ret, saved_errno;
1352 char *conv;
1354 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1355 &timestamp, &stripped)) {
1356 return -1;
1358 if (timestamp == 0) {
1359 return SMB_VFS_NEXT_RMDIR(handle, fname);
1361 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1362 TALLOC_FREE(stripped);
1363 if (conv == NULL) {
1364 return -1;
1366 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1367 saved_errno = errno;
1368 TALLOC_FREE(conv);
1369 errno = saved_errno;
1370 return ret;
1373 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1374 unsigned int flags)
1376 time_t timestamp;
1377 char *stripped;
1378 int ret, saved_errno;
1379 char *conv;
1381 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1382 &timestamp, &stripped)) {
1383 return -1;
1385 if (timestamp == 0) {
1386 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1388 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1389 TALLOC_FREE(stripped);
1390 if (conv == NULL) {
1391 return -1;
1393 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1394 saved_errno = errno;
1395 TALLOC_FREE(conv);
1396 errno = saved_errno;
1397 return ret;
1400 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1401 const char *fname, const char *aname,
1402 void *value, size_t size)
1404 time_t timestamp;
1405 char *stripped;
1406 ssize_t ret;
1407 int saved_errno;
1408 char *conv;
1410 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1411 &timestamp, &stripped)) {
1412 return -1;
1414 if (timestamp == 0) {
1415 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1416 size);
1418 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1419 TALLOC_FREE(stripped);
1420 if (conv == NULL) {
1421 return -1;
1423 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1424 saved_errno = errno;
1425 TALLOC_FREE(conv);
1426 errno = saved_errno;
1427 return ret;
1430 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1431 const char *fname,
1432 char *list, size_t size)
1434 time_t timestamp;
1435 char *stripped;
1436 ssize_t ret;
1437 int saved_errno;
1438 char *conv;
1440 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1441 &timestamp, &stripped)) {
1442 return -1;
1444 if (timestamp == 0) {
1445 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1447 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1448 TALLOC_FREE(stripped);
1449 if (conv == NULL) {
1450 return -1;
1452 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1453 saved_errno = errno;
1454 TALLOC_FREE(conv);
1455 errno = saved_errno;
1456 return ret;
1459 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1460 const char *fname, const char *aname)
1462 time_t timestamp;
1463 char *stripped;
1464 int ret, saved_errno;
1465 char *conv;
1467 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1468 &timestamp, &stripped)) {
1469 return -1;
1471 if (timestamp == 0) {
1472 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1474 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1475 TALLOC_FREE(stripped);
1476 if (conv == NULL) {
1477 return -1;
1479 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1480 saved_errno = errno;
1481 TALLOC_FREE(conv);
1482 errno = saved_errno;
1483 return ret;
1486 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1487 const char *fname,
1488 const char *aname, const void *value,
1489 size_t size, int flags)
1491 time_t timestamp;
1492 char *stripped;
1493 ssize_t ret;
1494 int saved_errno;
1495 char *conv;
1497 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1498 &timestamp, &stripped)) {
1499 return -1;
1501 if (timestamp == 0) {
1502 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1503 flags);
1505 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1506 TALLOC_FREE(stripped);
1507 if (conv == NULL) {
1508 return -1;
1510 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1511 saved_errno = errno;
1512 TALLOC_FREE(conv);
1513 errno = saved_errno;
1514 return ret;
1517 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1518 const char *fname, mode_t mode)
1520 time_t timestamp;
1521 char *stripped;
1522 ssize_t ret;
1523 int 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_CHMOD_ACL(handle, fname, mode);
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_CHMOD_ACL(handle, conv, mode);
1539 saved_errno = errno;
1540 TALLOC_FREE(conv);
1541 errno = saved_errno;
1542 return ret;
1545 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1546 const char *path,
1547 const char *name,
1548 TALLOC_CTX *mem_ctx,
1549 char **found_name)
1551 time_t timestamp;
1552 char *stripped;
1553 ssize_t ret;
1554 int saved_errno;
1555 char *conv;
1557 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1558 &timestamp, &stripped)) {
1559 return -1;
1561 if (timestamp == 0) {
1562 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1563 mem_ctx, found_name);
1565 if (stripped[0] == '\0') {
1566 *found_name = talloc_strdup(mem_ctx, name);
1567 if (*found_name == NULL) {
1568 errno = ENOMEM;
1569 return -1;
1571 return 0;
1573 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1574 TALLOC_FREE(stripped);
1575 if (conv == NULL) {
1576 return -1;
1578 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1579 mem_ctx, found_name);
1580 saved_errno = errno;
1581 TALLOC_FREE(conv);
1582 errno = saved_errno;
1583 return ret;
1586 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1587 const char *path, bool small_query,
1588 uint64_t *bsize, uint64_t *dfree,
1589 uint64_t *dsize)
1591 time_t timestamp;
1592 char *stripped;
1593 ssize_t ret;
1594 int saved_errno;
1595 char *conv;
1597 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1598 &timestamp, &stripped)) {
1599 return -1;
1601 if (timestamp == 0) {
1602 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1603 bsize, dfree, dsize);
1606 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1607 TALLOC_FREE(stripped);
1608 if (conv == NULL) {
1609 return -1;
1612 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1613 dsize);
1615 saved_errno = errno;
1616 TALLOC_FREE(conv);
1617 errno = saved_errno;
1619 return ret;
1622 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
1623 const char *service, const char *user)
1625 struct shadow_copy2_config *config;
1626 int ret;
1627 const char *snapdir;
1628 const char *gmt_format;
1629 const char *sort_order;
1630 const char *basedir;
1631 const char *mount_point;
1633 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
1634 (unsigned)handle->conn->cnum,
1635 handle->conn->connectpath));
1637 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
1638 if (ret < 0) {
1639 return ret;
1642 config = talloc_zero(handle->conn, struct shadow_copy2_config);
1643 if (config == NULL) {
1644 DEBUG(0, ("talloc_zero() failed\n"));
1645 errno = ENOMEM;
1646 return -1;
1649 gmt_format = lp_parm_const_string(SNUM(handle->conn),
1650 "shadow", "format",
1651 GMT_FORMAT);
1652 config->gmt_format = talloc_strdup(config, gmt_format);
1653 if (config->gmt_format == NULL) {
1654 DEBUG(0, ("talloc_strdup() failed\n"));
1655 errno = ENOMEM;
1656 return -1;
1659 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
1660 "shadow", "sscanf", false);
1662 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
1663 "shadow", "localtime",
1664 false);
1666 snapdir = lp_parm_const_string(SNUM(handle->conn),
1667 "shadow", "snapdir",
1668 ".snapshots");
1669 config->snapdir = talloc_strdup(config, snapdir);
1670 if (config->snapdir == NULL) {
1671 DEBUG(0, ("talloc_strdup() failed\n"));
1672 errno = ENOMEM;
1673 return -1;
1676 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
1677 "shadow",
1678 "snapdirseverywhere",
1679 false);
1681 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
1682 "shadow", "crossmountpoints",
1683 false);
1685 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
1686 "shadow", "fixinodes",
1687 false);
1689 sort_order = lp_parm_const_string(SNUM(handle->conn),
1690 "shadow", "sort", "desc");
1691 config->sort_order = talloc_strdup(config, sort_order);
1692 if (config->sort_order == NULL) {
1693 DEBUG(0, ("talloc_strdup() failed\n"));
1694 errno = ENOMEM;
1695 return -1;
1698 mount_point = lp_parm_const_string(SNUM(handle->conn),
1699 "shadow", "mountpoint", NULL);
1700 if (mount_point != NULL) {
1701 if (mount_point[0] != '/') {
1702 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
1703 "relative ('%s'), but it has to be an "
1704 "absolute path. Ignoring provided value.\n",
1705 mount_point));
1706 mount_point = NULL;
1707 } else {
1708 char *p;
1709 p = strstr(handle->conn->connectpath, mount_point);
1710 if (p != handle->conn->connectpath) {
1711 DEBUG(1, ("Warning: mount_point (%s) is not a "
1712 "subdirectory of the share root "
1713 "(%s). Ignoring provided value.\n",
1714 mount_point,
1715 handle->conn->connectpath));
1716 mount_point = NULL;
1721 if (mount_point != NULL) {
1722 config->mount_point = talloc_strdup(config, mount_point);
1723 if (config->mount_point == NULL) {
1724 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
1725 return -1;
1727 } else {
1728 config->mount_point = shadow_copy2_find_mount_point(config,
1729 handle);
1730 if (config->mount_point == NULL) {
1731 DEBUG(0, (__location__ ": shadow_copy2_find_mount_point"
1732 " failed: %s\n", strerror(errno)));
1733 return -1;
1737 basedir = lp_parm_const_string(SNUM(handle->conn),
1738 "shadow", "basedir", NULL);
1740 if (basedir != NULL) {
1741 if (basedir[0] != '/') {
1742 DEBUG(1, (__location__ " Warning: 'basedir' is "
1743 "relative ('%s'), but it has to be an "
1744 "absolute path. Disabling basedir.\n",
1745 basedir));
1746 } else {
1747 char *p;
1748 p = strstr(basedir, config->mount_point);
1749 if (p != basedir) {
1750 DEBUG(1, ("Warning: basedir (%s) is not a "
1751 "subdirectory of the share root's "
1752 "mount point (%s). "
1753 "Disabling basedir\n",
1754 basedir, config->mount_point));
1755 } else {
1756 config->basedir = talloc_strdup(config,
1757 basedir);
1758 if (config->basedir == NULL) {
1759 DEBUG(0, ("talloc_strdup() failed\n"));
1760 errno = ENOMEM;
1761 return -1;
1767 if (config->snapdirseverywhere && config->basedir != NULL) {
1768 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1769 "with 'snapdirseverywhere'. Disabling basedir.\n"));
1770 TALLOC_FREE(config->basedir);
1773 if (config->crossmountpoints && config->basedir != NULL) {
1774 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
1775 "with 'crossmountpoints'. Disabling basedir.\n"));
1776 TALLOC_FREE(config->basedir);
1779 if (config->basedir == NULL) {
1780 config->basedir = config->mount_point;
1783 if (strlen(config->basedir) != strlen(handle->conn->connectpath)) {
1784 config->rel_connectpath = talloc_strdup(config,
1785 handle->conn->connectpath + strlen(config->basedir));
1786 if (config->rel_connectpath == NULL) {
1787 DEBUG(0, ("talloc_strdup() failed\n"));
1788 errno = ENOMEM;
1789 return -1;
1793 if (config->snapdir[0] == '/') {
1794 config->snapdir_absolute = true;
1796 if (config->snapdirseverywhere == true) {
1797 DEBUG(1, (__location__ " Warning: An absolute snapdir "
1798 "is incompatible with 'snapdirseverywhere', "
1799 "setting 'snapdirseverywhere' to false.\n"));
1800 config->snapdirseverywhere = false;
1803 if (config->crossmountpoints == true) {
1804 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
1805 "is not supported with an absolute snapdir. "
1806 "Disabling it.\n"));
1807 config->crossmountpoints = false;
1810 config->snapshot_basepath = config->snapdir;
1811 } else {
1812 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
1813 config->mount_point, config->snapdir);
1814 if (config->snapshot_basepath == NULL) {
1815 DEBUG(0, ("talloc_asprintf() failed\n"));
1816 errno = ENOMEM;
1817 return -1;
1821 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
1822 " share root: '%s'\n"
1823 " basedir: '%s'\n"
1824 " mountpoint: '%s'\n"
1825 " rel share root: '%s'\n"
1826 " snapdir: '%s'\n"
1827 " snapshot base path: '%s'\n"
1828 " format: '%s'\n"
1829 " use sscanf: %s\n"
1830 " snapdirs everywhere: %s\n"
1831 " cross mountpoints: %s\n"
1832 " fix inodes: %s\n"
1833 " sort order: %s\n"
1835 handle->conn->connectpath,
1836 config->basedir,
1837 config->mount_point,
1838 config->rel_connectpath,
1839 config->snapdir,
1840 config->snapshot_basepath,
1841 config->gmt_format,
1842 config->use_sscanf ? "yes" : "no",
1843 config->snapdirseverywhere ? "yes" : "no",
1844 config->crossmountpoints ? "yes" : "no",
1845 config->fixinodes ? "yes" : "no",
1846 config->sort_order
1850 SMB_VFS_HANDLE_SET_DATA(handle, config,
1851 NULL, struct shadow_copy2_config,
1852 return -1);
1854 return 0;
1857 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1858 .connect_fn = shadow_copy2_connect,
1859 .opendir_fn = shadow_copy2_opendir,
1860 .disk_free_fn = shadow_copy2_disk_free,
1861 .rename_fn = shadow_copy2_rename,
1862 .link_fn = shadow_copy2_link,
1863 .symlink_fn = shadow_copy2_symlink,
1864 .stat_fn = shadow_copy2_stat,
1865 .lstat_fn = shadow_copy2_lstat,
1866 .fstat_fn = shadow_copy2_fstat,
1867 .open_fn = shadow_copy2_open,
1868 .unlink_fn = shadow_copy2_unlink,
1869 .chmod_fn = shadow_copy2_chmod,
1870 .chown_fn = shadow_copy2_chown,
1871 .chdir_fn = shadow_copy2_chdir,
1872 .ntimes_fn = shadow_copy2_ntimes,
1873 .readlink_fn = shadow_copy2_readlink,
1874 .mknod_fn = shadow_copy2_mknod,
1875 .realpath_fn = shadow_copy2_realpath,
1876 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1877 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1878 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1879 .mkdir_fn = shadow_copy2_mkdir,
1880 .rmdir_fn = shadow_copy2_rmdir,
1881 .getxattr_fn = shadow_copy2_getxattr,
1882 .listxattr_fn = shadow_copy2_listxattr,
1883 .removexattr_fn = shadow_copy2_removexattr,
1884 .setxattr_fn = shadow_copy2_setxattr,
1885 .chmod_acl_fn = shadow_copy2_chmod_acl,
1886 .chflags_fn = shadow_copy2_chflags,
1887 .get_real_filename_fn = shadow_copy2_get_real_filename,
1890 NTSTATUS vfs_shadow_copy2_init(void);
1891 NTSTATUS vfs_shadow_copy2_init(void)
1893 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1894 "shadow_copy2", &vfs_shadow_copy2_fns);