v3-4-ctdb: Deal with mmsnapdir -a
[obnox/samba-ctdb.git] / source3 / modules / vfs_shadow_copy2.c
1 /* 
2  * implementation of an Shadow Copy module - version 2
3  *
4  * Copyright (C) Andrew Tridgell     2007
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *  
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *  
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22
23 /*
24
25   This is a 2nd implemetation of a shadow copy module for exposing
26   snapshots to windows clients as shadow copies. This version has the
27   following features:
28
29      1) you don't need to populate your shares with symlinks to the
30      snapshots. This can be very important when you have thousands of
31      shares, or use [homes]
32
33      2) the inode number of the files is altered so it is different
34      from the original. This allows the 'restore' button to work
35      without a sharing violation
36
37   Module options:
38
39       shadow:snapdir = <directory where snapshots are kept>
40
41       This is the directory containing the @GMT-* snapshot directories. If it is an absolute
42       path it is used as-is. If it is a relative path, then it is taken relative to the mount
43       point of the filesystem that the root of this share is on
44
45       shadow:basedir = <base directory that snapshots are from>
46
47       This is an optional parameter that specifies the directory that
48       the snapshots are relative to. It defaults to the filesystem
49       mount point
50
51       shadow:fixinodes = yes/no
52
53       If you enable shadow:fixinodes then this module will modify the
54       apparent inode number of files in the snapshot directories using
55       a hash of the files path. This is needed for snapshot systems
56       where the snapshots have the same device:inode number as the
57       original files (such as happens with GPFS snapshots). If you
58       don't set this option then the 'restore' button in the shadow
59       copy UI will fail with a sharing violation.
60
61   Note that the directory names in the snapshot directory must take the form
62   @GMT-YYYY.MM.DD-HH.MM.SS
63   
64   The following command would generate a correctly formatted directory name:
65      date -u +@GMT-%Y.%m.%d-%H.%M.%S
66   
67  */
68
69 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
70
71 #undef DBGC_CLASS
72 #define DBGC_CLASS vfs_shadow_copy2_debug_level
73
74 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
75
76 /*
77   make very sure it is one of our special names 
78  */
79 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
80 {
81         unsigned year, month, day, hr, min, sec;
82         const char *p;
83         if (gmt_start) {
84                 (*gmt_start) = NULL;
85         }
86         p = strstr_m(name, "@GMT-");
87         if (p == NULL) return false;
88         if (p > name && p[-1] != '/') return False;
89         if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
90                    &day, &hr, &min, &sec) != 6) {
91                 return False;
92         }
93         if (p[24] != 0 && p[24] != '/') {
94                 return False;
95         }
96         if (gmt_start) {
97                 (*gmt_start) = p;
98         }
99         return True;
100 }
101
102 /*
103   shadow copy paths can also come into the server in this form:
104
105     /foo/bar/@GMT-XXXXX/some/file
106
107   This function normalises the filename to be of the form:
108
109     @GMT-XXXX/foo/bar/some/file
110  */
111 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
112 {
113         char *pcopy;
114         char buf[GMT_NAME_LEN];
115         size_t prefix_len;
116
117         if (path == gmt_start) {
118                 return path;
119         }
120
121         prefix_len = gmt_start - path - 1;
122
123         DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
124                    (int)prefix_len));
125
126         /*
127          * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
128          * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
129          * processing. As many VFS calls provide a const char *,
130          * unfortunately we have to make a copy.
131          */
132
133         pcopy = talloc_strdup(talloc_tos(), path);
134         if (pcopy == NULL) {
135                 return NULL;
136         }
137
138         gmt_start = pcopy + prefix_len;
139
140         /*
141          * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
142          */
143         memcpy(buf, gmt_start+1, GMT_NAME_LEN);
144
145         /*
146          * Make space for it including a trailing /
147          */
148         memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
149
150         /*
151          * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
152          */
153         memcpy(pcopy, buf, GMT_NAME_LEN);
154         pcopy[GMT_NAME_LEN] = '/';
155
156         DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
157
158         return pcopy;
159 }
160
161 /*
162   convert a name to the shadow directory
163  */
164
165 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
166         const char *name = fname; \
167         const char *gmt_start; \
168         if (shadow_copy2_match_name(fname, &gmt_start)) {       \
169                 char *name2; \
170                 rtype ret; \
171                 name2 = convert_shadow2_name(handle, fname, gmt_start); \
172                 if (name2 == NULL) { \
173                         errno = EINVAL; \
174                         return eret; \
175                 } \
176                 name = name2; \
177                 ret = SMB_VFS_NEXT_ ## op args; \
178                 talloc_free(name2); \
179                 if (ret != eret) extra; \
180                 return ret; \
181         } else { \
182                 return SMB_VFS_NEXT_ ## op args; \
183         } \
184 } while (0)
185
186 /*
187   convert a name to the shadow directory: NTSTATUS-specific handling
188  */
189
190 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
191         const char *name = fname; \
192         const char *gmt_start; \
193         if (shadow_copy2_match_name(fname, &gmt_start)) {       \
194                 char *name2; \
195                 NTSTATUS ret; \
196                 name2 = convert_shadow2_name(handle, fname, gmt_start); \
197                 if (name2 == NULL) { \
198                         errno = EINVAL; \
199                         return eret; \
200                 } \
201                 name = name2; \
202                 ret = SMB_VFS_NEXT_ ## op args; \
203                 talloc_free(name2); \
204                 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
205                 return ret; \
206         } else { \
207                 return SMB_VFS_NEXT_ ## op args; \
208         } \
209 } while (0)
210
211 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
212
213 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
214
215 #define SHADOW2_NEXT2(op, args) do { \
216         const char *gmt_start1, *gmt_start2; \
217         if (shadow_copy2_match_name(oldname, &gmt_start1) || \
218             shadow_copy2_match_name(newname, &gmt_start2)) {    \
219                 errno = EROFS; \
220                 return -1; \
221         } else { \
222                 return SMB_VFS_NEXT_ ## op args; \
223         } \
224 } while (0)
225
226 /*
227   find the mount point of a filesystem
228  */
229 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
230 {
231         char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
232         dev_t dev;
233         struct stat st;
234         char *p;
235
236         if (stat(path, &st) != 0) {
237                 talloc_free(path);
238                 return NULL;
239         }
240
241         dev = st.st_dev;
242
243         while ((p = strrchr(path, '/')) && p > path) {
244                 *p = 0;
245                 if (stat(path, &st) != 0) {
246                         talloc_free(path);
247                         return NULL;
248                 }
249                 if (st.st_dev != dev) {
250                         *p = '/';
251                         break;
252                 }
253         }
254
255         return path;    
256 }
257
258 /*
259   work out the location of the snapshot for this share
260  */
261 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
262 {
263         const char *snapdir;
264         char *mount_point;
265         const char *ret;
266
267         snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
268         if (snapdir == NULL) {
269                 return NULL;
270         }
271         /* if its an absolute path, we're done */
272         if (*snapdir == '/') {
273                 return snapdir;
274         }
275
276         /* other its relative to the filesystem mount point */
277         mount_point = find_mount_point(mem_ctx, handle);
278         if (mount_point == NULL) {
279                 return NULL;
280         }
281
282         ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
283         talloc_free(mount_point);
284         return ret;
285 }
286
287 /*
288   work out the location of the base directory for snapshots of this share
289  */
290 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
291 {
292         const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
293
294         /* other its the filesystem mount point */
295         if (basedir == NULL) {
296                 basedir = find_mount_point(mem_ctx, handle);
297         }
298
299         return basedir;
300 }
301
302 static const char *non_wcard_fname(TALLOC_CTX *mem_ctx, const char *fname)
303 {
304         char *result, *last_slash;
305
306         if (!ms_has_wild(fname)) {
307                 DEBUG(10, ("%s contains no wildcard\n", fname));
308                 return fname;
309         }
310         result = talloc_strdup(mem_ctx, fname);
311         if (result == NULL) {
312                 return NULL;
313         }
314         last_slash = strrchr_m(result, '/');
315         if (last_slash == NULL) {
316                 TALLOC_FREE(result);
317                 return NULL;
318         }
319         *last_slash = '\0';
320         DEBUG(10, ("non_wcard_fname returns %s\n", result));
321         return result;
322 }
323
324 /*
325   convert a filename from a share relative path, to a path in the
326   snapshot directory
327  */
328 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
329 {
330         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
331         const char *snapdir, *relpath, *baseoffset, *basedir;
332         size_t baselen;
333         char *ret, *prefix;
334         const char *no_wcard_name;
335         struct stat sbuf;
336
337         no_wcard_name = non_wcard_fname(talloc_tos(), fname);
338         if ((no_wcard_name != NULL) && (stat(fname, &sbuf) == 0)) {
339                 talloc_free(tmp_ctx);
340                 return talloc_strdup(handle->data, fname);
341         }
342
343         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
344         if (snapdir == NULL) {
345                 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
346                 talloc_free(tmp_ctx);
347                 return NULL;
348         }
349
350         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
351         if (basedir == NULL) {
352                 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
353                 talloc_free(tmp_ctx);
354                 return NULL;
355         }
356
357         prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
358         if (strncmp(fname, prefix, strlen(prefix)) == 0) {
359                 /* this looks like as we have already normalized it, leave it untouched*/
360                 talloc_free(tmp_ctx);
361                 return talloc_strdup(handle->data, fname);
362         }
363
364         if (strncmp(fname, "@GMT-", 5) != 0) {
365                 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
366                 if (fname == NULL) {
367                         talloc_free(tmp_ctx);
368                         return NULL;
369                 }
370         }
371
372         relpath = fname + GMT_NAME_LEN;
373         baselen = strlen(basedir);
374         baseoffset = handle->conn->connectpath + baselen;
375
376         /* some sanity checks */
377         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
378             (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
379                 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
380                          basedir, handle->conn->connectpath));
381                 talloc_free(tmp_ctx);
382                 return NULL;
383         }
384
385         if (*relpath == '/') relpath++;
386         if (*baseoffset == '/') baseoffset++;
387
388         ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s", 
389                               snapdir, 
390                               GMT_NAME_LEN, fname, 
391                               baseoffset, 
392                               relpath);
393         DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
394         talloc_free(tmp_ctx);
395         return ret;
396 }
397
398
399 /*
400   simple string hash
401  */
402 static uint32 string_hash(const char *s)
403 {
404         uint32 n = 0;
405         while (*s) {
406                 n = ((n << 5) + n) ^ (uint32)(*s++);
407         }
408         return n;
409 }
410
411 /*
412   modify a sbuf return to ensure that inodes in the shadow directory
413   are different from those in the main directory
414  */
415 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
416 {
417         if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
418                 /* some snapshot systems, like GPFS, return the name
419                    device:inode for the snapshot files as the current
420                    files. That breaks the 'restore' button in the shadow copy
421                    GUI, as the client gets a sharing violation.
422
423                    This is a crude way of allowing both files to be
424                    open at once. It has a slight chance of inode
425                    number collision, but I can't see a better approach
426                    without significant VFS changes
427                 */
428                 uint32_t shash = string_hash(fname) & 0xFF000000;
429                 if (shash == 0) {
430                         shash = 1;
431                 }
432                 sbuf->st_ex_ino ^= shash;
433         }
434 }
435
436 static int shadow_copy2_rename(vfs_handle_struct *handle,
437                         const char *oldname, const char *newname)
438 {
439         if (shadow_copy2_match_name(oldname, NULL)) {
440                 errno = EXDEV;
441                 return -1;
442         }
443         SHADOW2_NEXT2(RENAME, (handle, oldname, newname));
444 }
445
446 static int shadow_copy2_symlink(vfs_handle_struct *handle,
447                                 const char *oldname, const char *newname)
448 {
449         SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
450 }
451
452 static int shadow_copy2_link(vfs_handle_struct *handle,
453                           const char *oldname, const char *newname)
454 {
455         SHADOW2_NEXT2(LINK, (handle, oldname, newname));
456 }
457
458 static int shadow_copy2_open(vfs_handle_struct *handle,
459                              const char *fname, files_struct *fsp, int flags, mode_t mode)
460 {
461         SHADOW2_NEXT(OPEN, (handle, name, fsp, flags, mode), int, -1);
462 }
463
464 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
465                           const char *fname, const char *mask, uint32 attr)
466 {
467         SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
468 }
469
470 static int shadow_copy2_stat(vfs_handle_struct *handle,
471                       const char *fname, SMB_STRUCT_STAT *sbuf)
472 {
473         _SHADOW2_NEXT(STAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
474 }
475
476 static int shadow_copy2_lstat(vfs_handle_struct *handle,
477                        const char *fname, SMB_STRUCT_STAT *sbuf)
478 {
479         _SHADOW2_NEXT(LSTAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
480 }
481
482 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
483 {
484         int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
485         if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name, NULL)) {
486                 convert_sbuf(handle, fsp->fsp_name, sbuf);
487         }
488         return ret;
489 }
490
491 static int shadow_copy2_unlink(vfs_handle_struct *handle, const char *fname)
492 {
493         SHADOW2_NEXT(UNLINK, (handle, name), int, -1);
494 }
495
496 static int shadow_copy2_chmod(vfs_handle_struct *handle,
497                        const char *fname, mode_t mode)
498 {
499         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
500 }
501
502 static int shadow_copy2_chown(vfs_handle_struct *handle,
503                        const char *fname, uid_t uid, gid_t gid)
504 {
505         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
506 }
507
508 static int shadow_copy2_chdir(vfs_handle_struct *handle,
509                        const char *fname)
510 {
511         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
512 }
513
514 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
515                        const char *fname, struct smb_file_time *ft)
516 {
517         SHADOW2_NEXT(NTIMES, (handle, name, ft), int, -1);
518 }
519
520 static int shadow_copy2_readlink(vfs_handle_struct *handle,
521                                  const char *fname, char *buf, size_t bufsiz)
522 {
523         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
524 }
525
526 static int shadow_copy2_mknod(vfs_handle_struct *handle,
527                        const char *fname, mode_t mode, SMB_DEV_T dev)
528 {
529         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
530 }
531
532 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
533                             const char *fname, char *resolved_path)
534 {
535         const char *gmt_start;
536
537         if (shadow_copy2_match_name(fname, &gmt_start)
538             && (gmt_start[GMT_NAME_LEN] == '\0')) {
539                 char *copy, *result;
540
541                 copy = talloc_strdup(talloc_tos(), fname);
542                 if (copy == NULL) {
543                         errno = ENOMEM;
544                         return NULL;
545                 }
546
547                 copy[gmt_start - fname] = '.';
548                 copy[gmt_start - fname + 1] = '\0';
549
550                 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
551                 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *,
552                              NULL);
553         }
554
555         SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
556 }
557
558 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
559                                             const char *fname)
560 {
561         TALLOC_CTX *tmp_ctx = talloc_stackframe();
562         const char *snapdir, *baseoffset, *basedir, *gmt_start;
563         size_t baselen;
564         char *ret;
565         const char *no_wcard_name;
566         struct stat sbuf;
567
568         DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
569
570         no_wcard_name = non_wcard_fname(talloc_tos(), fname);
571         if ((no_wcard_name != NULL) && (stat(no_wcard_name, &sbuf) == 0)) {
572                 return handle->conn->connectpath;
573         }
574
575         if (!shadow_copy2_match_name(fname, &gmt_start)) {
576                 return handle->conn->connectpath;
577         }
578
579         fname = shadow_copy2_normalise_path(talloc_tos(), fname, gmt_start);
580         if (fname == NULL) {
581                 TALLOC_FREE(tmp_ctx);
582                 return NULL;
583         }
584
585         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
586         if (snapdir == NULL) {
587                 DEBUG(2,("no snapdir found for share at %s\n",
588                          handle->conn->connectpath));
589                 TALLOC_FREE(tmp_ctx);
590                 return NULL;
591         }
592
593         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
594         if (basedir == NULL) {
595                 DEBUG(2,("no basedir found for share at %s\n",
596                          handle->conn->connectpath));
597                 TALLOC_FREE(tmp_ctx);
598                 return NULL;
599         }
600
601         baselen = strlen(basedir);
602         baseoffset = handle->conn->connectpath + baselen;
603
604         /* some sanity checks */
605         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
606             (handle->conn->connectpath[baselen] != 0
607              && handle->conn->connectpath[baselen] != '/')) {
608                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
609                          "parent of %s\n", basedir,
610                          handle->conn->connectpath));
611                 TALLOC_FREE(tmp_ctx);
612                 return NULL;
613         }
614
615         if (*baseoffset == '/') baseoffset++;
616
617         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
618                               snapdir,
619                               GMT_NAME_LEN, fname,
620                               baseoffset);
621         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
622         TALLOC_FREE(tmp_ctx);
623         return ret;
624 }
625
626 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
627                                const char *fname, uint32 security_info,
628                                struct security_descriptor **ppdesc)
629 {
630         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
631 }
632
633 static NTSTATUS shadow_copy2_fget_nt_acl(vfs_handle_struct *handle,
634                                files_struct *fsp, uint32 security_info,
635                                struct security_descriptor **ppdesc)
636 {
637         char* fname = fsp->fsp_name;
638         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
639 }
640
641 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
642 {
643         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
644 }
645
646 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
647 {
648         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
649 }
650
651 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags)
652 {
653         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
654 }
655
656 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
657                                   const char *fname, const char *aname, void *value, size_t size)
658 {
659         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
660 }
661
662 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
663                                       const char *fname, const char *aname, void *value, size_t size)
664 {
665         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
666 }
667
668 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
669                                       char *list, size_t size)
670 {
671         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
672 }
673
674 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
675                                     const char *aname)
676 {
677         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
678 }
679
680 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
681                                      const char *aname)
682 {
683         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
684 }
685
686 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
687                                  const char *aname, const void *value, size_t size, int flags)
688 {
689         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
690 }
691
692 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
693                                   const char *aname, const void *value, size_t size, int flags)
694 {
695         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
696 }
697
698 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
699                            const char *fname, mode_t mode)
700 {
701         /* If the underlying VFS doesn't have ACL support... */
702         if (!handle->vfs_next.ops.chmod_acl) {
703                 errno = ENOSYS;
704                 return -1;
705         }
706         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
707 }
708
709 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
710                                               files_struct *fsp, 
711                                               SHADOW_COPY_DATA *shadow_copy2_data, 
712                                               bool labels)
713 {
714         SMB_STRUCT_DIR *p;
715         const char *snapdir;
716         SMB_STRUCT_DIRENT *d;
717         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
718
719         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
720         if (snapdir == NULL) {
721                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
722                          handle->conn->connectpath));
723                 errno = EINVAL;
724                 talloc_free(tmp_ctx);
725                 return -1;
726         }
727
728         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
729
730         if (!p) {
731                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
732                          " - %s\n", snapdir, strerror(errno)));
733                 talloc_free(tmp_ctx);
734                 errno = ENOSYS;
735                 return -1;
736         }
737
738         talloc_free(tmp_ctx);
739
740         shadow_copy2_data->num_volumes = 0;
741         shadow_copy2_data->labels      = NULL;
742
743         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
744                 SHADOW_COPY_LABEL *tlabels;
745
746                 /* ignore names not of the right form in the snapshot directory */
747                 if (!shadow_copy2_match_name(d->d_name, NULL)) {
748                         continue;
749                 }
750
751                 if (!labels) {
752                         /* the caller doesn't want the labels */
753                         shadow_copy2_data->num_volumes++;
754                         continue;
755                 }
756
757                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
758                                          shadow_copy2_data->labels,
759                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
760                 if (tlabels == NULL) {
761                         DEBUG(0,("shadow_copy2: out of memory\n"));
762                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
763                         return -1;
764                 }
765
766                 strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
767                 shadow_copy2_data->num_volumes++;
768                 shadow_copy2_data->labels = tlabels;
769         }
770
771         SMB_VFS_NEXT_CLOSEDIR(handle,p);
772         return 0;
773 }
774
775 /* VFS operations structure */
776
777 static vfs_op_tuple shadow_copy2_ops[] = {
778         {SMB_VFS_OP(shadow_copy2_opendir),  SMB_VFS_OP_OPENDIR,  SMB_VFS_LAYER_TRANSPARENT},
779
780         /* directory operations */
781         {SMB_VFS_OP(shadow_copy2_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
782         {SMB_VFS_OP(shadow_copy2_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
783
784         /* xattr and flags operations */
785         {SMB_VFS_OP(shadow_copy2_chflags),     SMB_VFS_OP_CHFLAGS,     SMB_VFS_LAYER_TRANSPARENT},
786         {SMB_VFS_OP(shadow_copy2_getxattr),    SMB_VFS_OP_GETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
787         {SMB_VFS_OP(shadow_copy2_lgetxattr),   SMB_VFS_OP_LGETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
788         {SMB_VFS_OP(shadow_copy2_listxattr),   SMB_VFS_OP_LISTXATTR,   SMB_VFS_LAYER_TRANSPARENT},
789         {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
790         {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
791         {SMB_VFS_OP(shadow_copy2_setxattr),    SMB_VFS_OP_SETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
792         {SMB_VFS_OP(shadow_copy2_lsetxattr),   SMB_VFS_OP_LSETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
793
794         /* File operations */
795         {SMB_VFS_OP(shadow_copy2_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
796         {SMB_VFS_OP(shadow_copy2_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
797         {SMB_VFS_OP(shadow_copy2_stat),       SMB_VFS_OP_STAT,     SMB_VFS_LAYER_TRANSPARENT},
798         {SMB_VFS_OP(shadow_copy2_lstat),      SMB_VFS_OP_LSTAT,    SMB_VFS_LAYER_TRANSPARENT},
799         {SMB_VFS_OP(shadow_copy2_fstat),      SMB_VFS_OP_FSTAT,    SMB_VFS_LAYER_TRANSPARENT},
800         {SMB_VFS_OP(shadow_copy2_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
801         {SMB_VFS_OP(shadow_copy2_chmod),      SMB_VFS_OP_CHMOD,    SMB_VFS_LAYER_TRANSPARENT},
802         {SMB_VFS_OP(shadow_copy2_chown),      SMB_VFS_OP_CHOWN,    SMB_VFS_LAYER_TRANSPARENT},
803         {SMB_VFS_OP(shadow_copy2_chdir),      SMB_VFS_OP_CHDIR,    SMB_VFS_LAYER_TRANSPARENT},
804         {SMB_VFS_OP(shadow_copy2_ntimes),     SMB_VFS_OP_NTIMES,   SMB_VFS_LAYER_TRANSPARENT},
805         {SMB_VFS_OP(shadow_copy2_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
806         {SMB_VFS_OP(shadow_copy2_readlink),   SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
807         {SMB_VFS_OP(shadow_copy2_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
808         {SMB_VFS_OP(shadow_copy2_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
809         {SMB_VFS_OP(shadow_copy2_realpath),   SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
810         {SMB_VFS_OP(shadow_copy2_connectpath), SMB_VFS_OP_CONNECTPATH, SMB_VFS_LAYER_OPAQUE},
811
812         /* NT File ACL operations */
813         {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
814         {SMB_VFS_OP(shadow_copy2_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
815
816         /* POSIX ACL operations */
817         {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
818
819         /* special shadown copy op */
820         {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), 
821          SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
822
823         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
824 };
825
826 NTSTATUS vfs_shadow_copy2_init(void);
827 NTSTATUS vfs_shadow_copy2_init(void)
828 {
829         NTSTATUS ret;
830
831         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
832
833         if (!NT_STATUS_IS_OK(ret))
834                 return ret;
835
836         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
837         if (vfs_shadow_copy2_debug_level == -1) {
838                 vfs_shadow_copy2_debug_level = DBGC_VFS;
839                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
840                         "vfs_shadow_copy2_init"));
841         } else {
842                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
843                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
844         }
845
846         return ret;
847 }