s4-samba.samdb: Fix masking names from outer context
[Samba.git] / source3 / modules / vfs_shadow_copy2.c
blob502f815235cd74aa0176aec17dcd64a4e624af6d
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, *prefix;
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 prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
439 if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
440 /* this looks like as we have already normalized it, leave it untouched*/
441 talloc_free(tmp_ctx);
442 return talloc_strdup(handle->data, fname);
445 if (strncmp(fname, "@GMT-", 5) != 0) {
446 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
447 if (fname == NULL) {
448 talloc_free(tmp_ctx);
449 return NULL;
453 ZERO_STRUCT(timestamp);
454 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
455 if (relpath == NULL) {
456 talloc_free(tmp_ctx);
457 return NULL;
460 /* relpath is the remaining portion of the path after the @GMT-xxx */
462 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
463 SHADOW_COPY2_DEFAULT_LOCALTIME))
465 timestamp_t = timegm(&timestamp);
466 localtime_r(&timestamp_t, &timestamp);
469 strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
471 baselen = strlen(basedir);
472 baseoffset = handle->conn->connectpath + baselen;
474 /* some sanity checks */
475 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
476 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
477 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
478 basedir, handle->conn->connectpath));
479 talloc_free(tmp_ctx);
480 return NULL;
483 if (*relpath == '/') relpath++;
484 if (*baseoffset == '/') baseoffset++;
486 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
487 snapdir,
488 snapshot,
489 baseoffset,
490 relpath);
491 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
492 talloc_free(tmp_ctx);
493 return ret;
498 simple string hash
500 static uint32 string_hash(const char *s)
502 uint32 n = 0;
503 while (*s) {
504 n = ((n << 5) + n) ^ (uint32)(*s++);
506 return n;
510 modify a sbuf return to ensure that inodes in the shadow directory
511 are different from those in the main directory
513 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
515 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
516 /* some snapshot systems, like GPFS, return the name
517 device:inode for the snapshot files as the current
518 files. That breaks the 'restore' button in the shadow copy
519 GUI, as the client gets a sharing violation.
521 This is a crude way of allowing both files to be
522 open at once. It has a slight chance of inode
523 number collision, but I can't see a better approach
524 without significant VFS changes
526 uint32_t shash = string_hash(fname) & 0xFF000000;
527 if (shash == 0) {
528 shash = 1;
530 sbuf->st_ex_ino ^= shash;
534 static int shadow_copy2_rename(vfs_handle_struct *handle,
535 const struct smb_filename *smb_fname_src,
536 const struct smb_filename *smb_fname_dst)
538 SHADOW2_NEXT2_SMB_FNAME(RENAME,
539 (handle, smb_fname_src, smb_fname_dst));
542 static int shadow_copy2_symlink(vfs_handle_struct *handle,
543 const char *oldname, const char *newname)
545 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
548 static int shadow_copy2_link(vfs_handle_struct *handle,
549 const char *oldname, const char *newname)
551 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
554 static int shadow_copy2_open(vfs_handle_struct *handle,
555 struct smb_filename *smb_fname, files_struct *fsp,
556 int flags, mode_t mode)
558 SHADOW2_NEXT_SMB_FNAME(OPEN,
559 (handle, smb_fname, fsp, flags, mode),
560 int, -1);
563 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
564 const char *fname, const char *mask, uint32 attr)
566 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
569 static int shadow_copy2_stat(vfs_handle_struct *handle,
570 struct smb_filename *smb_fname)
572 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
573 convert_sbuf(handle, smb_fname->base_name,
574 &smb_fname->st));
577 static int shadow_copy2_lstat(vfs_handle_struct *handle,
578 struct smb_filename *smb_fname)
580 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
581 convert_sbuf(handle, smb_fname->base_name,
582 &smb_fname->st));
585 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
587 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
588 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
589 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
591 return ret;
594 static int shadow_copy2_unlink(vfs_handle_struct *handle,
595 const struct smb_filename *smb_fname_in)
597 struct smb_filename *smb_fname = NULL;
598 NTSTATUS status;
600 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
601 if (!NT_STATUS_IS_OK(status)) {
602 errno = map_errno_from_nt_status(status);
603 return -1;
606 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
609 static int shadow_copy2_chmod(vfs_handle_struct *handle,
610 const char *fname, mode_t mode)
612 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
615 static int shadow_copy2_chown(vfs_handle_struct *handle,
616 const char *fname, uid_t uid, gid_t gid)
618 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
621 static int shadow_copy2_chdir(vfs_handle_struct *handle,
622 const char *fname)
624 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
627 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
628 const struct smb_filename *smb_fname_in,
629 struct smb_file_time *ft)
631 struct smb_filename *smb_fname = NULL;
632 NTSTATUS status;
634 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
635 if (!NT_STATUS_IS_OK(status)) {
636 errno = map_errno_from_nt_status(status);
637 return -1;
640 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
643 static int shadow_copy2_readlink(vfs_handle_struct *handle,
644 const char *fname, char *buf, size_t bufsiz)
646 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
649 static int shadow_copy2_mknod(vfs_handle_struct *handle,
650 const char *fname, mode_t mode, SMB_DEV_T dev)
652 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
655 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
656 const char *fname, char *resolved_path)
658 const char *gmt;
660 if (shadow_copy2_match_name(fname, &gmt)
661 && (gmt[GMT_NAME_LEN] == '\0')) {
662 char *copy;
664 copy = talloc_strdup(talloc_tos(), fname);
665 if (copy == NULL) {
666 errno = ENOMEM;
667 return NULL;
670 copy[gmt - fname] = '.';
671 copy[gmt - fname + 1] = '\0';
673 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
674 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *,
675 NULL);
677 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
680 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
681 const char *fname)
683 TALLOC_CTX *tmp_ctx = talloc_stackframe();
684 const char *snapdir, *baseoffset, *basedir, *gmt_start;
685 size_t baselen;
686 char *ret;
688 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
690 if (!shadow_copy2_match_name(fname, &gmt_start)) {
691 return handle->conn->connectpath;
694 fname = shadow_copy2_normalise_path(talloc_tos(), fname, gmt_start);
695 if (fname == NULL) {
696 TALLOC_FREE(tmp_ctx);
697 return NULL;
700 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
701 if (snapdir == NULL) {
702 DEBUG(2,("no snapdir found for share at %s\n",
703 handle->conn->connectpath));
704 TALLOC_FREE(tmp_ctx);
705 return NULL;
708 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
709 if (basedir == NULL) {
710 DEBUG(2,("no basedir found for share at %s\n",
711 handle->conn->connectpath));
712 TALLOC_FREE(tmp_ctx);
713 return NULL;
716 baselen = strlen(basedir);
717 baseoffset = handle->conn->connectpath + baselen;
719 /* some sanity checks */
720 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
721 (handle->conn->connectpath[baselen] != 0
722 && handle->conn->connectpath[baselen] != '/')) {
723 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
724 "parent of %s\n", basedir,
725 handle->conn->connectpath));
726 TALLOC_FREE(tmp_ctx);
727 return NULL;
730 if (*baseoffset == '/') baseoffset++;
732 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
733 snapdir,
734 GMT_NAME_LEN, fname,
735 baseoffset);
736 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
737 TALLOC_FREE(tmp_ctx);
738 return ret;
741 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
742 const char *fname, uint32 security_info,
743 struct security_descriptor **ppdesc)
745 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
748 static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
750 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
753 static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
755 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
758 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
759 unsigned int flags)
761 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
764 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
765 const char *fname, const char *aname, void *value, size_t size)
767 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
770 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
771 const char *fname, const char *aname, void *value, size_t size)
773 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
776 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
777 char *list, size_t size)
779 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
782 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
783 const char *aname)
785 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
788 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
789 const char *aname)
791 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
794 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
795 const char *aname, const void *value, size_t size, int flags)
797 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
800 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
801 const char *aname, const void *value, size_t size, int flags)
803 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
806 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
807 const char *fname, mode_t mode)
809 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
812 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
814 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
817 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
819 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
823 sort the shadow copy data in ascending or descending order
825 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
826 SHADOW_COPY_DATA *shadow_copy2_data)
828 int (*cmpfunc)(const void *, const void *);
829 const char *sort;
831 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
832 "sort", SHADOW_COPY2_DEFAULT_SORT);
833 if (sort == NULL) {
834 return;
837 if (strcmp(sort, "asc") == 0) {
838 cmpfunc = shadow_copy2_label_cmp_asc;
839 } else if (strcmp(sort, "desc") == 0) {
840 cmpfunc = shadow_copy2_label_cmp_desc;
841 } else {
842 return;
845 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
846 shadow_copy2_data->labels)
848 TYPESAFE_QSORT(shadow_copy2_data->labels,
849 shadow_copy2_data->num_volumes,
850 cmpfunc);
853 return;
856 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
857 files_struct *fsp,
858 SHADOW_COPY_DATA *shadow_copy2_data,
859 bool labels)
861 SMB_STRUCT_DIR *p;
862 const char *snapdir;
863 SMB_STRUCT_DIRENT *d;
864 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
865 char *snapshot;
867 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
868 if (snapdir == NULL) {
869 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
870 handle->conn->connectpath));
871 errno = EINVAL;
872 talloc_free(tmp_ctx);
873 return -1;
876 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
878 if (!p) {
879 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
880 " - %s\n", snapdir, strerror(errno)));
881 talloc_free(tmp_ctx);
882 errno = ENOSYS;
883 return -1;
886 shadow_copy2_data->num_volumes = 0;
887 shadow_copy2_data->labels = NULL;
889 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
890 SHADOW_COPY_LABEL *tlabels;
892 /* ignore names not of the right form in the snapshot directory */
893 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
894 d->d_name);
895 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
896 d->d_name, snapshot));
897 if (!snapshot) {
898 continue;
901 if (!labels) {
902 /* the caller doesn't want the labels */
903 shadow_copy2_data->num_volumes++;
904 continue;
907 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
908 shadow_copy2_data->labels,
909 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
910 if (tlabels == NULL) {
911 DEBUG(0,("shadow_copy2: out of memory\n"));
912 SMB_VFS_NEXT_CLOSEDIR(handle, p);
913 talloc_free(tmp_ctx);
914 return -1;
917 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
918 sizeof(*tlabels));
919 talloc_free(snapshot);
921 shadow_copy2_data->num_volumes++;
922 shadow_copy2_data->labels = tlabels;
925 SMB_VFS_NEXT_CLOSEDIR(handle,p);
927 shadow_copy2_sort_data(handle, shadow_copy2_data);
929 talloc_free(tmp_ctx);
930 return 0;
933 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
934 .opendir = shadow_copy2_opendir,
935 .mkdir = shadow_copy2_mkdir,
936 .rmdir = shadow_copy2_rmdir,
937 .chflags = shadow_copy2_chflags,
938 .getxattr = shadow_copy2_getxattr,
939 .lgetxattr = shadow_copy2_lgetxattr,
940 .listxattr = shadow_copy2_listxattr,
941 .removexattr = shadow_copy2_removexattr,
942 .lremovexattr = shadow_copy2_lremovexattr,
943 .setxattr = shadow_copy2_setxattr,
944 .lsetxattr = shadow_copy2_lsetxattr,
945 .open = shadow_copy2_open,
946 .rename = shadow_copy2_rename,
947 .stat = shadow_copy2_stat,
948 .lstat = shadow_copy2_lstat,
949 .fstat = shadow_copy2_fstat,
950 .unlink = shadow_copy2_unlink,
951 .chmod = shadow_copy2_chmod,
952 .chown = shadow_copy2_chown,
953 .chdir = shadow_copy2_chdir,
954 .ntimes = shadow_copy2_ntimes,
955 .symlink = shadow_copy2_symlink,
956 .vfs_readlink = shadow_copy2_readlink,
957 .link = shadow_copy2_link,
958 .mknod = shadow_copy2_mknod,
959 .realpath = shadow_copy2_realpath,
960 .connectpath = shadow_copy2_connectpath,
961 .get_nt_acl = shadow_copy2_get_nt_acl,
962 .chmod_acl = shadow_copy2_chmod_acl,
963 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
966 NTSTATUS vfs_shadow_copy2_init(void);
967 NTSTATUS vfs_shadow_copy2_init(void)
969 NTSTATUS ret;
971 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
972 &vfs_shadow_copy2_fns);
974 if (!NT_STATUS_IS_OK(ret))
975 return ret;
977 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
978 if (vfs_shadow_copy2_debug_level == -1) {
979 vfs_shadow_copy2_debug_level = DBGC_VFS;
980 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
981 "vfs_shadow_copy2_init"));
982 } else {
983 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
984 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
987 return ret;