VERSION: Disable git snapshots for the 4.1.2 release.
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobaa7e50ff510d44dbc81eff43bd2b79839bd9704b
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 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
111 size_t **poffsets,
112 unsigned *pnum_offsets)
114 unsigned num_offsets;
115 size_t *offsets;
116 const char *p;
118 num_offsets = 0;
120 p = str;
121 while ((p = strchr(p, '/')) != NULL) {
122 num_offsets += 1;
123 p += 1;
126 offsets = talloc_array(mem_ctx, size_t, num_offsets);
127 if (offsets == NULL) {
128 return false;
131 p = str;
132 num_offsets = 0;
133 while ((p = strchr(p, '/')) != NULL) {
134 offsets[num_offsets] = p-str;
135 num_offsets += 1;
136 p += 1;
139 *poffsets = offsets;
140 *pnum_offsets = num_offsets;
141 return true;
144 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
145 struct vfs_handle_struct *handle,
146 time_t snapshot)
148 const char *fmt;
149 struct tm snap_tm;
150 fstring snaptime_string;
151 size_t snaptime_len;
153 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
154 "format", GMT_FORMAT);
156 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
157 snaptime_len = snprintf(snaptime_string, sizeof(snaptime_string), fmt,
158 (unsigned long)snapshot);
159 if (snaptime_len <= 0) {
160 DEBUG(10, ("snprintf failed\n"));
161 return NULL;
163 } else {
164 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
165 if (localtime_r(&snapshot, &snap_tm) == 0) {
166 DEBUG(10, ("gmtime_r failed\n"));
167 return NULL;
169 } else {
170 if (gmtime_r(&snapshot, &snap_tm) == 0) {
171 DEBUG(10, ("gmtime_r failed\n"));
172 return NULL;
175 snaptime_len = strftime(snaptime_string, sizeof(snaptime_string), fmt,
176 &snap_tm);
177 if (snaptime_len == 0) {
178 DEBUG(10, ("strftime failed\n"));
179 return NULL;
182 return talloc_asprintf(mem_ctx, "/%s/%s",
183 lp_parm_const_string(
184 SNUM(handle->conn), "shadow", "snapdir",
185 ".snapshots"),
186 snaptime_string);
189 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
190 struct vfs_handle_struct *handle,
191 const char *name,
192 time_t *ptimestamp,
193 char **pstripped)
195 struct tm tm;
196 time_t timestamp;
197 const char *p;
198 char *q;
199 char *stripped;
200 size_t rest_len, dst_len;
202 p = strstr_m(name, "@GMT-");
203 if (p == NULL) {
204 goto no_snapshot;
206 if ((p > name) && (p[-1] != '/')) {
207 goto no_snapshot;
209 q = strptime(p, GMT_FORMAT, &tm);
210 if (q == NULL) {
211 goto no_snapshot;
213 tm.tm_isdst = -1;
214 timestamp = timegm(&tm);
215 if (timestamp == (time_t)-1) {
216 goto no_snapshot;
218 if ((p == name) && (q[0] == '\0')) {
219 if (pstripped != NULL) {
220 stripped = talloc_strdup(mem_ctx, "");
221 if (stripped == NULL) {
222 return false;
224 *pstripped = stripped;
226 *ptimestamp = timestamp;
227 return true;
229 if (q[0] != '/') {
230 goto no_snapshot;
232 q += 1;
234 rest_len = strlen(q);
235 dst_len = (p-name) + rest_len;
237 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
238 false)) {
239 char *insert;
240 bool have_insert;
241 insert = shadow_copy2_insert_string(talloc_tos(), handle,
242 timestamp);
243 if (insert == NULL) {
244 errno = ENOMEM;
245 return false;
248 have_insert = (strstr(name, insert+1) != NULL);
249 TALLOC_FREE(insert);
250 if (have_insert) {
251 goto no_snapshot;
255 if (pstripped != NULL) {
256 stripped = talloc_array(mem_ctx, char, dst_len+1);
257 if (stripped == NULL) {
258 errno = ENOMEM;
259 return false;
261 if (p > name) {
262 memcpy(stripped, name, p-name);
264 if (rest_len > 0) {
265 memcpy(stripped + (p-name), q, rest_len);
267 stripped[dst_len] = '\0';
268 *pstripped = stripped;
270 *ptimestamp = timestamp;
271 return true;
272 no_snapshot:
273 *ptimestamp = 0;
274 return true;
277 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
278 vfs_handle_struct *handle)
280 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
281 dev_t dev;
282 struct stat st;
283 char *p;
285 if (stat(path, &st) != 0) {
286 talloc_free(path);
287 return NULL;
290 dev = st.st_dev;
292 while ((p = strrchr(path, '/')) && p > path) {
293 *p = 0;
294 if (stat(path, &st) != 0) {
295 talloc_free(path);
296 return NULL;
298 if (st.st_dev != dev) {
299 *p = '/';
300 break;
304 return path;
307 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
308 struct vfs_handle_struct *handle,
309 const char *name, time_t timestamp)
311 struct smb_filename converted_fname;
312 char *result = NULL;
313 size_t *slashes = NULL;
314 unsigned num_slashes;
315 char *path = NULL;
316 size_t pathlen;
317 char *insert = NULL;
318 char *converted = NULL;
319 size_t insertlen;
320 int i, saved_errno;
321 size_t min_offset;
323 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
324 name);
325 if (path == NULL) {
326 errno = ENOMEM;
327 goto fail;
329 pathlen = talloc_get_size(path)-1;
331 DEBUG(10, ("converting %s\n", path));
333 if (!shadow_copy2_find_slashes(talloc_tos(), path,
334 &slashes, &num_slashes)) {
335 goto fail;
337 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
338 if (insert == NULL) {
339 goto fail;
341 insertlen = talloc_get_size(insert)-1;
342 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
343 if (converted == NULL) {
344 goto fail;
347 if (path[pathlen-1] != '/') {
349 * Append a fake slash to find the snapshot root
351 size_t *tmp;
352 tmp = talloc_realloc(talloc_tos(), slashes,
353 size_t, num_slashes+1);
354 if (tmp == NULL) {
355 goto fail;
357 slashes = tmp;
358 slashes[num_slashes] = pathlen;
359 num_slashes += 1;
362 min_offset = 0;
364 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
365 false)) {
366 char *mount_point;
368 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
369 handle);
370 if (mount_point == NULL) {
371 goto fail;
373 min_offset = strlen(mount_point);
374 TALLOC_FREE(mount_point);
377 memcpy(converted, path, pathlen+1);
378 converted[pathlen+insertlen] = '\0';
380 ZERO_STRUCT(converted_fname);
381 converted_fname.base_name = converted;
383 for (i = num_slashes-1; i>=0; i--) {
384 int ret;
385 size_t offset;
387 offset = slashes[i];
389 if (offset < min_offset) {
390 errno = ENOENT;
391 goto fail;
394 memcpy(converted+offset, insert, insertlen);
396 offset += insertlen;
397 memcpy(converted+offset, path + slashes[i],
398 pathlen - slashes[i]);
400 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
402 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
403 ret, ret == 0 ? "ok" : strerror(errno)));
404 if (ret == 0) {
405 /* success */
406 break;
408 if (errno == ENOTDIR) {
410 * This is a valid condition: We appended the
411 * .snaphots/@GMT.. to a file name. Just try
412 * with the upper levels.
414 continue;
416 if (errno != ENOENT) {
417 /* Other problem than "not found" */
418 goto fail;
422 if (i >= 0) {
424 * Found something
426 DEBUG(10, ("Found %s\n", converted));
427 result = converted;
428 converted = NULL;
429 } else {
430 errno = ENOENT;
432 fail:
433 saved_errno = errno;
434 TALLOC_FREE(converted);
435 TALLOC_FREE(insert);
436 TALLOC_FREE(slashes);
437 TALLOC_FREE(path);
438 errno = saved_errno;
439 return result;
443 modify a sbuf return to ensure that inodes in the shadow directory
444 are different from those in the main directory
446 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
447 SMB_STRUCT_STAT *sbuf)
449 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
450 /* some snapshot systems, like GPFS, return the name
451 device:inode for the snapshot files as the current
452 files. That breaks the 'restore' button in the shadow copy
453 GUI, as the client gets a sharing violation.
455 This is a crude way of allowing both files to be
456 open at once. It has a slight chance of inode
457 number collision, but I can't see a better approach
458 without significant VFS changes
460 uint32_t shash;
462 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
463 if (shash == 0) {
464 shash = 1;
466 sbuf->st_ex_ino ^= shash;
470 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
471 const char *fname,
472 const char *mask,
473 uint32 attr)
475 time_t timestamp;
476 char *stripped;
477 DIR *ret;
478 int saved_errno;
479 char *conv;
481 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
482 &timestamp, &stripped)) {
483 return NULL;
485 if (timestamp == 0) {
486 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
488 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
489 TALLOC_FREE(stripped);
490 if (conv == NULL) {
491 return NULL;
493 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
494 saved_errno = errno;
495 TALLOC_FREE(conv);
496 errno = saved_errno;
497 return ret;
500 static int shadow_copy2_rename(vfs_handle_struct *handle,
501 const struct smb_filename *smb_fname_src,
502 const struct smb_filename *smb_fname_dst)
504 time_t timestamp_src, timestamp_dst;
506 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
507 smb_fname_src->base_name,
508 &timestamp_src, NULL)) {
509 return -1;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
512 smb_fname_dst->base_name,
513 &timestamp_dst, NULL)) {
514 return -1;
516 if (timestamp_src != 0) {
517 errno = EXDEV;
518 return -1;
520 if (timestamp_dst != 0) {
521 errno = EROFS;
522 return -1;
524 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
527 static int shadow_copy2_symlink(vfs_handle_struct *handle,
528 const char *oldname, const char *newname)
530 time_t timestamp_old, timestamp_new;
532 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
533 &timestamp_old, NULL)) {
534 return -1;
536 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
537 &timestamp_new, NULL)) {
538 return -1;
540 if ((timestamp_old != 0) || (timestamp_new != 0)) {
541 errno = EROFS;
542 return -1;
544 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
547 static int shadow_copy2_link(vfs_handle_struct *handle,
548 const char *oldname, const char *newname)
550 time_t timestamp_old, timestamp_new;
552 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
553 &timestamp_old, NULL)) {
554 return -1;
556 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
557 &timestamp_new, NULL)) {
558 return -1;
560 if ((timestamp_old != 0) || (timestamp_new != 0)) {
561 errno = EROFS;
562 return -1;
564 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
567 static int shadow_copy2_stat(vfs_handle_struct *handle,
568 struct smb_filename *smb_fname)
570 time_t timestamp;
571 char *stripped, *tmp;
572 int ret, saved_errno;
574 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
575 smb_fname->base_name,
576 &timestamp, &stripped)) {
577 return -1;
579 if (timestamp == 0) {
580 return SMB_VFS_NEXT_STAT(handle, smb_fname);
583 tmp = smb_fname->base_name;
584 smb_fname->base_name = shadow_copy2_convert(
585 talloc_tos(), handle, stripped, timestamp);
586 TALLOC_FREE(stripped);
588 if (smb_fname->base_name == NULL) {
589 smb_fname->base_name = tmp;
590 return -1;
593 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
594 saved_errno = errno;
596 TALLOC_FREE(smb_fname->base_name);
597 smb_fname->base_name = tmp;
599 if (ret == 0) {
600 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
602 errno = saved_errno;
603 return ret;
606 static int shadow_copy2_lstat(vfs_handle_struct *handle,
607 struct smb_filename *smb_fname)
609 time_t timestamp;
610 char *stripped, *tmp;
611 int ret, saved_errno;
613 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
614 smb_fname->base_name,
615 &timestamp, &stripped)) {
616 return -1;
618 if (timestamp == 0) {
619 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
622 tmp = smb_fname->base_name;
623 smb_fname->base_name = shadow_copy2_convert(
624 talloc_tos(), handle, stripped, timestamp);
625 TALLOC_FREE(stripped);
627 if (smb_fname->base_name == NULL) {
628 smb_fname->base_name = tmp;
629 return -1;
632 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
633 saved_errno = errno;
635 TALLOC_FREE(smb_fname->base_name);
636 smb_fname->base_name = tmp;
638 if (ret == 0) {
639 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
641 errno = saved_errno;
642 return ret;
645 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
646 SMB_STRUCT_STAT *sbuf)
648 time_t timestamp;
649 int ret;
651 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
652 if (ret == -1) {
653 return ret;
655 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
656 fsp->fsp_name->base_name,
657 &timestamp, NULL)) {
658 return 0;
660 if (timestamp != 0) {
661 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
663 return 0;
666 static int shadow_copy2_open(vfs_handle_struct *handle,
667 struct smb_filename *smb_fname, files_struct *fsp,
668 int flags, mode_t mode)
670 time_t timestamp;
671 char *stripped, *tmp;
672 int ret, saved_errno;
674 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
675 smb_fname->base_name,
676 &timestamp, &stripped)) {
677 return -1;
679 if (timestamp == 0) {
680 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
683 tmp = smb_fname->base_name;
684 smb_fname->base_name = shadow_copy2_convert(
685 talloc_tos(), handle, stripped, timestamp);
686 TALLOC_FREE(stripped);
688 if (smb_fname->base_name == NULL) {
689 smb_fname->base_name = tmp;
690 return -1;
693 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
694 saved_errno = errno;
696 TALLOC_FREE(smb_fname->base_name);
697 smb_fname->base_name = tmp;
699 errno = saved_errno;
700 return ret;
703 static int shadow_copy2_unlink(vfs_handle_struct *handle,
704 const struct smb_filename *smb_fname)
706 time_t timestamp;
707 char *stripped;
708 int ret, saved_errno;
709 struct smb_filename *conv;
711 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
712 smb_fname->base_name,
713 &timestamp, &stripped)) {
714 return -1;
716 if (timestamp == 0) {
717 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
719 conv = cp_smb_filename(talloc_tos(), smb_fname);
720 if (conv == NULL) {
721 errno = ENOMEM;
722 return -1;
724 conv->base_name = shadow_copy2_convert(
725 conv, handle, stripped, timestamp);
726 TALLOC_FREE(stripped);
727 if (conv->base_name == NULL) {
728 return -1;
730 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
731 saved_errno = errno;
732 TALLOC_FREE(conv);
733 errno = saved_errno;
734 return ret;
737 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
738 mode_t mode)
740 time_t timestamp;
741 char *stripped;
742 int ret, saved_errno;
743 char *conv;
745 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
746 &timestamp, &stripped)) {
747 return -1;
749 if (timestamp == 0) {
750 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
752 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
753 TALLOC_FREE(stripped);
754 if (conv == NULL) {
755 return -1;
757 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
758 saved_errno = errno;
759 TALLOC_FREE(conv);
760 errno = saved_errno;
761 return ret;
764 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
765 uid_t uid, gid_t gid)
767 time_t timestamp;
768 char *stripped;
769 int ret, saved_errno;
770 char *conv;
772 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
773 &timestamp, &stripped)) {
774 return -1;
776 if (timestamp == 0) {
777 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
779 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
780 TALLOC_FREE(stripped);
781 if (conv == NULL) {
782 return -1;
784 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
785 saved_errno = errno;
786 TALLOC_FREE(conv);
787 errno = saved_errno;
788 return ret;
791 static int shadow_copy2_chdir(vfs_handle_struct *handle,
792 const char *fname)
794 time_t timestamp;
795 char *stripped;
796 int ret, saved_errno;
797 char *conv;
799 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
800 &timestamp, &stripped)) {
801 return -1;
803 if (timestamp == 0) {
804 return SMB_VFS_NEXT_CHDIR(handle, fname);
806 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
807 TALLOC_FREE(stripped);
808 if (conv == NULL) {
809 return -1;
811 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
812 saved_errno = errno;
813 TALLOC_FREE(conv);
814 errno = saved_errno;
815 return ret;
818 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
819 const struct smb_filename *smb_fname,
820 struct smb_file_time *ft)
822 time_t timestamp;
823 char *stripped;
824 int ret, saved_errno;
825 struct smb_filename *conv;
827 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
828 smb_fname->base_name,
829 &timestamp, &stripped)) {
830 return -1;
832 if (timestamp == 0) {
833 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
835 conv = cp_smb_filename(talloc_tos(), smb_fname);
836 if (conv == NULL) {
837 errno = ENOMEM;
838 return -1;
840 conv->base_name = shadow_copy2_convert(
841 conv, handle, stripped, timestamp);
842 TALLOC_FREE(stripped);
843 if (conv->base_name == NULL) {
844 return -1;
846 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
847 saved_errno = errno;
848 TALLOC_FREE(conv);
849 errno = saved_errno;
850 return ret;
853 static int shadow_copy2_readlink(vfs_handle_struct *handle,
854 const char *fname, char *buf, size_t bufsiz)
856 time_t timestamp;
857 char *stripped;
858 int ret, saved_errno;
859 char *conv;
861 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
862 &timestamp, &stripped)) {
863 return -1;
865 if (timestamp == 0) {
866 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
868 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
869 TALLOC_FREE(stripped);
870 if (conv == NULL) {
871 return -1;
873 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
874 saved_errno = errno;
875 TALLOC_FREE(conv);
876 errno = saved_errno;
877 return ret;
880 static int shadow_copy2_mknod(vfs_handle_struct *handle,
881 const char *fname, mode_t mode, SMB_DEV_T dev)
883 time_t timestamp;
884 char *stripped;
885 int ret, saved_errno;
886 char *conv;
888 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
889 &timestamp, &stripped)) {
890 return -1;
892 if (timestamp == 0) {
893 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
895 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
896 TALLOC_FREE(stripped);
897 if (conv == NULL) {
898 return -1;
900 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
901 saved_errno = errno;
902 TALLOC_FREE(conv);
903 errno = saved_errno;
904 return ret;
907 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
908 const char *fname)
910 time_t timestamp;
911 char *stripped = NULL;
912 char *tmp = NULL;
913 char *result = NULL;
914 char *inserted = NULL;
915 char *inserted_to, *inserted_end;
916 int saved_errno;
918 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
919 &timestamp, &stripped)) {
920 goto done;
922 if (timestamp == 0) {
923 return SMB_VFS_NEXT_REALPATH(handle, fname);
926 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
927 if (tmp == NULL) {
928 goto done;
931 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
932 if (result == NULL) {
933 goto done;
937 * Take away what we've inserted. This removes the @GMT-thingy
938 * completely, but will give a path under the share root.
940 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
941 if (inserted == NULL) {
942 goto done;
944 inserted_to = strstr_m(result, inserted);
945 if (inserted_to == NULL) {
946 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
947 goto done;
949 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
950 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
952 done:
953 saved_errno = errno;
954 TALLOC_FREE(inserted);
955 TALLOC_FREE(tmp);
956 TALLOC_FREE(stripped);
957 errno = saved_errno;
958 return result;
961 static char *have_snapdir(struct vfs_handle_struct *handle,
962 const char *path)
964 struct smb_filename smb_fname;
965 int ret;
967 ZERO_STRUCT(smb_fname);
968 smb_fname.base_name = talloc_asprintf(
969 talloc_tos(), "%s/%s", path,
970 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
971 ".snapshots"));
972 if (smb_fname.base_name == NULL) {
973 return NULL;
976 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
977 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
978 return smb_fname.base_name;
980 TALLOC_FREE(smb_fname.base_name);
981 return NULL;
984 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
985 struct vfs_handle_struct *handle,
986 struct smb_filename *smb_fname)
988 char *path, *p;
989 char *snapdir;
991 path = talloc_asprintf(mem_ctx, "%s/%s",
992 handle->conn->connectpath,
993 smb_fname->base_name);
994 if (path == NULL) {
995 return NULL;
998 snapdir = have_snapdir(handle, path);
999 if (snapdir != NULL) {
1000 TALLOC_FREE(path);
1001 return snapdir;
1004 while ((p = strrchr(path, '/')) && (p > path)) {
1006 p[0] = '\0';
1008 snapdir = have_snapdir(handle, path);
1009 if (snapdir != NULL) {
1010 TALLOC_FREE(path);
1011 return snapdir;
1014 TALLOC_FREE(path);
1015 return NULL;
1018 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1019 const char *name,
1020 char *gmt, size_t gmt_len)
1022 struct tm timestamp;
1023 time_t timestamp_t;
1024 unsigned long int timestamp_long;
1025 const char *fmt;
1027 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1028 "format", GMT_FORMAT);
1030 ZERO_STRUCT(timestamp);
1031 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1032 if (sscanf(name, fmt, &timestamp_long) != 1) {
1033 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no sscanf match %s: %s\n",
1034 fmt, name));
1035 return false;
1037 timestamp_t = timestamp_long;
1038 gmtime_r(&timestamp_t, &timestamp);
1039 } else {
1040 if (strptime(name, fmt, &timestamp) == NULL) {
1041 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1042 fmt, name));
1043 return false;
1045 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1047 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1048 timestamp.tm_isdst = -1;
1049 timestamp_t = mktime(&timestamp);
1050 gmtime_r(&timestamp_t, &timestamp);
1054 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1055 return true;
1058 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1060 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1063 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1065 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1069 sort the shadow copy data in ascending or descending order
1071 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1072 struct shadow_copy_data *shadow_copy2_data)
1074 int (*cmpfunc)(const void *, const void *);
1075 const char *sort;
1077 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1078 "sort", "desc");
1079 if (sort == NULL) {
1080 return;
1083 if (strcmp(sort, "asc") == 0) {
1084 cmpfunc = shadow_copy2_label_cmp_asc;
1085 } else if (strcmp(sort, "desc") == 0) {
1086 cmpfunc = shadow_copy2_label_cmp_desc;
1087 } else {
1088 return;
1091 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1092 shadow_copy2_data->labels)
1094 TYPESAFE_QSORT(shadow_copy2_data->labels,
1095 shadow_copy2_data->num_volumes,
1096 cmpfunc);
1100 static int shadow_copy2_get_shadow_copy_data(
1101 vfs_handle_struct *handle, files_struct *fsp,
1102 struct shadow_copy_data *shadow_copy2_data,
1103 bool labels)
1105 DIR *p;
1106 const char *snapdir;
1107 struct dirent *d;
1108 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1110 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1111 if (snapdir == NULL) {
1112 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1113 handle->conn->connectpath));
1114 errno = EINVAL;
1115 talloc_free(tmp_ctx);
1116 return -1;
1119 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1121 if (!p) {
1122 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1123 " - %s\n", snapdir, strerror(errno)));
1124 talloc_free(tmp_ctx);
1125 errno = ENOSYS;
1126 return -1;
1129 shadow_copy2_data->num_volumes = 0;
1130 shadow_copy2_data->labels = NULL;
1132 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1133 char snapshot[GMT_NAME_LEN+1];
1134 SHADOW_COPY_LABEL *tlabels;
1137 * ignore names not of the right form in the snapshot
1138 * directory
1140 if (!shadow_copy2_snapshot_to_gmt(
1141 handle, d->d_name,
1142 snapshot, sizeof(snapshot))) {
1144 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1145 "ignoring %s\n", d->d_name));
1146 continue;
1148 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1149 d->d_name, snapshot));
1151 if (!labels) {
1152 /* the caller doesn't want the labels */
1153 shadow_copy2_data->num_volumes++;
1154 continue;
1157 tlabels = talloc_realloc(shadow_copy2_data,
1158 shadow_copy2_data->labels,
1159 SHADOW_COPY_LABEL,
1160 shadow_copy2_data->num_volumes+1);
1161 if (tlabels == NULL) {
1162 DEBUG(0,("shadow_copy2: out of memory\n"));
1163 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1164 talloc_free(tmp_ctx);
1165 return -1;
1168 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1169 sizeof(*tlabels));
1171 shadow_copy2_data->num_volumes++;
1172 shadow_copy2_data->labels = tlabels;
1175 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1177 shadow_copy2_sort_data(handle, shadow_copy2_data);
1179 talloc_free(tmp_ctx);
1180 return 0;
1183 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1184 struct files_struct *fsp,
1185 uint32 security_info,
1186 TALLOC_CTX *mem_ctx,
1187 struct security_descriptor **ppdesc)
1189 time_t timestamp;
1190 char *stripped;
1191 NTSTATUS status;
1192 char *conv;
1194 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1195 fsp->fsp_name->base_name,
1196 &timestamp, &stripped)) {
1197 return map_nt_error_from_unix(errno);
1199 if (timestamp == 0) {
1200 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1201 mem_ctx,
1202 ppdesc);
1204 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1205 TALLOC_FREE(stripped);
1206 if (conv == NULL) {
1207 return map_nt_error_from_unix(errno);
1209 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1210 mem_ctx, ppdesc);
1211 TALLOC_FREE(conv);
1212 return status;
1215 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1216 const char *fname,
1217 uint32 security_info,
1218 TALLOC_CTX *mem_ctx,
1219 struct security_descriptor **ppdesc)
1221 time_t timestamp;
1222 char *stripped;
1223 NTSTATUS status;
1224 char *conv;
1226 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1227 &timestamp, &stripped)) {
1228 return map_nt_error_from_unix(errno);
1230 if (timestamp == 0) {
1231 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1232 mem_ctx, ppdesc);
1234 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1235 TALLOC_FREE(stripped);
1236 if (conv == NULL) {
1237 return map_nt_error_from_unix(errno);
1239 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1240 mem_ctx, ppdesc);
1241 TALLOC_FREE(conv);
1242 return status;
1245 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1246 const char *fname, mode_t mode)
1248 time_t timestamp;
1249 char *stripped;
1250 int ret, saved_errno;
1251 char *conv;
1253 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1254 &timestamp, &stripped)) {
1255 return -1;
1257 if (timestamp == 0) {
1258 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1260 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1261 TALLOC_FREE(stripped);
1262 if (conv == NULL) {
1263 return -1;
1265 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1266 saved_errno = errno;
1267 TALLOC_FREE(conv);
1268 errno = saved_errno;
1269 return ret;
1272 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1274 time_t timestamp;
1275 char *stripped;
1276 int ret, saved_errno;
1277 char *conv;
1279 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1280 &timestamp, &stripped)) {
1281 return -1;
1283 if (timestamp == 0) {
1284 return SMB_VFS_NEXT_RMDIR(handle, fname);
1286 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1287 TALLOC_FREE(stripped);
1288 if (conv == NULL) {
1289 return -1;
1291 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1292 saved_errno = errno;
1293 TALLOC_FREE(conv);
1294 errno = saved_errno;
1295 return ret;
1298 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1299 unsigned int flags)
1301 time_t timestamp;
1302 char *stripped;
1303 int ret, saved_errno;
1304 char *conv;
1306 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1307 &timestamp, &stripped)) {
1308 return -1;
1310 if (timestamp == 0) {
1311 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1313 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1314 TALLOC_FREE(stripped);
1315 if (conv == NULL) {
1316 return -1;
1318 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1319 saved_errno = errno;
1320 TALLOC_FREE(conv);
1321 errno = saved_errno;
1322 return ret;
1325 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1326 const char *fname, const char *aname,
1327 void *value, size_t size)
1329 time_t timestamp;
1330 char *stripped;
1331 ssize_t ret;
1332 int saved_errno;
1333 char *conv;
1335 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1336 &timestamp, &stripped)) {
1337 return -1;
1339 if (timestamp == 0) {
1340 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1341 size);
1343 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1344 TALLOC_FREE(stripped);
1345 if (conv == NULL) {
1346 return -1;
1348 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1349 saved_errno = errno;
1350 TALLOC_FREE(conv);
1351 errno = saved_errno;
1352 return ret;
1355 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1356 const char *fname,
1357 char *list, size_t size)
1359 time_t timestamp;
1360 char *stripped;
1361 ssize_t ret;
1362 int saved_errno;
1363 char *conv;
1365 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1366 &timestamp, &stripped)) {
1367 return -1;
1369 if (timestamp == 0) {
1370 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1372 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1373 TALLOC_FREE(stripped);
1374 if (conv == NULL) {
1375 return -1;
1377 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1378 saved_errno = errno;
1379 TALLOC_FREE(conv);
1380 errno = saved_errno;
1381 return ret;
1384 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1385 const char *fname, const char *aname)
1387 time_t timestamp;
1388 char *stripped;
1389 int ret, saved_errno;
1390 char *conv;
1392 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1393 &timestamp, &stripped)) {
1394 return -1;
1396 if (timestamp == 0) {
1397 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1399 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1400 TALLOC_FREE(stripped);
1401 if (conv == NULL) {
1402 return -1;
1404 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1405 saved_errno = errno;
1406 TALLOC_FREE(conv);
1407 errno = saved_errno;
1408 return ret;
1411 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1412 const char *fname,
1413 const char *aname, const void *value,
1414 size_t size, int flags)
1416 time_t timestamp;
1417 char *stripped;
1418 ssize_t ret;
1419 int saved_errno;
1420 char *conv;
1422 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1423 &timestamp, &stripped)) {
1424 return -1;
1426 if (timestamp == 0) {
1427 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1428 flags);
1430 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1431 TALLOC_FREE(stripped);
1432 if (conv == NULL) {
1433 return -1;
1435 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1436 saved_errno = errno;
1437 TALLOC_FREE(conv);
1438 errno = saved_errno;
1439 return ret;
1442 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1443 const char *fname, mode_t mode)
1445 time_t timestamp;
1446 char *stripped;
1447 ssize_t ret;
1448 int saved_errno;
1449 char *conv;
1451 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1452 &timestamp, &stripped)) {
1453 return -1;
1455 if (timestamp == 0) {
1456 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1458 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1459 TALLOC_FREE(stripped);
1460 if (conv == NULL) {
1461 return -1;
1463 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1464 saved_errno = errno;
1465 TALLOC_FREE(conv);
1466 errno = saved_errno;
1467 return ret;
1470 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1471 const char *path,
1472 const char *name,
1473 TALLOC_CTX *mem_ctx,
1474 char **found_name)
1476 time_t timestamp;
1477 char *stripped;
1478 ssize_t ret;
1479 int saved_errno;
1480 char *conv;
1482 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1483 &timestamp, &stripped)) {
1484 return -1;
1486 if (timestamp == 0) {
1487 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1488 mem_ctx, found_name);
1490 if (stripped[0] == '\0') {
1491 *found_name = talloc_strdup(mem_ctx, name);
1492 if (*found_name == NULL) {
1493 errno = ENOMEM;
1494 return -1;
1496 return 0;
1498 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1499 TALLOC_FREE(stripped);
1500 if (conv == NULL) {
1501 return -1;
1503 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1504 mem_ctx, found_name);
1505 saved_errno = errno;
1506 TALLOC_FREE(conv);
1507 errno = saved_errno;
1508 return ret;
1512 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1513 .opendir_fn = shadow_copy2_opendir,
1514 .rename_fn = shadow_copy2_rename,
1515 .link_fn = shadow_copy2_link,
1516 .symlink_fn = shadow_copy2_symlink,
1517 .stat_fn = shadow_copy2_stat,
1518 .lstat_fn = shadow_copy2_lstat,
1519 .fstat_fn = shadow_copy2_fstat,
1520 .open_fn = shadow_copy2_open,
1521 .unlink_fn = shadow_copy2_unlink,
1522 .chmod_fn = shadow_copy2_chmod,
1523 .chown_fn = shadow_copy2_chown,
1524 .chdir_fn = shadow_copy2_chdir,
1525 .ntimes_fn = shadow_copy2_ntimes,
1526 .readlink_fn = shadow_copy2_readlink,
1527 .mknod_fn = shadow_copy2_mknod,
1528 .realpath_fn = shadow_copy2_realpath,
1529 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1530 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1531 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1532 .mkdir_fn = shadow_copy2_mkdir,
1533 .rmdir_fn = shadow_copy2_rmdir,
1534 .getxattr_fn = shadow_copy2_getxattr,
1535 .listxattr_fn = shadow_copy2_listxattr,
1536 .removexattr_fn = shadow_copy2_removexattr,
1537 .setxattr_fn = shadow_copy2_setxattr,
1538 .chmod_acl_fn = shadow_copy2_chmod_acl,
1539 .chflags_fn = shadow_copy2_chflags,
1540 .get_real_filename_fn = shadow_copy2_get_real_filename,
1543 NTSTATUS vfs_shadow_copy2_init(void);
1544 NTSTATUS vfs_shadow_copy2_init(void)
1546 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1547 "shadow_copy2", &vfs_shadow_copy2_fns);