s3-vfs_shadow_copy2: Also accept a sscanf result
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob227453d27794303dd7538dc3846e691fdc44ff5d
1 /*
2 * Third attempt at a shadow copy module
4 * Copyright (C) Andrew Tridgell 2007 (portions taken from shadow_copy2)
5 * Copyright (C) Ed Plese 2009
6 * Copyright (C) Volker Lendecke 2011
7 * Copyright (C) Christian Ambach 2011
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 This is a 3rd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
28 following features:
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
46 rather than UTC.
48 Module options:
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
60 mount point
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:sscanf = yes/no (default is no)
89 The time is the unsigned long integer (%lu) in the format string
90 rather than a time strptime() can parse. The result must be a unix time_t
91 time.
93 shadow:localtime = yes/no (default is no)
95 This is an optional parameter that indicates whether the
96 snapshot names are in UTC/GMT or the local time.
99 The following command would generate a correctly formatted directory name
100 for use with the default parameters:
101 date -u +@GMT-%Y.%m.%d-%H.%M.%S
104 #include "includes.h"
105 #include "system/filesys.h"
106 #include "include/ntioctl.h"
107 #include <ccan/hash/hash.h>
108 #include "util_tdb.h"
110 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
111 #define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
113 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
114 size_t **poffsets,
115 unsigned *pnum_offsets)
117 unsigned num_offsets;
118 size_t *offsets;
119 const char *p;
121 num_offsets = 0;
123 p = str;
124 while ((p = strchr(p, '/')) != NULL) {
125 num_offsets += 1;
126 p += 1;
129 offsets = talloc_array(mem_ctx, size_t, num_offsets);
130 if (offsets == NULL) {
131 return false;
134 p = str;
135 num_offsets = 0;
136 while ((p = strchr(p, '/')) != NULL) {
137 offsets[num_offsets] = p-str;
138 num_offsets += 1;
139 p += 1;
142 *poffsets = offsets;
143 *pnum_offsets = num_offsets;
144 return true;
147 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
148 struct vfs_handle_struct *handle,
149 time_t snapshot)
151 const char *fmt;
152 struct tm snap_tm;
153 fstring gmt;
154 size_t gmt_len;
156 if (localtime_r(&snapshot, &snap_tm) == 0) {
157 DEBUG(10, ("gmtime_r failed\n"));
158 return NULL;
160 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
161 "format", GMT_FORMAT);
163 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
164 gmt_len = snprintf(gmt, sizeof(gmt), fmt,
165 (unsigned long)snapshot);
166 if (gmt_len == 0) {
167 DEBUG(10, ("snprintf failed\n"));
168 return NULL;
170 } else {
171 gmt_len = strftime(gmt, sizeof(gmt), fmt,
172 &snap_tm);
173 if (gmt_len == 0) {
174 DEBUG(10, ("strftime failed\n"));
175 return NULL;
178 return talloc_asprintf(mem_ctx, "/%s/%s",
179 lp_parm_const_string(
180 SNUM(handle->conn), "shadow", "snapdir",
181 ".snapshots"),
182 gmt);
185 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
186 struct vfs_handle_struct *handle,
187 const char *name,
188 time_t *ptimestamp,
189 char **pstripped)
191 struct tm tm;
192 time_t timestamp;
193 const char *p;
194 char *q;
195 char *stripped;
196 size_t rest_len, dst_len;
198 p = strstr_m(name, "@GMT-");
199 if (p == NULL) {
200 goto no_snapshot;
202 if ((p > name) && (p[-1] != '/')) {
203 goto no_snapshot;
205 q = strptime(p, GMT_FORMAT, &tm);
206 if (q == NULL) {
207 goto no_snapshot;
209 tm.tm_isdst = -1;
210 timestamp = mktime(&tm);
211 if (timestamp == (time_t)-1) {
212 goto no_snapshot;
214 if ((p == name) && (q[0] == '\0')) {
215 if (pstripped != NULL) {
216 stripped = talloc_strdup(mem_ctx, "");
217 if (stripped == NULL) {
218 return false;
220 *pstripped = stripped;
222 *ptimestamp = timestamp;
223 return true;
225 if (q[0] != '/') {
226 goto no_snapshot;
228 q += 1;
230 rest_len = strlen(q);
231 dst_len = (p-name) + rest_len;
233 if (lp_parm_bool(SNUM(handle->conn), "shadow", "snapdirseverywhere",
234 false)) {
235 char *insert;
236 bool have_insert;
237 insert = shadow_copy2_insert_string(talloc_tos(), handle,
238 timestamp);
239 if (insert == NULL) {
240 errno = ENOMEM;
241 return false;
244 have_insert = (strstr(name, insert+1) != NULL);
245 TALLOC_FREE(insert);
246 if (have_insert) {
247 goto no_snapshot;
251 if (pstripped != NULL) {
252 stripped = talloc_array(mem_ctx, char, dst_len+1);
253 if (stripped == NULL) {
254 errno = ENOMEM;
255 return false;
257 if (p > name) {
258 memcpy(stripped, name, p-name);
260 if (rest_len > 0) {
261 memcpy(stripped + (p-name), q, rest_len);
263 stripped[dst_len] = '\0';
264 *pstripped = stripped;
266 *ptimestamp = timestamp;
267 return true;
268 no_snapshot:
269 *ptimestamp = 0;
270 return true;
273 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
274 vfs_handle_struct *handle)
276 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
277 dev_t dev;
278 struct stat st;
279 char *p;
281 if (stat(path, &st) != 0) {
282 talloc_free(path);
283 return NULL;
286 dev = st.st_dev;
288 while ((p = strrchr(path, '/')) && p > path) {
289 *p = 0;
290 if (stat(path, &st) != 0) {
291 talloc_free(path);
292 return NULL;
294 if (st.st_dev != dev) {
295 *p = '/';
296 break;
300 return path;
303 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
304 struct vfs_handle_struct *handle,
305 const char *name, time_t timestamp)
307 struct smb_filename converted_fname;
308 char *result = NULL;
309 size_t *slashes = NULL;
310 unsigned num_slashes;
311 char *path = NULL;
312 size_t pathlen;
313 char *insert = NULL;
314 char *converted = NULL;
315 size_t insertlen;
316 int i, saved_errno;
317 size_t min_offset;
319 path = talloc_asprintf(mem_ctx, "%s/%s", handle->conn->connectpath,
320 name);
321 if (path == NULL) {
322 errno = ENOMEM;
323 goto fail;
325 pathlen = talloc_get_size(path)-1;
327 DEBUG(10, ("converting %s\n", path));
329 if (!shadow_copy2_find_slashes(talloc_tos(), path,
330 &slashes, &num_slashes)) {
331 goto fail;
333 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
334 if (insert == NULL) {
335 goto fail;
337 insertlen = talloc_get_size(insert)-1;
338 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
339 if (converted == NULL) {
340 goto fail;
343 if (path[pathlen-1] != '/') {
345 * Append a fake slash to find the snapshot root
347 size_t *tmp;
348 tmp = talloc_realloc(talloc_tos(), slashes,
349 size_t, num_slashes+1);
350 if (tmp == NULL) {
351 goto fail;
353 slashes = tmp;
354 slashes[num_slashes] = pathlen;
355 num_slashes += 1;
358 min_offset = 0;
360 if (!lp_parm_bool(SNUM(handle->conn), "shadow", "crossmountpoints",
361 false)) {
362 char *mount_point;
364 mount_point = shadow_copy2_find_mount_point(talloc_tos(),
365 handle);
366 if (mount_point == NULL) {
367 goto fail;
369 min_offset = strlen(mount_point);
370 TALLOC_FREE(mount_point);
373 memcpy(converted, path, pathlen+1);
374 converted[pathlen+insertlen] = '\0';
376 ZERO_STRUCT(converted_fname);
377 converted_fname.base_name = converted;
379 for (i = num_slashes-1; i>=0; i--) {
380 int ret;
381 size_t offset;
383 offset = slashes[i];
385 if (offset < min_offset) {
386 errno = ENOENT;
387 goto fail;
390 memcpy(converted+offset, insert, insertlen);
392 offset += insertlen;
393 memcpy(converted+offset, path + slashes[i],
394 pathlen - slashes[i]);
396 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
398 DEBUG(10, ("Trying %s: %d (%s)\n", converted,
399 ret, ret == 0 ? "ok" : strerror(errno)));
400 if (ret == 0) {
401 /* success */
402 break;
404 if (errno == ENOTDIR) {
406 * This is a valid condition: We appended the
407 * .snaphots/@GMT.. to a file name. Just try
408 * with the upper levels.
410 continue;
412 if (errno != ENOENT) {
413 /* Other problem than "not found" */
414 goto fail;
418 if (i >= 0) {
420 * Found something
422 DEBUG(10, ("Found %s\n", converted));
423 result = converted;
424 converted = NULL;
425 } else {
426 errno = ENOENT;
428 fail:
429 saved_errno = errno;
430 TALLOC_FREE(converted);
431 TALLOC_FREE(insert);
432 TALLOC_FREE(slashes);
433 TALLOC_FREE(path);
434 errno = saved_errno;
435 return result;
439 modify a sbuf return to ensure that inodes in the shadow directory
440 are different from those in the main directory
442 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
443 SMB_STRUCT_STAT *sbuf)
445 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
446 /* some snapshot systems, like GPFS, return the name
447 device:inode for the snapshot files as the current
448 files. That breaks the 'restore' button in the shadow copy
449 GUI, as the client gets a sharing violation.
451 This is a crude way of allowing both files to be
452 open at once. It has a slight chance of inode
453 number collision, but I can't see a better approach
454 without significant VFS changes
456 uint32_t shash;
458 shash = hash(fname, strlen(fname), 0) & 0xFF000000;
459 if (shash == 0) {
460 shash = 1;
462 sbuf->st_ex_ino ^= shash;
466 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
467 const char *fname,
468 const char *mask,
469 uint32 attr)
471 time_t timestamp;
472 char *stripped;
473 DIR *ret;
474 int saved_errno;
475 char *conv;
477 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
478 &timestamp, &stripped)) {
479 return NULL;
481 if (timestamp == 0) {
482 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
484 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
485 TALLOC_FREE(stripped);
486 if (conv == NULL) {
487 return NULL;
489 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
490 saved_errno = errno;
491 TALLOC_FREE(conv);
492 errno = saved_errno;
493 return ret;
496 static int shadow_copy2_rename(vfs_handle_struct *handle,
497 const struct smb_filename *smb_fname_src,
498 const struct smb_filename *smb_fname_dst)
500 time_t timestamp_src, timestamp_dst;
502 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
503 smb_fname_src->base_name,
504 &timestamp_src, NULL)) {
505 return -1;
507 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
508 smb_fname_dst->base_name,
509 &timestamp_dst, NULL)) {
510 return -1;
512 if (timestamp_src != 0) {
513 errno = EXDEV;
514 return -1;
516 if (timestamp_dst != 0) {
517 errno = EROFS;
518 return -1;
520 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
523 static int shadow_copy2_symlink(vfs_handle_struct *handle,
524 const char *oldname, const char *newname)
526 time_t timestamp_old, timestamp_new;
528 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
529 &timestamp_old, NULL)) {
530 return -1;
532 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
533 &timestamp_new, NULL)) {
534 return -1;
536 if ((timestamp_old != 0) || (timestamp_new != 0)) {
537 errno = EROFS;
538 return -1;
540 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
543 static int shadow_copy2_link(vfs_handle_struct *handle,
544 const char *oldname, const char *newname)
546 time_t timestamp_old, timestamp_new;
548 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
549 &timestamp_old, NULL)) {
550 return -1;
552 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
553 &timestamp_new, NULL)) {
554 return -1;
556 if ((timestamp_old != 0) || (timestamp_new != 0)) {
557 errno = EROFS;
558 return -1;
560 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
563 static int shadow_copy2_stat(vfs_handle_struct *handle,
564 struct smb_filename *smb_fname)
566 time_t timestamp;
567 char *stripped, *tmp;
568 int ret, saved_errno;
570 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
571 smb_fname->base_name,
572 &timestamp, &stripped)) {
573 return -1;
575 if (timestamp == 0) {
576 return SMB_VFS_NEXT_STAT(handle, smb_fname);
579 tmp = smb_fname->base_name;
580 smb_fname->base_name = shadow_copy2_convert(
581 talloc_tos(), handle, stripped, timestamp);
582 TALLOC_FREE(stripped);
584 if (smb_fname->base_name == NULL) {
585 smb_fname->base_name = tmp;
586 return -1;
589 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
590 saved_errno = errno;
592 TALLOC_FREE(smb_fname->base_name);
593 smb_fname->base_name = tmp;
595 if (ret == 0) {
596 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
598 errno = saved_errno;
599 return ret;
602 static int shadow_copy2_lstat(vfs_handle_struct *handle,
603 struct smb_filename *smb_fname)
605 time_t timestamp;
606 char *stripped, *tmp;
607 int ret, saved_errno;
609 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
610 smb_fname->base_name,
611 &timestamp, &stripped)) {
612 return -1;
614 if (timestamp == 0) {
615 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
618 tmp = smb_fname->base_name;
619 smb_fname->base_name = shadow_copy2_convert(
620 talloc_tos(), handle, stripped, timestamp);
621 TALLOC_FREE(stripped);
623 if (smb_fname->base_name == NULL) {
624 smb_fname->base_name = tmp;
625 return -1;
628 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
629 saved_errno = errno;
631 TALLOC_FREE(smb_fname->base_name);
632 smb_fname->base_name = tmp;
634 if (ret == 0) {
635 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
637 errno = saved_errno;
638 return ret;
641 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
642 SMB_STRUCT_STAT *sbuf)
644 time_t timestamp;
645 int ret;
647 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
648 if (ret == -1) {
649 return ret;
651 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
652 fsp->fsp_name->base_name,
653 &timestamp, NULL)) {
654 return 0;
656 if (timestamp != 0) {
657 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
659 return 0;
662 static int shadow_copy2_open(vfs_handle_struct *handle,
663 struct smb_filename *smb_fname, files_struct *fsp,
664 int flags, mode_t mode)
666 time_t timestamp;
667 char *stripped, *tmp;
668 int ret, saved_errno;
670 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
671 smb_fname->base_name,
672 &timestamp, &stripped)) {
673 return -1;
675 if (timestamp == 0) {
676 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
679 tmp = smb_fname->base_name;
680 smb_fname->base_name = shadow_copy2_convert(
681 talloc_tos(), handle, stripped, timestamp);
682 TALLOC_FREE(stripped);
684 if (smb_fname->base_name == NULL) {
685 smb_fname->base_name = tmp;
686 return -1;
689 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
690 saved_errno = errno;
692 TALLOC_FREE(smb_fname->base_name);
693 smb_fname->base_name = tmp;
695 errno = saved_errno;
696 return ret;
699 static int shadow_copy2_unlink(vfs_handle_struct *handle,
700 const struct smb_filename *smb_fname)
702 time_t timestamp;
703 char *stripped;
704 int ret, saved_errno;
705 struct smb_filename *conv;
706 NTSTATUS status;
708 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
709 smb_fname->base_name,
710 &timestamp, &stripped)) {
711 return -1;
713 if (timestamp == 0) {
714 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
716 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
717 if (!NT_STATUS_IS_OK(status)) {
718 errno = ENOMEM;
719 return -1;
721 conv->base_name = shadow_copy2_convert(
722 conv, handle, stripped, timestamp);
723 TALLOC_FREE(stripped);
724 if (conv->base_name == NULL) {
725 return -1;
727 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
728 saved_errno = errno;
729 TALLOC_FREE(conv);
730 errno = saved_errno;
731 return ret;
734 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
735 mode_t mode)
737 time_t timestamp;
738 char *stripped;
739 int ret, saved_errno;
740 char *conv;
742 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
743 &timestamp, &stripped)) {
744 return -1;
746 if (timestamp == 0) {
747 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
749 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
750 TALLOC_FREE(stripped);
751 if (conv == NULL) {
752 return -1;
754 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
755 saved_errno = errno;
756 TALLOC_FREE(conv);
757 errno = saved_errno;
758 return ret;
761 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
762 uid_t uid, gid_t gid)
764 time_t timestamp;
765 char *stripped;
766 int ret, saved_errno;
767 char *conv;
769 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
770 &timestamp, &stripped)) {
771 return -1;
773 if (timestamp == 0) {
774 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
776 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
777 TALLOC_FREE(stripped);
778 if (conv == NULL) {
779 return -1;
781 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
782 saved_errno = errno;
783 TALLOC_FREE(conv);
784 errno = saved_errno;
785 return ret;
788 static int shadow_copy2_chdir(vfs_handle_struct *handle,
789 const char *fname)
791 time_t timestamp;
792 char *stripped;
793 int ret, saved_errno;
794 char *conv;
796 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
797 &timestamp, &stripped)) {
798 return -1;
800 if (timestamp == 0) {
801 return SMB_VFS_NEXT_CHDIR(handle, fname);
803 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
804 TALLOC_FREE(stripped);
805 if (conv == NULL) {
806 return -1;
808 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
809 saved_errno = errno;
810 TALLOC_FREE(conv);
811 errno = saved_errno;
812 return ret;
815 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
816 const struct smb_filename *smb_fname,
817 struct smb_file_time *ft)
819 time_t timestamp;
820 char *stripped;
821 int ret, saved_errno;
822 struct smb_filename *conv;
823 NTSTATUS status;
825 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
826 smb_fname->base_name,
827 &timestamp, &stripped)) {
828 return -1;
830 if (timestamp == 0) {
831 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
833 status = copy_smb_filename(talloc_tos(), smb_fname, &conv);
834 if (!NT_STATUS_IS_OK(status)) {
835 errno = ENOMEM;
836 return -1;
838 conv->base_name = shadow_copy2_convert(
839 conv, handle, stripped, timestamp);
840 TALLOC_FREE(stripped);
841 if (conv->base_name == NULL) {
842 return -1;
844 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
845 saved_errno = errno;
846 TALLOC_FREE(conv);
847 errno = saved_errno;
848 return ret;
851 static int shadow_copy2_readlink(vfs_handle_struct *handle,
852 const char *fname, char *buf, size_t bufsiz)
854 time_t timestamp;
855 char *stripped;
856 int ret, saved_errno;
857 char *conv;
859 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
860 &timestamp, &stripped)) {
861 return -1;
863 if (timestamp == 0) {
864 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
866 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
867 TALLOC_FREE(stripped);
868 if (conv == NULL) {
869 return -1;
871 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
872 saved_errno = errno;
873 TALLOC_FREE(conv);
874 errno = saved_errno;
875 return ret;
878 static int shadow_copy2_mknod(vfs_handle_struct *handle,
879 const char *fname, mode_t mode, SMB_DEV_T dev)
881 time_t timestamp;
882 char *stripped;
883 int ret, saved_errno;
884 char *conv;
886 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
887 &timestamp, &stripped)) {
888 return -1;
890 if (timestamp == 0) {
891 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
893 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
894 TALLOC_FREE(stripped);
895 if (conv == NULL) {
896 return -1;
898 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
899 saved_errno = errno;
900 TALLOC_FREE(conv);
901 errno = saved_errno;
902 return ret;
905 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
906 const char *fname)
908 time_t timestamp;
909 char *stripped = NULL;
910 char *tmp = NULL;
911 char *result = NULL;
912 char *inserted = NULL;
913 char *inserted_to, *inserted_end;
914 int saved_errno;
916 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
917 &timestamp, &stripped)) {
918 goto done;
920 if (timestamp == 0) {
921 return SMB_VFS_NEXT_REALPATH(handle, fname);
924 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
925 if (tmp == NULL) {
926 goto done;
929 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
930 if (result == NULL) {
931 goto done;
935 * Take away what we've inserted. This removes the @GMT-thingy
936 * completely, but will give a path under the share root.
938 inserted = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
939 if (inserted == NULL) {
940 goto done;
942 inserted_to = strstr_m(result, inserted);
943 if (inserted_to == NULL) {
944 DEBUG(2, ("SMB_VFS_NEXT_REALPATH removed %s\n", inserted));
945 goto done;
947 inserted_end = inserted_to + talloc_get_size(inserted) - 1;
948 memmove(inserted_to, inserted_end, strlen(inserted_end)+1);
950 done:
951 saved_errno = errno;
952 TALLOC_FREE(inserted);
953 TALLOC_FREE(tmp);
954 TALLOC_FREE(stripped);
955 errno = saved_errno;
956 return result;
959 static char *have_snapdir(struct vfs_handle_struct *handle,
960 const char *path)
962 struct smb_filename smb_fname;
963 int ret;
965 ZERO_STRUCT(smb_fname);
966 smb_fname.base_name = talloc_asprintf(
967 talloc_tos(), "%s/%s", path,
968 lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
969 ".snapshots"));
970 if (smb_fname.base_name == NULL) {
971 return NULL;
974 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
975 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
976 return smb_fname.base_name;
978 TALLOC_FREE(smb_fname.base_name);
979 return NULL;
982 static char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
983 struct vfs_handle_struct *handle,
984 struct smb_filename *smb_fname)
986 char *path, *p;
987 char *snapdir;
989 path = talloc_asprintf(mem_ctx, "%s/%s",
990 handle->conn->connectpath,
991 smb_fname->base_name);
992 if (path == NULL) {
993 return NULL;
996 snapdir = have_snapdir(handle, path);
997 if (snapdir != NULL) {
998 TALLOC_FREE(path);
999 return snapdir;
1002 while ((p = strrchr(path, '/')) && (p > path)) {
1004 p[0] = '\0';
1006 snapdir = have_snapdir(handle, path);
1007 if (snapdir != NULL) {
1008 TALLOC_FREE(path);
1009 return snapdir;
1012 TALLOC_FREE(path);
1013 return NULL;
1016 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1017 const char *name,
1018 char *gmt, size_t gmt_len)
1020 struct tm timestamp;
1021 time_t timestamp_t;
1022 unsigned long int timestamp_long;
1023 const char *fmt;
1025 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
1026 "format", GMT_FORMAT);
1028 ZERO_STRUCT(timestamp);
1029 if (lp_parm_bool(SNUM(handle->conn), "shadow", "sscanf", false)) {
1030 if (sscanf(name, fmt, &timestamp_long) != 1) {
1031 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no sscanf match %s: %s\n",
1032 fmt, name));
1033 return false;
1035 timestamp_t = timestamp_long;
1036 gmtime_r(&timestamp_t, &timestamp);
1037 } else {
1038 if (strptime(name, fmt, &timestamp) == NULL) {
1039 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
1040 fmt, name));
1041 return false;
1043 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
1045 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime", false)) {
1046 timestamp.tm_isdst = -1;
1047 timestamp_t = mktime(&timestamp);
1048 gmtime_r(&timestamp_t, &timestamp);
1052 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1053 return true;
1056 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1058 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1061 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1063 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1067 sort the shadow copy data in ascending or descending order
1069 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1070 struct shadow_copy_data *shadow_copy2_data)
1072 int (*cmpfunc)(const void *, const void *);
1073 const char *sort;
1075 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
1076 "sort", "desc");
1077 if (sort == NULL) {
1078 return;
1081 if (strcmp(sort, "asc") == 0) {
1082 cmpfunc = shadow_copy2_label_cmp_asc;
1083 } else if (strcmp(sort, "desc") == 0) {
1084 cmpfunc = shadow_copy2_label_cmp_desc;
1085 } else {
1086 return;
1089 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1090 shadow_copy2_data->labels)
1092 TYPESAFE_QSORT(shadow_copy2_data->labels,
1093 shadow_copy2_data->num_volumes,
1094 cmpfunc);
1098 static int shadow_copy2_get_shadow_copy_data(
1099 vfs_handle_struct *handle, files_struct *fsp,
1100 struct shadow_copy_data *shadow_copy2_data,
1101 bool labels)
1103 DIR *p;
1104 const char *snapdir;
1105 struct dirent *d;
1106 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1108 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1109 if (snapdir == NULL) {
1110 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1111 handle->conn->connectpath));
1112 errno = EINVAL;
1113 talloc_free(tmp_ctx);
1114 return -1;
1117 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1119 if (!p) {
1120 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1121 " - %s\n", snapdir, strerror(errno)));
1122 talloc_free(tmp_ctx);
1123 errno = ENOSYS;
1124 return -1;
1127 shadow_copy2_data->num_volumes = 0;
1128 shadow_copy2_data->labels = NULL;
1130 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1131 char snapshot[GMT_NAME_LEN+1];
1132 SHADOW_COPY_LABEL *tlabels;
1135 * ignore names not of the right form in the snapshot
1136 * directory
1138 if (!shadow_copy2_snapshot_to_gmt(
1139 handle, d->d_name,
1140 snapshot, sizeof(snapshot))) {
1142 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1143 "ignoring %s\n", d->d_name));
1144 continue;
1146 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1147 d->d_name, snapshot));
1149 if (!labels) {
1150 /* the caller doesn't want the labels */
1151 shadow_copy2_data->num_volumes++;
1152 continue;
1155 tlabels = talloc_realloc(shadow_copy2_data,
1156 shadow_copy2_data->labels,
1157 SHADOW_COPY_LABEL,
1158 shadow_copy2_data->num_volumes+1);
1159 if (tlabels == NULL) {
1160 DEBUG(0,("shadow_copy2: out of memory\n"));
1161 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1162 talloc_free(tmp_ctx);
1163 return -1;
1166 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1167 sizeof(*tlabels));
1169 shadow_copy2_data->num_volumes++;
1170 shadow_copy2_data->labels = tlabels;
1173 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1175 shadow_copy2_sort_data(handle, shadow_copy2_data);
1177 talloc_free(tmp_ctx);
1178 return 0;
1181 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1182 struct files_struct *fsp,
1183 uint32 security_info,
1184 struct security_descriptor **ppdesc)
1186 time_t timestamp;
1187 char *stripped;
1188 NTSTATUS status;
1189 char *conv;
1191 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1192 fsp->fsp_name->base_name,
1193 &timestamp, &stripped)) {
1194 return map_nt_error_from_unix(errno);
1196 if (timestamp == 0) {
1197 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1198 ppdesc);
1200 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1201 TALLOC_FREE(stripped);
1202 if (conv == NULL) {
1203 return map_nt_error_from_unix(errno);
1205 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1206 TALLOC_FREE(conv);
1207 return status;
1210 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1211 const char *fname,
1212 uint32 security_info,
1213 struct security_descriptor **ppdesc)
1215 time_t timestamp;
1216 char *stripped;
1217 NTSTATUS status;
1218 char *conv;
1220 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1221 &timestamp, &stripped)) {
1222 return map_nt_error_from_unix(errno);
1224 if (timestamp == 0) {
1225 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1226 ppdesc);
1228 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1229 TALLOC_FREE(stripped);
1230 if (conv == NULL) {
1231 return map_nt_error_from_unix(errno);
1233 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info, ppdesc);
1234 TALLOC_FREE(conv);
1235 return status;
1238 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1239 const char *fname, mode_t mode)
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_MKDIR(handle, fname, mode);
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_MKDIR(handle, conv, mode);
1259 saved_errno = errno;
1260 TALLOC_FREE(conv);
1261 errno = saved_errno;
1262 return ret;
1265 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1267 time_t timestamp;
1268 char *stripped;
1269 int ret, saved_errno;
1270 char *conv;
1272 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1273 &timestamp, &stripped)) {
1274 return -1;
1276 if (timestamp == 0) {
1277 return SMB_VFS_NEXT_RMDIR(handle, fname);
1279 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1280 TALLOC_FREE(stripped);
1281 if (conv == NULL) {
1282 return -1;
1284 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1285 saved_errno = errno;
1286 TALLOC_FREE(conv);
1287 errno = saved_errno;
1288 return ret;
1291 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1292 unsigned int flags)
1294 time_t timestamp;
1295 char *stripped;
1296 int ret, saved_errno;
1297 char *conv;
1299 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1300 &timestamp, &stripped)) {
1301 return -1;
1303 if (timestamp == 0) {
1304 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1306 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1307 TALLOC_FREE(stripped);
1308 if (conv == NULL) {
1309 return -1;
1311 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1312 saved_errno = errno;
1313 TALLOC_FREE(conv);
1314 errno = saved_errno;
1315 return ret;
1318 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1319 const char *fname, const char *aname,
1320 void *value, size_t size)
1322 time_t timestamp;
1323 char *stripped;
1324 ssize_t ret;
1325 int saved_errno;
1326 char *conv;
1328 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1329 &timestamp, &stripped)) {
1330 return -1;
1332 if (timestamp == 0) {
1333 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1334 size);
1336 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1337 TALLOC_FREE(stripped);
1338 if (conv == NULL) {
1339 return -1;
1341 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1342 saved_errno = errno;
1343 TALLOC_FREE(conv);
1344 errno = saved_errno;
1345 return ret;
1348 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1349 const char *fname,
1350 char *list, size_t size)
1352 time_t timestamp;
1353 char *stripped;
1354 ssize_t ret;
1355 int saved_errno;
1356 char *conv;
1358 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1359 &timestamp, &stripped)) {
1360 return -1;
1362 if (timestamp == 0) {
1363 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1365 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1366 TALLOC_FREE(stripped);
1367 if (conv == NULL) {
1368 return -1;
1370 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1371 saved_errno = errno;
1372 TALLOC_FREE(conv);
1373 errno = saved_errno;
1374 return ret;
1377 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1378 const char *fname, const char *aname)
1380 time_t timestamp;
1381 char *stripped;
1382 int ret, saved_errno;
1383 char *conv;
1385 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1386 &timestamp, &stripped)) {
1387 return -1;
1389 if (timestamp == 0) {
1390 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1392 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1393 TALLOC_FREE(stripped);
1394 if (conv == NULL) {
1395 return -1;
1397 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1398 saved_errno = errno;
1399 TALLOC_FREE(conv);
1400 errno = saved_errno;
1401 return ret;
1404 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1405 const char *fname,
1406 const char *aname, const void *value,
1407 size_t size, int flags)
1409 time_t timestamp;
1410 char *stripped;
1411 ssize_t ret;
1412 int saved_errno;
1413 char *conv;
1415 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1416 &timestamp, &stripped)) {
1417 return -1;
1419 if (timestamp == 0) {
1420 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1421 flags);
1423 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1424 TALLOC_FREE(stripped);
1425 if (conv == NULL) {
1426 return -1;
1428 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1429 saved_errno = errno;
1430 TALLOC_FREE(conv);
1431 errno = saved_errno;
1432 return ret;
1435 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1436 const char *fname, mode_t mode)
1438 time_t timestamp;
1439 char *stripped;
1440 ssize_t ret;
1441 int saved_errno;
1442 char *conv;
1444 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1445 &timestamp, &stripped)) {
1446 return -1;
1448 if (timestamp == 0) {
1449 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1451 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1452 TALLOC_FREE(stripped);
1453 if (conv == NULL) {
1454 return -1;
1456 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1457 saved_errno = errno;
1458 TALLOC_FREE(conv);
1459 errno = saved_errno;
1460 return ret;
1463 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1464 const char *path,
1465 const char *name,
1466 TALLOC_CTX *mem_ctx,
1467 char **found_name)
1469 time_t timestamp;
1470 char *stripped;
1471 ssize_t ret;
1472 int saved_errno;
1473 char *conv;
1475 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1476 &timestamp, &stripped)) {
1477 return -1;
1479 if (timestamp == 0) {
1480 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1481 mem_ctx, found_name);
1483 if (stripped[0] == '\0') {
1484 *found_name = talloc_strdup(mem_ctx, name);
1485 if (*found_name == NULL) {
1486 errno = ENOMEM;
1487 return -1;
1489 return 0;
1491 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1492 TALLOC_FREE(stripped);
1493 if (conv == NULL) {
1494 return -1;
1496 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1497 mem_ctx, found_name);
1498 saved_errno = errno;
1499 TALLOC_FREE(conv);
1500 errno = saved_errno;
1501 return ret;
1505 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
1506 .opendir_fn = shadow_copy2_opendir,
1507 .rename_fn = shadow_copy2_rename,
1508 .link_fn = shadow_copy2_link,
1509 .symlink_fn = shadow_copy2_symlink,
1510 .stat_fn = shadow_copy2_stat,
1511 .lstat_fn = shadow_copy2_lstat,
1512 .fstat_fn = shadow_copy2_fstat,
1513 .open_fn = shadow_copy2_open,
1514 .unlink_fn = shadow_copy2_unlink,
1515 .chmod_fn = shadow_copy2_chmod,
1516 .chown_fn = shadow_copy2_chown,
1517 .chdir_fn = shadow_copy2_chdir,
1518 .ntimes_fn = shadow_copy2_ntimes,
1519 .readlink_fn = shadow_copy2_readlink,
1520 .mknod_fn = shadow_copy2_mknod,
1521 .realpath_fn = shadow_copy2_realpath,
1522 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
1523 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
1524 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
1525 .mkdir_fn = shadow_copy2_mkdir,
1526 .rmdir_fn = shadow_copy2_rmdir,
1527 .getxattr_fn = shadow_copy2_getxattr,
1528 .listxattr_fn = shadow_copy2_listxattr,
1529 .removexattr_fn = shadow_copy2_removexattr,
1530 .setxattr_fn = shadow_copy2_setxattr,
1531 .chmod_acl_fn = shadow_copy2_chmod_acl,
1532 .chflags_fn = shadow_copy2_chflags,
1533 .get_real_filename_fn = shadow_copy2_get_real_filename,
1536 NTSTATUS vfs_shadow_copy2_init(void);
1537 NTSTATUS vfs_shadow_copy2_init(void)
1539 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1540 "shadow_copy2", &vfs_shadow_copy2_fns);