s3:torture: add idmap_tdb_common test code
[Samba/id10ts.git] / source3 / modules / vfs_shadow_copy2.c
blob24356ece1f976f2eb4702500a7e487814a6282c5
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:localtime = yes/no (default is no)
89 This is an optional parameter that indicates whether the
90 snapshot names are in UTC/GMT or the local time.
93 The following command would generate a correctly formatted directory name
94 for use with the default parameters:
95 date -u +@GMT-%Y.%m.%d-%H.%M.%S
98 #include "includes.h"
99 #include "system/filesys.h"
100 #include "include/ntioctl.h"
101 #include "smbd/proto.h"
102 #include <ccan/hash/hash.h>
103 #include "util_tdb.h"
105 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
106 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
108 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
109 size_t **poffsets,
110 unsigned *pnum_offsets)
112 unsigned num_offsets;
113 size_t *offsets;
114 const char *p;
116 num_offsets = 0;
118 p = str;
119 while ((p = strchr(p, '/')) != NULL) {
120 num_offsets += 1;
121 p += 1;
124 offsets = talloc_array(mem_ctx, size_t, num_offsets);
125 if (offsets == NULL) {
126 return false;
129 p = str;
130 num_offsets = 0;
131 while ((p = strchr(p, '/')) != NULL) {
132 offsets[num_offsets] = p-str;
133 num_offsets += 1;
134 p += 1;
137 *poffsets = offsets;
138 *pnum_offsets = num_offsets;
139 return true;
142 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
143 struct vfs_handle_struct *handle,
144 time_t snapshot)
146 struct tm snap_tm;
147 fstring gmt;
148 size_t gmt_len;
150 if (localtime_r(&snapshot, &snap_tm) == 0) {
151 DEBUG(10, ("gmtime_r failed\n"));
152 return NULL;
154 gmt_len = strftime(gmt, sizeof(gmt),
155 lp_parm_const_string(SNUM(handle->conn), "shadow",
156 "format", GMT_FORMAT),
157 &snap_tm);
158 if (gmt_len == 0) {
159 DEBUG(10, ("strftime failed\n"));
160 return NULL;
162 return talloc_asprintf(talloc_tos(), "/%s/%s",
163 lp_parm_const_string(
164 SNUM(handle->conn), "shadow", "snapdir",
165 ".snapshots"),
166 gmt);
169 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
170 struct vfs_handle_struct *handle,
171 const char *name,
172 time_t *ptimestamp,
173 char **pstripped)
175 struct tm tm;
176 time_t timestamp;
177 const char *p;
178 char *q;
179 char *stripped;
180 size_t rest_len, dst_len;
182 p = strstr_m(name, "@GMT-");
183 if (p == NULL) {
184 goto no_snapshot;
186 if ((p > name) && (p[-1] != '/')) {
187 goto no_snapshot;
189 q = strptime(p, GMT_FORMAT, &tm);
190 if (q == NULL) {
191 goto no_snapshot;
193 tm.tm_isdst = -1;
194 timestamp = mktime(&tm);
195 if (timestamp == (time_t)-1) {
196 goto no_snapshot;
198 if ((p == name) && (q[0] == '\0')) {
199 if (pstripped != NULL) {
200 stripped = talloc_strdup(mem_ctx, "");
201 if (stripped == NULL) {
202 return false;
204 *pstripped = stripped;
206 *ptimestamp = timestamp;
207 return true;
209 if (q[0] != '/') {
210 goto no_snapshot;
212 q += 1;
214 rest_len = strlen(q);
215 dst_len = (p-name) + rest_len;
217 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
218 false)) {
219 char *insert;
220 bool have_insert;
221 insert = shadow_copy2_insert_string(talloc_tos(), handle,
222 timestamp);
223 if (insert == NULL) {
224 errno = ENOMEM;
225 return false;
228 have_insert = (strstr(name, insert+1) != NULL);
229 TALLOC_FREE(insert);
230 if (have_insert) {
231 goto no_snapshot;
235 if (pstripped != NULL) {
236 stripped = talloc_array(mem_ctx, char, dst_len+1);
237 if (stripped == NULL) {
238 errno = ENOMEM;
239 return false;
241 if (p > name) {
242 memcpy(stripped, name, p-name);
244 if (rest_len > 0) {
245 memcpy(stripped + (p-name), q, rest_len);
247 stripped[dst_len] = '\0';
248 *pstripped = stripped;
250 *ptimestamp = timestamp;
251 return true;
252 no_snapshot:
253 *ptimestamp = 0;
254 return true;
257 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
258 vfs_handle_struct *handle)
260 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
261 dev_t dev;
262 struct stat st;
263 char *p;
265 if (stat(path, &st) != 0) {
266 talloc_free(path);
267 return NULL;
270 dev = st.st_dev;
272 while ((p = strrchr(path, '/')) && p > path) {
273 *p = 0;
274 if (stat(path, &st) != 0) {
275 talloc_free(path);
276 return NULL;
278 if (st.st_dev != dev) {
279 *p = '/';
280 break;
284 return path;
287 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
288 struct vfs_handle_struct *handle,
289 const char *name, time_t timestamp)
291 struct smb_filename converted_fname;
292 char *result = NULL;
293 size_t *slashes = NULL;
294 unsigned num_slashes;
295 char *path = NULL;
296 size_t pathlen;
297 char *insert = NULL;
298 char *converted = NULL;
299 size_t insertlen;
300 int i, saved_errno;
301 size_t min_offset;
303 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
304 name);
305 if (path == NULL) {
306 errno = ENOMEM;
307 goto fail;
309 pathlen = talloc_get_size(path)-1;
311 DEBUG(10, ("converting %s\n", path));
313 if (!shadow_copy2_find_slashes(talloc_tos(), path,
314 &slashes, &num_slashes)) {
315 goto fail;
317 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
318 if (insert == NULL) {
319 goto fail;
321 insertlen = talloc_get_size(insert)-1;
322 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
323 if (converted == NULL) {
324 goto fail;
327 if (path[pathlen-1] != '/') {
329 * Append a fake slash to find the snapshot root
331 size_t *tmp;
332 tmp = talloc_realloc(talloc_tos(), slashes,
333 size_t, num_slashes+1);
334 if (tmp == NULL) {
335 goto fail;
337 slashes = tmp;
338 slashes[num_slashes] = pathlen;
339 num_slashes += 1;
342 min_offset = 0;
344 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
345 false)) {
346 char *mount_point;
348 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
349 handle);
350 if (mount_point == NULL) {
351 goto fail;
353 min_offset = strlen(mount_point);
354 TALLOC_FREE(mount_point);
357 memcpy(converted, path, pathlen+1);
358 converted[pathlen+insertlen] = '\0';
360 ZERO_STRUCT(converted_fname);
361 converted_fname.base_name = converted;
363 for (i = num_slashes-1; i>=0; i--) {
364 int ret;
365 size_t offset;
367 offset = slashes[i];
369 if (offset < min_offset) {
370 errno = ENOENT;
371 goto fail;
374 memcpy(converted+offset, insert, insertlen);
376 offset += insertlen;
377 memcpy(converted+offset, path + slashes[i],
378 pathlen - slashes[i]);
380 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
382 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
383 ret, ret == 0 ? "ok" : strerror(errno)));
384 if (ret == 0) {
385 /* success */
386 break;
388 if (errno == ENOTDIR) {
390 * This is a valid condition: We appended the
391 * .snaphots/@GMT.. to a file name. Just try
392 * with the upper levels.
394 continue;
396 if (errno != ENOENT) {
397 /* Other problem than "not found" */
398 goto fail;
402 if (i >= 0) {
404 * Found something
406 DEBUG(10, ("Found %s\n", converted));
407 result = converted;
408 converted = NULL;
409 } else {
410 errno = ENOENT;
412 fail:
413 saved_errno = errno;
414 TALLOC_FREE(converted);
415 TALLOC_FREE(insert);
416 TALLOC_FREE(slashes);
417 TALLOC_FREE(path);
418 errno = saved_errno;
419 return result;
423 modify a sbuf return to ensure that inodes in the shadow directory
424 are different from those in the main directory
426 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
427 SMB_STRUCT_STAT *sbuf)
429 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
430 /* some snapshot systems, like GPFS, return the name
431 device:inode for the snapshot files as the current
432 files. That breaks the 'restore' button in the shadow copy
433 GUI, as the client gets a sharing violation.
435 This is a crude way of allowing both files to be
436 open at once. It has a slight chance of inode
437 number collision, but I can't see a better approach
438 without significant VFS changes
440 uint32_t shash;
442 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
443 if (shash == 0) {
444 shash = 1;
446 sbuf->st_ex_ino ^= shash;
450 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
451 const char *fname,
452 const char *mask,
453 uint32 attr)
455 time_t timestamp;
456 char *stripped;
457 DIR *ret;
458 int saved_errno;
459 char *conv;
461 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
462 &timestamp, &stripped)) {
463 return NULL;
465 if (timestamp == 0) {
466 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
468 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
469 TALLOC_FREE(stripped);
470 if (conv == NULL) {
471 return NULL;
473 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
474 saved_errno = errno;
475 TALLOC_FREE(conv);
476 errno = saved_errno;
477 return ret;
480 static int shadow_copy2_rename(vfs_handle_struct *handle,
481 const struct smb_filename *smb_fname_src,
482 const struct smb_filename *smb_fname_dst)
484 time_t timestamp_src, timestamp_dst;
486 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
487 smb_fname_src->base_name,
488 &timestamp_src, NULL)) {
489 return -1;
491 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
492 smb_fname_dst->base_name,
493 &timestamp_dst, NULL)) {
494 return -1;
496 if (timestamp_src != 0) {
497 errno = EXDEV;
498 return -1;
500 if (timestamp_dst != 0) {
501 errno = EROFS;
502 return -1;
504 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
507 static int shadow_copy2_symlink(vfs_handle_struct *handle,
508 const char *oldname, const char *newname)
510 time_t timestamp_old, timestamp_new;
512 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
513 &timestamp_old, NULL)) {
514 return -1;
516 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
517 &timestamp_new, NULL)) {
518 return -1;
520 if ((timestamp_old != 0) || (timestamp_new != 0)) {
521 errno = EROFS;
522 return -1;
524 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
527 static int shadow_copy2_link(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_LINK(handle, oldname, newname);
547 static int shadow_copy2_stat(vfs_handle_struct *handle,
548 struct smb_filename *smb_fname)
550 time_t timestamp;
551 char *stripped, *tmp;
552 int ret, saved_errno;
554 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
555 smb_fname->base_name,
556 &timestamp, &stripped)) {
557 return -1;
559 if (timestamp == 0) {
560 return SMB_VFS_NEXT_STAT(handle, smb_fname);
563 tmp = smb_fname->base_name;
564 smb_fname->base_name = shadow_copy2_convert(
565 talloc_tos(), handle, stripped, timestamp);
566 TALLOC_FREE(stripped);
568 if (smb_fname->base_name == NULL) {
569 smb_fname->base_name = tmp;
570 return -1;
573 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
574 saved_errno = errno;
576 TALLOC_FREE(smb_fname->base_name);
577 smb_fname->base_name = tmp;
579 if (ret == 0) {
580 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
582 errno = saved_errno;
583 return ret;
586 static int shadow_copy2_lstat(vfs_handle_struct *handle,
587 struct smb_filename *smb_fname)
589 time_t timestamp;
590 char *stripped, *tmp;
591 int ret, saved_errno;
593 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
594 smb_fname->base_name,
595 &timestamp, &stripped)) {
596 return -1;
598 if (timestamp == 0) {
599 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
602 tmp = smb_fname->base_name;
603 smb_fname->base_name = shadow_copy2_convert(
604 talloc_tos(), handle, stripped, timestamp);
605 TALLOC_FREE(stripped);
607 if (smb_fname->base_name == NULL) {
608 smb_fname->base_name = tmp;
609 return -1;
612 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
613 saved_errno = errno;
615 TALLOC_FREE(smb_fname->base_name);
616 smb_fname->base_name = tmp;
618 if (ret == 0) {
619 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
621 errno = saved_errno;
622 return ret;
625 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
626 SMB_STRUCT_STAT *sbuf)
628 time_t timestamp;
629 int ret;
631 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
632 if (ret == -1) {
633 return ret;
635 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
636 fsp->fsp_name->base_name,
637 &timestamp, NULL)) {
638 return 0;
640 if (timestamp != 0) {
641 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
643 return 0;
646 static int shadow_copy2_open(vfs_handle_struct *handle,
647 struct smb_filename *smb_fname, files_struct *fsp,
648 int flags, mode_t mode)
650 time_t timestamp;
651 char *stripped, *tmp;
652 int ret, saved_errno;
654 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
655 smb_fname->base_name,
656 &timestamp, &stripped)) {
657 return -1;
659 if (timestamp == 0) {
660 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
663 tmp = smb_fname->base_name;
664 smb_fname->base_name = shadow_copy2_convert(
665 talloc_tos(), handle, stripped, timestamp);
666 TALLOC_FREE(stripped);
668 if (smb_fname->base_name == NULL) {
669 smb_fname->base_name = tmp;
670 return -1;
673 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
674 saved_errno = errno;
676 TALLOC_FREE(smb_fname->base_name);
677 smb_fname->base_name = tmp;
679 errno = saved_errno;
680 return ret;
683 static int shadow_copy2_unlink(vfs_handle_struct *handle,
684 const struct smb_filename *smb_fname)
686 time_t timestamp;
687 char *stripped;
688 int ret, saved_errno;
689 struct smb_filename *conv;
690 NTSTATUS status;
692 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
693 smb_fname->base_name,
694 &timestamp, &stripped)) {
695 return -1;
697 if (timestamp == 0) {
698 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
700 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
701 if (!NT_STATUS_IS_OK(status)) {
702 errno = ENOMEM;
703 return -1;
705 conv->base_name = shadow_copy2_convert(
706 conv, handle, stripped, timestamp);
707 TALLOC_FREE(stripped);
708 if (conv->base_name == NULL) {
709 return -1;
711 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
712 saved_errno = errno;
713 TALLOC_FREE(conv);
714 errno = saved_errno;
715 return ret;
718 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
719 mode_t mode)
721 time_t timestamp;
722 char *stripped;
723 int ret, saved_errno;
724 char *conv;
726 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
727 &timestamp, &stripped)) {
728 return -1;
730 if (timestamp == 0) {
731 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
733 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
734 TALLOC_FREE(stripped);
735 if (conv == NULL) {
736 return -1;
738 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
739 saved_errno = errno;
740 TALLOC_FREE(conv);
741 errno = saved_errno;
742 return ret;
745 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
746 uid_t uid, gid_t gid)
748 time_t timestamp;
749 char *stripped;
750 int ret, saved_errno;
751 char *conv;
753 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
754 &timestamp, &stripped)) {
755 return -1;
757 if (timestamp == 0) {
758 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
760 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
761 TALLOC_FREE(stripped);
762 if (conv == NULL) {
763 return -1;
765 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
766 saved_errno = errno;
767 TALLOC_FREE(conv);
768 errno = saved_errno;
769 return ret;
772 static int shadow_copy2_chdir(vfs_handle_struct *handle,
773 const char *fname)
775 time_t timestamp;
776 char *stripped;
777 int ret, saved_errno;
778 char *conv;
780 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
781 &timestamp, &stripped)) {
782 return -1;
784 if (timestamp == 0) {
785 return SMB_VFS_NEXT_CHDIR(handle, fname);
787 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
788 TALLOC_FREE(stripped);
789 if (conv == NULL) {
790 return -1;
792 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
793 saved_errno = errno;
794 TALLOC_FREE(conv);
795 errno = saved_errno;
796 return ret;
799 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
800 const struct smb_filename *smb_fname,
801 struct smb_file_time *ft)
803 time_t timestamp;
804 char *stripped;
805 int ret, saved_errno;
806 struct smb_filename *conv;
807 NTSTATUS status;
809 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
810 smb_fname->base_name,
811 &timestamp, &stripped)) {
812 return -1;
814 if (timestamp == 0) {
815 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
817 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
818 if (!NT_STATUS_IS_OK(status)) {
819 errno = ENOMEM;
820 return -1;
822 conv->base_name = shadow_copy2_convert(
823 conv, handle, stripped, timestamp);
824 TALLOC_FREE(stripped);
825 if (conv->base_name == NULL) {
826 return -1;
828 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
829 saved_errno = errno;
830 TALLOC_FREE(conv);
831 errno = saved_errno;
832 return ret;
835 static int shadow_copy2_readlink(vfs_handle_struct *handle,
836 const char *fname, char *buf, size_t bufsiz)
838 time_t timestamp;
839 char *stripped;
840 int ret, saved_errno;
841 char *conv;
843 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
844 &timestamp, &stripped)) {
845 return -1;
847 if (timestamp == 0) {
848 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
850 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
851 TALLOC_FREE(stripped);
852 if (conv == NULL) {
853 return -1;
855 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
856 saved_errno = errno;
857 TALLOC_FREE(conv);
858 errno = saved_errno;
859 return ret;
862 static int shadow_copy2_mknod(vfs_handle_struct *handle,
863 const char *fname, mode_t mode, SMB_DEV_T dev)
865 time_t timestamp;
866 char *stripped;
867 int ret, saved_errno;
868 char *conv;
870 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
871 &timestamp, &stripped)) {
872 return -1;
874 if (timestamp == 0) {
875 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
877 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
878 TALLOC_FREE(stripped);
879 if (conv == NULL) {
880 return -1;
882 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
883 saved_errno = errno;
884 TALLOC_FREE(conv);
885 errno = saved_errno;
886 return ret;
889 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
890 const char *fname)
892 time_t timestamp;
893 char *stripped = NULL;
894 char *tmp = NULL;
895 char *result = NULL;
896 char *inserted = NULL;
897 char *inserted_to, *inserted_end;
898 int saved_errno;
900 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
901 &timestamp, &stripped)) {
902 goto done;
904 if (timestamp == 0) {
905 return SMB_VFS_NEXT_REALPATH(handle, fname);
908 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
909 if (tmp == NULL) {
910 goto done;
913 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
914 if (result == NULL) {
915 goto done;
919 * Take away what we've inserted. This removes the @GMT-thingy
920 * completely, but will give a path under the share root.
922 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
923 if (inserted == NULL) {
924 goto done;
926 inserted_to = strstr_m(result, inserted);
927 if (inserted_to == NULL) {
928 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
929 goto done;
931 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
932 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
934 done:
935 saved_errno = errno;
936 TALLOC_FREE(inserted);
937 TALLOC_FREE(tmp);
938 TALLOC_FREE(stripped);
939 errno = saved_errno;
940 return result;
943 static char *have_snapdir(struct vfs_handle_struct *handle,
944 const char *path)
946 struct smb_filename smb_fname;
947 int ret;
949 ZERO_STRUCT(smb_fname);
950 smb_fname.base_name = talloc_asprintf(
951 talloc_tos(), "%s/%s", path,
952 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
953 ".snapshots"));
954 if (smb_fname.base_name == NULL) {
955 return NULL;
958 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
959 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
960 return smb_fname.base_name;
962 TALLOC_FREE(smb_fname.base_name);
963 return NULL;
966 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
967 struct vfs_handle_struct *handle,
968 struct smb_filename *smb_fname)
970 char *path, *p;
971 char *snapdir;
973 path = talloc_asprintf(mem_ctx, "%s/%s",
974 handle->conn->connectpath,
975 smb_fname->base_name);
976 if (path == NULL) {
977 return NULL;
980 snapdir = have_snapdir(handle, path);
981 if (snapdir != NULL) {
982 TALLOC_FREE(path);
983 return snapdir;
986 while ((p = strrchr(path, '/')) && (p > path)) {
988 p[0] = '\0';
990 snapdir = have_snapdir(handle, path);
991 if (snapdir != NULL) {
992 TALLOC_FREE(path);
993 return snapdir;
996 TALLOC_FREE(path);
997 return NULL;
1000 static bool shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
1001 vfs_handle_struct *handle,
1002 const char *name,
1003 char *gmt, size_t gmt_len)
1005 struct tm timestamp;
1006 time_t timestamp_t;
1007 const char *fmt;
1009 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1010 "format", GMT_FORMAT);
1012 ZERO_STRUCT(timestamp);
1013 if (strptime(name, fmt, &timestamp) == NULL) {
1014 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1015 fmt, name));
1016 return false;
1019 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1021 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1022 timestamp.tm_isdst = -1;
1023 timestamp_t = mktime(&timestamp);
1024 gmtime_r(&timestamp_t, &timestamp);
1026 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1027 return true;
1030 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1032 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1035 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1037 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1041 sort the shadow copy data in ascending or descending order
1043 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1044 struct shadow_copy_data *shadow_copy2_data)
1046 int (*cmpfunc)(const void *, const void *);
1047 const char *sort;
1049 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1050 "sort", NULL);
1051 if (sort == NULL) {
1052 return;
1055 if (strcmp(sort, "asc") == 0) {
1056 cmpfunc = shadow_copy2_label_cmp_asc;
1057 } else if (strcmp(sort, "desc") == 0) {
1058 cmpfunc = shadow_copy2_label_cmp_desc;
1059 } else {
1060 return;
1063 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1064 shadow_copy2_data->labels)
1066 TYPESAFE_QSORT(shadow_copy2_data->labels,
1067 shadow_copy2_data->num_volumes,
1068 cmpfunc);
1071 return;
1074 static int shadow_copy2_get_shadow_copy_data(
1075 vfs_handle_struct *handle, files_struct *fsp,
1076 struct shadow_copy_data *shadow_copy2_data,
1077 bool labels)
1079 DIR *p;
1080 const char *snapdir;
1081 struct dirent *d;
1082 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1084 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1085 if (snapdir == NULL) {
1086 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1087 handle->conn->connectpath));
1088 errno = EINVAL;
1089 talloc_free(tmp_ctx);
1090 return -1;
1093 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1095 if (!p) {
1096 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1097 " - %s\n", snapdir, strerror(errno)));
1098 talloc_free(tmp_ctx);
1099 errno = ENOSYS;
1100 return -1;
1103 shadow_copy2_data->num_volumes = 0;
1104 shadow_copy2_data->labels = NULL;
1106 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1107 char snapshot[GMT_NAME_LEN+1];
1108 SHADOW_COPY_LABEL *tlabels;
1111 * ignore names not of the right form in the snapshot
1112 * directory
1114 if (!shadow_copy2_snapshot_to_gmt(
1115 tmp_ctx, handle, d->d_name,
1116 snapshot, sizeof(snapshot))) {
1118 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1119 "ignoring %s\n", d->d_name));
1120 continue;
1122 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1123 d->d_name, snapshot));
1125 if (!labels) {
1126 /* the caller doesn't want the labels */
1127 shadow_copy2_data->num_volumes++;
1128 continue;
1131 tlabels = talloc_realloc(shadow_copy2_data,
1132 shadow_copy2_data->labels,
1133 SHADOW_COPY_LABEL,
1134 shadow_copy2_data->num_volumes+1);
1135 if (tlabels == NULL) {
1136 DEBUG(0,("shadow_copy2: out of memory\n"));
1137 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1138 talloc_free(tmp_ctx);
1139 return -1;
1142 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1143 sizeof(*tlabels));
1145 shadow_copy2_data->num_volumes++;
1146 shadow_copy2_data->labels = tlabels;
1149 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1151 shadow_copy2_sort_data(handle, shadow_copy2_data);
1153 talloc_free(tmp_ctx);
1154 return 0;
1157 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1158 struct files_struct *fsp,
1159 uint32 security_info,
1160 struct security_descriptor **ppdesc)
1162 time_t timestamp;
1163 char *stripped;
1164 NTSTATUS status;
1165 char *conv;
1167 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1168 fsp->fsp_name->base_name,
1169 &timestamp, &stripped)) {
1170 return map_nt_error_from_unix(errno);
1172 if (timestamp == 0) {
1173 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1174 ppdesc);
1176 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1177 TALLOC_FREE(stripped);
1178 if (conv == NULL) {
1179 return map_nt_error_from_unix(errno);
1181 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1182 TALLOC_FREE(conv);
1183 return status;
1186 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1187 const char *fname,
1188 uint32 security_info,
1189 struct security_descriptor **ppdesc)
1191 time_t timestamp;
1192 char *stripped;
1193 NTSTATUS status;
1194 char *conv;
1196 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1197 &timestamp, &stripped)) {
1198 return map_nt_error_from_unix(errno);
1200 if (timestamp == 0) {
1201 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
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, ppdesc);
1210 TALLOC_FREE(conv);
1211 return status;
1214 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1215 const char *fname, mode_t mode)
1217 time_t timestamp;
1218 char *stripped;
1219 int ret, saved_errno;
1220 char *conv;
1222 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1223 &timestamp, &stripped)) {
1224 return -1;
1226 if (timestamp == 0) {
1227 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1229 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1230 TALLOC_FREE(stripped);
1231 if (conv == NULL) {
1232 return -1;
1234 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1235 saved_errno = errno;
1236 TALLOC_FREE(conv);
1237 errno = saved_errno;
1238 return ret;
1241 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1243 time_t timestamp;
1244 char *stripped;
1245 int ret, saved_errno;
1246 char *conv;
1248 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1249 &timestamp, &stripped)) {
1250 return -1;
1252 if (timestamp == 0) {
1253 return SMB_VFS_NEXT_RMDIR(handle, fname);
1255 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1256 TALLOC_FREE(stripped);
1257 if (conv == NULL) {
1258 return -1;
1260 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1261 saved_errno = errno;
1262 TALLOC_FREE(conv);
1263 errno = saved_errno;
1264 return ret;
1267 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1268 unsigned int flags)
1270 time_t timestamp;
1271 char *stripped;
1272 int ret, saved_errno;
1273 char *conv;
1275 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1276 &timestamp, &stripped)) {
1277 return -1;
1279 if (timestamp == 0) {
1280 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1282 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1283 TALLOC_FREE(stripped);
1284 if (conv == NULL) {
1285 return -1;
1287 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1288 saved_errno = errno;
1289 TALLOC_FREE(conv);
1290 errno = saved_errno;
1291 return ret;
1294 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1295 const char *fname, const char *aname,
1296 void *value, size_t size)
1298 time_t timestamp;
1299 char *stripped;
1300 ssize_t ret;
1301 int saved_errno;
1302 char *conv;
1304 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1305 &timestamp, &stripped)) {
1306 return -1;
1308 if (timestamp == 0) {
1309 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1310 size);
1312 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1313 TALLOC_FREE(stripped);
1314 if (conv == NULL) {
1315 return -1;
1317 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1318 saved_errno = errno;
1319 TALLOC_FREE(conv);
1320 errno = saved_errno;
1321 return ret;
1324 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1325 const char *fname,
1326 char *list, size_t size)
1328 time_t timestamp;
1329 char *stripped;
1330 ssize_t ret;
1331 int saved_errno;
1332 char *conv;
1334 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1335 &timestamp, &stripped)) {
1336 return -1;
1338 if (timestamp == 0) {
1339 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1341 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1342 TALLOC_FREE(stripped);
1343 if (conv == NULL) {
1344 return -1;
1346 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1347 saved_errno = errno;
1348 TALLOC_FREE(conv);
1349 errno = saved_errno;
1350 return ret;
1353 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1354 const char *fname, const char *aname)
1356 time_t timestamp;
1357 char *stripped;
1358 int ret, saved_errno;
1359 char *conv;
1361 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1362 &timestamp, &stripped)) {
1363 return -1;
1365 if (timestamp == 0) {
1366 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1368 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1369 TALLOC_FREE(stripped);
1370 if (conv == NULL) {
1371 return -1;
1373 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1374 saved_errno = errno;
1375 TALLOC_FREE(conv);
1376 errno = saved_errno;
1377 return ret;
1380 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1381 const char *fname,
1382 const char *aname, const void *value,
1383 size_t size, int flags)
1385 time_t timestamp;
1386 char *stripped;
1387 ssize_t ret;
1388 int saved_errno;
1389 char *conv;
1391 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1392 &timestamp, &stripped)) {
1393 return -1;
1395 if (timestamp == 0) {
1396 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1397 flags);
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_SETXATTR(handle, conv, aname, value, size, flags);
1405 saved_errno = errno;
1406 TALLOC_FREE(conv);
1407 errno = saved_errno;
1408 return ret;
1411 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1412 const char *fname, mode_t mode)
1414 time_t timestamp;
1415 char *stripped;
1416 ssize_t ret;
1417 int saved_errno;
1418 char *conv;
1420 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1421 &timestamp, &stripped)) {
1422 return -1;
1424 if (timestamp == 0) {
1425 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1427 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1428 TALLOC_FREE(stripped);
1429 if (conv == NULL) {
1430 return -1;
1432 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1433 saved_errno = errno;
1434 TALLOC_FREE(conv);
1435 errno = saved_errno;
1436 return ret;
1439 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1440 const char *path,
1441 const char *name,
1442 TALLOC_CTX *mem_ctx,
1443 char **found_name)
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, path,
1452 &timestamp, &stripped)) {
1453 return -1;
1455 if (timestamp == 0) {
1456 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1457 mem_ctx, found_name);
1459 if (stripped[0] == '\0') {
1460 *found_name = talloc_strdup(mem_ctx, name);
1461 if (*found_name == NULL) {
1462 errno = ENOMEM;
1463 return -1;
1465 return 0;
1467 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1468 TALLOC_FREE(stripped);
1469 if (conv == NULL) {
1470 return -1;
1472 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1473 mem_ctx, found_name);
1474 saved_errno = errno;
1475 TALLOC_FREE(conv);
1476 errno = saved_errno;
1477 return ret;
1481 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1482 .opendir_fn = shadow_copy2_opendir,
1483 .rename_fn = shadow_copy2_rename,
1484 .link_fn = shadow_copy2_link,
1485 .symlink_fn = shadow_copy2_symlink,
1486 .stat_fn = shadow_copy2_stat,
1487 .lstat_fn = shadow_copy2_lstat,
1488 .fstat_fn = shadow_copy2_fstat,
1489 .open_fn = shadow_copy2_open,
1490 .unlink_fn = shadow_copy2_unlink,
1491 .chmod_fn = shadow_copy2_chmod,
1492 .chown_fn = shadow_copy2_chown,
1493 .chdir_fn = shadow_copy2_chdir,
1494 .ntimes_fn = shadow_copy2_ntimes,
1495 .readlink_fn = shadow_copy2_readlink,
1496 .mknod_fn = shadow_copy2_mknod,
1497 .realpath_fn = shadow_copy2_realpath,
1498 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1499 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1500 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1501 .mkdir_fn = shadow_copy2_mkdir,
1502 .rmdir_fn = shadow_copy2_rmdir,
1503 .getxattr_fn = shadow_copy2_getxattr,
1504 .listxattr_fn = shadow_copy2_listxattr,
1505 .removexattr_fn = shadow_copy2_removexattr,
1506 .setxattr_fn = shadow_copy2_setxattr,
1507 .chmod_acl_fn = shadow_copy2_chmod_acl,
1508 .chflags_fn = shadow_copy2_chflags,
1509 .get_real_filename_fn = shadow_copy2_get_real_filename,
1512 NTSTATUS vfs_shadow_copy2_init(void);
1513 NTSTATUS vfs_shadow_copy2_init(void)
1515 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1516 "shadow_copy2", &vfs_shadow_copy2_fns);