source: trunk/server/source3/modules/vfs_shadow_copy2.c

Last change on this file was 920, checked in by Silvan Scherrer, 9 years ago

Samba Server: apply latest security patches to trunk

File size: 29.1 KB
Line 
1/*
2 * implementation of an Shadow Copy module - version 2
3 *
4 * Copyright (C) Andrew Tridgell 2007
5 * Copyright (C) Ed Plese 2009
6 *
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.
11 *
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.
16 *
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.
20 */
21
22#include "includes.h"
23#include "smbd/smbd.h"
24#include "smbd/globals.h"
25#include "../libcli/security/security.h"
26#include "system/filesys.h"
27#include "ntioctl.h"
28
29/*
30
31 This is a 2nd implemetation of a shadow copy module for exposing
32 snapshots to windows clients as shadow copies. This version has the
33 following features:
34
35 1) you don't need to populate your shares with symlinks to the
36 snapshots. This can be very important when you have thousands of
37 shares, or use [homes]
38
39 2) the inode number of the files is altered so it is different
40 from the original. This allows the 'restore' button to work
41 without a sharing violation
42
43 3) shadow copy results can be sorted before being sent to the
44 client. This is beneficial for filesystems that don't read
45 directories alphabetically (the default unix).
46
47 4) vanity naming for snapshots. Snapshots can be named in any
48 format compatible with str[fp]time conversions.
49
50 5) time stamps in snapshot names can be represented in localtime
51 rather than UTC.
52
53 Module options:
54
55 shadow:snapdir = <directory where snapshots are kept>
56
57 This is the directory containing the @GMT-* snapshot directories. If it is an absolute
58 path it is used as-is. If it is a relative path, then it is taken relative to the mount
59 point of the filesystem that the root of this share is on
60
61 shadow:basedir = <base directory that snapshots are from>
62
63 This is an optional parameter that specifies the directory that
64 the snapshots are relative to. It defaults to the filesystem
65 mount point
66
67 shadow:fixinodes = yes/no
68
69 If you enable shadow:fixinodes then this module will modify the
70 apparent inode number of files in the snapshot directories using
71 a hash of the files path. This is needed for snapshot systems
72 where the snapshots have the same device:inode number as the
73 original files (such as happens with GPFS snapshots). If you
74 don't set this option then the 'restore' button in the shadow
75 copy UI will fail with a sharing violation.
76
77 shadow:sort = asc/desc, or not specified for unsorted (default)
78
79 This is an optional parameter that specifies that the shadow
80 copy directories should be sorted before sending them to the
81 client. This can be beneficial as unix filesystems are usually
82 not listed alphabetically sorted. If enabled, you typically
83 want to specify descending order.
84
85 shadow:format = <format specification for snapshot names>
86
87 This is an optional parameter that specifies the format
88 specification for the naming of snapshots. The format must
89 be compatible with the conversion specifications recognized
90 by str[fp]time. The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
91
92 shadow:localtime = yes/no (default is no)
93
94 This is an optional parameter that indicates whether the
95 snapshot names are in UTC/GMT or the local time.
96
97
98 The following command would generate a correctly formatted directory name
99 for use with the default parameters:
100 date -u +@GMT-%Y.%m.%d-%H.%M.%S
101
102 */
103
104static int vfs_shadow_copy2_debug_level = DBGC_VFS;
105
106#undef DBGC_CLASS
107#define DBGC_CLASS vfs_shadow_copy2_debug_level
108
109#define GMT_NAME_LEN 24 /* length of a @GMT- name */
110#define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
111
112#define SHADOW_COPY2_DEFAULT_SORT NULL
113#define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
114#define SHADOW_COPY2_DEFAULT_LOCALTIME false
115
116/*
117 make very sure it is one of our special names
118 */
119static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
120{
121 unsigned year, month, day, hr, min, sec;
122 const char *p;
123 if (gmt_start) {
124 (*gmt_start) = NULL;
125 }
126 p = strstr_m(name, "@GMT-");
127 if (p == NULL) return false;
128 if (p > name && p[-1] != '/') return False;
129 if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
130 &day, &hr, &min, &sec) != 6) {
131 return False;
132 }
133 if (p[24] != 0 && p[24] != '/') {
134 return False;
135 }
136 if (gmt_start) {
137 (*gmt_start) = p;
138 }
139 return True;
140}
141
142static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
143 vfs_handle_struct *handle, const char *name)
144{
145 struct tm timestamp;
146 time_t timestamp_t;
147 char gmt[GMT_NAME_LEN + 1];
148 const char *fmt;
149
150 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
151 "format", SHADOW_COPY2_DEFAULT_FORMAT);
152
153 ZERO_STRUCT(timestamp);
154 if (strptime(name, fmt, &timestamp) == NULL) {
155 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
156 fmt, name));
157 return NULL;
158 }
159
160 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
161 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
162 SHADOW_COPY2_DEFAULT_LOCALTIME))
163 {
164 timestamp.tm_isdst = -1;
165 timestamp_t = mktime(&timestamp);
166 gmtime_r(&timestamp_t, &timestamp);
167 }
168 strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, &timestamp);
169
170 return talloc_strdup(mem_ctx, gmt);
171}
172
173/*
174 shadow copy paths can also come into the server in this form:
175
176 /foo/bar/@GMT-XXXXX/some/file
177
178 This function normalises the filename to be of the form:
179
180 @GMT-XXXX/foo/bar/some/file
181 */
182static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
183{
184 char *pcopy;
185 char buf[GMT_NAME_LEN];
186 size_t prefix_len;
187
188 if (path == gmt_start) {
189 return path;
190 }
191
192 prefix_len = gmt_start - path - 1;
193
194 DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
195 (int)prefix_len));
196
197 /*
198 * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
199 * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
200 * processing. As many VFS calls provide a const char *,
201 * unfortunately we have to make a copy.
202 */
203
204 pcopy = talloc_strdup(talloc_tos(), path);
205 if (pcopy == NULL) {
206 return NULL;
207 }
208
209 gmt_start = pcopy + prefix_len;
210
211 /*
212 * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
213 */
214 memcpy(buf, gmt_start+1, GMT_NAME_LEN);
215
216 /*
217 * Make space for it including a trailing /
218 */
219 memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
220
221 /*
222 * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
223 */
224 memcpy(pcopy, buf, GMT_NAME_LEN);
225 pcopy[GMT_NAME_LEN] = '/';
226
227 DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
228
229 return pcopy;
230}
231
232/*
233 convert a name to the shadow directory
234 */
235
236#define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
237 const char *name = fname; \
238 const char *gmt_start; \
239 if (shadow_copy2_match_name(fname, &gmt_start)) { \
240 char *name2; \
241 rtype ret; \
242 name2 = convert_shadow2_name(handle, fname, gmt_start); \
243 if (name2 == NULL) { \
244 errno = EINVAL; \
245 return eret; \
246 } \
247 name = name2; \
248 ret = SMB_VFS_NEXT_ ## op args; \
249 talloc_free(name2); \
250 if (ret != eret) extra; \
251 return ret; \
252 } else { \
253 return SMB_VFS_NEXT_ ## op args; \
254 } \
255} while (0)
256
257#define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
258 const char *gmt_start; \
259 if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
260 char *name2; \
261 char *smb_base_name_tmp = NULL; \
262 rtype ret; \
263 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
264 if (name2 == NULL) { \
265 errno = EINVAL; \
266 return eret; \
267 } \
268 smb_base_name_tmp = smb_fname->base_name; \
269 smb_fname->base_name = name2; \
270 ret = SMB_VFS_NEXT_ ## op args; \
271 smb_fname->base_name = smb_base_name_tmp; \
272 talloc_free(name2); \
273 if (ret != eret) extra; \
274 return ret; \
275 } else { \
276 return SMB_VFS_NEXT_ ## op args; \
277 } \
278} while (0)
279
280/*
281 convert a name to the shadow directory: NTSTATUS-specific handling
282 */
283
284#define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
285 const char *name = fname; \
286 const char *gmt_start; \
287 if (shadow_copy2_match_name(fname, &gmt_start)) { \
288 char *name2; \
289 NTSTATUS ret; \
290 name2 = convert_shadow2_name(handle, fname, gmt_start); \
291 if (name2 == NULL) { \
292 errno = EINVAL; \
293 return eret; \
294 } \
295 name = name2; \
296 ret = SMB_VFS_NEXT_ ## op args; \
297 talloc_free(name2); \
298 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
299 return ret; \
300 } else { \
301 return SMB_VFS_NEXT_ ## op args; \
302 } \
303} while (0)
304
305#define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
306
307#define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
308
309#define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
310
311#define SHADOW2_NEXT2(op, args) do { \
312 const char *gmt_start1, *gmt_start2; \
313 if (shadow_copy2_match_name(oldname, &gmt_start1) || \
314 shadow_copy2_match_name(newname, &gmt_start2)) { \
315 errno = EROFS; \
316 return -1; \
317 } else { \
318 return SMB_VFS_NEXT_ ## op args; \
319 } \
320} while (0)
321
322#define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
323 const char *gmt_start1, *gmt_start2; \
324 if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
325 shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
326 errno = EROFS; \
327 return -1; \
328 } else { \
329 return SMB_VFS_NEXT_ ## op args; \
330 } \
331} while (0)
332
333
334/*
335 find the mount point of a filesystem
336 */
337static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
338{
339 char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
340 dev_t dev;
341 struct stat st;
342 char *p;
343
344 if (stat(path, &st) != 0) {
345 talloc_free(path);
346 return NULL;
347 }
348
349 dev = st.st_dev;
350
351 while ((p = strrchr(path, '/')) && p > path) {
352 *p = 0;
353 if (stat(path, &st) != 0) {
354 talloc_free(path);
355 return NULL;
356 }
357 if (st.st_dev != dev) {
358 *p = '/';
359 break;
360 }
361 }
362
363 return path;
364}
365
366/*
367 work out the location of the snapshot for this share
368 */
369static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
370{
371 const char *snapdir;
372 char *mount_point;
373 const char *ret;
374
375 snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
376 if (snapdir == NULL) {
377 return NULL;
378 }
379 /* if its an absolute path, we're done */
380 if (*snapdir == '/') {
381 return snapdir;
382 }
383
384 /* other its relative to the filesystem mount point */
385 mount_point = find_mount_point(mem_ctx, handle);
386 if (mount_point == NULL) {
387 return NULL;
388 }
389
390 ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
391 talloc_free(mount_point);
392 return ret;
393}
394
395/*
396 work out the location of the base directory for snapshots of this share
397 */
398static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
399{
400 const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
401
402 /* other its the filesystem mount point */
403 if (basedir == NULL) {
404 basedir = find_mount_point(mem_ctx, handle);
405 }
406
407 return basedir;
408}
409
410/*
411 convert a filename from a share relative path, to a path in the
412 snapshot directory
413 */
414static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
415{
416 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
417 const char *snapdir, *relpath, *baseoffset, *basedir;
418 size_t baselen;
419 char *ret, *prefix;
420
421 struct tm timestamp;
422 time_t timestamp_t;
423 char snapshot[MAXPATHLEN];
424 const char *fmt;
425
426 fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
427 "format", SHADOW_COPY2_DEFAULT_FORMAT);
428
429 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
430 if (snapdir == NULL) {
431 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
432 talloc_free(tmp_ctx);
433 return NULL;
434 }
435
436 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
437 if (basedir == NULL) {
438 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
439 talloc_free(tmp_ctx);
440 return NULL;
441 }
442
443 prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
444 if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
445 /* this looks like as we have already normalized it, leave it untouched*/
446 talloc_free(tmp_ctx);
447 return talloc_strdup(handle->data, fname);
448 }
449
450 if (strncmp(fname, "@GMT-", 5) != 0) {
451 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
452 if (fname == NULL) {
453 talloc_free(tmp_ctx);
454 return NULL;
455 }
456 }
457
458 ZERO_STRUCT(timestamp);
459 relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
460 if (relpath == NULL) {
461 talloc_free(tmp_ctx);
462 return NULL;
463 }
464
465 /* relpath is the remaining portion of the path after the @GMT-xxx */
466
467 if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
468 SHADOW_COPY2_DEFAULT_LOCALTIME))
469 {
470 timestamp_t = timegm(&timestamp);
471 localtime_r(&timestamp_t, &timestamp);
472 }
473
474 strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
475
476 baselen = strlen(basedir);
477 baseoffset = handle->conn->connectpath + baselen;
478
479 /* some sanity checks */
480 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
481 (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
482 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
483 basedir, handle->conn->connectpath));
484 talloc_free(tmp_ctx);
485 return NULL;
486 }
487
488 if (*relpath == '/') relpath++;
489 if (*baseoffset == '/') baseoffset++;
490
491 ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
492 snapdir,
493 snapshot,
494 baseoffset,
495 relpath);
496 DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
497 talloc_free(tmp_ctx);
498 return ret;
499}
500
501
502/*
503 simple string hash
504 */
505static uint32 string_hash(const char *s)
506{
507 uint32 n = 0;
508 while (*s) {
509 n = ((n << 5) + n) ^ (uint32)(*s++);
510 }
511 return n;
512}
513
514/*
515 modify a sbuf return to ensure that inodes in the shadow directory
516 are different from those in the main directory
517 */
518static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
519{
520 if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {
521 /* some snapshot systems, like GPFS, return the name
522 device:inode for the snapshot files as the current
523 files. That breaks the 'restore' button in the shadow copy
524 GUI, as the client gets a sharing violation.
525
526 This is a crude way of allowing both files to be
527 open at once. It has a slight chance of inode
528 number collision, but I can't see a better approach
529 without significant VFS changes
530 */
531 uint32_t shash = string_hash(fname) & 0xFF000000;
532 if (shash == 0) {
533 shash = 1;
534 }
535 sbuf->st_ex_ino ^= shash;
536 }
537}
538
539static int shadow_copy2_rename(vfs_handle_struct *handle,
540 const struct smb_filename *smb_fname_src,
541 const struct smb_filename *smb_fname_dst)
542{
543 if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
544 errno = EXDEV;
545 return -1;
546 }
547 SHADOW2_NEXT2_SMB_FNAME(RENAME,
548 (handle, smb_fname_src, smb_fname_dst));
549}
550
551static int shadow_copy2_symlink(vfs_handle_struct *handle,
552 const char *oldname, const char *newname)
553{
554 SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
555}
556
557static int shadow_copy2_link(vfs_handle_struct *handle,
558 const char *oldname, const char *newname)
559{
560 SHADOW2_NEXT2(LINK, (handle, oldname, newname));
561}
562
563static int shadow_copy2_open(vfs_handle_struct *handle,
564 struct smb_filename *smb_fname, files_struct *fsp,
565 int flags, mode_t mode)
566{
567 SHADOW2_NEXT_SMB_FNAME(OPEN,
568 (handle, smb_fname, fsp, flags, mode),
569 int, -1);
570}
571
572static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
573 const char *fname, const char *mask, uint32 attr)
574{
575 SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
576}
577
578static int shadow_copy2_stat(vfs_handle_struct *handle,
579 struct smb_filename *smb_fname)
580{
581 _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
582 convert_sbuf(handle, smb_fname->base_name,
583 &smb_fname->st));
584}
585
586static int shadow_copy2_lstat(vfs_handle_struct *handle,
587 struct smb_filename *smb_fname)
588{
589 _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
590 convert_sbuf(handle, smb_fname->base_name,
591 &smb_fname->st));
592}
593
594static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
595{
596 int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
597 if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
598 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
599 }
600 return ret;
601}
602
603static int shadow_copy2_unlink(vfs_handle_struct *handle,
604 const struct smb_filename *smb_fname_in)
605{
606 struct smb_filename *smb_fname = NULL;
607 NTSTATUS status;
608
609 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
610 if (!NT_STATUS_IS_OK(status)) {
611 errno = map_errno_from_nt_status(status);
612 return -1;
613 }
614
615 SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
616}
617
618static int shadow_copy2_chmod(vfs_handle_struct *handle,
619 const char *fname, mode_t mode)
620{
621 SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
622}
623
624static int shadow_copy2_chown(vfs_handle_struct *handle,
625 const char *fname, uid_t uid, gid_t gid)
626{
627 SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
628}
629
630static int shadow_copy2_chdir(vfs_handle_struct *handle,
631 const char *fname)
632{
633 SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
634}
635
636static int shadow_copy2_ntimes(vfs_handle_struct *handle,
637 const struct smb_filename *smb_fname_in,
638 struct smb_file_time *ft)
639{
640 struct smb_filename *smb_fname = NULL;
641 NTSTATUS status;
642
643 status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
644 if (!NT_STATUS_IS_OK(status)) {
645 errno = map_errno_from_nt_status(status);
646 return -1;
647 }
648
649 SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
650}
651
652static int shadow_copy2_readlink(vfs_handle_struct *handle,
653 const char *fname, char *buf, size_t bufsiz)
654{
655 SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
656}
657
658static int shadow_copy2_mknod(vfs_handle_struct *handle,
659 const char *fname, mode_t mode, SMB_DEV_T dev)
660{
661 SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
662}
663
664static char *shadow_copy2_realpath(vfs_handle_struct *handle,
665 const char *fname)
666{
667 const char *gmt;
668
669 if (shadow_copy2_match_name(fname, &gmt)
670 && (gmt[GMT_NAME_LEN] == '\0')) {
671 char *copy;
672
673 copy = talloc_strdup(talloc_tos(), fname);
674 if (copy == NULL) {
675 errno = ENOMEM;
676 return NULL;
677 }
678
679 copy[gmt - fname] = '.';
680 copy[gmt - fname + 1] = '\0';
681
682 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
683 SHADOW2_NEXT(REALPATH, (handle, name), char *,
684 NULL);
685 }
686 SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
687}
688
689static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
690 const char *fname)
691{
692 TALLOC_CTX *tmp_ctx;
693 const char *snapdir, *baseoffset, *basedir, *gmt_start;
694 size_t baselen;
695 char *ret;
696
697 DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
698
699 if (!shadow_copy2_match_name(fname, &gmt_start)) {
700 return handle->conn->connectpath;
701 }
702
703 /*
704 * We have to create a real temporary context because we have
705 * to put our result on talloc_tos(). Thus we can't use a
706 * talloc_stackframe() here.
707 */
708 tmp_ctx = talloc_new(talloc_tos());
709
710 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
711 if (fname == NULL) {
712 TALLOC_FREE(tmp_ctx);
713 return NULL;
714 }
715
716 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
717 if (snapdir == NULL) {
718 DEBUG(2,("no snapdir found for share at %s\n",
719 handle->conn->connectpath));
720 TALLOC_FREE(tmp_ctx);
721 return NULL;
722 }
723
724 basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
725 if (basedir == NULL) {
726 DEBUG(2,("no basedir found for share at %s\n",
727 handle->conn->connectpath));
728 TALLOC_FREE(tmp_ctx);
729 return NULL;
730 }
731
732 baselen = strlen(basedir);
733 baseoffset = handle->conn->connectpath + baselen;
734
735 /* some sanity checks */
736 if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
737 (handle->conn->connectpath[baselen] != 0
738 && handle->conn->connectpath[baselen] != '/')) {
739 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
740 "parent of %s\n", basedir,
741 handle->conn->connectpath));
742 TALLOC_FREE(tmp_ctx);
743 return NULL;
744 }
745
746 if (*baseoffset == '/') baseoffset++;
747
748 ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
749 snapdir,
750 GMT_NAME_LEN, fname,
751 baseoffset);
752 DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
753 TALLOC_FREE(tmp_ctx);
754 return ret;
755}
756
757static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
758 const char *fname, uint32 security_info,
759 struct security_descriptor **ppdesc)
760{
761 SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
762}
763
764static int shadow_copy2_mkdir(vfs_handle_struct *handle, const char *fname, mode_t mode)
765{
766 SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
767}
768
769static bool check_access_snapdir(struct vfs_handle_struct *handle,
770 const char *path)
771{
772 struct smb_filename smb_fname;
773 int ret;
774 NTSTATUS status;
775 uint32_t access_granted = 0;
776
777 ZERO_STRUCT(smb_fname);
778 smb_fname.base_name = talloc_asprintf(talloc_tos(),
779 "%s",
780 path);
781 if (smb_fname.base_name == NULL) {
782 return false;
783 }
784
785 ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
786 if (ret != 0 || !S_ISDIR(smb_fname.st.st_ex_mode)) {
787 TALLOC_FREE(smb_fname.base_name);
788 return false;
789 }
790
791 status = smbd_check_open_rights(handle->conn,
792 &smb_fname,
793 SEC_DIR_LIST,
794 &access_granted);
795 if (!NT_STATUS_IS_OK(status)) {
796 DEBUG(0,("user does not have list permission "
797 "on snapdir %s\n",
798 smb_fname.base_name));
799 TALLOC_FREE(smb_fname.base_name);
800 return false;
801 }
802 TALLOC_FREE(smb_fname.base_name);
803 return true;
804}
805
806static int shadow_copy2_rmdir(vfs_handle_struct *handle, const char *fname)
807{
808 SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
809}
810
811static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
812 unsigned int flags)
813{
814 SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
815}
816
817static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
818 const char *fname, const char *aname, void *value, size_t size)
819{
820 SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
821}
822
823static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
824 const char *fname, const char *aname, void *value, size_t size)
825{
826 SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
827}
828
829static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname,
830 char *list, size_t size)
831{
832 SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
833}
834
835static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname,
836 const char *aname)
837{
838 SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
839}
840
841static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname,
842 const char *aname)
843{
844 SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
845}
846
847static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname,
848 const char *aname, const void *value, size_t size, int flags)
849{
850 SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
851}
852
853static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname,
854 const char *aname, const void *value, size_t size, int flags)
855{
856 SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
857}
858
859static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
860 const char *fname, mode_t mode)
861{
862 SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
863}
864
865static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
866{
867 return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
868}
869
870static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
871{
872 return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
873}
874
875/*
876 sort the shadow copy data in ascending or descending order
877 */
878static void shadow_copy2_sort_data(vfs_handle_struct *handle,
879 struct shadow_copy_data *shadow_copy2_data)
880{
881 int (*cmpfunc)(const void *, const void *);
882 const char *sort;
883
884 sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
885 "sort", SHADOW_COPY2_DEFAULT_SORT);
886 if (sort == NULL) {
887 return;
888 }
889
890 if (strcmp(sort, "asc") == 0) {
891 cmpfunc = shadow_copy2_label_cmp_asc;
892 } else if (strcmp(sort, "desc") == 0) {
893 cmpfunc = shadow_copy2_label_cmp_desc;
894 } else {
895 return;
896 }
897
898 if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
899 shadow_copy2_data->labels)
900 {
901 TYPESAFE_QSORT(shadow_copy2_data->labels,
902 shadow_copy2_data->num_volumes,
903 cmpfunc);
904 }
905
906 return;
907}
908
909static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle,
910 files_struct *fsp,
911 struct shadow_copy_data *shadow_copy2_data,
912 bool labels)
913{
914 SMB_STRUCT_DIR *p;
915 const char *snapdir;
916 SMB_STRUCT_DIRENT *d;
917 TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
918 char *snapshot;
919 bool ret;
920
921 snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
922 if (snapdir == NULL) {
923 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
924 handle->conn->connectpath));
925 errno = EINVAL;
926 talloc_free(tmp_ctx);
927 return -1;
928 }
929 ret = check_access_snapdir(handle, snapdir);
930 if (!ret) {
931 DEBUG(0,("access denied on listing snapdir %s\n", snapdir));
932 errno = EACCES;
933 talloc_free(tmp_ctx);
934 return -1;
935 }
936
937 p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
938
939 if (!p) {
940 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
941 " - %s\n", snapdir, strerror(errno)));
942 talloc_free(tmp_ctx);
943 errno = ENOSYS;
944 return -1;
945 }
946
947 shadow_copy2_data->num_volumes = 0;
948 shadow_copy2_data->labels = NULL;
949
950 while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
951 SHADOW_COPY_LABEL *tlabels;
952
953 /* ignore names not of the right form in the snapshot directory */
954 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
955 d->d_name);
956 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
957 d->d_name, snapshot));
958 if (!snapshot) {
959 continue;
960 }
961
962 if (!labels) {
963 /* the caller doesn't want the labels */
964 shadow_copy2_data->num_volumes++;
965 continue;
966 }
967
968 tlabels = talloc_realloc(shadow_copy2_data,
969 shadow_copy2_data->labels,
970 SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
971 if (tlabels == NULL) {
972 DEBUG(0,("shadow_copy2: out of memory\n"));
973 SMB_VFS_NEXT_CLOSEDIR(handle, p);
974 talloc_free(tmp_ctx);
975 return -1;
976 }
977
978 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
979 sizeof(*tlabels));
980 talloc_free(snapshot);
981
982 shadow_copy2_data->num_volumes++;
983 shadow_copy2_data->labels = tlabels;
984 }
985
986 SMB_VFS_NEXT_CLOSEDIR(handle,p);
987
988 shadow_copy2_sort_data(handle, shadow_copy2_data);
989
990 talloc_free(tmp_ctx);
991 return 0;
992}
993
994static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
995 .opendir = shadow_copy2_opendir,
996 .mkdir = shadow_copy2_mkdir,
997 .rmdir = shadow_copy2_rmdir,
998 .chflags = shadow_copy2_chflags,
999 .getxattr = shadow_copy2_getxattr,
1000 .lgetxattr = shadow_copy2_lgetxattr,
1001 .listxattr = shadow_copy2_listxattr,
1002 .removexattr = shadow_copy2_removexattr,
1003 .lremovexattr = shadow_copy2_lremovexattr,
1004 .setxattr = shadow_copy2_setxattr,
1005 .lsetxattr = shadow_copy2_lsetxattr,
1006 .open_fn = shadow_copy2_open,
1007 .rename = shadow_copy2_rename,
1008 .stat = shadow_copy2_stat,
1009 .lstat = shadow_copy2_lstat,
1010 .fstat = shadow_copy2_fstat,
1011 .unlink = shadow_copy2_unlink,
1012 .chmod = shadow_copy2_chmod,
1013 .chown = shadow_copy2_chown,
1014 .chdir = shadow_copy2_chdir,
1015 .ntimes = shadow_copy2_ntimes,
1016 .symlink = shadow_copy2_symlink,
1017 .vfs_readlink = shadow_copy2_readlink,
1018 .link = shadow_copy2_link,
1019 .mknod = shadow_copy2_mknod,
1020 .realpath = shadow_copy2_realpath,
1021 .connectpath = shadow_copy2_connectpath,
1022 .get_nt_acl = shadow_copy2_get_nt_acl,
1023 .chmod_acl = shadow_copy2_chmod_acl,
1024 .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
1025};
1026
1027NTSTATUS vfs_shadow_copy2_init(void);
1028NTSTATUS vfs_shadow_copy2_init(void)
1029{
1030 NTSTATUS ret;
1031
1032 ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
1033 &vfs_shadow_copy2_fns);
1034
1035 if (!NT_STATUS_IS_OK(ret))
1036 return ret;
1037
1038 vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
1039 if (vfs_shadow_copy2_debug_level == -1) {
1040 vfs_shadow_copy2_debug_level = DBGC_VFS;
1041 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
1042 "vfs_shadow_copy2_init"));
1043 } else {
1044 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
1045 "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
1046 }
1047
1048 return ret;
1049}
Note: See TracBrowser for help on using the repository browser.