29247ac7c27f1839cc8a6c65e996a1ca8f28251b
[samba.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)
80 {
81         unsigned year, month, day, hr, min, sec;
82         if (name[0] != '@') return False;
83         if (strncmp(name, "@GMT-", 5) != 0) return False;
84         if (sscanf(name, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
85                    &day, &hr, &min, &sec) != 6) {
86                 return False;
87         }
88         if (name[24] != 0 && name[24] != '/') {
89                 return False;
90         }
91         return True;
92 }
93
94 /*
95   convert a name to the shadow directory
96  */
97
98 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
99         const char *name = fname; \
100         if (shadow_copy2_match_name(fname)) { \
101                 char *name2; \
102                 rtype ret; \
103                 name2 = convert_shadow2_name(handle, fname); \
104                 if (name2 == NULL) { \
105                         errno = EINVAL; \
106                         return eret; \
107                 } \
108                 name = name2; \
109                 ret = SMB_VFS_NEXT_ ## op args; \
110                 talloc_free(name2); \
111                 if (ret != eret) extra; \
112                 return ret; \
113         } else { \
114                 return SMB_VFS_NEXT_ ## op args; \
115         } \
116 } while (0)
117
118 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
119         if (shadow_copy2_match_name(smb_fname->base_name)) { \
120                 char *name2; \
121                 char *smb_base_name_tmp = NULL; \
122                 rtype ret; \
123                 name2 = convert_shadow2_name(handle, smb_fname->base_name); \
124                 if (name2 == NULL) { \
125                         errno = EINVAL; \
126                         return eret; \
127                 } \
128                 smb_base_name_tmp = smb_fname->base_name; \
129                 smb_fname->base_name = name2; \
130                 ret = SMB_VFS_NEXT_ ## op args; \
131                 smb_fname->base_name = smb_base_name_tmp; \
132                 talloc_free(name2); \
133                 if (ret != eret) extra; \
134                 return ret; \
135         } else { \
136                 return SMB_VFS_NEXT_ ## op args; \
137         } \
138 } while (0)
139
140 /*
141   convert a name to the shadow directory: NTSTATUS-specific handling
142  */
143
144 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
145         const char *name = fname; \
146         if (shadow_copy2_match_name(fname)) { \
147                 char *name2; \
148                 NTSTATUS ret; \
149                 name2 = convert_shadow2_name(handle, fname); \
150                 if (name2 == NULL) { \
151                         errno = EINVAL; \
152                         return eret; \
153                 } \
154                 name = name2; \
155                 ret = SMB_VFS_NEXT_ ## op args; \
156                 talloc_free(name2); \
157                 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
158                 return ret; \
159         } else { \
160                 return SMB_VFS_NEXT_ ## op args; \
161         } \
162 } while (0)
163
164 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
165
166 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
167
168 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
169
170 #define SHADOW2_NEXT2(op, args) do { \
171         if (shadow_copy2_match_name(oldname) || shadow_copy2_match_name(newname)) { \
172                 errno = EROFS; \
173                 return -1; \
174         } else { \
175                 return SMB_VFS_NEXT_ ## op args; \
176         } \
177 } while (0)
178
179 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
180         if (shadow_copy2_match_name(smb_fname_src->base_name) || \
181             shadow_copy2_match_name(smb_fname_dst->base_name)) { \
182                 errno = EROFS; \
183                 return -1; \
184         } else { \
185                 return SMB_VFS_NEXT_ ## op args; \
186         } \
187 } while (0)
188
189
190 /*
191   find the mount point of a filesystem
192  */
193 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
194 {
195         char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
196         dev_t dev;
197         struct stat st;
198         char *p;
199
200         if (stat(path, &st) != 0) {
201                 talloc_free(path);
202                 return NULL;
203         }
204
205         dev = st.st_dev;
206
207         while ((p = strrchr(path, '/')) && p > path) {
208                 *p = 0;
209                 if (stat(path, &st) != 0) {
210                         talloc_free(path);
211                         return NULL;
212                 }
213                 if (st.st_dev != dev) {
214                         *p = '/';
215                         break;
216                 }
217         }
218
219         return path;    
220 }
221
222 /*
223   work out the location of the snapshot for this share
224  */
225 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
226 {
227         const char *snapdir;
228         char *mount_point;
229         const char *ret;
230
231         snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
232         if (snapdir == NULL) {
233                 return NULL;
234         }
235         /* if its an absolute path, we're done */
236         if (*snapdir == '/') {
237                 return snapdir;
238         }
239
240         /* other its relative to the filesystem mount point */
241         mount_point = find_mount_point(mem_ctx, handle);
242         if (mount_point == NULL) {
243                 return NULL;
244         }
245
246         ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
247         talloc_free(mount_point);
248         return ret;
249 }
250
251 /*
252   work out the location of the base directory for snapshots of this share
253  */
254 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
255 {
256         const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
257
258         /* other its the filesystem mount point */
259         if (basedir == NULL) {
260                 basedir = find_mount_point(mem_ctx, handle);
261         }
262
263         return basedir;
264 }
265
266 /*
267   convert a filename from a share relative path, to a path in the
268   snapshot directory
269  */
270 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname)
271 {
272         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
273         const char *snapdir, *relpath, *baseoffset, *basedir;
274         size_t baselen;
275         char *ret;
276
277         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
278         if (snapdir == NULL) {
279                 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
280                 talloc_free(tmp_ctx);
281                 return NULL;
282         }
283
284         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
285         if (basedir == NULL) {
286                 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
287                 talloc_free(tmp_ctx);
288                 return NULL;
289         }
290
291         relpath = fname + GMT_NAME_LEN;
292         baselen = strlen(basedir);
293         baseoffset = handle->conn->connectpath + baselen;
294
295         /* some sanity checks */
296         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
297             (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
298                 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
299                          basedir, handle->conn->connectpath));
300                 talloc_free(tmp_ctx);
301                 return NULL;
302         }
303
304         if (*relpath == '/') relpath++;
305         if (*baseoffset == '/') baseoffset++;
306
307         ret = talloc_asprintf(handle->data, "%s/%.*s/%s/%s", 
308                               snapdir, 
309                               GMT_NAME_LEN, fname, 
310                               baseoffset, 
311                               relpath);
312         DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
313         talloc_free(tmp_ctx);
314         return ret;
315 }
316
317
318 /*
319   simple string hash
320  */
321 static uint32 string_hash(const char *s)
322 {
323         uint32 n = 0;
324         while (*s) {
325                 n = ((n << 5) + n) ^ (uint32)(*s++);
326         }
327         return n;
328 }
329
330 /*
331   modify a sbuf return to ensure that inodes in the shadow directory
332   are different from those in the main directory
333  */
334 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
335 {
336         if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
337                 /* some snapshot systems, like GPFS, return the name
338                    device:inode for the snapshot files as the current
339                    files. That breaks the 'restore' button in the shadow copy
340                    GUI, as the client gets a sharing violation.
341
342                    This is a crude way of allowing both files to be
343                    open at once. It has a slight chance of inode
344                    number collision, but I can't see a better approach
345                    without significant VFS changes
346                 */
347                 uint32_t shash = string_hash(fname) & 0xFF000000;
348                 if (shash == 0) {
349                         shash = 1;
350                 }
351                 sbuf->st_ex_ino ^= shash;
352         }
353 }
354
355 static int shadow_copy2_rename(vfs_handle_struct *handle,
356                                const struct smb_filename *smb_fname_src,
357                                const struct smb_filename *smb_fname_dst)
358 {
359         SHADOW2_NEXT2_SMB_FNAME(RENAME,
360                                 (handle, smb_fname_src, smb_fname_dst));
361 }
362
363 static int shadow_copy2_symlink(vfs_handle_struct *handle,
364                                 const char *oldname, const char *newname)
365 {
366         SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
367 }
368
369 static int shadow_copy2_link(vfs_handle_struct *handle,
370                           const char *oldname, const char *newname)
371 {
372         SHADOW2_NEXT2(LINK, (handle, oldname, newname));
373 }
374
375 static int shadow_copy2_open(vfs_handle_struct *handle,
376                              struct smb_filename *smb_fname, files_struct *fsp,
377                              int flags, mode_t mode)
378 {
379         SHADOW2_NEXT_SMB_FNAME(OPEN,
380                                (handle, smb_fname, fsp, flags, mode),
381                                int, -1);
382 }
383
384 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
385                           const char *fname, const char *mask, uint32 attr)
386 {
387         SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
388 }
389
390 static int shadow_copy2_stat(vfs_handle_struct *handle,
391                              struct smb_filename *smb_fname)
392 {
393         _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
394                                 convert_sbuf(handle, smb_fname->base_name,
395                                              &smb_fname->st));
396 }
397
398 static int shadow_copy2_lstat(vfs_handle_struct *handle,
399                               struct smb_filename *smb_fname)
400 {
401         _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
402                                 convert_sbuf(handle, smb_fname->base_name,
403                                              &smb_fname->st));
404 }
405
406 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
407 {
408         int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
409         if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name)) {
410                 convert_sbuf(handle, fsp->fsp_name, sbuf);
411         }
412         return ret;
413 }
414
415 static int shadow_copy2_unlink(vfs_handle_struct *handle,
416                                const struct smb_filename *smb_fname_in)
417 {
418         struct smb_filename *smb_fname = NULL;
419         NTSTATUS status;
420
421         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
422         if (!NT_STATUS_IS_OK(status)) {
423                 errno = map_errno_from_nt_status(status);
424                 return -1;
425         }
426
427         SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
428 }
429
430 static int shadow_copy2_chmod(vfs_handle_struct *handle,
431                        const char *fname, mode_t mode)
432 {
433         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
434 }
435
436 static int shadow_copy2_chown(vfs_handle_struct *handle,
437                        const char *fname, uid_t uid, gid_t gid)
438 {
439         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
440 }
441
442 static int shadow_copy2_chdir(vfs_handle_struct *handle,
443                        const char *fname)
444 {
445         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
446 }
447
448 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
449                                const struct smb_filename *smb_fname_in,
450                                struct smb_file_time *ft)
451 {
452         struct smb_filename *smb_fname = NULL;
453         NTSTATUS status;
454
455         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
456         if (!NT_STATUS_IS_OK(status)) {
457                 errno = map_errno_from_nt_status(status);
458                 return -1;
459         }
460
461         SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
462 }
463
464 static int shadow_copy2_readlink(vfs_handle_struct *handle,
465                                  const char *fname, char *buf, size_t bufsiz)
466 {
467         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
468 }
469
470 static int shadow_copy2_mknod(vfs_handle_struct *handle,
471                        const char *fname, mode_t mode, SMB_DEV_T dev)
472 {
473         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
474 }
475
476 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
477                             const char *fname, char *resolved_path)
478 {
479         SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
480 }
481
482 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
483                                             const char *fname)
484 {
485         TALLOC_CTX *tmp_ctx = talloc_stackframe();
486         const char *snapdir, *baseoffset, *basedir;
487         size_t baselen;
488         char *ret;
489
490         if (!shadow_copy2_match_name(fname)) {
491                 return handle->conn->connectpath;
492         }
493
494         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
495         if (snapdir == NULL) {
496                 DEBUG(2,("no snapdir found for share at %s\n",
497                          handle->conn->connectpath));
498                 TALLOC_FREE(tmp_ctx);
499                 return NULL;
500         }
501
502         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
503         if (basedir == NULL) {
504                 DEBUG(2,("no basedir found for share at %s\n",
505                          handle->conn->connectpath));
506                 TALLOC_FREE(tmp_ctx);
507                 return NULL;
508         }
509
510         baselen = strlen(basedir);
511         baseoffset = handle->conn->connectpath + baselen;
512
513         /* some sanity checks */
514         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
515             (handle->conn->connectpath[baselen] != 0
516              && handle->conn->connectpath[baselen] != '/')) {
517                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
518                          "parent of %s\n", basedir,
519                          handle->conn->connectpath));
520                 TALLOC_FREE(tmp_ctx);
521                 return NULL;
522         }
523
524         if (*baseoffset == '/') baseoffset++;
525
526         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
527                               snapdir,
528                               GMT_NAME_LEN, fname,
529                               baseoffset);
530         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
531         TALLOC_FREE(tmp_ctx);
532         return ret;
533 }
534
535 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
536                                const char *fname, uint32 security_info,
537                                struct security_descriptor **ppdesc)
538 {
539         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
540 }
541
542 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
543 {
544         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
545 }
546
547 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
548 {
549         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
550 }
551
552 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
553                                 unsigned int flags)
554 {
555         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
556 }
557
558 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
559                                   const char *fname, const char *aname, void *value, size_t size)
560 {
561         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
562 }
563
564 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
565                                       const char *fname, const char *aname, void *value, size_t size)
566 {
567         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
568 }
569
570 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
571                                       char *list, size_t size)
572 {
573         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
574 }
575
576 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
577                                     const char *aname)
578 {
579         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
580 }
581
582 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
583                                      const char *aname)
584 {
585         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
586 }
587
588 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
589                                  const char *aname, const void *value, size_t size, int flags)
590 {
591         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
592 }
593
594 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
595                                   const char *aname, const void *value, size_t size, int flags)
596 {
597         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
598 }
599
600 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
601                            const char *fname, mode_t mode)
602 {
603         /* If the underlying VFS doesn't have ACL support... */
604         if (!handle->vfs_next.ops.chmod_acl) {
605                 errno = ENOSYS;
606                 return -1;
607         }
608         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
609 }
610
611 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
612                                               files_struct *fsp, 
613                                               SHADOW_COPY_DATA *shadow_copy2_data, 
614                                               bool labels)
615 {
616         SMB_STRUCT_DIR *p;
617         const char *snapdir;
618         SMB_STRUCT_DIRENT *d;
619         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
620
621         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
622         if (snapdir == NULL) {
623                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
624                          handle->conn->connectpath));
625                 errno = EINVAL;
626                 talloc_free(tmp_ctx);
627                 return -1;
628         }
629
630         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
631
632         if (!p) {
633                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
634                          " - %s\n", snapdir, strerror(errno)));
635                 talloc_free(tmp_ctx);
636                 errno = ENOSYS;
637                 return -1;
638         }
639
640         talloc_free(tmp_ctx);
641
642         shadow_copy2_data->num_volumes = 0;
643         shadow_copy2_data->labels      = NULL;
644
645         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
646                 SHADOW_COPY_LABEL *tlabels;
647
648                 /* ignore names not of the right form in the snapshot directory */
649                 if (!shadow_copy2_match_name(d->d_name)) {
650                         continue;
651                 }
652
653                 if (!labels) {
654                         /* the caller doesn't want the labels */
655                         shadow_copy2_data->num_volumes++;
656                         continue;
657                 }
658
659                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
660                                          shadow_copy2_data->labels,
661                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
662                 if (tlabels == NULL) {
663                         DEBUG(0,("shadow_copy2: out of memory\n"));
664                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
665                         return -1;
666                 }
667
668                 strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
669                 shadow_copy2_data->num_volumes++;
670                 shadow_copy2_data->labels = tlabels;
671         }
672
673         SMB_VFS_NEXT_CLOSEDIR(handle,p);
674         return 0;
675 }
676
677 /* VFS operations structure */
678
679 static vfs_op_tuple shadow_copy2_ops[] = {
680         {SMB_VFS_OP(shadow_copy2_opendir),  SMB_VFS_OP_OPENDIR,  SMB_VFS_LAYER_TRANSPARENT},
681
682         /* directory operations */
683         {SMB_VFS_OP(shadow_copy2_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
684         {SMB_VFS_OP(shadow_copy2_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
685
686         /* xattr and flags operations */
687         {SMB_VFS_OP(shadow_copy2_chflags),     SMB_VFS_OP_CHFLAGS,     SMB_VFS_LAYER_TRANSPARENT},
688         {SMB_VFS_OP(shadow_copy2_getxattr),    SMB_VFS_OP_GETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
689         {SMB_VFS_OP(shadow_copy2_lgetxattr),   SMB_VFS_OP_LGETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
690         {SMB_VFS_OP(shadow_copy2_listxattr),   SMB_VFS_OP_LISTXATTR,   SMB_VFS_LAYER_TRANSPARENT},
691         {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
692         {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
693         {SMB_VFS_OP(shadow_copy2_setxattr),    SMB_VFS_OP_SETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
694         {SMB_VFS_OP(shadow_copy2_lsetxattr),   SMB_VFS_OP_LSETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
695
696         /* File operations */
697         {SMB_VFS_OP(shadow_copy2_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
698         {SMB_VFS_OP(shadow_copy2_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
699         {SMB_VFS_OP(shadow_copy2_stat),       SMB_VFS_OP_STAT,     SMB_VFS_LAYER_TRANSPARENT},
700         {SMB_VFS_OP(shadow_copy2_lstat),      SMB_VFS_OP_LSTAT,    SMB_VFS_LAYER_TRANSPARENT},
701         {SMB_VFS_OP(shadow_copy2_fstat),      SMB_VFS_OP_FSTAT,    SMB_VFS_LAYER_TRANSPARENT},
702         {SMB_VFS_OP(shadow_copy2_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
703         {SMB_VFS_OP(shadow_copy2_chmod),      SMB_VFS_OP_CHMOD,    SMB_VFS_LAYER_TRANSPARENT},
704         {SMB_VFS_OP(shadow_copy2_chown),      SMB_VFS_OP_CHOWN,    SMB_VFS_LAYER_TRANSPARENT},
705         {SMB_VFS_OP(shadow_copy2_chdir),      SMB_VFS_OP_CHDIR,    SMB_VFS_LAYER_TRANSPARENT},
706         {SMB_VFS_OP(shadow_copy2_ntimes),     SMB_VFS_OP_NTIMES,   SMB_VFS_LAYER_TRANSPARENT},
707         {SMB_VFS_OP(shadow_copy2_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
708         {SMB_VFS_OP(shadow_copy2_readlink),   SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
709         {SMB_VFS_OP(shadow_copy2_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
710         {SMB_VFS_OP(shadow_copy2_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
711         {SMB_VFS_OP(shadow_copy2_realpath),   SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
712         {SMB_VFS_OP(shadow_copy2_connectpath), SMB_VFS_OP_CONNECTPATH, SMB_VFS_LAYER_OPAQUE},
713
714         /* NT File ACL operations */
715         {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
716
717         /* POSIX ACL operations */
718         {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
719
720         /* special shadown copy op */
721         {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), 
722          SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
723
724         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
725 };
726
727 NTSTATUS vfs_shadow_copy2_init(void);
728 NTSTATUS vfs_shadow_copy2_init(void)
729 {
730         NTSTATUS ret;
731
732         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
733
734         if (!NT_STATUS_IS_OK(ret))
735                 return ret;
736
737         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
738         if (vfs_shadow_copy2_debug_level == -1) {
739                 vfs_shadow_copy2_debug_level = DBGC_VFS;
740                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
741                         "vfs_shadow_copy2_init"));
742         } else {
743                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
744                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
745         }
746
747         return ret;
748 }