s3:smbd: change blocking.c to use fsp_fnum_dbg() for fsp->fnum logging.
[Samba/gebeck_regimport.git] / source3 / modules / vfs_shadow_copy2.c
blobbf61c9226227873a0ffc11ed8a45f3070fe6f8a6
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 <ccan/hash/hash.h>
102 #include "util_tdb.h"
104 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
105 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
107 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
108 size_t **poffsets,
109 unsigned *pnum_offsets)
111 unsigned num_offsets;
112 size_t *offsets;
113 const char *p;
115 num_offsets = 0;
117 p = str;
118 while ((p = strchr(p, '/')) != NULL) {
119 num_offsets += 1;
120 p += 1;
123 offsets = talloc_array(mem_ctx, size_t, num_offsets);
124 if (offsets == NULL) {
125 return false;
128 p = str;
129 num_offsets = 0;
130 while ((p = strchr(p, '/')) != NULL) {
131 offsets[num_offsets] = p-str;
132 num_offsets += 1;
133 p += 1;
136 *poffsets = offsets;
137 *pnum_offsets = num_offsets;
138 return true;
141 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
142 struct vfs_handle_struct *handle,
143 time_t snapshot)
145 struct tm snap_tm;
146 fstring gmt;
147 size_t gmt_len;
149 if (localtime_r(&snapshot, &snap_tm) == 0) {
150 DEBUG(10, ("gmtime_r failed\n"));
151 return NULL;
153 gmt_len = strftime(gmt, sizeof(gmt),
154 lp_parm_const_string(SNUM(handle->conn), "shadow",
155 "format", GMT_FORMAT),
156 &snap_tm);
157 if (gmt_len == 0) {
158 DEBUG(10, ("strftime failed\n"));
159 return NULL;
161 return talloc_asprintf(mem_ctx, "/%s/%s",
162 lp_parm_const_string(
163 SNUM(handle->conn), "shadow", "snapdir",
164 ".snapshots"),
165 gmt);
168 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
169 struct vfs_handle_struct *handle,
170 const char *name,
171 time_t *ptimestamp,
172 char **pstripped)
174 struct tm tm;
175 time_t timestamp;
176 const char *p;
177 char *q;
178 char *stripped;
179 size_t rest_len, dst_len;
181 p = strstr_m(name, "@GMT-");
182 if (p == NULL) {
183 goto no_snapshot;
185 if ((p > name) && (p[-1] != '/')) {
186 goto no_snapshot;
188 q = strptime(p, GMT_FORMAT, &tm);
189 if (q == NULL) {
190 goto no_snapshot;
192 tm.tm_isdst = -1;
193 timestamp = mktime(&tm);
194 if (timestamp == (time_t)-1) {
195 goto no_snapshot;
197 if ((p == name) && (q[0] == '\0')) {
198 if (pstripped != NULL) {
199 stripped = talloc_strdup(mem_ctx, "");
200 if (stripped == NULL) {
201 return false;
203 *pstripped = stripped;
205 *ptimestamp = timestamp;
206 return true;
208 if (q[0] != '/') {
209 goto no_snapshot;
211 q += 1;
213 rest_len = strlen(q);
214 dst_len = (p-name) + rest_len;
216 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
217 false)) {
218 char *insert;
219 bool have_insert;
220 insert = shadow_copy2_insert_string(talloc_tos(), handle,
221 timestamp);
222 if (insert == NULL) {
223 errno = ENOMEM;
224 return false;
227 have_insert = (strstr(name, insert+1) != NULL);
228 TALLOC_FREE(insert);
229 if (have_insert) {
230 goto no_snapshot;
234 if (pstripped != NULL) {
235 stripped = talloc_array(mem_ctx, char, dst_len+1);
236 if (stripped == NULL) {
237 errno = ENOMEM;
238 return false;
240 if (p > name) {
241 memcpy(stripped, name, p-name);
243 if (rest_len > 0) {
244 memcpy(stripped + (p-name), q, rest_len);
246 stripped[dst_len] = '\0';
247 *pstripped = stripped;
249 *ptimestamp = timestamp;
250 return true;
251 no_snapshot:
252 *ptimestamp = 0;
253 return true;
256 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
257 vfs_handle_struct *handle)
259 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
260 dev_t dev;
261 struct stat st;
262 char *p;
264 if (stat(path, &st) != 0) {
265 talloc_free(path);
266 return NULL;
269 dev = st.st_dev;
271 while ((p = strrchr(path, '/')) && p > path) {
272 *p = 0;
273 if (stat(path, &st) != 0) {
274 talloc_free(path);
275 return NULL;
277 if (st.st_dev != dev) {
278 *p = '/';
279 break;
283 return path;
286 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
287 struct vfs_handle_struct *handle,
288 const char *name, time_t timestamp)
290 struct smb_filename converted_fname;
291 char *result = NULL;
292 size_t *slashes = NULL;
293 unsigned num_slashes;
294 char *path = NULL;
295 size_t pathlen;
296 char *insert = NULL;
297 char *converted = NULL;
298 size_t insertlen;
299 int i, saved_errno;
300 size_t min_offset;
302 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
303 name);
304 if (path == NULL) {
305 errno = ENOMEM;
306 goto fail;
308 pathlen = talloc_get_size(path)-1;
310 DEBUG(10, ("converting %s\n", path));
312 if (!shadow_copy2_find_slashes(talloc_tos(), path,
313 &slashes, &num_slashes)) {
314 goto fail;
316 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
317 if (insert == NULL) {
318 goto fail;
320 insertlen = talloc_get_size(insert)-1;
321 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
322 if (converted == NULL) {
323 goto fail;
326 if (path[pathlen-1] != '/') {
328 * Append a fake slash to find the snapshot root
330 size_t *tmp;
331 tmp = talloc_realloc(talloc_tos(), slashes,
332 size_t, num_slashes+1);
333 if (tmp == NULL) {
334 goto fail;
336 slashes = tmp;
337 slashes[num_slashes] = pathlen;
338 num_slashes += 1;
341 min_offset = 0;
343 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
344 false)) {
345 char *mount_point;
347 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
348 handle);
349 if (mount_point == NULL) {
350 goto fail;
352 min_offset = strlen(mount_point);
353 TALLOC_FREE(mount_point);
356 memcpy(converted, path, pathlen+1);
357 converted[pathlen+insertlen] = '\0';
359 ZERO_STRUCT(converted_fname);
360 converted_fname.base_name = converted;
362 for (i = num_slashes-1; i>=0; i--) {
363 int ret;
364 size_t offset;
366 offset = slashes[i];
368 if (offset < min_offset) {
369 errno = ENOENT;
370 goto fail;
373 memcpy(converted+offset, insert, insertlen);
375 offset += insertlen;
376 memcpy(converted+offset, path + slashes[i],
377 pathlen - slashes[i]);
379 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
381 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
382 ret, ret == 0 ? "ok" : strerror(errno)));
383 if (ret == 0) {
384 /* success */
385 break;
387 if (errno == ENOTDIR) {
389 * This is a valid condition: We appended the
390 * .snaphots/@GMT.. to a file name. Just try
391 * with the upper levels.
393 continue;
395 if (errno != ENOENT) {
396 /* Other problem than "not found" */
397 goto fail;
401 if (i >= 0) {
403 * Found something
405 DEBUG(10, ("Found %s\n", converted));
406 result = converted;
407 converted = NULL;
408 } else {
409 errno = ENOENT;
411 fail:
412 saved_errno = errno;
413 TALLOC_FREE(converted);
414 TALLOC_FREE(insert);
415 TALLOC_FREE(slashes);
416 TALLOC_FREE(path);
417 errno = saved_errno;
418 return result;
422 modify a sbuf return to ensure that inodes in the shadow directory
423 are different from those in the main directory
425 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
426 SMB_STRUCT_STAT *sbuf)
428 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
429 /* some snapshot systems, like GPFS, return the name
430 device:inode for the snapshot files as the current
431 files. That breaks the 'restore' button in the shadow copy
432 GUI, as the client gets a sharing violation.
434 This is a crude way of allowing both files to be
435 open at once. It has a slight chance of inode
436 number collision, but I can't see a better approach
437 without significant VFS changes
439 uint32_t shash;
441 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
442 if (shash == 0) {
443 shash = 1;
445 sbuf->st_ex_ino ^= shash;
449 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
450 const char *fname,
451 const char *mask,
452 uint32 attr)
454 time_t timestamp;
455 char *stripped;
456 DIR *ret;
457 int saved_errno;
458 char *conv;
460 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
461 &timestamp, &stripped)) {
462 return NULL;
464 if (timestamp == 0) {
465 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
467 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
468 TALLOC_FREE(stripped);
469 if (conv == NULL) {
470 return NULL;
472 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
473 saved_errno = errno;
474 TALLOC_FREE(conv);
475 errno = saved_errno;
476 return ret;
479 static int shadow_copy2_rename(vfs_handle_struct *handle,
480 const struct smb_filename *smb_fname_src,
481 const struct smb_filename *smb_fname_dst)
483 time_t timestamp_src, timestamp_dst;
485 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
486 smb_fname_src->base_name,
487 &timestamp_src, NULL)) {
488 return -1;
490 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
491 smb_fname_dst->base_name,
492 &timestamp_dst, NULL)) {
493 return -1;
495 if (timestamp_src != 0) {
496 errno = EXDEV;
497 return -1;
499 if (timestamp_dst != 0) {
500 errno = EROFS;
501 return -1;
503 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
506 static int shadow_copy2_symlink(vfs_handle_struct *handle,
507 const char *oldname, const char *newname)
509 time_t timestamp_old, timestamp_new;
511 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
512 &timestamp_old, NULL)) {
513 return -1;
515 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
516 &timestamp_new, NULL)) {
517 return -1;
519 if ((timestamp_old != 0) || (timestamp_new != 0)) {
520 errno = EROFS;
521 return -1;
523 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
526 static int shadow_copy2_link(vfs_handle_struct *handle,
527 const char *oldname, const char *newname)
529 time_t timestamp_old, timestamp_new;
531 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
532 &timestamp_old, NULL)) {
533 return -1;
535 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
536 &timestamp_new, NULL)) {
537 return -1;
539 if ((timestamp_old != 0) || (timestamp_new != 0)) {
540 errno = EROFS;
541 return -1;
543 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
546 static int shadow_copy2_stat(vfs_handle_struct *handle,
547 struct smb_filename *smb_fname)
549 time_t timestamp;
550 char *stripped, *tmp;
551 int ret, saved_errno;
553 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
554 smb_fname->base_name,
555 &timestamp, &stripped)) {
556 return -1;
558 if (timestamp == 0) {
559 return SMB_VFS_NEXT_STAT(handle, smb_fname);
562 tmp = smb_fname->base_name;
563 smb_fname->base_name = shadow_copy2_convert(
564 talloc_tos(), handle, stripped, timestamp);
565 TALLOC_FREE(stripped);
567 if (smb_fname->base_name == NULL) {
568 smb_fname->base_name = tmp;
569 return -1;
572 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
573 saved_errno = errno;
575 TALLOC_FREE(smb_fname->base_name);
576 smb_fname->base_name = tmp;
578 if (ret == 0) {
579 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
581 errno = saved_errno;
582 return ret;
585 static int shadow_copy2_lstat(vfs_handle_struct *handle,
586 struct smb_filename *smb_fname)
588 time_t timestamp;
589 char *stripped, *tmp;
590 int ret, saved_errno;
592 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
593 smb_fname->base_name,
594 &timestamp, &stripped)) {
595 return -1;
597 if (timestamp == 0) {
598 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
601 tmp = smb_fname->base_name;
602 smb_fname->base_name = shadow_copy2_convert(
603 talloc_tos(), handle, stripped, timestamp);
604 TALLOC_FREE(stripped);
606 if (smb_fname->base_name == NULL) {
607 smb_fname->base_name = tmp;
608 return -1;
611 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
612 saved_errno = errno;
614 TALLOC_FREE(smb_fname->base_name);
615 smb_fname->base_name = tmp;
617 if (ret == 0) {
618 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
620 errno = saved_errno;
621 return ret;
624 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
625 SMB_STRUCT_STAT *sbuf)
627 time_t timestamp;
628 int ret;
630 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
631 if (ret == -1) {
632 return ret;
634 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
635 fsp->fsp_name->base_name,
636 &timestamp, NULL)) {
637 return 0;
639 if (timestamp != 0) {
640 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
642 return 0;
645 static int shadow_copy2_open(vfs_handle_struct *handle,
646 struct smb_filename *smb_fname, files_struct *fsp,
647 int flags, mode_t mode)
649 time_t timestamp;
650 char *stripped, *tmp;
651 int ret, saved_errno;
653 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
654 smb_fname->base_name,
655 &timestamp, &stripped)) {
656 return -1;
658 if (timestamp == 0) {
659 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
662 tmp = smb_fname->base_name;
663 smb_fname->base_name = shadow_copy2_convert(
664 talloc_tos(), handle, stripped, timestamp);
665 TALLOC_FREE(stripped);
667 if (smb_fname->base_name == NULL) {
668 smb_fname->base_name = tmp;
669 return -1;
672 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
673 saved_errno = errno;
675 TALLOC_FREE(smb_fname->base_name);
676 smb_fname->base_name = tmp;
678 errno = saved_errno;
679 return ret;
682 static int shadow_copy2_unlink(vfs_handle_struct *handle,
683 const struct smb_filename *smb_fname)
685 time_t timestamp;
686 char *stripped;
687 int ret, saved_errno;
688 struct smb_filename *conv;
689 NTSTATUS status;
691 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
692 smb_fname->base_name,
693 &timestamp, &stripped)) {
694 return -1;
696 if (timestamp == 0) {
697 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
699 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
700 if (!NT_STATUS_IS_OK(status)) {
701 errno = ENOMEM;
702 return -1;
704 conv->base_name = shadow_copy2_convert(
705 conv, handle, stripped, timestamp);
706 TALLOC_FREE(stripped);
707 if (conv->base_name == NULL) {
708 return -1;
710 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
711 saved_errno = errno;
712 TALLOC_FREE(conv);
713 errno = saved_errno;
714 return ret;
717 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
718 mode_t mode)
720 time_t timestamp;
721 char *stripped;
722 int ret, saved_errno;
723 char *conv;
725 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
726 &timestamp, &stripped)) {
727 return -1;
729 if (timestamp == 0) {
730 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
732 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
733 TALLOC_FREE(stripped);
734 if (conv == NULL) {
735 return -1;
737 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
738 saved_errno = errno;
739 TALLOC_FREE(conv);
740 errno = saved_errno;
741 return ret;
744 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
745 uid_t uid, gid_t gid)
747 time_t timestamp;
748 char *stripped;
749 int ret, saved_errno;
750 char *conv;
752 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
753 &timestamp, &stripped)) {
754 return -1;
756 if (timestamp == 0) {
757 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
759 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
760 TALLOC_FREE(stripped);
761 if (conv == NULL) {
762 return -1;
764 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
765 saved_errno = errno;
766 TALLOC_FREE(conv);
767 errno = saved_errno;
768 return ret;
771 static int shadow_copy2_chdir(vfs_handle_struct *handle,
772 const char *fname)
774 time_t timestamp;
775 char *stripped;
776 int ret, saved_errno;
777 char *conv;
779 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
780 &timestamp, &stripped)) {
781 return -1;
783 if (timestamp == 0) {
784 return SMB_VFS_NEXT_CHDIR(handle, fname);
786 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
787 TALLOC_FREE(stripped);
788 if (conv == NULL) {
789 return -1;
791 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
792 saved_errno = errno;
793 TALLOC_FREE(conv);
794 errno = saved_errno;
795 return ret;
798 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
799 const struct smb_filename *smb_fname,
800 struct smb_file_time *ft)
802 time_t timestamp;
803 char *stripped;
804 int ret, saved_errno;
805 struct smb_filename *conv;
806 NTSTATUS status;
808 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
809 smb_fname->base_name,
810 &timestamp, &stripped)) {
811 return -1;
813 if (timestamp == 0) {
814 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
816 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
817 if (!NT_STATUS_IS_OK(status)) {
818 errno = ENOMEM;
819 return -1;
821 conv->base_name = shadow_copy2_convert(
822 conv, handle, stripped, timestamp);
823 TALLOC_FREE(stripped);
824 if (conv->base_name == NULL) {
825 return -1;
827 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
828 saved_errno = errno;
829 TALLOC_FREE(conv);
830 errno = saved_errno;
831 return ret;
834 static int shadow_copy2_readlink(vfs_handle_struct *handle,
835 const char *fname, char *buf, size_t bufsiz)
837 time_t timestamp;
838 char *stripped;
839 int ret, saved_errno;
840 char *conv;
842 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
843 &timestamp, &stripped)) {
844 return -1;
846 if (timestamp == 0) {
847 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
849 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
850 TALLOC_FREE(stripped);
851 if (conv == NULL) {
852 return -1;
854 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
855 saved_errno = errno;
856 TALLOC_FREE(conv);
857 errno = saved_errno;
858 return ret;
861 static int shadow_copy2_mknod(vfs_handle_struct *handle,
862 const char *fname, mode_t mode, SMB_DEV_T dev)
864 time_t timestamp;
865 char *stripped;
866 int ret, saved_errno;
867 char *conv;
869 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
870 &timestamp, &stripped)) {
871 return -1;
873 if (timestamp == 0) {
874 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
876 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
877 TALLOC_FREE(stripped);
878 if (conv == NULL) {
879 return -1;
881 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
882 saved_errno = errno;
883 TALLOC_FREE(conv);
884 errno = saved_errno;
885 return ret;
888 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
889 const char *fname)
891 time_t timestamp;
892 char *stripped = NULL;
893 char *tmp = NULL;
894 char *result = NULL;
895 char *inserted = NULL;
896 char *inserted_to, *inserted_end;
897 int saved_errno;
899 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
900 &timestamp, &stripped)) {
901 goto done;
903 if (timestamp == 0) {
904 return SMB_VFS_NEXT_REALPATH(handle, fname);
907 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
908 if (tmp == NULL) {
909 goto done;
912 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
913 if (result == NULL) {
914 goto done;
918 * Take away what we've inserted. This removes the @GMT-thingy
919 * completely, but will give a path under the share root.
921 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
922 if (inserted == NULL) {
923 goto done;
925 inserted_to = strstr_m(result, inserted);
926 if (inserted_to == NULL) {
927 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
928 goto done;
930 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
931 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
933 done:
934 saved_errno = errno;
935 TALLOC_FREE(inserted);
936 TALLOC_FREE(tmp);
937 TALLOC_FREE(stripped);
938 errno = saved_errno;
939 return result;
942 static char *have_snapdir(struct vfs_handle_struct *handle,
943 const char *path)
945 struct smb_filename smb_fname;
946 int ret;
948 ZERO_STRUCT(smb_fname);
949 smb_fname.base_name = talloc_asprintf(
950 talloc_tos(), "%s/%s", path,
951 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
952 ".snapshots"));
953 if (smb_fname.base_name == NULL) {
954 return NULL;
957 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
958 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
959 return smb_fname.base_name;
961 TALLOC_FREE(smb_fname.base_name);
962 return NULL;
965 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
966 struct vfs_handle_struct *handle,
967 struct smb_filename *smb_fname)
969 char *path, *p;
970 char *snapdir;
972 path = talloc_asprintf(mem_ctx, "%s/%s",
973 handle->conn->connectpath,
974 smb_fname->base_name);
975 if (path == NULL) {
976 return NULL;
979 snapdir = have_snapdir(handle, path);
980 if (snapdir != NULL) {
981 TALLOC_FREE(path);
982 return snapdir;
985 while ((p = strrchr(path, '/')) && (p > path)) {
987 p[0] = '\0';
989 snapdir = have_snapdir(handle, path);
990 if (snapdir != NULL) {
991 TALLOC_FREE(path);
992 return snapdir;
995 TALLOC_FREE(path);
996 return NULL;
999 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1000 const char *name,
1001 char *gmt, size_t gmt_len)
1003 struct tm timestamp;
1004 time_t timestamp_t;
1005 const char *fmt;
1007 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1008 "format", GMT_FORMAT);
1010 ZERO_STRUCT(timestamp);
1011 if (strptime(name, fmt, &timestamp) == NULL) {
1012 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1013 fmt, name));
1014 return false;
1017 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1019 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1020 timestamp.tm_isdst = -1;
1021 timestamp_t = mktime(&timestamp);
1022 gmtime_r(&timestamp_t, &timestamp);
1024 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1025 return true;
1028 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1030 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1033 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1035 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1039 sort the shadow copy data in ascending or descending order
1041 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1042 struct shadow_copy_data *shadow_copy2_data)
1044 int (*cmpfunc)(const void *, const void *);
1045 const char *sort;
1047 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1048 "sort", "desc");
1049 if (sort == NULL) {
1050 return;
1053 if (strcmp(sort, "asc") == 0) {
1054 cmpfunc = shadow_copy2_label_cmp_asc;
1055 } else if (strcmp(sort, "desc") == 0) {
1056 cmpfunc = shadow_copy2_label_cmp_desc;
1057 } else {
1058 return;
1061 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1062 shadow_copy2_data->labels)
1064 TYPESAFE_QSORT(shadow_copy2_data->labels,
1065 shadow_copy2_data->num_volumes,
1066 cmpfunc);
1069 return;
1072 static int shadow_copy2_get_shadow_copy_data(
1073 vfs_handle_struct *handle, files_struct *fsp,
1074 struct shadow_copy_data *shadow_copy2_data,
1075 bool labels)
1077 DIR *p;
1078 const char *snapdir;
1079 struct dirent *d;
1080 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1082 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1083 if (snapdir == NULL) {
1084 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1085 handle->conn->connectpath));
1086 errno = EINVAL;
1087 talloc_free(tmp_ctx);
1088 return -1;
1091 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1093 if (!p) {
1094 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1095 " - %s\n", snapdir, strerror(errno)));
1096 talloc_free(tmp_ctx);
1097 errno = ENOSYS;
1098 return -1;
1101 shadow_copy2_data->num_volumes = 0;
1102 shadow_copy2_data->labels = NULL;
1104 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1105 char snapshot[GMT_NAME_LEN+1];
1106 SHADOW_COPY_LABEL *tlabels;
1109 * ignore names not of the right form in the snapshot
1110 * directory
1112 if (!shadow_copy2_snapshot_to_gmt(
1113 handle, d->d_name,
1114 snapshot, sizeof(snapshot))) {
1116 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1117 "ignoring %s\n", d->d_name));
1118 continue;
1120 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1121 d->d_name, snapshot));
1123 if (!labels) {
1124 /* the caller doesn't want the labels */
1125 shadow_copy2_data->num_volumes++;
1126 continue;
1129 tlabels = talloc_realloc(shadow_copy2_data,
1130 shadow_copy2_data->labels,
1131 SHADOW_COPY_LABEL,
1132 shadow_copy2_data->num_volumes+1);
1133 if (tlabels == NULL) {
1134 DEBUG(0,("shadow_copy2: out of memory\n"));
1135 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1136 talloc_free(tmp_ctx);
1137 return -1;
1140 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1141 sizeof(*tlabels));
1143 shadow_copy2_data->num_volumes++;
1144 shadow_copy2_data->labels = tlabels;
1147 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1149 shadow_copy2_sort_data(handle, shadow_copy2_data);
1151 talloc_free(tmp_ctx);
1152 return 0;
1155 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1156 struct files_struct *fsp,
1157 uint32 security_info,
1158 struct security_descriptor **ppdesc)
1160 time_t timestamp;
1161 char *stripped;
1162 NTSTATUS status;
1163 char *conv;
1165 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1166 fsp->fsp_name->base_name,
1167 &timestamp, &stripped)) {
1168 return map_nt_error_from_unix(errno);
1170 if (timestamp == 0) {
1171 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1172 ppdesc);
1174 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1175 TALLOC_FREE(stripped);
1176 if (conv == NULL) {
1177 return map_nt_error_from_unix(errno);
1179 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1180 TALLOC_FREE(conv);
1181 return status;
1184 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1185 const char *fname,
1186 uint32 security_info,
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, fname,
1195 &timestamp, &stripped)) {
1196 return map_nt_error_from_unix(errno);
1198 if (timestamp == 0) {
1199 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1200 ppdesc);
1202 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1203 TALLOC_FREE(stripped);
1204 if (conv == NULL) {
1205 return map_nt_error_from_unix(errno);
1207 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1208 TALLOC_FREE(conv);
1209 return status;
1212 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1213 const char *fname, mode_t mode)
1215 time_t timestamp;
1216 char *stripped;
1217 int ret, saved_errno;
1218 char *conv;
1220 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1221 &timestamp, &stripped)) {
1222 return -1;
1224 if (timestamp == 0) {
1225 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1227 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1228 TALLOC_FREE(stripped);
1229 if (conv == NULL) {
1230 return -1;
1232 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1233 saved_errno = errno;
1234 TALLOC_FREE(conv);
1235 errno = saved_errno;
1236 return ret;
1239 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1241 time_t timestamp;
1242 char *stripped;
1243 int ret, saved_errno;
1244 char *conv;
1246 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1247 &timestamp, &stripped)) {
1248 return -1;
1250 if (timestamp == 0) {
1251 return SMB_VFS_NEXT_RMDIR(handle, fname);
1253 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1254 TALLOC_FREE(stripped);
1255 if (conv == NULL) {
1256 return -1;
1258 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1259 saved_errno = errno;
1260 TALLOC_FREE(conv);
1261 errno = saved_errno;
1262 return ret;
1265 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1266 unsigned int flags)
1268 time_t timestamp;
1269 char *stripped;
1270 int ret, saved_errno;
1271 char *conv;
1273 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1274 &timestamp, &stripped)) {
1275 return -1;
1277 if (timestamp == 0) {
1278 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1280 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1281 TALLOC_FREE(stripped);
1282 if (conv == NULL) {
1283 return -1;
1285 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1286 saved_errno = errno;
1287 TALLOC_FREE(conv);
1288 errno = saved_errno;
1289 return ret;
1292 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1293 const char *fname, const char *aname,
1294 void *value, size_t size)
1296 time_t timestamp;
1297 char *stripped;
1298 ssize_t ret;
1299 int saved_errno;
1300 char *conv;
1302 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1303 &timestamp, &stripped)) {
1304 return -1;
1306 if (timestamp == 0) {
1307 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1308 size);
1310 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1311 TALLOC_FREE(stripped);
1312 if (conv == NULL) {
1313 return -1;
1315 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1316 saved_errno = errno;
1317 TALLOC_FREE(conv);
1318 errno = saved_errno;
1319 return ret;
1322 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1323 const char *fname,
1324 char *list, size_t size)
1326 time_t timestamp;
1327 char *stripped;
1328 ssize_t ret;
1329 int saved_errno;
1330 char *conv;
1332 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1333 &timestamp, &stripped)) {
1334 return -1;
1336 if (timestamp == 0) {
1337 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1339 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1340 TALLOC_FREE(stripped);
1341 if (conv == NULL) {
1342 return -1;
1344 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1345 saved_errno = errno;
1346 TALLOC_FREE(conv);
1347 errno = saved_errno;
1348 return ret;
1351 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1352 const char *fname, const char *aname)
1354 time_t timestamp;
1355 char *stripped;
1356 int ret, saved_errno;
1357 char *conv;
1359 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1360 &timestamp, &stripped)) {
1361 return -1;
1363 if (timestamp == 0) {
1364 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1366 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1367 TALLOC_FREE(stripped);
1368 if (conv == NULL) {
1369 return -1;
1371 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1372 saved_errno = errno;
1373 TALLOC_FREE(conv);
1374 errno = saved_errno;
1375 return ret;
1378 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1379 const char *fname,
1380 const char *aname, const void *value,
1381 size_t size, int flags)
1383 time_t timestamp;
1384 char *stripped;
1385 ssize_t ret;
1386 int saved_errno;
1387 char *conv;
1389 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1390 &timestamp, &stripped)) {
1391 return -1;
1393 if (timestamp == 0) {
1394 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1395 flags);
1397 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1398 TALLOC_FREE(stripped);
1399 if (conv == NULL) {
1400 return -1;
1402 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1403 saved_errno = errno;
1404 TALLOC_FREE(conv);
1405 errno = saved_errno;
1406 return ret;
1409 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1410 const char *fname, mode_t mode)
1412 time_t timestamp;
1413 char *stripped;
1414 ssize_t ret;
1415 int saved_errno;
1416 char *conv;
1418 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1419 &timestamp, &stripped)) {
1420 return -1;
1422 if (timestamp == 0) {
1423 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1425 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1426 TALLOC_FREE(stripped);
1427 if (conv == NULL) {
1428 return -1;
1430 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1431 saved_errno = errno;
1432 TALLOC_FREE(conv);
1433 errno = saved_errno;
1434 return ret;
1437 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1438 const char *path,
1439 const char *name,
1440 TALLOC_CTX *mem_ctx,
1441 char **found_name)
1443 time_t timestamp;
1444 char *stripped;
1445 ssize_t ret;
1446 int saved_errno;
1447 char *conv;
1449 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1450 &timestamp, &stripped)) {
1451 return -1;
1453 if (timestamp == 0) {
1454 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1455 mem_ctx, found_name);
1457 if (stripped[0] == '\0') {
1458 *found_name = talloc_strdup(mem_ctx, name);
1459 if (*found_name == NULL) {
1460 errno = ENOMEM;
1461 return -1;
1463 return 0;
1465 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1466 TALLOC_FREE(stripped);
1467 if (conv == NULL) {
1468 return -1;
1470 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1471 mem_ctx, found_name);
1472 saved_errno = errno;
1473 TALLOC_FREE(conv);
1474 errno = saved_errno;
1475 return ret;
1479 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1480 .opendir_fn = shadow_copy2_opendir,
1481 .rename_fn = shadow_copy2_rename,
1482 .link_fn = shadow_copy2_link,
1483 .symlink_fn = shadow_copy2_symlink,
1484 .stat_fn = shadow_copy2_stat,
1485 .lstat_fn = shadow_copy2_lstat,
1486 .fstat_fn = shadow_copy2_fstat,
1487 .open_fn = shadow_copy2_open,
1488 .unlink_fn = shadow_copy2_unlink,
1489 .chmod_fn = shadow_copy2_chmod,
1490 .chown_fn = shadow_copy2_chown,
1491 .chdir_fn = shadow_copy2_chdir,
1492 .ntimes_fn = shadow_copy2_ntimes,
1493 .readlink_fn = shadow_copy2_readlink,
1494 .mknod_fn = shadow_copy2_mknod,
1495 .realpath_fn = shadow_copy2_realpath,
1496 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1497 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1498 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1499 .mkdir_fn = shadow_copy2_mkdir,
1500 .rmdir_fn = shadow_copy2_rmdir,
1501 .getxattr_fn = shadow_copy2_getxattr,
1502 .listxattr_fn = shadow_copy2_listxattr,
1503 .removexattr_fn = shadow_copy2_removexattr,
1504 .setxattr_fn = shadow_copy2_setxattr,
1505 .chmod_acl_fn = shadow_copy2_chmod_acl,
1506 .chflags_fn = shadow_copy2_chflags,
1507 .get_real_filename_fn = shadow_copy2_get_real_filename,
1510 NTSTATUS vfs_shadow_copy2_init(void);
1511 NTSTATUS vfs_shadow_copy2_init(void)
1513 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1514 "shadow_copy2", &vfs_shadow_copy2_fns);