s3: Fix shadow-copy module for drag&drop from a snapshot
[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 /*
303   convert a filename from a share relative path, to a path in the
304   snapshot directory
305  */
306 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
307 {
308         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
309         const char *snapdir, *relpath, *baseoffset, *basedir;
310         size_t baselen;
311         char *ret, *prefix;
312
313         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
314         if (snapdir == NULL) {
315                 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
316                 talloc_free(tmp_ctx);
317                 return NULL;
318         }
319
320         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
321         if (basedir == NULL) {
322                 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
323                 talloc_free(tmp_ctx);
324                 return NULL;
325         }
326
327         prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
328         if (strncmp(fname, prefix, strlen(prefix)) == 0) {
329                 /* this looks like as we have already normalized it, leave it untouched*/
330                 talloc_free(tmp_ctx);
331                 return talloc_strdup(handle->data, fname);
332         }
333
334         if (strncmp(fname, "@GMT-", 5) != 0) {
335                 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
336                 if (fname == NULL) {
337                         talloc_free(tmp_ctx);
338                         return NULL;
339                 }
340         }
341
342         relpath = fname + GMT_NAME_LEN;
343         baselen = strlen(basedir);
344         baseoffset = handle->conn->connectpath + baselen;
345
346         /* some sanity checks */
347         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
348             (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
349                 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
350                          basedir, handle->conn->connectpath));
351                 talloc_free(tmp_ctx);
352                 return NULL;
353         }
354
355         if (*relpath == '/') relpath++;
356         if (*baseoffset == '/') baseoffset++;
357
358         ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s", 
359                               snapdir, 
360                               GMT_NAME_LEN, fname, 
361                               baseoffset, 
362                               relpath);
363         DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
364         talloc_free(tmp_ctx);
365         return ret;
366 }
367
368
369 /*
370   simple string hash
371  */
372 static uint32 string_hash(const char *s)
373 {
374         uint32 n = 0;
375         while (*s) {
376                 n = ((n << 5) + n) ^ (uint32)(*s++);
377         }
378         return n;
379 }
380
381 /*
382   modify a sbuf return to ensure that inodes in the shadow directory
383   are different from those in the main directory
384  */
385 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
386 {
387         if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
388                 /* some snapshot systems, like GPFS, return the name
389                    device:inode for the snapshot files as the current
390                    files. That breaks the 'restore' button in the shadow copy
391                    GUI, as the client gets a sharing violation.
392
393                    This is a crude way of allowing both files to be
394                    open at once. It has a slight chance of inode
395                    number collision, but I can't see a better approach
396                    without significant VFS changes
397                 */
398                 uint32_t shash = string_hash(fname) & 0xFF000000;
399                 if (shash == 0) {
400                         shash = 1;
401                 }
402                 sbuf->st_ex_ino ^= shash;
403         }
404 }
405
406 static int shadow_copy2_rename(vfs_handle_struct *handle,
407                         const char *oldname, const char *newname)
408 {
409         if (shadow_copy2_match_name(oldname, NULL)) {
410                 errno = EXDEV;
411                 return -1;
412         }
413         SHADOW2_NEXT2(RENAME, (handle, oldname, newname));
414 }
415
416 static int shadow_copy2_symlink(vfs_handle_struct *handle,
417                                 const char *oldname, const char *newname)
418 {
419         SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
420 }
421
422 static int shadow_copy2_link(vfs_handle_struct *handle,
423                           const char *oldname, const char *newname)
424 {
425         SHADOW2_NEXT2(LINK, (handle, oldname, newname));
426 }
427
428 static int shadow_copy2_open(vfs_handle_struct *handle,
429                              const char *fname, files_struct *fsp, int flags, mode_t mode)
430 {
431         SHADOW2_NEXT(OPEN, (handle, name, fsp, flags, mode), int, -1);
432 }
433
434 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
435                           const char *fname, const char *mask, uint32 attr)
436 {
437         SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
438 }
439
440 static int shadow_copy2_stat(vfs_handle_struct *handle,
441                       const char *fname, SMB_STRUCT_STAT *sbuf)
442 {
443         _SHADOW2_NEXT(STAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
444 }
445
446 static int shadow_copy2_lstat(vfs_handle_struct *handle,
447                        const char *fname, SMB_STRUCT_STAT *sbuf)
448 {
449         _SHADOW2_NEXT(LSTAT, (handle, name, sbuf), int, -1, convert_sbuf(handle, fname, sbuf));
450 }
451
452 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
453 {
454         int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
455         if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name, NULL)) {
456                 convert_sbuf(handle, fsp->fsp_name, sbuf);
457         }
458         return ret;
459 }
460
461 static int shadow_copy2_unlink(vfs_handle_struct *handle, const char *fname)
462 {
463         SHADOW2_NEXT(UNLINK, (handle, name), int, -1);
464 }
465
466 static int shadow_copy2_chmod(vfs_handle_struct *handle,
467                        const char *fname, mode_t mode)
468 {
469         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
470 }
471
472 static int shadow_copy2_chown(vfs_handle_struct *handle,
473                        const char *fname, uid_t uid, gid_t gid)
474 {
475         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
476 }
477
478 static int shadow_copy2_chdir(vfs_handle_struct *handle,
479                        const char *fname)
480 {
481         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
482 }
483
484 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
485                        const char *fname, struct smb_file_time *ft)
486 {
487         SHADOW2_NEXT(NTIMES, (handle, name, ft), int, -1);
488 }
489
490 static int shadow_copy2_readlink(vfs_handle_struct *handle,
491                                  const char *fname, char *buf, size_t bufsiz)
492 {
493         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
494 }
495
496 static int shadow_copy2_mknod(vfs_handle_struct *handle,
497                        const char *fname, mode_t mode, SMB_DEV_T dev)
498 {
499         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
500 }
501
502 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
503                             const char *fname, char *resolved_path)
504 {
505         const char *gmt_start;
506
507         if (shadow_copy2_match_name(fname, &gmt_start)
508             && (gmt_start[GMT_NAME_LEN] == '\0')) {
509                 char *copy, *result;
510
511                 copy = talloc_strdup(talloc_tos(), fname);
512                 if (copy == NULL) {
513                         errno = ENOMEM;
514                         return NULL;
515                 }
516
517                 copy[gmt_start - fname] = '.';
518                 copy[gmt_start - fname + 1] = '\0';
519
520                 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
521                 SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *,
522                              NULL);
523         }
524
525         SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
526 }
527
528 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
529                                             const char *fname)
530 {
531         TALLOC_CTX *tmp_ctx = talloc_stackframe();
532         const char *snapdir, *baseoffset, *basedir, *gmt_start;
533         size_t baselen;
534         char *ret;
535
536         DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
537
538         if (!shadow_copy2_match_name(fname, &gmt_start)) {
539                 return handle->conn->connectpath;
540         }
541
542         fname = shadow_copy2_normalise_path(talloc_tos(), fname, gmt_start);
543         if (fname == NULL) {
544                 TALLOC_FREE(tmp_ctx);
545                 return NULL;
546         }
547
548         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
549         if (snapdir == NULL) {
550                 DEBUG(2,("no snapdir found for share at %s\n",
551                          handle->conn->connectpath));
552                 TALLOC_FREE(tmp_ctx);
553                 return NULL;
554         }
555
556         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
557         if (basedir == NULL) {
558                 DEBUG(2,("no basedir found for share at %s\n",
559                          handle->conn->connectpath));
560                 TALLOC_FREE(tmp_ctx);
561                 return NULL;
562         }
563
564         baselen = strlen(basedir);
565         baseoffset = handle->conn->connectpath + baselen;
566
567         /* some sanity checks */
568         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
569             (handle->conn->connectpath[baselen] != 0
570              && handle->conn->connectpath[baselen] != '/')) {
571                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
572                          "parent of %s\n", basedir,
573                          handle->conn->connectpath));
574                 TALLOC_FREE(tmp_ctx);
575                 return NULL;
576         }
577
578         if (*baseoffset == '/') baseoffset++;
579
580         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
581                               snapdir,
582                               GMT_NAME_LEN, fname,
583                               baseoffset);
584         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
585         TALLOC_FREE(tmp_ctx);
586         return ret;
587 }
588
589 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
590                                const char *fname, uint32 security_info,
591                                struct security_descriptor **ppdesc)
592 {
593         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
594 }
595
596 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
597 {
598         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
599 }
600
601 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
602 {
603         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
604 }
605
606 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags)
607 {
608         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
609 }
610
611 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
612                                   const char *fname, const char *aname, void *value, size_t size)
613 {
614         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
615 }
616
617 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
618                                       const char *fname, const char *aname, void *value, size_t size)
619 {
620         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
621 }
622
623 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
624                                       char *list, size_t size)
625 {
626         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
627 }
628
629 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
630                                     const char *aname)
631 {
632         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
633 }
634
635 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
636                                      const char *aname)
637 {
638         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
639 }
640
641 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
642                                  const char *aname, const void *value, size_t size, int flags)
643 {
644         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
645 }
646
647 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
648                                   const char *aname, const void *value, size_t size, int flags)
649 {
650         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
651 }
652
653 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
654                            const char *fname, mode_t mode)
655 {
656         /* If the underlying VFS doesn't have ACL support... */
657         if (!handle->vfs_next.ops.chmod_acl) {
658                 errno = ENOSYS;
659                 return -1;
660         }
661         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
662 }
663
664 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
665                                               files_struct *fsp, 
666                                               SHADOW_COPY_DATA *shadow_copy2_data, 
667                                               bool labels)
668 {
669         SMB_STRUCT_DIR *p;
670         const char *snapdir;
671         SMB_STRUCT_DIRENT *d;
672         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
673
674         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
675         if (snapdir == NULL) {
676                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
677                          handle->conn->connectpath));
678                 errno = EINVAL;
679                 talloc_free(tmp_ctx);
680                 return -1;
681         }
682
683         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
684
685         if (!p) {
686                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
687                          " - %s\n", snapdir, strerror(errno)));
688                 talloc_free(tmp_ctx);
689                 errno = ENOSYS;
690                 return -1;
691         }
692
693         talloc_free(tmp_ctx);
694
695         shadow_copy2_data->num_volumes = 0;
696         shadow_copy2_data->labels      = NULL;
697
698         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
699                 SHADOW_COPY_LABEL *tlabels;
700
701                 /* ignore names not of the right form in the snapshot directory */
702                 if (!shadow_copy2_match_name(d->d_name, NULL)) {
703                         continue;
704                 }
705
706                 if (!labels) {
707                         /* the caller doesn't want the labels */
708                         shadow_copy2_data->num_volumes++;
709                         continue;
710                 }
711
712                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
713                                          shadow_copy2_data->labels,
714                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
715                 if (tlabels == NULL) {
716                         DEBUG(0,("shadow_copy2: out of memory\n"));
717                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
718                         return -1;
719                 }
720
721                 strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
722                 shadow_copy2_data->num_volumes++;
723                 shadow_copy2_data->labels = tlabels;
724         }
725
726         SMB_VFS_NEXT_CLOSEDIR(handle,p);
727         return 0;
728 }
729
730 /* VFS operations structure */
731
732 static vfs_op_tuple shadow_copy2_ops[] = {
733         {SMB_VFS_OP(shadow_copy2_opendir),  SMB_VFS_OP_OPENDIR,  SMB_VFS_LAYER_TRANSPARENT},
734
735         /* directory operations */
736         {SMB_VFS_OP(shadow_copy2_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
737         {SMB_VFS_OP(shadow_copy2_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
738
739         /* xattr and flags operations */
740         {SMB_VFS_OP(shadow_copy2_chflags),     SMB_VFS_OP_CHFLAGS,     SMB_VFS_LAYER_TRANSPARENT},
741         {SMB_VFS_OP(shadow_copy2_getxattr),    SMB_VFS_OP_GETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
742         {SMB_VFS_OP(shadow_copy2_lgetxattr),   SMB_VFS_OP_LGETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
743         {SMB_VFS_OP(shadow_copy2_listxattr),   SMB_VFS_OP_LISTXATTR,   SMB_VFS_LAYER_TRANSPARENT},
744         {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
745         {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
746         {SMB_VFS_OP(shadow_copy2_setxattr),    SMB_VFS_OP_SETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
747         {SMB_VFS_OP(shadow_copy2_lsetxattr),   SMB_VFS_OP_LSETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
748
749         /* File operations */
750         {SMB_VFS_OP(shadow_copy2_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
751         {SMB_VFS_OP(shadow_copy2_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
752         {SMB_VFS_OP(shadow_copy2_stat),       SMB_VFS_OP_STAT,     SMB_VFS_LAYER_TRANSPARENT},
753         {SMB_VFS_OP(shadow_copy2_lstat),      SMB_VFS_OP_LSTAT,    SMB_VFS_LAYER_TRANSPARENT},
754         {SMB_VFS_OP(shadow_copy2_fstat),      SMB_VFS_OP_FSTAT,    SMB_VFS_LAYER_TRANSPARENT},
755         {SMB_VFS_OP(shadow_copy2_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
756         {SMB_VFS_OP(shadow_copy2_chmod),      SMB_VFS_OP_CHMOD,    SMB_VFS_LAYER_TRANSPARENT},
757         {SMB_VFS_OP(shadow_copy2_chown),      SMB_VFS_OP_CHOWN,    SMB_VFS_LAYER_TRANSPARENT},
758         {SMB_VFS_OP(shadow_copy2_chdir),      SMB_VFS_OP_CHDIR,    SMB_VFS_LAYER_TRANSPARENT},
759         {SMB_VFS_OP(shadow_copy2_ntimes),     SMB_VFS_OP_NTIMES,   SMB_VFS_LAYER_TRANSPARENT},
760         {SMB_VFS_OP(shadow_copy2_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
761         {SMB_VFS_OP(shadow_copy2_readlink),   SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
762         {SMB_VFS_OP(shadow_copy2_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
763         {SMB_VFS_OP(shadow_copy2_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
764         {SMB_VFS_OP(shadow_copy2_realpath),   SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
765         {SMB_VFS_OP(shadow_copy2_connectpath), SMB_VFS_OP_CONNECTPATH, SMB_VFS_LAYER_OPAQUE},
766
767         /* NT File ACL operations */
768         {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
769
770         /* POSIX ACL operations */
771         {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
772
773         /* special shadown copy op */
774         {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), 
775          SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
776
777         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
778 };
779
780 NTSTATUS vfs_shadow_copy2_init(void);
781 NTSTATUS vfs_shadow_copy2_init(void)
782 {
783         NTSTATUS ret;
784
785         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
786
787         if (!NT_STATUS_IS_OK(ret))
788                 return ret;
789
790         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
791         if (vfs_shadow_copy2_debug_level == -1) {
792                 vfs_shadow_copy2_debug_level = DBGC_VFS;
793                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
794                         "vfs_shadow_copy2_init"));
795         } else {
796                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
797                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
798         }
799
800         return ret;
801 }