s3:smb2_close: add add smbd_smb2_close_send/recv as wrapper
[Samba/gebeck_regimport.git] / source3 / modules / vfs_shadow_copy2.c
blob0e6e14fc72c6f01fe7a7c42a13d403a47a538f5c
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(mem_ctx, "/%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(vfs_handle_struct *handle,
1001 const char *name,
1002 char *gmt, size_t gmt_len)
1004 struct tm timestamp;
1005 time_t timestamp_t;
1006 const char *fmt;
1008 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1009 "format", GMT_FORMAT);
1011 ZERO_STRUCT(timestamp);
1012 if (strptime(name, fmt, &timestamp) == NULL) {
1013 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1014 fmt, name));
1015 return false;
1018 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1020 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1021 timestamp.tm_isdst = -1;
1022 timestamp_t = mktime(&timestamp);
1023 gmtime_r(&timestamp_t, &timestamp);
1025 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1026 return true;
1029 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1031 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1034 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1036 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1040 sort the shadow copy data in ascending or descending order
1042 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1043 struct shadow_copy_data *shadow_copy2_data)
1045 int (*cmpfunc)(const void *, const void *);
1046 const char *sort;
1048 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1049 "sort", "desc");
1050 if (sort == NULL) {
1051 return;
1054 if (strcmp(sort, "asc") == 0) {
1055 cmpfunc = shadow_copy2_label_cmp_asc;
1056 } else if (strcmp(sort, "desc") == 0) {
1057 cmpfunc = shadow_copy2_label_cmp_desc;
1058 } else {
1059 return;
1062 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1063 shadow_copy2_data->labels)
1065 TYPESAFE_QSORT(shadow_copy2_data->labels,
1066 shadow_copy2_data->num_volumes,
1067 cmpfunc);
1070 return;
1073 static int shadow_copy2_get_shadow_copy_data(
1074 vfs_handle_struct *handle, files_struct *fsp,
1075 struct shadow_copy_data *shadow_copy2_data,
1076 bool labels)
1078 DIR *p;
1079 const char *snapdir;
1080 struct dirent *d;
1081 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1083 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1084 if (snapdir == NULL) {
1085 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1086 handle->conn->connectpath));
1087 errno = EINVAL;
1088 talloc_free(tmp_ctx);
1089 return -1;
1092 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1094 if (!p) {
1095 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1096 " - %s\n", snapdir, strerror(errno)));
1097 talloc_free(tmp_ctx);
1098 errno = ENOSYS;
1099 return -1;
1102 shadow_copy2_data->num_volumes = 0;
1103 shadow_copy2_data->labels = NULL;
1105 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1106 char snapshot[GMT_NAME_LEN+1];
1107 SHADOW_COPY_LABEL *tlabels;
1110 * ignore names not of the right form in the snapshot
1111 * directory
1113 if (!shadow_copy2_snapshot_to_gmt(
1114 handle, d->d_name,
1115 snapshot, sizeof(snapshot))) {
1117 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1118 "ignoring %s\n", d->d_name));
1119 continue;
1121 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1122 d->d_name, snapshot));
1124 if (!labels) {
1125 /* the caller doesn't want the labels */
1126 shadow_copy2_data->num_volumes++;
1127 continue;
1130 tlabels = talloc_realloc(shadow_copy2_data,
1131 shadow_copy2_data->labels,
1132 SHADOW_COPY_LABEL,
1133 shadow_copy2_data->num_volumes+1);
1134 if (tlabels == NULL) {
1135 DEBUG(0,("shadow_copy2: out of memory\n"));
1136 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1137 talloc_free(tmp_ctx);
1138 return -1;
1141 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1142 sizeof(*tlabels));
1144 shadow_copy2_data->num_volumes++;
1145 shadow_copy2_data->labels = tlabels;
1148 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1150 shadow_copy2_sort_data(handle, shadow_copy2_data);
1152 talloc_free(tmp_ctx);
1153 return 0;
1156 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1157 struct files_struct *fsp,
1158 uint32 security_info,
1159 struct security_descriptor **ppdesc)
1161 time_t timestamp;
1162 char *stripped;
1163 NTSTATUS status;
1164 char *conv;
1166 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1167 fsp->fsp_name->base_name,
1168 &timestamp, &stripped)) {
1169 return map_nt_error_from_unix(errno);
1171 if (timestamp == 0) {
1172 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1173 ppdesc);
1175 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1176 TALLOC_FREE(stripped);
1177 if (conv == NULL) {
1178 return map_nt_error_from_unix(errno);
1180 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1181 TALLOC_FREE(conv);
1182 return status;
1185 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1186 const char *fname,
1187 uint32 security_info,
1188 struct security_descriptor **ppdesc)
1190 time_t timestamp;
1191 char *stripped;
1192 NTSTATUS status;
1193 char *conv;
1195 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1196 &timestamp, &stripped)) {
1197 return map_nt_error_from_unix(errno);
1199 if (timestamp == 0) {
1200 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1201 ppdesc);
1203 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1204 TALLOC_FREE(stripped);
1205 if (conv == NULL) {
1206 return map_nt_error_from_unix(errno);
1208 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1209 TALLOC_FREE(conv);
1210 return status;
1213 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1214 const char *fname, mode_t mode)
1216 time_t timestamp;
1217 char *stripped;
1218 int ret, saved_errno;
1219 char *conv;
1221 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1222 &timestamp, &stripped)) {
1223 return -1;
1225 if (timestamp == 0) {
1226 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1228 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1229 TALLOC_FREE(stripped);
1230 if (conv == NULL) {
1231 return -1;
1233 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1234 saved_errno = errno;
1235 TALLOC_FREE(conv);
1236 errno = saved_errno;
1237 return ret;
1240 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1242 time_t timestamp;
1243 char *stripped;
1244 int ret, saved_errno;
1245 char *conv;
1247 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1248 &timestamp, &stripped)) {
1249 return -1;
1251 if (timestamp == 0) {
1252 return SMB_VFS_NEXT_RMDIR(handle, fname);
1254 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1255 TALLOC_FREE(stripped);
1256 if (conv == NULL) {
1257 return -1;
1259 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1260 saved_errno = errno;
1261 TALLOC_FREE(conv);
1262 errno = saved_errno;
1263 return ret;
1266 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1267 unsigned int flags)
1269 time_t timestamp;
1270 char *stripped;
1271 int ret, saved_errno;
1272 char *conv;
1274 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1275 &timestamp, &stripped)) {
1276 return -1;
1278 if (timestamp == 0) {
1279 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1281 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1282 TALLOC_FREE(stripped);
1283 if (conv == NULL) {
1284 return -1;
1286 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1287 saved_errno = errno;
1288 TALLOC_FREE(conv);
1289 errno = saved_errno;
1290 return ret;
1293 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1294 const char *fname, const char *aname,
1295 void *value, size_t size)
1297 time_t timestamp;
1298 char *stripped;
1299 ssize_t ret;
1300 int saved_errno;
1301 char *conv;
1303 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1304 &timestamp, &stripped)) {
1305 return -1;
1307 if (timestamp == 0) {
1308 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1309 size);
1311 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1312 TALLOC_FREE(stripped);
1313 if (conv == NULL) {
1314 return -1;
1316 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1317 saved_errno = errno;
1318 TALLOC_FREE(conv);
1319 errno = saved_errno;
1320 return ret;
1323 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1324 const char *fname,
1325 char *list, size_t size)
1327 time_t timestamp;
1328 char *stripped;
1329 ssize_t ret;
1330 int saved_errno;
1331 char *conv;
1333 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1334 &timestamp, &stripped)) {
1335 return -1;
1337 if (timestamp == 0) {
1338 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1340 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1341 TALLOC_FREE(stripped);
1342 if (conv == NULL) {
1343 return -1;
1345 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1346 saved_errno = errno;
1347 TALLOC_FREE(conv);
1348 errno = saved_errno;
1349 return ret;
1352 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1353 const char *fname, const char *aname)
1355 time_t timestamp;
1356 char *stripped;
1357 int ret, saved_errno;
1358 char *conv;
1360 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1361 &timestamp, &stripped)) {
1362 return -1;
1364 if (timestamp == 0) {
1365 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1367 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1368 TALLOC_FREE(stripped);
1369 if (conv == NULL) {
1370 return -1;
1372 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1373 saved_errno = errno;
1374 TALLOC_FREE(conv);
1375 errno = saved_errno;
1376 return ret;
1379 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1380 const char *fname,
1381 const char *aname, const void *value,
1382 size_t size, int flags)
1384 time_t timestamp;
1385 char *stripped;
1386 ssize_t ret;
1387 int saved_errno;
1388 char *conv;
1390 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1391 &timestamp, &stripped)) {
1392 return -1;
1394 if (timestamp == 0) {
1395 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1396 flags);
1398 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1399 TALLOC_FREE(stripped);
1400 if (conv == NULL) {
1401 return -1;
1403 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1404 saved_errno = errno;
1405 TALLOC_FREE(conv);
1406 errno = saved_errno;
1407 return ret;
1410 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1411 const char *fname, mode_t mode)
1413 time_t timestamp;
1414 char *stripped;
1415 ssize_t ret;
1416 int saved_errno;
1417 char *conv;
1419 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1420 &timestamp, &stripped)) {
1421 return -1;
1423 if (timestamp == 0) {
1424 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1426 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1427 TALLOC_FREE(stripped);
1428 if (conv == NULL) {
1429 return -1;
1431 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1432 saved_errno = errno;
1433 TALLOC_FREE(conv);
1434 errno = saved_errno;
1435 return ret;
1438 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1439 const char *path,
1440 const char *name,
1441 TALLOC_CTX *mem_ctx,
1442 char **found_name)
1444 time_t timestamp;
1445 char *stripped;
1446 ssize_t ret;
1447 int saved_errno;
1448 char *conv;
1450 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1451 &timestamp, &stripped)) {
1452 return -1;
1454 if (timestamp == 0) {
1455 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1456 mem_ctx, found_name);
1458 if (stripped[0] == '\0') {
1459 *found_name = talloc_strdup(mem_ctx, name);
1460 if (*found_name == NULL) {
1461 errno = ENOMEM;
1462 return -1;
1464 return 0;
1466 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1467 TALLOC_FREE(stripped);
1468 if (conv == NULL) {
1469 return -1;
1471 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1472 mem_ctx, found_name);
1473 saved_errno = errno;
1474 TALLOC_FREE(conv);
1475 errno = saved_errno;
1476 return ret;
1480 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1481 .opendir_fn = shadow_copy2_opendir,
1482 .rename_fn = shadow_copy2_rename,
1483 .link_fn = shadow_copy2_link,
1484 .symlink_fn = shadow_copy2_symlink,
1485 .stat_fn = shadow_copy2_stat,
1486 .lstat_fn = shadow_copy2_lstat,
1487 .fstat_fn = shadow_copy2_fstat,
1488 .open_fn = shadow_copy2_open,
1489 .unlink_fn = shadow_copy2_unlink,
1490 .chmod_fn = shadow_copy2_chmod,
1491 .chown_fn = shadow_copy2_chown,
1492 .chdir_fn = shadow_copy2_chdir,
1493 .ntimes_fn = shadow_copy2_ntimes,
1494 .readlink_fn = shadow_copy2_readlink,
1495 .mknod_fn = shadow_copy2_mknod,
1496 .realpath_fn = shadow_copy2_realpath,
1497 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1498 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1499 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1500 .mkdir_fn = shadow_copy2_mkdir,
1501 .rmdir_fn = shadow_copy2_rmdir,
1502 .getxattr_fn = shadow_copy2_getxattr,
1503 .listxattr_fn = shadow_copy2_listxattr,
1504 .removexattr_fn = shadow_copy2_removexattr,
1505 .setxattr_fn = shadow_copy2_setxattr,
1506 .chmod_acl_fn = shadow_copy2_chmod_acl,
1507 .chflags_fn = shadow_copy2_chflags,
1508 .get_real_filename_fn = shadow_copy2_get_real_filename,
1511 NTSTATUS vfs_shadow_copy2_init(void);
1512 NTSTATUS vfs_shadow_copy2_init(void)
1514 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1515 "shadow_copy2", &vfs_shadow_copy2_fns);