WHATSNEW: Update changes.
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
bloba4265474bf6038b860e3bfd8d2e243bff0a7f378
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"
26 This is a 2nd implemetation of a shadow copy module for exposing
27 snapshots to windows clients as shadow copies. This version has the
28 following features:
30 1) you don't need to populate your shares with symlinks to the
31 snapshots. This can be very important when you have thousands of
32 shares, or use [homes]
34 2) the inode number of the files is altered so it is different
35 from the original. This allows the 'restore' button to work
36 without a sharing violation
38 3) shadow copy results can be sorted before being sent to the
39 client. This is beneficial for filesystems that don't read
40 directories alphabetically (the default unix).
42 4) vanity naming for snapshots. Snapshots can be named in any
43 format compatible with str[fp]time conversions.
45 5) time stamps in snapshot names can be represented in localtime
46 rather than UTC.
48 Module options:
50 shadow:snapdir = <directory where snapshots are kept>
52 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
53 path it is used as-is. If it is a relative path, then it is taken relative to the mount
54 point of the filesystem that the root of this share is on
56 shadow:basedir = <base directory that snapshots are from>
58 This is an optional parameter that specifies the directory that
59 the snapshots are relative to. It defaults to the filesystem
60 mount point
62 shadow:fixinodes = yes/no
64 If you enable shadow:fixinodes then this module will modify the
65 apparent inode number of files in the snapshot directories using
66 a hash of the files path. This is needed for snapshot systems
67 where the snapshots have the same device:inode number as the
68 original files (such as happens with GPFS snapshots). If you
69 don't set this option then the 'restore' button in the shadow
70 copy UI will fail with a sharing violation.
72 shadow:sort = asc/desc, or not specified for unsorted (default)
74 This is an optional parameter that specifies that the shadow
75 copy directories should be sorted before sending them to the
76 client. This can be beneficial as unix filesystems are usually
77 not listed alphabetically sorted. If enabled, you typically
78 want to specify descending order.
80 shadow:format = <format specification for snapshot names>
82 This is an optional parameter that specifies the format
83 specification for the naming of snapshots. The format must
84 be compatible with the conversion specifications recognized
85 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
87 shadow:localtime = yes/no (default is no)
89 This is an optional parameter that indicates whether the
90 snapshot names are in UTC/GMT or the local time.
93 The following command would generate a correctly formatted directory name
94 for use with the default parameters:
95 date -u +@GMT-%Y.%m.%d-%H.%M.%S
99 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
101 #undef DBGC_CLASS
102 #define DBGC_CLASS vfs_shadow_copy2_debug_level
104 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
105 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
107 #define SHADOW_COPY2_DEFAULT_SORT NULL
108 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
109 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
112 make very sure it is one of our special names
114 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
116 unsigned year, month, day, hr, min, sec;
117 const char *p;
118 if (gmt_start) {
119 (*gmt_start) = NULL;
121 p = strstr_m(name, "@GMT-");
122 if (p == NULL) return false;
123 if (p > name && p[-1] != '/') return False;
124 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
125 &day, &hr, &min, &sec) != 6) {
126 return False;
128 if (p[24] != 0 && p[24] != '/') {
129 return False;
131 if (gmt_start) {
132 (*gmt_start) = p;
134 return True;
137 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
138 vfs_handle_struct *handle, const char *name)
140 struct tm timestamp;
141 time_t timestamp_t;
142 char gmt[GMT_NAME_LEN + 1];
143 const char *fmt;
145 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
146 "format", SHADOW_COPY2_DEFAULT_FORMAT);
148 ZERO_STRUCT(timestamp);
149 if (strptime(name, fmt, &timestamp) == NULL) {
150 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
151 fmt, name));
152 return NULL;
155 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
156 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
157 SHADOW_COPY2_DEFAULT_LOCALTIME))
159 timestamp.tm_isdst = -1;
160 timestamp_t = mktime(&timestamp);
161 gmtime_r(&timestamp_t, &timestamp);
163 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, &timestamp);
165 return talloc_strdup(mem_ctx, gmt);
169 shadow copy paths can also come into the server in this form:
171 /foo/bar/@GMT-XXXXX/some/file
173 This function normalises the filename to be of the form:
175 @GMT-XXXX/foo/bar/some/file
177 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
179 char *pcopy;
180 char buf[GMT_NAME_LEN];
181 size_t prefix_len;
183 if (path == gmt_start) {
184 return path;
187 prefix_len = gmt_start - path - 1;
189 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
190 (int)prefix_len));
193 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
194 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
195 * processing. As many VFS calls provide a const char *,
196 * unfortunately we have to make a copy.
199 pcopy = talloc_strdup(talloc_tos(), path);
200 if (pcopy == NULL) {
201 return NULL;
204 gmt_start = pcopy + prefix_len;
207 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
209 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
212 * Make space for it including a trailing /
214 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
217 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
219 memcpy(pcopy, buf, GMT_NAME_LEN);
220 pcopy[GMT_NAME_LEN] = '/';
222 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
224 return pcopy;
228 convert a name to the shadow directory
231 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
232 const char *name = fname; \
233 const char *gmt_start; \
234 if (shadow_copy2_match_name(fname, &gmt_start)) { \
235 char *name2; \
236 rtype ret; \
237 name2 = convert_shadow2_name(handle, fname, gmt_start); \
238 if (name2 == NULL) { \
239 errno = EINVAL; \
240 return eret; \
242 name = name2; \
243 ret = SMB_VFS_NEXT_ ## op args; \
244 talloc_free(name2); \
245 if (ret != eret) extra; \
246 return ret; \
247 } else { \
248 return SMB_VFS_NEXT_ ## op args; \
250 } while (0)
252 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
253 const char *gmt_start; \
254 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
255 char *name2; \
256 char *smb_base_name_tmp = NULL; \
257 rtype ret; \
258 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
259 if (name2 == NULL) { \
260 errno = EINVAL; \
261 return eret; \
263 smb_base_name_tmp = smb_fname->base_name; \
264 smb_fname->base_name = name2; \
265 ret = SMB_VFS_NEXT_ ## op args; \
266 smb_fname->base_name = smb_base_name_tmp; \
267 talloc_free(name2); \
268 if (ret != eret) extra; \
269 return ret; \
270 } else { \
271 return SMB_VFS_NEXT_ ## op args; \
273 } while (0)
276 convert a name to the shadow directory: NTSTATUS-specific handling
279 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
280 const char *name = fname; \
281 const char *gmt_start; \
282 if (shadow_copy2_match_name(fname, &gmt_start)) { \
283 char *name2; \
284 NTSTATUS ret; \
285 name2 = convert_shadow2_name(handle, fname, gmt_start); \
286 if (name2 == NULL) { \
287 errno = EINVAL; \
288 return eret; \
290 name = name2; \
291 ret = SMB_VFS_NEXT_ ## op args; \
292 talloc_free(name2); \
293 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
294 return ret; \
295 } else { \
296 return SMB_VFS_NEXT_ ## op args; \
298 } while (0)
300 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
302 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
304 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
306 #define SHADOW2_NEXT2(op, args) do { \
307 const char *gmt_start1, *gmt_start2; \
308 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
309 shadow_copy2_match_name(newname, &gmt_start2)) { \
310 errno = EROFS; \
311 return -1; \
312 } else { \
313 return SMB_VFS_NEXT_ ## op args; \
315 } while (0)
317 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
318 const char *gmt_start1, *gmt_start2; \
319 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
320 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
321 errno = EROFS; \
322 return -1; \
323 } else { \
324 return SMB_VFS_NEXT_ ## op args; \
326 } while (0)
330 find the mount point of a filesystem
332 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
334 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
335 dev_t dev;
336 struct stat st;
337 char *p;
339 if (stat(path, &st) != 0) {
340 talloc_free(path);
341 return NULL;
344 dev = st.st_dev;
346 while ((p = strrchr(path, '/')) && p > path) {
347 *p = 0;
348 if (stat(path, &st) != 0) {
349 talloc_free(path);
350 return NULL;
352 if (st.st_dev != dev) {
353 *p = '/';
354 break;
358 return path;
362 work out the location of the snapshot for this share
364 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
366 const char *snapdir;
367 char *mount_point;
368 const char *ret;
370 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
371 if (snapdir == NULL) {
372 return NULL;
374 /* if its an absolute path, we're done */
375 if (*snapdir == '/') {
376 return snapdir;
379 /* other its relative to the filesystem mount point */
380 mount_point = find_mount_point(mem_ctx, handle);
381 if (mount_point == NULL) {
382 return NULL;
385 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
386 talloc_free(mount_point);
387 return ret;
391 work out the location of the base directory for snapshots of this share
393 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
395 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
397 /* other its the filesystem mount point */
398 if (basedir == NULL) {
399 basedir = find_mount_point(mem_ctx, handle);
402 return basedir;
406 convert a filename from a share relative path, to a path in the
407 snapshot directory
409 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
411 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
412 const char *snapdir, *relpath, *baseoffset, *basedir;
413 size_t baselen;
414 char *ret;
416 struct tm timestamp;
417 time_t timestamp_t;
418 char snapshot[MAXPATHLEN];
419 const char *fmt;
421 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
422 "format", SHADOW_COPY2_DEFAULT_FORMAT);
424 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
425 if (snapdir == NULL) {
426 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
427 talloc_free(tmp_ctx);
428 return NULL;
431 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
432 if (basedir == NULL) {
433 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
434 talloc_free(tmp_ctx);
435 return NULL;
438 if (strncmp(fname, "@GMT-", 5) != 0) {
439 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
440 if (fname == NULL) {
441 talloc_free(tmp_ctx);
442 return NULL;
446 ZERO_STRUCT(timestamp);
447 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
448 if (relpath == NULL) {
449 talloc_free(tmp_ctx);
450 return NULL;
453 /* relpath is the remaining portion of the path after the @GMT-xxx */
455 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
456 SHADOW_COPY2_DEFAULT_LOCALTIME))
458 timestamp_t = timegm(&timestamp);
459 localtime_r(&timestamp_t, &timestamp);
462 strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
464 baselen = strlen(basedir);
465 baseoffset = handle->conn->connectpath + baselen;
467 /* some sanity checks */
468 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
469 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
470 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
471 basedir, handle->conn->connectpath));
472 talloc_free(tmp_ctx);
473 return NULL;
476 if (*relpath == '/') relpath++;
477 if (*baseoffset == '/') baseoffset++;
479 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
480 snapdir,
481 snapshot,
482 baseoffset,
483 relpath);
484 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
485 talloc_free(tmp_ctx);
486 return ret;
491 simple string hash
493 static uint32 string_hash(const char *s)
495 uint32 n = 0;
496 while (*s) {
497 n = ((n << 5) + n) ^ (uint32)(*s++);
499 return n;
503 modify a sbuf return to ensure that inodes in the shadow directory
504 are different from those in the main directory
506 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
508 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
509 /* some snapshot systems, like GPFS, return the name
510 device:inode for the snapshot files as the current
511 files. That breaks the 'restore' button in the shadow copy
512 GUI, as the client gets a sharing violation.
514 This is a crude way of allowing both files to be
515 open at once. It has a slight chance of inode
516 number collision, but I can't see a better approach
517 without significant VFS changes
519 uint32_t shash = string_hash(fname) & 0xFF000000;
520 if (shash == 0) {
521 shash = 1;
523 sbuf->st_ex_ino ^= shash;
527 static int shadow_copy2_rename(vfs_handle_struct *handle,
528 const struct smb_filename *smb_fname_src,
529 const struct smb_filename *smb_fname_dst)
531 SHADOW2_NEXT2_SMB_FNAME(RENAME,
532 (handle, smb_fname_src, smb_fname_dst));
535 static int shadow_copy2_symlink(vfs_handle_struct *handle,
536 const char *oldname, const char *newname)
538 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
541 static int shadow_copy2_link(vfs_handle_struct *handle,
542 const char *oldname, const char *newname)
544 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
547 static int shadow_copy2_open(vfs_handle_struct *handle,
548 struct smb_filename *smb_fname, files_struct *fsp,
549 int flags, mode_t mode)
551 SHADOW2_NEXT_SMB_FNAME(OPEN,
552 (handle, smb_fname, fsp, flags, mode),
553 int, -1);
556 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
557 const char *fname, const char *mask, uint32 attr)
559 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
562 static int shadow_copy2_stat(vfs_handle_struct *handle,
563 struct smb_filename *smb_fname)
565 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
566 convert_sbuf(handle, smb_fname->base_name,
567 &smb_fname->st));
570 static int shadow_copy2_lstat(vfs_handle_struct *handle,
571 struct smb_filename *smb_fname)
573 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
574 convert_sbuf(handle, smb_fname->base_name,
575 &smb_fname->st));
578 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
580 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
581 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
582 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
584 return ret;
587 static int shadow_copy2_unlink(vfs_handle_struct *handle,
588 const struct smb_filename *smb_fname_in)
590 struct smb_filename *smb_fname = NULL;
591 NTSTATUS status;
593 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
594 if (!NT_STATUS_IS_OK(status)) {
595 errno = map_errno_from_nt_status(status);
596 return -1;
599 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
602 static int shadow_copy2_chmod(vfs_handle_struct *handle,
603 const char *fname, mode_t mode)
605 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
608 static int shadow_copy2_chown(vfs_handle_struct *handle,
609 const char *fname, uid_t uid, gid_t gid)
611 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
614 static int shadow_copy2_chdir(vfs_handle_struct *handle,
615 const char *fname)
617 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
620 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
621 const struct smb_filename *smb_fname_in,
622 struct smb_file_time *ft)
624 struct smb_filename *smb_fname = NULL;
625 NTSTATUS status;
627 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
628 if (!NT_STATUS_IS_OK(status)) {
629 errno = map_errno_from_nt_status(status);
630 return -1;
633 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
636 static int shadow_copy2_readlink(vfs_handle_struct *handle,
637 const char *fname, char *buf, size_t bufsiz)
639 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
642 static int shadow_copy2_mknod(vfs_handle_struct *handle,
643 const char *fname, mode_t mode, SMB_DEV_T dev)
645 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
648 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
649 const char *fname, char *resolved_path)
651 const char *gmt;
653 if (shadow_copy2_match_name(fname, &gmt)
654 && (gmt[GMT_NAME_LEN] == '\0')) {
655 char *copy, *result;
657 copy = talloc_strdup(talloc_tos(), fname);
658 if (copy == NULL) {
659 errno = ENOMEM;
660 return NULL;
663 copy[gmt - fname] = '.';
665 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
666 result = SMB_VFS_NEXT_REALPATH(handle, copy, resolved_path);
667 TALLOC_FREE(copy);
668 return result;
670 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
673 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
674 const char *fname)
676 TALLOC_CTX *tmp_ctx = talloc_stackframe();
677 const char *snapdir, *baseoffset, *basedir, *gmt_start;
678 size_t baselen;
679 char *ret;
681 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
683 if (!shadow_copy2_match_name(fname, &gmt_start)) {
684 return handle->conn->connectpath;
687 fname = shadow_copy2_normalise_path(talloc_tos(), fname, gmt_start);
688 if (fname == NULL) {
689 TALLOC_FREE(tmp_ctx);
690 return NULL;
693 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
694 if (snapdir == NULL) {
695 DEBUG(2,("no snapdir found for share at %s\n",
696 handle->conn->connectpath));
697 TALLOC_FREE(tmp_ctx);
698 return NULL;
701 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
702 if (basedir == NULL) {
703 DEBUG(2,("no basedir found for share at %s\n",
704 handle->conn->connectpath));
705 TALLOC_FREE(tmp_ctx);
706 return NULL;
709 baselen = strlen(basedir);
710 baseoffset = handle->conn->connectpath + baselen;
712 /* some sanity checks */
713 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
714 (handle->conn->connectpath[baselen] != 0
715 && handle->conn->connectpath[baselen] != '/')) {
716 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
717 "parent of %s\n", basedir,
718 handle->conn->connectpath));
719 TALLOC_FREE(tmp_ctx);
720 return NULL;
723 if (*baseoffset == '/') baseoffset++;
725 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
726 snapdir,
727 GMT_NAME_LEN, fname,
728 baseoffset);
729 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
730 TALLOC_FREE(tmp_ctx);
731 return ret;
734 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
735 const char *fname, uint32 security_info,
736 struct security_descriptor **ppdesc)
738 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
741 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
743 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
746 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
748 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
751 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
752 unsigned int flags)
754 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
757 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
758 const char *fname, const char *aname, void *value, size_t size)
760 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
763 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
764 const char *fname, const char *aname, void *value, size_t size)
766 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
769 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
770 char *list, size_t size)
772 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
775 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
776 const char *aname)
778 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
781 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
782 const char *aname)
784 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
787 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
788 const char *aname, const void *value, size_t size, int flags)
790 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
793 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
794 const char *aname, const void *value, size_t size, int flags)
796 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
799 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
800 const char *fname, mode_t mode)
802 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
805 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
807 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
810 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
812 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
816 sort the shadow copy data in ascending or descending order
818 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
819 SHADOW_COPY_DATA *shadow_copy2_data)
821 int (*cmpfunc)(const void *, const void *);
822 const char *sort;
824 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
825 "sort", SHADOW_COPY2_DEFAULT_SORT);
826 if (sort == NULL) {
827 return;
830 if (strcmp(sort, "asc") == 0) {
831 cmpfunc = shadow_copy2_label_cmp_asc;
832 } else if (strcmp(sort, "desc") == 0) {
833 cmpfunc = shadow_copy2_label_cmp_desc;
834 } else {
835 return;
838 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
839 shadow_copy2_data->labels)
841 TYPESAFE_QSORT(shadow_copy2_data->labels,
842 shadow_copy2_data->num_volumes,
843 cmpfunc);
846 return;
849 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
850 files_struct *fsp,
851 SHADOW_COPY_DATA *shadow_copy2_data,
852 bool labels)
854 SMB_STRUCT_DIR *p;
855 const char *snapdir;
856 SMB_STRUCT_DIRENT *d;
857 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
858 char *snapshot;
860 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
861 if (snapdir == NULL) {
862 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
863 handle->conn->connectpath));
864 errno = EINVAL;
865 talloc_free(tmp_ctx);
866 return -1;
869 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
871 if (!p) {
872 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
873 " - %s\n", snapdir, strerror(errno)));
874 talloc_free(tmp_ctx);
875 errno = ENOSYS;
876 return -1;
879 shadow_copy2_data->num_volumes = 0;
880 shadow_copy2_data->labels = NULL;
882 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
883 SHADOW_COPY_LABEL *tlabels;
885 /* ignore names not of the right form in the snapshot directory */
886 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
887 d->d_name);
888 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
889 d->d_name, snapshot));
890 if (!snapshot) {
891 continue;
894 if (!labels) {
895 /* the caller doesn't want the labels */
896 shadow_copy2_data->num_volumes++;
897 continue;
900 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
901 shadow_copy2_data->labels,
902 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
903 if (tlabels == NULL) {
904 DEBUG(0,("shadow_copy2: out of memory\n"));
905 SMB_VFS_NEXT_CLOSEDIR(handle, p);
906 talloc_free(tmp_ctx);
907 return -1;
910 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
911 sizeof(*tlabels));
912 talloc_free(snapshot);
914 shadow_copy2_data->num_volumes++;
915 shadow_copy2_data->labels = tlabels;
918 SMB_VFS_NEXT_CLOSEDIR(handle,p);
920 shadow_copy2_sort_data(handle, shadow_copy2_data);
922 talloc_free(tmp_ctx);
923 return 0;
926 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
927 .opendir = shadow_copy2_opendir,
928 .mkdir = shadow_copy2_mkdir,
929 .rmdir = shadow_copy2_rmdir,
930 .chflags = shadow_copy2_chflags,
931 .getxattr = shadow_copy2_getxattr,
932 .lgetxattr = shadow_copy2_lgetxattr,
933 .listxattr = shadow_copy2_listxattr,
934 .removexattr = shadow_copy2_removexattr,
935 .lremovexattr = shadow_copy2_lremovexattr,
936 .setxattr = shadow_copy2_setxattr,
937 .lsetxattr = shadow_copy2_lsetxattr,
938 .open = shadow_copy2_open,
939 .rename = shadow_copy2_rename,
940 .stat = shadow_copy2_stat,
941 .lstat = shadow_copy2_lstat,
942 .fstat = shadow_copy2_fstat,
943 .unlink = shadow_copy2_unlink,
944 .chmod = shadow_copy2_chmod,
945 .chown = shadow_copy2_chown,
946 .chdir = shadow_copy2_chdir,
947 .ntimes = shadow_copy2_ntimes,
948 .symlink = shadow_copy2_symlink,
949 .vfs_readlink = shadow_copy2_readlink,
950 .link = shadow_copy2_link,
951 .mknod = shadow_copy2_mknod,
952 .realpath = shadow_copy2_realpath,
953 .connectpath = shadow_copy2_connectpath,
954 .get_nt_acl = shadow_copy2_get_nt_acl,
955 .chmod_acl = shadow_copy2_chmod_acl,
956 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
959 NTSTATUS vfs_shadow_copy2_init(void);
960 NTSTATUS vfs_shadow_copy2_init(void)
962 NTSTATUS ret;
964 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
965 &vfs_shadow_copy2_fns);
967 if (!NT_STATUS_IS_OK(ret))
968 return ret;
970 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
971 if (vfs_shadow_copy2_debug_level == -1) {
972 vfs_shadow_copy2_debug_level = DBGC_VFS;
973 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
974 "vfs_shadow_copy2_init"));
975 } else {
976 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
977 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
980 return ret;