s3: Remove SHADOW_COPY_DATA typedef (cherry picked from commit 0ec9a90c29b86435f32c1d...
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blobf612c1b9caf91f7bfb514935785ce7a0155b3ac7
1 /*
2 * implementation of an Shadow Copy module - version 2
4 * Copyright (C) Andrew Tridgell 2007
5 * Copyright (C) Ed Plese 2009
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
25 #include "ntioctl.h"
29 This is a 2nd implemetation of a shadow copy module for exposing
30 snapshots to windows clients as shadow copies. This version has the
31 following features:
33 1) you don't need to populate your shares with symlinks to the
34 snapshots. This can be very important when you have thousands of
35 shares, or use [homes]
37 2) the inode number of the files is altered so it is different
38 from the original. This allows the 'restore' button to work
39 without a sharing violation
41 3) shadow copy results can be sorted before being sent to the
42 client. This is beneficial for filesystems that don't read
43 directories alphabetically (the default unix).
45 4) vanity naming for snapshots. Snapshots can be named in any
46 format compatible with str[fp]time conversions.
48 5) time stamps in snapshot names can be represented in localtime
49 rather than UTC.
51 Module options:
53 shadow:snapdir = <directory where snapshots are kept>
55 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
56 path it is used as-is. If it is a relative path, then it is taken relative to the mount
57 point of the filesystem that the root of this share is on
59 shadow:basedir = <base directory that snapshots are from>
61 This is an optional parameter that specifies the directory that
62 the snapshots are relative to. It defaults to the filesystem
63 mount point
65 shadow:fixinodes = yes/no
67 If you enable shadow:fixinodes then this module will modify the
68 apparent inode number of files in the snapshot directories using
69 a hash of the files path. This is needed for snapshot systems
70 where the snapshots have the same device:inode number as the
71 original files (such as happens with GPFS snapshots). If you
72 don't set this option then the 'restore' button in the shadow
73 copy UI will fail with a sharing violation.
75 shadow:sort = asc/desc, or not specified for unsorted (default)
77 This is an optional parameter that specifies that the shadow
78 copy directories should be sorted before sending them to the
79 client. This can be beneficial as unix filesystems are usually
80 not listed alphabetically sorted. If enabled, you typically
81 want to specify descending order.
83 shadow:format = <format specification for snapshot names>
85 This is an optional parameter that specifies the format
86 specification for the naming of snapshots. The format must
87 be compatible with the conversion specifications recognized
88 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
90 shadow:localtime = yes/no (default is no)
92 This is an optional parameter that indicates whether the
93 snapshot names are in UTC/GMT or the local time.
96 The following command would generate a correctly formatted directory name
97 for use with the default parameters:
98 date -u +@GMT-%Y.%m.%d-%H.%M.%S
102 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
104 #undef DBGC_CLASS
105 #define DBGC_CLASS vfs_shadow_copy2_debug_level
107 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
108 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
110 #define SHADOW_COPY2_DEFAULT_SORT NULL
111 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
112 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
115 make very sure it is one of our special names
117 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
119 unsigned year, month, day, hr, min, sec;
120 const char *p;
121 if (gmt_start) {
122 (*gmt_start) = NULL;
124 p = strstr_m(name, "@GMT-");
125 if (p == NULL) return false;
126 if (p > name && p[-1] != '/') return False;
127 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
128 &day, &hr, &min, &sec) != 6) {
129 return False;
131 if (p[24] != 0 && p[24] != '/') {
132 return False;
134 if (gmt_start) {
135 (*gmt_start) = p;
137 return True;
140 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
141 vfs_handle_struct *handle, const char *name)
143 struct tm timestamp;
144 time_t timestamp_t;
145 char gmt[GMT_NAME_LEN + 1];
146 const char *fmt;
148 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
149 "format", SHADOW_COPY2_DEFAULT_FORMAT);
151 ZERO_STRUCT(timestamp);
152 if (strptime(name, fmt, &timestamp) == NULL) {
153 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
154 fmt, name));
155 return NULL;
158 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
159 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
160 SHADOW_COPY2_DEFAULT_LOCALTIME))
162 timestamp.tm_isdst = -1;
163 timestamp_t = mktime(&timestamp);
164 gmtime_r(&timestamp_t, &timestamp);
166 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, &timestamp);
168 return talloc_strdup(mem_ctx, gmt);
172 shadow copy paths can also come into the server in this form:
174 /foo/bar/@GMT-XXXXX/some/file
176 This function normalises the filename to be of the form:
178 @GMT-XXXX/foo/bar/some/file
180 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
182 char *pcopy;
183 char buf[GMT_NAME_LEN];
184 size_t prefix_len;
186 if (path == gmt_start) {
187 return path;
190 prefix_len = gmt_start - path - 1;
192 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
193 (int)prefix_len));
196 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
197 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
198 * processing. As many VFS calls provide a const char *,
199 * unfortunately we have to make a copy.
202 pcopy = talloc_strdup(talloc_tos(), path);
203 if (pcopy == NULL) {
204 return NULL;
207 gmt_start = pcopy + prefix_len;
210 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
212 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
215 * Make space for it including a trailing /
217 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
220 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
222 memcpy(pcopy, buf, GMT_NAME_LEN);
223 pcopy[GMT_NAME_LEN] = '/';
225 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
227 return pcopy;
231 convert a name to the shadow directory
234 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
235 const char *name = fname; \
236 const char *gmt_start; \
237 if (shadow_copy2_match_name(fname, &gmt_start)) { \
238 char *name2; \
239 rtype ret; \
240 name2 = convert_shadow2_name(handle, fname, gmt_start); \
241 if (name2 == NULL) { \
242 errno = EINVAL; \
243 return eret; \
245 name = name2; \
246 ret = SMB_VFS_NEXT_ ## op args; \
247 talloc_free(name2); \
248 if (ret != eret) extra; \
249 return ret; \
250 } else { \
251 return SMB_VFS_NEXT_ ## op args; \
253 } while (0)
255 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
256 const char *gmt_start; \
257 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
258 char *name2; \
259 char *smb_base_name_tmp = NULL; \
260 rtype ret; \
261 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
262 if (name2 == NULL) { \
263 errno = EINVAL; \
264 return eret; \
266 smb_base_name_tmp = smb_fname->base_name; \
267 smb_fname->base_name = name2; \
268 ret = SMB_VFS_NEXT_ ## op args; \
269 smb_fname->base_name = smb_base_name_tmp; \
270 talloc_free(name2); \
271 if (ret != eret) extra; \
272 return ret; \
273 } else { \
274 return SMB_VFS_NEXT_ ## op args; \
276 } while (0)
279 convert a name to the shadow directory: NTSTATUS-specific handling
282 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
283 const char *name = fname; \
284 const char *gmt_start; \
285 if (shadow_copy2_match_name(fname, &gmt_start)) { \
286 char *name2; \
287 NTSTATUS ret; \
288 name2 = convert_shadow2_name(handle, fname, gmt_start); \
289 if (name2 == NULL) { \
290 errno = EINVAL; \
291 return eret; \
293 name = name2; \
294 ret = SMB_VFS_NEXT_ ## op args; \
295 talloc_free(name2); \
296 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
297 return ret; \
298 } else { \
299 return SMB_VFS_NEXT_ ## op args; \
301 } while (0)
303 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
305 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
307 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
309 #define SHADOW2_NEXT2(op, args) do { \
310 const char *gmt_start1, *gmt_start2; \
311 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
312 shadow_copy2_match_name(newname, &gmt_start2)) { \
313 errno = EROFS; \
314 return -1; \
315 } else { \
316 return SMB_VFS_NEXT_ ## op args; \
318 } while (0)
320 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
321 const char *gmt_start1, *gmt_start2; \
322 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
323 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
324 errno = EROFS; \
325 return -1; \
326 } else { \
327 return SMB_VFS_NEXT_ ## op args; \
329 } while (0)
333 find the mount point of a filesystem
335 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
337 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
338 dev_t dev;
339 struct stat st;
340 char *p;
342 if (stat(path, &st) != 0) {
343 talloc_free(path);
344 return NULL;
347 dev = st.st_dev;
349 while ((p = strrchr(path, '/')) && p > path) {
350 *p = 0;
351 if (stat(path, &st) != 0) {
352 talloc_free(path);
353 return NULL;
355 if (st.st_dev != dev) {
356 *p = '/';
357 break;
361 return path;
365 work out the location of the snapshot for this share
367 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
369 const char *snapdir;
370 char *mount_point;
371 const char *ret;
373 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
374 if (snapdir == NULL) {
375 return NULL;
377 /* if its an absolute path, we're done */
378 if (*snapdir == '/') {
379 return snapdir;
382 /* other its relative to the filesystem mount point */
383 mount_point = find_mount_point(mem_ctx, handle);
384 if (mount_point == NULL) {
385 return NULL;
388 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
389 talloc_free(mount_point);
390 return ret;
394 work out the location of the base directory for snapshots of this share
396 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
398 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
400 /* other its the filesystem mount point */
401 if (basedir == NULL) {
402 basedir = find_mount_point(mem_ctx, handle);
405 return basedir;
409 convert a filename from a share relative path, to a path in the
410 snapshot directory
412 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
414 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
415 const char *snapdir, *relpath, *baseoffset, *basedir;
416 size_t baselen;
417 char *ret, *prefix;
419 struct tm timestamp;
420 time_t timestamp_t;
421 char snapshot[MAXPATHLEN];
422 const char *fmt;
424 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
425 "format", SHADOW_COPY2_DEFAULT_FORMAT);
427 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
428 if (snapdir == NULL) {
429 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
430 talloc_free(tmp_ctx);
431 return NULL;
434 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
435 if (basedir == NULL) {
436 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
437 talloc_free(tmp_ctx);
438 return NULL;
441 prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
442 if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
443 /* this looks like as we have already normalized it, leave it untouched*/
444 talloc_free(tmp_ctx);
445 return talloc_strdup(handle->data, fname);
448 if (strncmp(fname, "@GMT-", 5) != 0) {
449 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
450 if (fname == NULL) {
451 talloc_free(tmp_ctx);
452 return NULL;
456 ZERO_STRUCT(timestamp);
457 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
458 if (relpath == NULL) {
459 talloc_free(tmp_ctx);
460 return NULL;
463 /* relpath is the remaining portion of the path after the @GMT-xxx */
465 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
466 SHADOW_COPY2_DEFAULT_LOCALTIME))
468 timestamp_t = timegm(&timestamp);
469 localtime_r(&timestamp_t, &timestamp);
472 strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
474 baselen = strlen(basedir);
475 baseoffset = handle->conn->connectpath + baselen;
477 /* some sanity checks */
478 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
479 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
480 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
481 basedir, handle->conn->connectpath));
482 talloc_free(tmp_ctx);
483 return NULL;
486 if (*relpath == '/') relpath++;
487 if (*baseoffset == '/') baseoffset++;
489 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
490 snapdir,
491 snapshot,
492 baseoffset,
493 relpath);
494 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
495 talloc_free(tmp_ctx);
496 return ret;
501 simple string hash
503 static uint32 string_hash(const char *s)
505 uint32 n = 0;
506 while (*s) {
507 n = ((n << 5) + n) ^ (uint32)(*s++);
509 return n;
513 modify a sbuf return to ensure that inodes in the shadow directory
514 are different from those in the main directory
516 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
518 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
519 /* some snapshot systems, like GPFS, return the name
520 device:inode for the snapshot files as the current
521 files. That breaks the 'restore' button in the shadow copy
522 GUI, as the client gets a sharing violation.
524 This is a crude way of allowing both files to be
525 open at once. It has a slight chance of inode
526 number collision, but I can't see a better approach
527 without significant VFS changes
529 uint32_t shash = string_hash(fname) & 0xFF000000;
530 if (shash == 0) {
531 shash = 1;
533 sbuf->st_ex_ino ^= shash;
537 static int shadow_copy2_rename(vfs_handle_struct *handle,
538 const struct smb_filename *smb_fname_src,
539 const struct smb_filename *smb_fname_dst)
541 if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
542 errno = EXDEV;
543 return -1;
545 SHADOW2_NEXT2_SMB_FNAME(RENAME,
546 (handle, smb_fname_src, smb_fname_dst));
549 static int shadow_copy2_symlink(vfs_handle_struct *handle,
550 const char *oldname, const char *newname)
552 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
555 static int shadow_copy2_link(vfs_handle_struct *handle,
556 const char *oldname, const char *newname)
558 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
561 static int shadow_copy2_open(vfs_handle_struct *handle,
562 struct smb_filename *smb_fname, files_struct *fsp,
563 int flags, mode_t mode)
565 SHADOW2_NEXT_SMB_FNAME(OPEN,
566 (handle, smb_fname, fsp, flags, mode),
567 int, -1);
570 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
571 const char *fname, const char *mask, uint32 attr)
573 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
576 static int shadow_copy2_stat(vfs_handle_struct *handle,
577 struct smb_filename *smb_fname)
579 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
580 convert_sbuf(handle, smb_fname->base_name,
581 &smb_fname->st));
584 static int shadow_copy2_lstat(vfs_handle_struct *handle,
585 struct smb_filename *smb_fname)
587 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
588 convert_sbuf(handle, smb_fname->base_name,
589 &smb_fname->st));
592 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
594 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
595 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
596 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
598 return ret;
601 static int shadow_copy2_unlink(vfs_handle_struct *handle,
602 const struct smb_filename *smb_fname_in)
604 struct smb_filename *smb_fname = NULL;
605 NTSTATUS status;
607 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
608 if (!NT_STATUS_IS_OK(status)) {
609 errno = map_errno_from_nt_status(status);
610 return -1;
613 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
616 static int shadow_copy2_chmod(vfs_handle_struct *handle,
617 const char *fname, mode_t mode)
619 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
622 static int shadow_copy2_chown(vfs_handle_struct *handle,
623 const char *fname, uid_t uid, gid_t gid)
625 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
628 static int shadow_copy2_chdir(vfs_handle_struct *handle,
629 const char *fname)
631 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
634 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
635 const struct smb_filename *smb_fname_in,
636 struct smb_file_time *ft)
638 struct smb_filename *smb_fname = NULL;
639 NTSTATUS status;
641 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
642 if (!NT_STATUS_IS_OK(status)) {
643 errno = map_errno_from_nt_status(status);
644 return -1;
647 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
650 static int shadow_copy2_readlink(vfs_handle_struct *handle,
651 const char *fname, char *buf, size_t bufsiz)
653 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
656 static int shadow_copy2_mknod(vfs_handle_struct *handle,
657 const char *fname, mode_t mode, SMB_DEV_T dev)
659 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
662 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
663 const char *fname)
665 const char *gmt;
667 if (shadow_copy2_match_name(fname, &gmt)
668 && (gmt[GMT_NAME_LEN] == '\0')) {
669 char *copy;
671 copy = talloc_strdup(talloc_tos(), fname);
672 if (copy == NULL) {
673 errno = ENOMEM;
674 return NULL;
677 copy[gmt - fname] = '.';
678 copy[gmt - fname + 1] = '\0';
680 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
681 SHADOW2_NEXT(REALPATH, (handle, name), char *,
682 NULL);
684 SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
687 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
688 const char *fname)
690 TALLOC_CTX *tmp_ctx;
691 const char *snapdir, *baseoffset, *basedir, *gmt_start;
692 size_t baselen;
693 char *ret;
695 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
697 if (!shadow_copy2_match_name(fname, &gmt_start)) {
698 return handle->conn->connectpath;
702 * We have to create a real temporary context because we have
703 * to put our result on talloc_tos(). Thus we can't use a
704 * talloc_stackframe() here.
706 tmp_ctx = talloc_new(talloc_tos());
708 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
709 if (fname == NULL) {
710 TALLOC_FREE(tmp_ctx);
711 return NULL;
714 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
715 if (snapdir == NULL) {
716 DEBUG(2,("no snapdir found for share at %s\n",
717 handle->conn->connectpath));
718 TALLOC_FREE(tmp_ctx);
719 return NULL;
722 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
723 if (basedir == NULL) {
724 DEBUG(2,("no basedir found for share at %s\n",
725 handle->conn->connectpath));
726 TALLOC_FREE(tmp_ctx);
727 return NULL;
730 baselen = strlen(basedir);
731 baseoffset = handle->conn->connectpath + baselen;
733 /* some sanity checks */
734 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
735 (handle->conn->connectpath[baselen] != 0
736 && handle->conn->connectpath[baselen] != '/')) {
737 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
738 "parent of %s\n", basedir,
739 handle->conn->connectpath));
740 TALLOC_FREE(tmp_ctx);
741 return NULL;
744 if (*baseoffset == '/') baseoffset++;
746 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
747 snapdir,
748 GMT_NAME_LEN, fname,
749 baseoffset);
750 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
751 TALLOC_FREE(tmp_ctx);
752 return ret;
755 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
756 const char *fname, uint32 security_info,
757 struct security_descriptor **ppdesc)
759 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
762 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
764 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
767 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
769 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
772 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
773 unsigned int flags)
775 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
778 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
779 const char *fname, const char *aname, void *value, size_t size)
781 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
784 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
785 const char *fname, const char *aname, void *value, size_t size)
787 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
790 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
791 char *list, size_t size)
793 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
796 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
797 const char *aname)
799 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
802 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
803 const char *aname)
805 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
808 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
809 const char *aname, const void *value, size_t size, int flags)
811 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
814 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
815 const char *aname, const void *value, size_t size, int flags)
817 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
820 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
821 const char *fname, mode_t mode)
823 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
826 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
828 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
831 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
833 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
837 sort the shadow copy data in ascending or descending order
839 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
840 struct shadow_copy_data *shadow_copy2_data)
842 int (*cmpfunc)(const void *, const void *);
843 const char *sort;
845 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
846 "sort", SHADOW_COPY2_DEFAULT_SORT);
847 if (sort == NULL) {
848 return;
851 if (strcmp(sort, "asc") == 0) {
852 cmpfunc = shadow_copy2_label_cmp_asc;
853 } else if (strcmp(sort, "desc") == 0) {
854 cmpfunc = shadow_copy2_label_cmp_desc;
855 } else {
856 return;
859 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
860 shadow_copy2_data->labels)
862 TYPESAFE_QSORT(shadow_copy2_data->labels,
863 shadow_copy2_data->num_volumes,
864 cmpfunc);
867 return;
870 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
871 files_struct *fsp,
872 struct shadow_copy_data *shadow_copy2_data,
873 bool labels)
875 SMB_STRUCT_DIR *p;
876 const char *snapdir;
877 SMB_STRUCT_DIRENT *d;
878 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
879 char *snapshot;
881 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
882 if (snapdir == NULL) {
883 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
884 handle->conn->connectpath));
885 errno = EINVAL;
886 talloc_free(tmp_ctx);
887 return -1;
890 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
892 if (!p) {
893 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
894 " - %s\n", snapdir, strerror(errno)));
895 talloc_free(tmp_ctx);
896 errno = ENOSYS;
897 return -1;
900 shadow_copy2_data->num_volumes = 0;
901 shadow_copy2_data->labels = NULL;
903 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
904 SHADOW_COPY_LABEL *tlabels;
906 /* ignore names not of the right form in the snapshot directory */
907 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
908 d->d_name);
909 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
910 d->d_name, snapshot));
911 if (!snapshot) {
912 continue;
915 if (!labels) {
916 /* the caller doesn't want the labels */
917 shadow_copy2_data->num_volumes++;
918 continue;
921 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
922 shadow_copy2_data->labels,
923 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
924 if (tlabels == NULL) {
925 DEBUG(0,("shadow_copy2: out of memory\n"));
926 SMB_VFS_NEXT_CLOSEDIR(handle, p);
927 talloc_free(tmp_ctx);
928 return -1;
931 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
932 sizeof(*tlabels));
933 talloc_free(snapshot);
935 shadow_copy2_data->num_volumes++;
936 shadow_copy2_data->labels = tlabels;
939 SMB_VFS_NEXT_CLOSEDIR(handle,p);
941 shadow_copy2_sort_data(handle, shadow_copy2_data);
943 talloc_free(tmp_ctx);
944 return 0;
947 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
948 .opendir = shadow_copy2_opendir,
949 .mkdir = shadow_copy2_mkdir,
950 .rmdir = shadow_copy2_rmdir,
951 .chflags = shadow_copy2_chflags,
952 .getxattr = shadow_copy2_getxattr,
953 .lgetxattr = shadow_copy2_lgetxattr,
954 .listxattr = shadow_copy2_listxattr,
955 .removexattr = shadow_copy2_removexattr,
956 .lremovexattr = shadow_copy2_lremovexattr,
957 .setxattr = shadow_copy2_setxattr,
958 .lsetxattr = shadow_copy2_lsetxattr,
959 .open_fn = shadow_copy2_open,
960 .rename = shadow_copy2_rename,
961 .stat = shadow_copy2_stat,
962 .lstat = shadow_copy2_lstat,
963 .fstat = shadow_copy2_fstat,
964 .unlink = shadow_copy2_unlink,
965 .chmod = shadow_copy2_chmod,
966 .chown = shadow_copy2_chown,
967 .chdir = shadow_copy2_chdir,
968 .ntimes = shadow_copy2_ntimes,
969 .symlink = shadow_copy2_symlink,
970 .vfs_readlink = shadow_copy2_readlink,
971 .link = shadow_copy2_link,
972 .mknod = shadow_copy2_mknod,
973 .realpath = shadow_copy2_realpath,
974 .connectpath = shadow_copy2_connectpath,
975 .get_nt_acl = shadow_copy2_get_nt_acl,
976 .chmod_acl = shadow_copy2_chmod_acl,
977 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
980 NTSTATUS vfs_shadow_copy2_init(void);
981 NTSTATUS vfs_shadow_copy2_init(void)
983 NTSTATUS ret;
985 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
986 &vfs_shadow_copy2_fns);
988 if (!NT_STATUS_IS_OK(ret))
989 return ret;
991 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
992 if (vfs_shadow_copy2_debug_level == -1) {
993 vfs_shadow_copy2_debug_level = DBGC_VFS;
994 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
995 "vfs_shadow_copy2_init"));
996 } else {
997 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
998 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
1001 return ret;