s3: VFS: Ensure shadow:format cannot contain a / path separator.
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob78f90795cf3952c1ea638a071d614f77c8cdc595
1 /*
2 * shadow_copy2: a shadow copy module (second implementation)
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
8 * Copyright (C) Michael Adam 2013
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * This is a second implemetation of a shadow copy module for exposing
27 * file system snapshots to windows clients as shadow copies.
29 * See the manual page for documentation.
32 #include "includes.h"
33 #include "smbd/smbd.h"
34 #include "system/filesys.h"
35 #include "include/ntioctl.h"
36 #include "util_tdb.h"
37 #include "lib/util_path.h"
39 struct shadow_copy2_config {
40 char *gmt_format;
41 bool use_sscanf;
42 bool use_localtime;
43 char *snapdir;
44 bool snapdirseverywhere;
45 bool crossmountpoints;
46 bool fixinodes;
47 char *sort_order;
48 bool snapdir_absolute;
49 char *mount_point;
50 char *rel_connectpath; /* share root, relative to a snapshot root */
51 char *snapshot_basepath; /* the absolute version of snapdir */
52 char *shadow_cwd; /* Absolute $cwd path. */
53 /* Absolute connectpath - can vary depending on $cwd. */
54 char *shadow_connectpath;
57 static bool shadow_copy2_find_slashes(TALLOC_CTX *mem_ctx, const char *str,
58 size_t **poffsets,
59 unsigned *pnum_offsets)
61 unsigned num_offsets;
62 size_t *offsets;
63 const char *p;
65 num_offsets = 0;
67 p = str;
68 while ((p = strchr(p, '/')) != NULL) {
69 num_offsets += 1;
70 p += 1;
73 offsets = talloc_array(mem_ctx, size_t, num_offsets);
74 if (offsets == NULL) {
75 return false;
78 p = str;
79 num_offsets = 0;
80 while ((p = strchr(p, '/')) != NULL) {
81 offsets[num_offsets] = p-str;
82 num_offsets += 1;
83 p += 1;
86 *poffsets = offsets;
87 *pnum_offsets = num_offsets;
88 return true;
91 /**
92 * Given a timestamp, build the posix level GMT-tag string
93 * based on the configurable format.
95 static size_t shadow_copy2_posix_gmt_string(struct vfs_handle_struct *handle,
96 time_t snapshot,
97 char *snaptime_string,
98 size_t len)
100 struct tm snap_tm;
101 size_t snaptime_len;
102 struct shadow_copy2_config *config;
104 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
105 return 0);
107 if (config->use_sscanf) {
108 snaptime_len = snprintf(snaptime_string,
109 len,
110 config->gmt_format,
111 (unsigned long)snapshot);
112 if (snaptime_len <= 0) {
113 DEBUG(10, ("snprintf failed\n"));
114 return snaptime_len;
116 } else {
117 if (config->use_localtime) {
118 if (localtime_r(&snapshot, &snap_tm) == 0) {
119 DEBUG(10, ("gmtime_r failed\n"));
120 return -1;
122 } else {
123 if (gmtime_r(&snapshot, &snap_tm) == 0) {
124 DEBUG(10, ("gmtime_r failed\n"));
125 return -1;
128 snaptime_len = strftime(snaptime_string,
129 len,
130 config->gmt_format,
131 &snap_tm);
132 if (snaptime_len == 0) {
133 DEBUG(10, ("strftime failed\n"));
134 return 0;
138 return snaptime_len;
142 * Given a timestamp, build the string to insert into a path
143 * as a path component for creating the local path to the
144 * snapshot at the given timestamp of the input path.
146 * In the case of a parallel snapdir (specified with an
147 * absolute path), this is the inital portion of the
148 * local path of any snapshot file. The complete path is
149 * obtained by appending the portion of the file's path
150 * below the share root's mountpoint.
152 static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
153 struct vfs_handle_struct *handle,
154 time_t snapshot)
156 fstring snaptime_string;
157 size_t snaptime_len = 0;
158 char *result = NULL;
159 struct shadow_copy2_config *config;
161 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
162 return NULL);
164 snaptime_len = shadow_copy2_posix_gmt_string(handle,
165 snapshot,
166 snaptime_string,
167 sizeof(snaptime_string));
168 if (snaptime_len <= 0) {
169 return NULL;
172 if (config->snapdir_absolute) {
173 result = talloc_asprintf(mem_ctx, "%s/%s",
174 config->snapdir, snaptime_string);
175 } else {
176 result = talloc_asprintf(mem_ctx, "/%s/%s",
177 config->snapdir, snaptime_string);
179 if (result == NULL) {
180 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
183 return result;
187 * Build the posix snapshot path for the connection
188 * at the given timestamp, i.e. the absolute posix path
189 * that contains the snapshot for this file system.
191 * This only applies to classical case, i.e. not
192 * to the "snapdirseverywhere" mode.
194 static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx,
195 struct vfs_handle_struct *handle,
196 time_t snapshot)
198 fstring snaptime_string;
199 size_t snaptime_len = 0;
200 char *result = NULL;
201 struct shadow_copy2_config *config;
203 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
204 return NULL);
206 snaptime_len = shadow_copy2_posix_gmt_string(handle,
207 snapshot,
208 snaptime_string,
209 sizeof(snaptime_string));
210 if (snaptime_len <= 0) {
211 return NULL;
214 result = talloc_asprintf(mem_ctx, "%s/%s",
215 config->snapshot_basepath, snaptime_string);
216 if (result == NULL) {
217 DEBUG(1, (__location__ " talloc_asprintf failed\n"));
220 return result;
223 static char *make_path_absolute(TALLOC_CTX *mem_ctx,
224 struct shadow_copy2_config *config,
225 const char *name)
227 char *newpath = NULL;
228 char *abs_path = NULL;
230 if (name[0] != '/') {
231 newpath = talloc_asprintf(mem_ctx,
232 "%s/%s",
233 config->shadow_cwd,
234 name);
235 if (newpath == NULL) {
236 return NULL;
238 name = newpath;
240 abs_path = canonicalize_absolute_path(mem_ctx, name);
241 TALLOC_FREE(newpath);
242 return abs_path;
245 /* Return a $cwd-relative path. */
246 static bool make_relative_path(const char *cwd, char *abs_path)
248 size_t cwd_len = strlen(cwd);
249 size_t abs_len = strlen(abs_path);
251 if (abs_len < cwd_len) {
252 return false;
254 if (memcmp(abs_path, cwd, cwd_len) != 0) {
255 return false;
257 if (abs_path[cwd_len] != '/' && abs_path[cwd_len] != '\0') {
258 return false;
260 if (abs_path[cwd_len] == '/') {
261 cwd_len++;
263 memmove(abs_path, &abs_path[cwd_len], abs_len + 1 - cwd_len);
264 return true;
268 * Strip a snapshot component from a filename as
269 * handed in via the smb layer.
270 * Returns the parsed timestamp and the stripped filename.
272 static bool shadow_copy2_strip_snapshot_internal(TALLOC_CTX *mem_ctx,
273 struct vfs_handle_struct *handle,
274 const char *orig_name,
275 time_t *ptimestamp,
276 char **pstripped,
277 char **psnappath)
279 struct tm tm;
280 time_t timestamp = 0;
281 const char *p;
282 char *q;
283 char *stripped = NULL;
284 size_t rest_len, dst_len;
285 struct shadow_copy2_config *config;
286 const char *snapdir;
287 ssize_t snapdirlen;
288 ptrdiff_t len_before_gmt;
289 const char *name = orig_name;
291 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
292 return false);
294 DEBUG(10, (__location__ ": enter path '%s'\n", name));
296 p = strstr_m(name, "@GMT-");
297 if (p == NULL) {
298 DEBUG(11, ("@GMT not found\n"));
299 goto no_snapshot;
301 if ((p > name) && (p[-1] != '/')) {
302 /* the GMT-token does not start a path-component */
303 DEBUG(10, ("not at start, p=%p, name=%p, p[-1]=%d\n",
304 p, name, (int)p[-1]));
305 goto no_snapshot;
309 * Figure out whether we got an already converted string. One
310 * case where this happens is in a smb2 create call with the
311 * mxac create blob set. We do the get_acl call on
312 * fsp->fsp_name, which is already converted. We are converted
313 * if we got a file name of the form ".snapshots/@GMT-",
314 * i.e. ".snapshots/" precedes "p".
317 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir",
318 ".snapshots");
319 snapdirlen = strlen(snapdir);
320 len_before_gmt = p - name;
322 if ((len_before_gmt >= (snapdirlen + 1)) && (p[-1] == '/')) {
323 const char *parent_snapdir = p - (snapdirlen+1);
325 DEBUG(10, ("parent_snapdir = %s\n", parent_snapdir));
327 if (strncmp(parent_snapdir, snapdir, snapdirlen) == 0) {
328 DEBUG(10, ("name=%s is already converted\n", name));
329 goto no_snapshot;
332 q = strptime(p, GMT_FORMAT, &tm);
333 if (q == NULL) {
334 DEBUG(10, ("strptime failed\n"));
335 goto no_snapshot;
337 tm.tm_isdst = -1;
338 timestamp = timegm(&tm);
339 if (timestamp == (time_t)-1) {
340 DEBUG(10, ("timestamp==-1\n"));
341 goto no_snapshot;
343 if (q[0] == '\0') {
345 * The name consists of only the GMT token or the GMT
346 * token is at the end of the path. XP seems to send
347 * @GMT- at the end under certain circumstances even
348 * with a path prefix.
350 if (pstripped != NULL) {
351 stripped = talloc_strndup(mem_ctx, name, p - name);
352 if (stripped == NULL) {
353 return false;
355 *pstripped = stripped;
357 *ptimestamp = timestamp;
358 return true;
360 if (q[0] != '/') {
362 * It is not a complete path component, i.e. the path
363 * component continues after the gmt-token.
365 DEBUG(10, ("q[0] = %d\n", (int)q[0]));
366 goto no_snapshot;
368 q += 1;
370 rest_len = strlen(q);
371 dst_len = (p-name) + rest_len;
373 if (config->snapdirseverywhere) {
374 char *insert;
375 bool have_insert;
376 insert = shadow_copy2_insert_string(talloc_tos(), handle,
377 timestamp);
378 if (insert == NULL) {
379 errno = ENOMEM;
380 return false;
383 DEBUG(10, (__location__ ": snapdirseverywhere mode.\n"
384 "path '%s'.\n"
385 "insert string '%s'\n", name, insert));
387 have_insert = (strstr(name, insert+1) != NULL);
388 DEBUG(10, ("have_insert=%d, name=%s, insert+1=%s\n",
389 (int)have_insert, name, insert+1));
390 if (have_insert) {
391 DEBUG(10, (__location__ ": insert string '%s' found in "
392 "path '%s' found in snapdirseverywhere mode "
393 "==> already converted\n", insert, name));
394 TALLOC_FREE(insert);
395 goto no_snapshot;
397 TALLOC_FREE(insert);
398 } else {
399 char *snapshot_path;
400 char *s;
402 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
403 handle,
404 timestamp);
405 if (snapshot_path == NULL) {
406 errno = ENOMEM;
407 return false;
410 DEBUG(10, (__location__ " path: '%s'.\n"
411 "snapshot path: '%s'\n", name, snapshot_path));
413 s = strstr(name, snapshot_path);
414 if (s == name) {
416 * this starts with "snapshot_basepath/GMT-Token"
417 * so it is already a converted absolute
418 * path. Don't process further.
420 DEBUG(10, (__location__ ": path '%s' starts with "
421 "snapshot path '%s' (not in "
422 "snapdirseverywhere mode) ==> "
423 "already converted\n", name, snapshot_path));
424 talloc_free(snapshot_path);
425 goto no_snapshot;
427 talloc_free(snapshot_path);
430 if (pstripped != NULL) {
431 stripped = talloc_array(mem_ctx, char, dst_len+1);
432 if (stripped == NULL) {
433 errno = ENOMEM;
434 return false;
436 if (p > name) {
437 memcpy(stripped, name, p-name);
439 if (rest_len > 0) {
440 memcpy(stripped + (p-name), q, rest_len);
442 stripped[dst_len] = '\0';
443 *pstripped = stripped;
445 *ptimestamp = timestamp;
446 return true;
447 no_snapshot:
448 *ptimestamp = 0;
449 return true;
452 static bool shadow_copy2_strip_snapshot(TALLOC_CTX *mem_ctx,
453 struct vfs_handle_struct *handle,
454 const char *orig_name,
455 time_t *ptimestamp,
456 char **pstripped)
458 return shadow_copy2_strip_snapshot_internal(mem_ctx,
459 handle,
460 orig_name,
461 ptimestamp,
462 pstripped,
463 NULL);
466 static char *shadow_copy2_find_mount_point(TALLOC_CTX *mem_ctx,
467 vfs_handle_struct *handle)
469 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
470 dev_t dev;
471 struct stat st;
472 char *p;
474 if (stat(path, &st) != 0) {
475 talloc_free(path);
476 return NULL;
479 dev = st.st_dev;
481 while ((p = strrchr(path, '/')) && p > path) {
482 *p = 0;
483 if (stat(path, &st) != 0) {
484 talloc_free(path);
485 return NULL;
487 if (st.st_dev != dev) {
488 *p = '/';
489 break;
493 return path;
497 * Convert from a name as handed in via the SMB layer
498 * and a timestamp into the local path of the snapshot
499 * of the provided file at the provided time.
500 * Also return the path in the snapshot corresponding
501 * to the file's share root.
503 static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx,
504 struct vfs_handle_struct *handle,
505 const char *name, time_t timestamp,
506 size_t *snaproot_len)
508 struct smb_filename converted_fname;
509 char *result = NULL;
510 size_t *slashes = NULL;
511 unsigned num_slashes;
512 char *path = NULL;
513 size_t pathlen;
514 char *insert = NULL;
515 char *converted = NULL;
516 size_t insertlen, connectlen = 0;
517 int i, saved_errno;
518 size_t min_offset;
519 struct shadow_copy2_config *config;
520 size_t in_share_offset = 0;
522 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
523 return NULL);
525 DEBUG(10, ("converting '%s'\n", name));
527 if (!config->snapdirseverywhere) {
528 int ret;
529 char *snapshot_path;
531 snapshot_path = shadow_copy2_snapshot_path(talloc_tos(),
532 handle,
533 timestamp);
534 if (snapshot_path == NULL) {
535 goto fail;
538 if (config->rel_connectpath == NULL) {
539 converted = talloc_asprintf(mem_ctx, "%s/%s",
540 snapshot_path, name);
541 } else {
542 converted = talloc_asprintf(mem_ctx, "%s/%s/%s",
543 snapshot_path,
544 config->rel_connectpath,
545 name);
547 if (converted == NULL) {
548 goto fail;
551 ZERO_STRUCT(converted_fname);
552 converted_fname.base_name = converted;
554 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
555 DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
556 converted,
557 ret, ret == 0 ? "ok" : strerror(errno)));
558 if (ret == 0) {
559 DEBUG(10, ("Found %s\n", converted));
560 result = converted;
561 converted = NULL;
562 if (snaproot_len != NULL) {
563 *snaproot_len = strlen(snapshot_path);
564 if (config->rel_connectpath != NULL) {
565 *snaproot_len +=
566 strlen(config->rel_connectpath) + 1;
569 goto fail;
570 } else {
571 errno = ENOENT;
572 goto fail;
574 /* never reached ... */
577 connectlen = strlen(handle->conn->connectpath);
578 if (name[0] == 0) {
579 path = talloc_strdup(mem_ctx, handle->conn->connectpath);
580 } else {
581 path = talloc_asprintf(
582 mem_ctx, "%s/%s", handle->conn->connectpath, name);
584 if (path == NULL) {
585 errno = ENOMEM;
586 goto fail;
588 pathlen = talloc_get_size(path)-1;
590 if (!shadow_copy2_find_slashes(talloc_tos(), path,
591 &slashes, &num_slashes)) {
592 goto fail;
595 insert = shadow_copy2_insert_string(talloc_tos(), handle, timestamp);
596 if (insert == NULL) {
597 goto fail;
599 insertlen = talloc_get_size(insert)-1;
602 * Note: We deliberatly don't expensively initialize the
603 * array with talloc_zero here: Putting zero into
604 * converted[pathlen+insertlen] below is sufficient, because
605 * in the following for loop, the insert string is inserted
606 * at various slash places. So the memory up to position
607 * pathlen+insertlen will always be initialized when the
608 * converted string is used.
610 converted = talloc_array(mem_ctx, char, pathlen + insertlen + 1);
611 if (converted == NULL) {
612 goto fail;
615 if (path[pathlen-1] != '/') {
617 * Append a fake slash to find the snapshot root
619 size_t *tmp;
620 tmp = talloc_realloc(talloc_tos(), slashes,
621 size_t, num_slashes+1);
622 if (tmp == NULL) {
623 goto fail;
625 slashes = tmp;
626 slashes[num_slashes] = pathlen;
627 num_slashes += 1;
630 min_offset = 0;
632 if (!config->crossmountpoints) {
633 min_offset = strlen(config->mount_point);
636 memcpy(converted, path, pathlen+1);
637 converted[pathlen+insertlen] = '\0';
639 ZERO_STRUCT(converted_fname);
640 converted_fname.base_name = converted;
642 for (i = num_slashes-1; i>=0; i--) {
643 int ret;
644 size_t offset;
646 offset = slashes[i];
648 if (offset < min_offset) {
649 errno = ENOENT;
650 goto fail;
653 if (offset >= connectlen) {
654 in_share_offset = offset;
657 memcpy(converted+offset, insert, insertlen);
659 offset += insertlen;
660 memcpy(converted+offset, path + slashes[i],
661 pathlen - slashes[i]);
663 ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
665 DEBUG(10, ("Trying[snapdirseverywhere] %s: %d (%s)\n",
666 converted,
667 ret, ret == 0 ? "ok" : strerror(errno)));
668 if (ret == 0) {
669 /* success */
670 if (snaproot_len != NULL) {
671 *snaproot_len = in_share_offset + insertlen;
673 break;
675 if (errno == ENOTDIR) {
677 * This is a valid condition: We appended the
678 * .snaphots/@GMT.. to a file name. Just try
679 * with the upper levels.
681 continue;
683 if (errno != ENOENT) {
684 /* Other problem than "not found" */
685 goto fail;
689 if (i >= 0) {
691 * Found something
693 DEBUG(10, ("Found %s\n", converted));
694 result = converted;
695 converted = NULL;
696 } else {
697 errno = ENOENT;
699 fail:
700 saved_errno = errno;
701 TALLOC_FREE(converted);
702 TALLOC_FREE(insert);
703 TALLOC_FREE(slashes);
704 TALLOC_FREE(path);
705 errno = saved_errno;
706 return result;
710 * Convert from a name as handed in via the SMB layer
711 * and a timestamp into the local path of the snapshot
712 * of the provided file at the provided time.
714 static char *shadow_copy2_convert(TALLOC_CTX *mem_ctx,
715 struct vfs_handle_struct *handle,
716 const char *name, time_t timestamp)
718 return shadow_copy2_do_convert(mem_ctx, handle, name, timestamp, NULL);
722 modify a sbuf return to ensure that inodes in the shadow directory
723 are different from those in the main directory
725 static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
726 SMB_STRUCT_STAT *sbuf)
728 struct shadow_copy2_config *config;
730 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
731 return);
733 if (config->fixinodes) {
734 /* some snapshot systems, like GPFS, return the name
735 device:inode for the snapshot files as the current
736 files. That breaks the 'restore' button in the shadow copy
737 GUI, as the client gets a sharing violation.
739 This is a crude way of allowing both files to be
740 open at once. It has a slight chance of inode
741 number collision, but I can't see a better approach
742 without significant VFS changes
744 TDB_DATA key = { .dptr = discard_const_p(uint8_t, fname),
745 .dsize = strlen(fname) };
746 uint32_t shash;
748 shash = tdb_jenkins_hash(&key) & 0xFF000000;
749 if (shash == 0) {
750 shash = 1;
752 sbuf->st_ex_ino ^= shash;
756 static DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
757 const char *fname,
758 const char *mask,
759 uint32_t attr)
761 time_t timestamp = 0;
762 char *stripped = NULL;
763 DIR *ret;
764 int saved_errno;
765 char *conv;
767 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
768 &timestamp, &stripped)) {
769 return NULL;
771 if (timestamp == 0) {
772 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
774 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
775 TALLOC_FREE(stripped);
776 if (conv == NULL) {
777 return NULL;
779 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
780 saved_errno = errno;
781 TALLOC_FREE(conv);
782 errno = saved_errno;
783 return ret;
786 static int shadow_copy2_rename(vfs_handle_struct *handle,
787 const struct smb_filename *smb_fname_src,
788 const struct smb_filename *smb_fname_dst)
790 time_t timestamp_src = 0;
791 time_t timestamp_dst = 0;
793 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
794 smb_fname_src->base_name,
795 &timestamp_src, NULL)) {
796 return -1;
798 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
799 smb_fname_dst->base_name,
800 &timestamp_dst, NULL)) {
801 return -1;
803 if (timestamp_src != 0) {
804 errno = EXDEV;
805 return -1;
807 if (timestamp_dst != 0) {
808 errno = EROFS;
809 return -1;
811 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
814 static int shadow_copy2_symlink(vfs_handle_struct *handle,
815 const char *oldname, const char *newname)
817 time_t timestamp_old = 0;
818 time_t timestamp_new = 0;
820 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
821 &timestamp_old, NULL)) {
822 return -1;
824 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
825 &timestamp_new, NULL)) {
826 return -1;
828 if ((timestamp_old != 0) || (timestamp_new != 0)) {
829 errno = EROFS;
830 return -1;
832 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
835 static int shadow_copy2_link(vfs_handle_struct *handle,
836 const char *oldname, const char *newname)
838 time_t timestamp_old = 0;
839 time_t timestamp_new = 0;
841 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, oldname,
842 &timestamp_old, NULL)) {
843 return -1;
845 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, newname,
846 &timestamp_new, NULL)) {
847 return -1;
849 if ((timestamp_old != 0) || (timestamp_new != 0)) {
850 errno = EROFS;
851 return -1;
853 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
856 static int shadow_copy2_stat(vfs_handle_struct *handle,
857 struct smb_filename *smb_fname)
859 time_t timestamp = 0;
860 char *stripped = NULL;
861 char *tmp;
862 int ret, saved_errno;
864 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
865 smb_fname->base_name,
866 &timestamp, &stripped)) {
867 return -1;
869 if (timestamp == 0) {
870 return SMB_VFS_NEXT_STAT(handle, smb_fname);
873 tmp = smb_fname->base_name;
874 smb_fname->base_name = shadow_copy2_convert(
875 talloc_tos(), handle, stripped, timestamp);
876 TALLOC_FREE(stripped);
878 if (smb_fname->base_name == NULL) {
879 smb_fname->base_name = tmp;
880 return -1;
883 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
884 saved_errno = errno;
886 TALLOC_FREE(smb_fname->base_name);
887 smb_fname->base_name = tmp;
889 if (ret == 0) {
890 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
892 errno = saved_errno;
893 return ret;
896 static int shadow_copy2_lstat(vfs_handle_struct *handle,
897 struct smb_filename *smb_fname)
899 time_t timestamp = 0;
900 char *stripped = NULL;
901 char *tmp;
902 int ret, saved_errno;
904 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
905 smb_fname->base_name,
906 &timestamp, &stripped)) {
907 return -1;
909 if (timestamp == 0) {
910 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
913 tmp = smb_fname->base_name;
914 smb_fname->base_name = shadow_copy2_convert(
915 talloc_tos(), handle, stripped, timestamp);
916 TALLOC_FREE(stripped);
918 if (smb_fname->base_name == NULL) {
919 smb_fname->base_name = tmp;
920 return -1;
923 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
924 saved_errno = errno;
926 TALLOC_FREE(smb_fname->base_name);
927 smb_fname->base_name = tmp;
929 if (ret == 0) {
930 convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
932 errno = saved_errno;
933 return ret;
936 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
937 SMB_STRUCT_STAT *sbuf)
939 time_t timestamp = 0;
940 int ret;
942 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
943 if (ret == -1) {
944 return ret;
946 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
947 fsp->fsp_name->base_name,
948 &timestamp, NULL)) {
949 return 0;
951 if (timestamp != 0) {
952 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
954 return 0;
957 static int shadow_copy2_open(vfs_handle_struct *handle,
958 struct smb_filename *smb_fname, files_struct *fsp,
959 int flags, mode_t mode)
961 time_t timestamp = 0;
962 char *stripped = NULL;
963 char *tmp;
964 int ret, saved_errno;
966 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
967 smb_fname->base_name,
968 &timestamp, &stripped)) {
969 return -1;
971 if (timestamp == 0) {
972 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
975 tmp = smb_fname->base_name;
976 smb_fname->base_name = shadow_copy2_convert(
977 talloc_tos(), handle, stripped, timestamp);
978 TALLOC_FREE(stripped);
980 if (smb_fname->base_name == NULL) {
981 smb_fname->base_name = tmp;
982 return -1;
985 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
986 saved_errno = errno;
988 TALLOC_FREE(smb_fname->base_name);
989 smb_fname->base_name = tmp;
991 errno = saved_errno;
992 return ret;
995 static int shadow_copy2_unlink(vfs_handle_struct *handle,
996 const struct smb_filename *smb_fname)
998 time_t timestamp = 0;
999 char *stripped = NULL;
1000 int ret, saved_errno;
1001 struct smb_filename *conv;
1003 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1004 smb_fname->base_name,
1005 &timestamp, &stripped)) {
1006 return -1;
1008 if (timestamp == 0) {
1009 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1011 conv = cp_smb_filename(talloc_tos(), smb_fname);
1012 if (conv == NULL) {
1013 errno = ENOMEM;
1014 return -1;
1016 conv->base_name = shadow_copy2_convert(
1017 conv, handle, stripped, timestamp);
1018 TALLOC_FREE(stripped);
1019 if (conv->base_name == NULL) {
1020 return -1;
1022 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1023 saved_errno = errno;
1024 TALLOC_FREE(conv);
1025 errno = saved_errno;
1026 return ret;
1029 static int shadow_copy2_chmod(vfs_handle_struct *handle, const char *fname,
1030 mode_t mode)
1032 time_t timestamp = 0;
1033 char *stripped = NULL;
1034 int ret, saved_errno;
1035 char *conv;
1037 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1038 &timestamp, &stripped)) {
1039 return -1;
1041 if (timestamp == 0) {
1042 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
1044 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1045 TALLOC_FREE(stripped);
1046 if (conv == NULL) {
1047 return -1;
1049 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1050 saved_errno = errno;
1051 TALLOC_FREE(conv);
1052 errno = saved_errno;
1053 return ret;
1056 static int shadow_copy2_chown(vfs_handle_struct *handle, const char *fname,
1057 uid_t uid, gid_t gid)
1059 time_t timestamp = 0;
1060 char *stripped = NULL;
1061 int ret, saved_errno;
1062 char *conv;
1064 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1065 &timestamp, &stripped)) {
1066 return -1;
1068 if (timestamp == 0) {
1069 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
1071 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1072 TALLOC_FREE(stripped);
1073 if (conv == NULL) {
1074 return -1;
1076 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1077 saved_errno = errno;
1078 TALLOC_FREE(conv);
1079 errno = saved_errno;
1080 return ret;
1083 static void store_cwd_data(vfs_handle_struct *handle,
1084 const char *connectpath)
1086 struct shadow_copy2_config *config = NULL;
1087 char *cwd = NULL;
1089 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1090 return);
1092 TALLOC_FREE(config->shadow_cwd);
1093 cwd = SMB_VFS_NEXT_GETWD(handle);
1094 if (cwd == NULL) {
1095 smb_panic("getwd failed\n");
1097 DBG_DEBUG("shadow cwd = %s\n", cwd);
1098 config->shadow_cwd = talloc_strdup(config, cwd);
1099 SAFE_FREE(cwd);
1100 if (config->shadow_cwd == NULL) {
1101 smb_panic("talloc failed\n");
1103 TALLOC_FREE(config->shadow_connectpath);
1104 if (connectpath) {
1105 DBG_DEBUG("shadow conectpath = %s\n", connectpath);
1106 config->shadow_connectpath = talloc_strdup(config, connectpath);
1107 if (config->shadow_connectpath == NULL) {
1108 smb_panic("talloc failed\n");
1113 static int shadow_copy2_chdir(vfs_handle_struct *handle,
1114 const char *fname)
1116 time_t timestamp = 0;
1117 char *stripped = NULL;
1118 char *snappath = NULL;
1119 int ret = -1;
1120 int saved_errno = 0;
1121 char *conv = NULL;
1122 size_t rootpath_len = 0;
1124 if (!shadow_copy2_strip_snapshot_internal(talloc_tos(), handle, fname,
1125 &timestamp, &stripped, &snappath)) {
1126 return -1;
1128 if (stripped != NULL) {
1129 conv = shadow_copy2_do_convert(talloc_tos(),
1130 handle,
1131 stripped,
1132 timestamp,
1133 &rootpath_len);
1134 TALLOC_FREE(stripped);
1135 if (conv == NULL) {
1136 return -1;
1138 fname = conv;
1141 ret = SMB_VFS_NEXT_CHDIR(handle, fname);
1142 if (ret == -1) {
1143 saved_errno = errno;
1146 if (ret == 0) {
1147 if (conv != NULL && rootpath_len != 0) {
1148 conv[rootpath_len] = '\0';
1149 } else if (snappath != 0) {
1150 TALLOC_FREE(conv);
1151 conv = snappath;
1153 store_cwd_data(handle, conv);
1156 TALLOC_FREE(stripped);
1157 TALLOC_FREE(conv);
1159 if (saved_errno != 0) {
1160 errno = saved_errno;
1162 return ret;
1165 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
1166 const struct smb_filename *smb_fname,
1167 struct smb_file_time *ft)
1169 time_t timestamp = 0;
1170 char *stripped = NULL;
1171 int ret, saved_errno;
1172 struct smb_filename *conv;
1174 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1175 smb_fname->base_name,
1176 &timestamp, &stripped)) {
1177 return -1;
1179 if (timestamp == 0) {
1180 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1182 conv = cp_smb_filename(talloc_tos(), smb_fname);
1183 if (conv == NULL) {
1184 errno = ENOMEM;
1185 return -1;
1187 conv->base_name = shadow_copy2_convert(
1188 conv, handle, stripped, timestamp);
1189 TALLOC_FREE(stripped);
1190 if (conv->base_name == NULL) {
1191 return -1;
1193 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1194 saved_errno = errno;
1195 TALLOC_FREE(conv);
1196 errno = saved_errno;
1197 return ret;
1200 static int shadow_copy2_readlink(vfs_handle_struct *handle,
1201 const char *fname, char *buf, size_t bufsiz)
1203 time_t timestamp = 0;
1204 char *stripped = NULL;
1205 int ret, saved_errno;
1206 char *conv;
1208 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1209 &timestamp, &stripped)) {
1210 return -1;
1212 if (timestamp == 0) {
1213 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1215 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1216 TALLOC_FREE(stripped);
1217 if (conv == NULL) {
1218 return -1;
1220 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1221 saved_errno = errno;
1222 TALLOC_FREE(conv);
1223 errno = saved_errno;
1224 return ret;
1227 static int shadow_copy2_mknod(vfs_handle_struct *handle,
1228 const char *fname, mode_t mode, SMB_DEV_T dev)
1230 time_t timestamp = 0;
1231 char *stripped = NULL;
1232 int ret, saved_errno;
1233 char *conv;
1235 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1236 &timestamp, &stripped)) {
1237 return -1;
1239 if (timestamp == 0) {
1240 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1242 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1243 TALLOC_FREE(stripped);
1244 if (conv == NULL) {
1245 return -1;
1247 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1248 saved_errno = errno;
1249 TALLOC_FREE(conv);
1250 errno = saved_errno;
1251 return ret;
1254 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
1255 const char *fname)
1257 time_t timestamp = 0;
1258 char *stripped = NULL;
1259 char *tmp = NULL;
1260 char *result = NULL;
1261 int saved_errno;
1263 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1264 &timestamp, &stripped)) {
1265 goto done;
1267 if (timestamp == 0) {
1268 return SMB_VFS_NEXT_REALPATH(handle, fname);
1271 tmp = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1272 if (tmp == NULL) {
1273 goto done;
1276 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1278 done:
1279 saved_errno = errno;
1280 TALLOC_FREE(tmp);
1281 TALLOC_FREE(stripped);
1282 errno = saved_errno;
1283 return result;
1287 * Check whether a given directory contains a
1288 * snapshot directory as direct subdirectory.
1289 * If yes, return the path of the snapshot-subdir,
1290 * otherwise return NULL.
1292 static char *have_snapdir(struct vfs_handle_struct *handle,
1293 const char *path)
1295 struct smb_filename smb_fname;
1296 int ret;
1297 struct shadow_copy2_config *config;
1299 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1300 return NULL);
1302 ZERO_STRUCT(smb_fname);
1303 smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
1304 path, config->snapdir);
1305 if (smb_fname.base_name == NULL) {
1306 return NULL;
1309 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1310 if ((ret == 0) && (S_ISDIR(smb_fname.st.st_ex_mode))) {
1311 return smb_fname.base_name;
1313 TALLOC_FREE(smb_fname.base_name);
1314 return NULL;
1317 static bool check_access_snapdir(struct vfs_handle_struct *handle,
1318 const char *path)
1320 struct smb_filename smb_fname;
1321 int ret;
1322 NTSTATUS status;
1324 ZERO_STRUCT(smb_fname);
1325 smb_fname.base_name = talloc_asprintf(talloc_tos(),
1326 "%s",
1327 path);
1328 if (smb_fname.base_name == NULL) {
1329 return false;
1332 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
1333 if (ret != 0 || !S_ISDIR(smb_fname.st.st_ex_mode)) {
1334 TALLOC_FREE(smb_fname.base_name);
1335 return false;
1338 status = smbd_check_access_rights(handle->conn,
1339 &smb_fname,
1340 false,
1341 SEC_DIR_LIST);
1342 if (!NT_STATUS_IS_OK(status)) {
1343 DEBUG(0,("user does not have list permission "
1344 "on snapdir %s\n",
1345 smb_fname.base_name));
1346 TALLOC_FREE(smb_fname.base_name);
1347 return false;
1349 TALLOC_FREE(smb_fname.base_name);
1350 return true;
1354 * Find the snapshot directory (if any) for the given
1355 * filename (which is relative to the share).
1357 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
1358 struct vfs_handle_struct *handle,
1359 struct smb_filename *smb_fname)
1361 char *path, *p;
1362 const char *snapdir;
1363 struct shadow_copy2_config *config;
1365 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1366 return NULL);
1369 * If the non-snapdisrseverywhere mode, we should not search!
1371 if (!config->snapdirseverywhere) {
1372 return config->snapshot_basepath;
1375 path = talloc_asprintf(mem_ctx, "%s/%s",
1376 handle->conn->connectpath,
1377 smb_fname->base_name);
1378 if (path == NULL) {
1379 return NULL;
1382 snapdir = have_snapdir(handle, path);
1383 if (snapdir != NULL) {
1384 TALLOC_FREE(path);
1385 return snapdir;
1388 while ((p = strrchr(path, '/')) && (p > path)) {
1390 p[0] = '\0';
1392 snapdir = have_snapdir(handle, path);
1393 if (snapdir != NULL) {
1394 TALLOC_FREE(path);
1395 return snapdir;
1398 TALLOC_FREE(path);
1399 return NULL;
1402 static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
1403 const char *name,
1404 char *gmt, size_t gmt_len)
1406 struct tm timestamp;
1407 time_t timestamp_t;
1408 unsigned long int timestamp_long;
1409 const char *fmt;
1410 struct shadow_copy2_config *config;
1412 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1413 return NULL);
1415 fmt = config->gmt_format;
1417 ZERO_STRUCT(timestamp);
1418 if (config->use_sscanf) {
1419 if (sscanf(name, fmt, &timestamp_long) != 1) {
1420 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1421 "no sscanf match %s: %s\n",
1422 fmt, name));
1423 return false;
1425 timestamp_t = timestamp_long;
1426 gmtime_r(&timestamp_t, &timestamp);
1427 } else {
1428 if (strptime(name, fmt, &timestamp) == NULL) {
1429 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
1430 "no match %s: %s\n",
1431 fmt, name));
1432 return false;
1434 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
1435 fmt, name));
1437 if (config->use_localtime) {
1438 timestamp.tm_isdst = -1;
1439 timestamp_t = mktime(&timestamp);
1440 gmtime_r(&timestamp_t, &timestamp);
1444 strftime(gmt, gmt_len, GMT_FORMAT, &timestamp);
1445 return true;
1448 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
1450 return strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1453 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
1455 return -strncmp((const char *)x, (const char *)y, sizeof(SHADOW_COPY_LABEL));
1459 sort the shadow copy data in ascending or descending order
1461 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
1462 struct shadow_copy_data *shadow_copy2_data)
1464 int (*cmpfunc)(const void *, const void *);
1465 const char *sort;
1466 struct shadow_copy2_config *config;
1468 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1469 return);
1471 sort = config->sort_order;
1472 if (sort == NULL) {
1473 return;
1476 if (strcmp(sort, "asc") == 0) {
1477 cmpfunc = shadow_copy2_label_cmp_asc;
1478 } else if (strcmp(sort, "desc") == 0) {
1479 cmpfunc = shadow_copy2_label_cmp_desc;
1480 } else {
1481 return;
1484 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
1485 shadow_copy2_data->labels)
1487 TYPESAFE_QSORT(shadow_copy2_data->labels,
1488 shadow_copy2_data->num_volumes,
1489 cmpfunc);
1493 static int shadow_copy2_get_shadow_copy_data(
1494 vfs_handle_struct *handle, files_struct *fsp,
1495 struct shadow_copy_data *shadow_copy2_data,
1496 bool labels)
1498 DIR *p;
1499 const char *snapdir;
1500 struct dirent *d;
1501 TALLOC_CTX *tmp_ctx = talloc_stackframe();
1502 bool ret;
1504 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
1505 if (snapdir == NULL) {
1506 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
1507 handle->conn->connectpath));
1508 errno = EINVAL;
1509 talloc_free(tmp_ctx);
1510 return -1;
1512 ret = check_access_snapdir(handle, snapdir);
1513 if (!ret) {
1514 DEBUG(0,("access denied on listing snapdir %s\n", snapdir));
1515 errno = EACCES;
1516 talloc_free(tmp_ctx);
1517 return -1;
1520 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
1522 if (!p) {
1523 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
1524 " - %s\n", snapdir, strerror(errno)));
1525 talloc_free(tmp_ctx);
1526 errno = ENOSYS;
1527 return -1;
1530 shadow_copy2_data->num_volumes = 0;
1531 shadow_copy2_data->labels = NULL;
1533 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
1534 char snapshot[GMT_NAME_LEN+1];
1535 SHADOW_COPY_LABEL *tlabels;
1538 * ignore names not of the right form in the snapshot
1539 * directory
1541 if (!shadow_copy2_snapshot_to_gmt(
1542 handle, d->d_name,
1543 snapshot, sizeof(snapshot))) {
1545 DEBUG(6, ("shadow_copy2_get_shadow_copy_data: "
1546 "ignoring %s\n", d->d_name));
1547 continue;
1549 DEBUG(6,("shadow_copy2_get_shadow_copy_data: %s -> %s\n",
1550 d->d_name, snapshot));
1552 if (!labels) {
1553 /* the caller doesn't want the labels */
1554 shadow_copy2_data->num_volumes++;
1555 continue;
1558 tlabels = talloc_realloc(shadow_copy2_data,
1559 shadow_copy2_data->labels,
1560 SHADOW_COPY_LABEL,
1561 shadow_copy2_data->num_volumes+1);
1562 if (tlabels == NULL) {
1563 DEBUG(0,("shadow_copy2: out of memory\n"));
1564 SMB_VFS_NEXT_CLOSEDIR(handle, p);
1565 talloc_free(tmp_ctx);
1566 return -1;
1569 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
1570 sizeof(*tlabels));
1572 shadow_copy2_data->num_volumes++;
1573 shadow_copy2_data->labels = tlabels;
1576 SMB_VFS_NEXT_CLOSEDIR(handle,p);
1578 shadow_copy2_sort_data(handle, shadow_copy2_data);
1580 talloc_free(tmp_ctx);
1581 return 0;
1584 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
1585 struct files_struct *fsp,
1586 uint32_t security_info,
1587 TALLOC_CTX *mem_ctx,
1588 struct security_descriptor **ppdesc)
1590 time_t timestamp = 0;
1591 char *stripped = NULL;
1592 NTSTATUS status;
1593 char *conv;
1595 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
1596 fsp->fsp_name->base_name,
1597 &timestamp, &stripped)) {
1598 return map_nt_error_from_unix(errno);
1600 if (timestamp == 0) {
1601 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1602 mem_ctx,
1603 ppdesc);
1605 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1606 TALLOC_FREE(stripped);
1607 if (conv == NULL) {
1608 return map_nt_error_from_unix(errno);
1610 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1611 mem_ctx, ppdesc);
1612 TALLOC_FREE(conv);
1613 return status;
1616 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
1617 const char *fname,
1618 uint32_t security_info,
1619 TALLOC_CTX *mem_ctx,
1620 struct security_descriptor **ppdesc)
1622 time_t timestamp = 0;
1623 char *stripped = NULL;
1624 NTSTATUS status;
1625 char *conv;
1627 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1628 &timestamp, &stripped)) {
1629 return map_nt_error_from_unix(errno);
1631 if (timestamp == 0) {
1632 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1633 mem_ctx, ppdesc);
1635 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1636 TALLOC_FREE(stripped);
1637 if (conv == NULL) {
1638 return map_nt_error_from_unix(errno);
1640 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1641 mem_ctx, ppdesc);
1642 TALLOC_FREE(conv);
1643 return status;
1646 static int shadow_copy2_mkdir(vfs_handle_struct *handle,
1647 const char *fname, mode_t mode)
1649 time_t timestamp = 0;
1650 char *stripped = NULL;
1651 int ret, saved_errno;
1652 char *conv;
1654 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1655 &timestamp, &stripped)) {
1656 return -1;
1658 if (timestamp == 0) {
1659 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1661 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1662 TALLOC_FREE(stripped);
1663 if (conv == NULL) {
1664 return -1;
1666 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1667 saved_errno = errno;
1668 TALLOC_FREE(conv);
1669 errno = saved_errno;
1670 return ret;
1673 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
1675 time_t timestamp = 0;
1676 char *stripped = NULL;
1677 int ret, saved_errno;
1678 char *conv;
1680 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1681 &timestamp, &stripped)) {
1682 return -1;
1684 if (timestamp == 0) {
1685 return SMB_VFS_NEXT_RMDIR(handle, fname);
1687 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1688 TALLOC_FREE(stripped);
1689 if (conv == NULL) {
1690 return -1;
1692 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1693 saved_errno = errno;
1694 TALLOC_FREE(conv);
1695 errno = saved_errno;
1696 return ret;
1699 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
1700 unsigned int flags)
1702 time_t timestamp = 0;
1703 char *stripped = NULL;
1704 int ret, saved_errno;
1705 char *conv;
1707 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1708 &timestamp, &stripped)) {
1709 return -1;
1711 if (timestamp == 0) {
1712 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1714 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1715 TALLOC_FREE(stripped);
1716 if (conv == NULL) {
1717 return -1;
1719 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1720 saved_errno = errno;
1721 TALLOC_FREE(conv);
1722 errno = saved_errno;
1723 return ret;
1726 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
1727 const char *fname, const char *aname,
1728 void *value, size_t size)
1730 time_t timestamp = 0;
1731 char *stripped = NULL;
1732 ssize_t ret;
1733 int saved_errno;
1734 char *conv;
1736 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1737 &timestamp, &stripped)) {
1738 return -1;
1740 if (timestamp == 0) {
1741 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1742 size);
1744 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1745 TALLOC_FREE(stripped);
1746 if (conv == NULL) {
1747 return -1;
1749 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1750 saved_errno = errno;
1751 TALLOC_FREE(conv);
1752 errno = saved_errno;
1753 return ret;
1756 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
1757 const char *fname,
1758 char *list, size_t size)
1760 time_t timestamp = 0;
1761 char *stripped = NULL;
1762 ssize_t ret;
1763 int saved_errno;
1764 char *conv;
1766 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1767 &timestamp, &stripped)) {
1768 return -1;
1770 if (timestamp == 0) {
1771 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1773 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1774 TALLOC_FREE(stripped);
1775 if (conv == NULL) {
1776 return -1;
1778 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1779 saved_errno = errno;
1780 TALLOC_FREE(conv);
1781 errno = saved_errno;
1782 return ret;
1785 static int shadow_copy2_removexattr(vfs_handle_struct *handle,
1786 const char *fname, const char *aname)
1788 time_t timestamp = 0;
1789 char *stripped = NULL;
1790 int ret, saved_errno;
1791 char *conv;
1793 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1794 &timestamp, &stripped)) {
1795 return -1;
1797 if (timestamp == 0) {
1798 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1800 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1801 TALLOC_FREE(stripped);
1802 if (conv == NULL) {
1803 return -1;
1805 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1806 saved_errno = errno;
1807 TALLOC_FREE(conv);
1808 errno = saved_errno;
1809 return ret;
1812 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
1813 const char *fname,
1814 const char *aname, const void *value,
1815 size_t size, int flags)
1817 time_t timestamp = 0;
1818 char *stripped = NULL;
1819 ssize_t ret;
1820 int saved_errno;
1821 char *conv;
1823 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1824 &timestamp, &stripped)) {
1825 return -1;
1827 if (timestamp == 0) {
1828 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1829 flags);
1831 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1832 TALLOC_FREE(stripped);
1833 if (conv == NULL) {
1834 return -1;
1836 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1837 saved_errno = errno;
1838 TALLOC_FREE(conv);
1839 errno = saved_errno;
1840 return ret;
1843 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
1844 const char *fname, mode_t mode)
1846 time_t timestamp = 0;
1847 char *stripped = NULL;
1848 ssize_t ret;
1849 int saved_errno;
1850 char *conv;
1852 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1853 &timestamp, &stripped)) {
1854 return -1;
1856 if (timestamp == 0) {
1857 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1859 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1860 TALLOC_FREE(stripped);
1861 if (conv == NULL) {
1862 return -1;
1864 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1865 saved_errno = errno;
1866 TALLOC_FREE(conv);
1867 errno = saved_errno;
1868 return ret;
1871 static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
1872 const char *path,
1873 const char *name,
1874 TALLOC_CTX *mem_ctx,
1875 char **found_name)
1877 time_t timestamp = 0;
1878 char *stripped = NULL;
1879 ssize_t ret;
1880 int saved_errno;
1881 char *conv;
1883 DEBUG(10, ("shadow_copy2_get_real_filename called for path=[%s], "
1884 "name=[%s]\n", path, name));
1886 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
1887 &timestamp, &stripped)) {
1888 DEBUG(10, ("shadow_copy2_strip_snapshot failed\n"));
1889 return -1;
1891 if (timestamp == 0) {
1892 DEBUG(10, ("timestamp == 0\n"));
1893 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1894 mem_ctx, found_name);
1896 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
1897 TALLOC_FREE(stripped);
1898 if (conv == NULL) {
1899 DEBUG(10, ("shadow_copy2_convert failed\n"));
1900 return -1;
1902 DEBUG(10, ("Calling NEXT_GET_REAL_FILE_NAME for conv=[%s], "
1903 "name=[%s]\n", conv, name));
1904 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1905 mem_ctx, found_name);
1906 DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret));
1907 saved_errno = errno;
1908 TALLOC_FREE(conv);
1909 errno = saved_errno;
1910 return ret;
1913 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
1914 const char *fname)
1916 time_t timestamp = 0;
1917 char *stripped = NULL;
1918 char *tmp = NULL;
1919 char *result = NULL;
1920 char *parent_dir = NULL;
1921 int saved_errno;
1922 size_t rootpath_len = 0;
1923 struct shadow_copy2_config *config = NULL;
1925 SMB_VFS_HANDLE_GET_DATA(handle, config, struct shadow_copy2_config,
1926 return NULL);
1928 DBG_DEBUG("Calc connect path for [%s]\n", fname);
1930 if (config->shadow_connectpath != NULL) {
1931 DBG_DEBUG("cached connect path is [%s]\n",
1932 config->shadow_connectpath);
1933 return config->shadow_connectpath;
1936 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
1937 &timestamp, &stripped)) {
1938 goto done;
1940 if (timestamp == 0) {
1941 return SMB_VFS_NEXT_CONNECTPATH(handle, fname);
1944 tmp = shadow_copy2_do_convert(talloc_tos(), handle, stripped, timestamp,
1945 &rootpath_len);
1946 if (tmp == NULL) {
1947 if (errno != ENOENT) {
1948 goto done;
1952 * If the converted path does not exist, and converting
1953 * the parent yields something that does exist, then
1954 * this path refers to something that has not been
1955 * created yet, relative to the parent path.
1956 * The snapshot finding is relative to the parent.
1957 * (usually snapshots are read/only but this is not
1958 * necessarily true).
1959 * This code also covers getting a wildcard in the
1960 * last component, because this function is called
1961 * prior to sanitizing the path, and in SMB1 we may
1962 * get wildcards in path names.
1964 if (!parent_dirname(talloc_tos(), stripped, &parent_dir,
1965 NULL)) {
1966 errno = ENOMEM;
1967 goto done;
1970 tmp = shadow_copy2_do_convert(talloc_tos(), handle, parent_dir,
1971 timestamp, &rootpath_len);
1972 if (tmp == NULL) {
1973 goto done;
1977 DBG_DEBUG("converted path is [%s] root path is [%.*s]\n", tmp,
1978 (int)rootpath_len, tmp);
1980 tmp[rootpath_len] = '\0';
1981 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1982 if (result == NULL) {
1983 goto done;
1986 DBG_DEBUG("connect path is [%s]\n", result);
1988 done:
1989 saved_errno = errno;
1990 TALLOC_FREE(tmp);
1991 TALLOC_FREE(stripped);
1992 TALLOC_FREE(parent_dir);
1993 errno = saved_errno;
1994 return result;
1997 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
1998 const char *path, uint64_t *bsize,
1999 uint64_t *dfree, uint64_t *dsize)
2001 time_t timestamp = 0;
2002 char *stripped = NULL;
2003 ssize_t ret;
2004 int saved_errno;
2005 char *conv;
2007 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
2008 &timestamp, &stripped)) {
2009 return -1;
2011 if (timestamp == 0) {
2012 return SMB_VFS_NEXT_DISK_FREE(handle, path,
2013 bsize, dfree, dsize);
2016 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
2017 TALLOC_FREE(stripped);
2018 if (conv == NULL) {
2019 return -1;
2022 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, bsize, dfree, dsize);
2024 saved_errno = errno;
2025 TALLOC_FREE(conv);
2026 errno = saved_errno;
2028 return ret;
2031 static int shadow_copy2_get_quota(vfs_handle_struct *handle, const char *path,
2032 enum SMB_QUOTA_TYPE qtype, unid_t id,
2033 SMB_DISK_QUOTA *dq)
2035 time_t timestamp = 0;
2036 char *stripped = NULL;
2037 int ret;
2038 int saved_errno;
2039 char *conv;
2041 if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path, &timestamp,
2042 &stripped)) {
2043 return -1;
2045 if (timestamp == 0) {
2046 return SMB_VFS_NEXT_GET_QUOTA(handle, path, qtype, id, dq);
2049 conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
2050 TALLOC_FREE(stripped);
2051 if (conv == NULL) {
2052 return -1;
2055 ret = SMB_VFS_NEXT_GET_QUOTA(handle, conv, qtype, id, dq);
2057 saved_errno = errno;
2058 TALLOC_FREE(conv);
2059 errno = saved_errno;
2061 return ret;
2064 static int shadow_copy2_connect(struct vfs_handle_struct *handle,
2065 const char *service, const char *user)
2067 struct shadow_copy2_config *config;
2068 int ret;
2069 const char *snapdir;
2070 const char *gmt_format;
2071 const char *sort_order;
2072 const char *basedir = NULL;
2073 const char *snapsharepath = NULL;
2074 const char *mount_point;
2076 DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
2077 (unsigned)handle->conn->cnum,
2078 handle->conn->connectpath));
2080 ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
2081 if (ret < 0) {
2082 return ret;
2085 config = talloc_zero(handle->conn, struct shadow_copy2_config);
2086 if (config == NULL) {
2087 DEBUG(0, ("talloc_zero() failed\n"));
2088 errno = ENOMEM;
2089 return -1;
2092 gmt_format = lp_parm_const_string(SNUM(handle->conn),
2093 "shadow", "format",
2094 GMT_FORMAT);
2095 config->gmt_format = talloc_strdup(config, gmt_format);
2096 if (config->gmt_format == NULL) {
2097 DEBUG(0, ("talloc_strdup() failed\n"));
2098 errno = ENOMEM;
2099 return -1;
2102 /* config->gmt_format must not contain a path separator. */
2103 if (strchr(config->gmt_format, '/') != NULL) {
2104 DEBUG(0, ("shadow:format %s must not contain a /"
2105 "character. Unable to initialize module.\n",
2106 config->gmt_format));
2107 errno = EINVAL;
2108 return -1;
2111 config->use_sscanf = lp_parm_bool(SNUM(handle->conn),
2112 "shadow", "sscanf", false);
2114 config->use_localtime = lp_parm_bool(SNUM(handle->conn),
2115 "shadow", "localtime",
2116 false);
2118 snapdir = lp_parm_const_string(SNUM(handle->conn),
2119 "shadow", "snapdir",
2120 ".snapshots");
2121 config->snapdir = talloc_strdup(config, snapdir);
2122 if (config->snapdir == NULL) {
2123 DEBUG(0, ("talloc_strdup() failed\n"));
2124 errno = ENOMEM;
2125 return -1;
2128 config->snapdirseverywhere = lp_parm_bool(SNUM(handle->conn),
2129 "shadow",
2130 "snapdirseverywhere",
2131 false);
2133 config->crossmountpoints = lp_parm_bool(SNUM(handle->conn),
2134 "shadow", "crossmountpoints",
2135 false);
2137 if (config->crossmountpoints && !config->snapdirseverywhere) {
2138 DBG_WARNING("Warning: 'crossmountpoints' depends on "
2139 "'snapdirseverywhere'. Disabling crossmountpoints.\n");
2142 config->fixinodes = lp_parm_bool(SNUM(handle->conn),
2143 "shadow", "fixinodes",
2144 false);
2146 sort_order = lp_parm_const_string(SNUM(handle->conn),
2147 "shadow", "sort", "desc");
2148 config->sort_order = talloc_strdup(config, sort_order);
2149 if (config->sort_order == NULL) {
2150 DEBUG(0, ("talloc_strdup() failed\n"));
2151 errno = ENOMEM;
2152 return -1;
2155 mount_point = lp_parm_const_string(SNUM(handle->conn),
2156 "shadow", "mountpoint", NULL);
2157 if (mount_point != NULL) {
2158 if (mount_point[0] != '/') {
2159 DEBUG(1, (__location__ " Warning: 'mountpoint' is "
2160 "relative ('%s'), but it has to be an "
2161 "absolute path. Ignoring provided value.\n",
2162 mount_point));
2163 mount_point = NULL;
2164 } else {
2165 char *p;
2166 p = strstr(handle->conn->connectpath, mount_point);
2167 if (p != handle->conn->connectpath) {
2168 DBG_WARNING("Warning: the share root (%s) is "
2169 "not a subdirectory of the "
2170 "specified mountpoint (%s). "
2171 "Ignoring provided value.\n",
2172 handle->conn->connectpath,
2173 mount_point);
2174 mount_point = NULL;
2179 if (mount_point != NULL) {
2180 config->mount_point = talloc_strdup(config, mount_point);
2181 if (config->mount_point == NULL) {
2182 DEBUG(0, (__location__ " talloc_strdup() failed\n"));
2183 return -1;
2185 } else {
2186 config->mount_point = shadow_copy2_find_mount_point(config,
2187 handle);
2188 if (config->mount_point == NULL) {
2189 DBG_WARNING("shadow_copy2_find_mount_point "
2190 "of the share root '%s' failed: %s\n",
2191 handle->conn->connectpath, strerror(errno));
2192 return -1;
2196 basedir = lp_parm_const_string(SNUM(handle->conn),
2197 "shadow", "basedir", NULL);
2199 if (basedir != NULL) {
2200 if (basedir[0] != '/') {
2201 DEBUG(1, (__location__ " Warning: 'basedir' is "
2202 "relative ('%s'), but it has to be an "
2203 "absolute path. Disabling basedir.\n",
2204 basedir));
2205 basedir = NULL;
2206 } else {
2207 char *p;
2208 p = strstr(basedir, config->mount_point);
2209 if (p != basedir) {
2210 DEBUG(1, ("Warning: basedir (%s) is not a "
2211 "subdirectory of the share root's "
2212 "mount point (%s). "
2213 "Disabling basedir\n",
2214 basedir, config->mount_point));
2215 basedir = NULL;
2220 if (config->snapdirseverywhere && basedir != NULL) {
2221 DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
2222 "with 'snapdirseverywhere'. Disabling basedir.\n"));
2223 basedir = NULL;
2226 snapsharepath = lp_parm_const_string(SNUM(handle->conn), "shadow",
2227 "snapsharepath", NULL);
2228 if (snapsharepath != NULL) {
2229 if (snapsharepath[0] == '/') {
2230 DBG_WARNING("Warning: 'snapsharepath' is "
2231 "absolute ('%s'), but it has to be a "
2232 "relative path. Disabling snapsharepath.\n",
2233 snapsharepath);
2234 snapsharepath = NULL;
2236 if (config->snapdirseverywhere && snapsharepath != NULL) {
2237 DBG_WARNING("Warning: 'snapsharepath' is incompatible "
2238 "with 'snapdirseverywhere'. Disabling "
2239 "snapsharepath.\n");
2240 snapsharepath = NULL;
2244 if (basedir != NULL && snapsharepath != NULL) {
2245 DBG_WARNING("Warning: 'snapsharepath' is incompatible with "
2246 "'basedir'. Disabling snapsharepath\n");
2247 snapsharepath = NULL;
2250 if (snapsharepath != NULL) {
2251 config->rel_connectpath = talloc_strdup(config, snapsharepath);
2252 if (config->rel_connectpath == NULL) {
2253 DBG_ERR("talloc_strdup() failed\n");
2254 errno = ENOMEM;
2255 return -1;
2259 if (basedir == NULL) {
2260 basedir = config->mount_point;
2263 if (config->rel_connectpath == NULL &&
2264 strlen(basedir) < strlen(handle->conn->connectpath)) {
2265 config->rel_connectpath = talloc_strdup(config,
2266 handle->conn->connectpath + strlen(basedir));
2267 if (config->rel_connectpath == NULL) {
2268 DEBUG(0, ("talloc_strdup() failed\n"));
2269 errno = ENOMEM;
2270 return -1;
2274 if (config->snapdir[0] == '/') {
2275 config->snapdir_absolute = true;
2277 if (config->snapdirseverywhere == true) {
2278 DEBUG(1, (__location__ " Warning: An absolute snapdir "
2279 "is incompatible with 'snapdirseverywhere', "
2280 "setting 'snapdirseverywhere' to false.\n"));
2281 config->snapdirseverywhere = false;
2284 if (config->crossmountpoints == true) {
2285 DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
2286 "is not supported with an absolute snapdir. "
2287 "Disabling it.\n"));
2288 config->crossmountpoints = false;
2291 config->snapshot_basepath = config->snapdir;
2292 } else {
2293 config->snapshot_basepath = talloc_asprintf(config, "%s/%s",
2294 config->mount_point, config->snapdir);
2295 if (config->snapshot_basepath == NULL) {
2296 DEBUG(0, ("talloc_asprintf() failed\n"));
2297 errno = ENOMEM;
2298 return -1;
2302 trim_string(config->mount_point, NULL, "/");
2303 trim_string(config->rel_connectpath, "/", "/");
2304 trim_string(config->snapdir, NULL, "/");
2305 trim_string(config->snapshot_basepath, NULL, "/");
2307 DEBUG(10, ("shadow_copy2_connect: configuration:\n"
2308 " share root: '%s'\n"
2309 " mountpoint: '%s'\n"
2310 " rel share root: '%s'\n"
2311 " snapdir: '%s'\n"
2312 " snapshot base path: '%s'\n"
2313 " format: '%s'\n"
2314 " use sscanf: %s\n"
2315 " snapdirs everywhere: %s\n"
2316 " cross mountpoints: %s\n"
2317 " fix inodes: %s\n"
2318 " sort order: %s\n"
2320 handle->conn->connectpath,
2321 config->mount_point,
2322 config->rel_connectpath,
2323 config->snapdir,
2324 config->snapshot_basepath,
2325 config->gmt_format,
2326 config->use_sscanf ? "yes" : "no",
2327 config->snapdirseverywhere ? "yes" : "no",
2328 config->crossmountpoints ? "yes" : "no",
2329 config->fixinodes ? "yes" : "no",
2330 config->sort_order
2334 SMB_VFS_HANDLE_SET_DATA(handle, config,
2335 NULL, struct shadow_copy2_config,
2336 return -1);
2338 return 0;
2341 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
2342 .connect_fn = shadow_copy2_connect,
2343 .opendir_fn = shadow_copy2_opendir,
2344 .disk_free_fn = shadow_copy2_disk_free,
2345 .get_quota_fn = shadow_copy2_get_quota,
2346 .rename_fn = shadow_copy2_rename,
2347 .link_fn = shadow_copy2_link,
2348 .symlink_fn = shadow_copy2_symlink,
2349 .stat_fn = shadow_copy2_stat,
2350 .lstat_fn = shadow_copy2_lstat,
2351 .fstat_fn = shadow_copy2_fstat,
2352 .open_fn = shadow_copy2_open,
2353 .unlink_fn = shadow_copy2_unlink,
2354 .chmod_fn = shadow_copy2_chmod,
2355 .chown_fn = shadow_copy2_chown,
2356 .chdir_fn = shadow_copy2_chdir,
2357 .ntimes_fn = shadow_copy2_ntimes,
2358 .readlink_fn = shadow_copy2_readlink,
2359 .mknod_fn = shadow_copy2_mknod,
2360 .realpath_fn = shadow_copy2_realpath,
2361 .get_nt_acl_fn = shadow_copy2_get_nt_acl,
2362 .fget_nt_acl_fn = shadow_copy2_fget_nt_acl,
2363 .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
2364 .mkdir_fn = shadow_copy2_mkdir,
2365 .rmdir_fn = shadow_copy2_rmdir,
2366 .getxattr_fn = shadow_copy2_getxattr,
2367 .listxattr_fn = shadow_copy2_listxattr,
2368 .removexattr_fn = shadow_copy2_removexattr,
2369 .setxattr_fn = shadow_copy2_setxattr,
2370 .chmod_acl_fn = shadow_copy2_chmod_acl,
2371 .chflags_fn = shadow_copy2_chflags,
2372 .get_real_filename_fn = shadow_copy2_get_real_filename,
2373 .connectpath_fn = shadow_copy2_connectpath,
2376 NTSTATUS vfs_shadow_copy2_init(void);
2377 NTSTATUS vfs_shadow_copy2_init(void)
2379 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2380 "shadow_copy2", &vfs_shadow_copy2_fns);