25c5096da7775389f2bfa775b881bdd87dbe7d0a
[nivanova/samba-autobuild/.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, const char *fname)
416 {
417         SHADOW2_NEXT(UNLINK, (handle, name), int, -1);
418 }
419
420 static int shadow_copy2_chmod(vfs_handle_struct *handle,
421                        const char *fname, mode_t mode)
422 {
423         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
424 }
425
426 static int shadow_copy2_chown(vfs_handle_struct *handle,
427                        const char *fname, uid_t uid, gid_t gid)
428 {
429         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
430 }
431
432 static int shadow_copy2_chdir(vfs_handle_struct *handle,
433                        const char *fname)
434 {
435         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
436 }
437
438 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
439                        const char *fname, struct smb_file_time *ft)
440 {
441         SHADOW2_NEXT(NTIMES, (handle, name, ft), int, -1);
442 }
443
444 static int shadow_copy2_readlink(vfs_handle_struct *handle,
445                                  const char *fname, char *buf, size_t bufsiz)
446 {
447         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
448 }
449
450 static int shadow_copy2_mknod(vfs_handle_struct *handle,
451                        const char *fname, mode_t mode, SMB_DEV_T dev)
452 {
453         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
454 }
455
456 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
457                             const char *fname, char *resolved_path)
458 {
459         SHADOW2_NEXT(REALPATH, (handle, name, resolved_path), char *, NULL);
460 }
461
462 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
463                                             const char *fname)
464 {
465         TALLOC_CTX *tmp_ctx = talloc_stackframe();
466         const char *snapdir, *baseoffset, *basedir;
467         size_t baselen;
468         char *ret;
469
470         if (!shadow_copy2_match_name(fname)) {
471                 return handle->conn->connectpath;
472         }
473
474         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
475         if (snapdir == NULL) {
476                 DEBUG(2,("no snapdir found for share at %s\n",
477                          handle->conn->connectpath));
478                 TALLOC_FREE(tmp_ctx);
479                 return NULL;
480         }
481
482         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
483         if (basedir == NULL) {
484                 DEBUG(2,("no basedir found for share at %s\n",
485                          handle->conn->connectpath));
486                 TALLOC_FREE(tmp_ctx);
487                 return NULL;
488         }
489
490         baselen = strlen(basedir);
491         baseoffset = handle->conn->connectpath + baselen;
492
493         /* some sanity checks */
494         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
495             (handle->conn->connectpath[baselen] != 0
496              && handle->conn->connectpath[baselen] != '/')) {
497                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
498                          "parent of %s\n", basedir,
499                          handle->conn->connectpath));
500                 TALLOC_FREE(tmp_ctx);
501                 return NULL;
502         }
503
504         if (*baseoffset == '/') baseoffset++;
505
506         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
507                               snapdir,
508                               GMT_NAME_LEN, fname,
509                               baseoffset);
510         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
511         TALLOC_FREE(tmp_ctx);
512         return ret;
513 }
514
515 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
516                                const char *fname, uint32 security_info,
517                                struct security_descriptor **ppdesc)
518 {
519         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
520 }
521
522 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
523 {
524         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
525 }
526
527 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
528 {
529         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
530 }
531
532 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname, int flags)
533 {
534         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
535 }
536
537 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
538                                   const char *fname, const char *aname, void *value, size_t size)
539 {
540         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
541 }
542
543 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
544                                       const char *fname, const char *aname, void *value, size_t size)
545 {
546         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
547 }
548
549 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
550                                       char *list, size_t size)
551 {
552         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
553 }
554
555 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
556                                     const char *aname)
557 {
558         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
559 }
560
561 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
562                                      const char *aname)
563 {
564         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
565 }
566
567 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
568                                  const char *aname, const void *value, size_t size, int flags)
569 {
570         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
571 }
572
573 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
574                                   const char *aname, const void *value, size_t size, int flags)
575 {
576         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
577 }
578
579 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
580                            const char *fname, mode_t mode)
581 {
582         /* If the underlying VFS doesn't have ACL support... */
583         if (!handle->vfs_next.ops.chmod_acl) {
584                 errno = ENOSYS;
585                 return -1;
586         }
587         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
588 }
589
590 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
591                                               files_struct *fsp, 
592                                               SHADOW_COPY_DATA *shadow_copy2_data, 
593                                               bool labels)
594 {
595         SMB_STRUCT_DIR *p;
596         const char *snapdir;
597         SMB_STRUCT_DIRENT *d;
598         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
599
600         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
601         if (snapdir == NULL) {
602                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
603                          handle->conn->connectpath));
604                 errno = EINVAL;
605                 talloc_free(tmp_ctx);
606                 return -1;
607         }
608
609         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
610
611         if (!p) {
612                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
613                          " - %s\n", snapdir, strerror(errno)));
614                 talloc_free(tmp_ctx);
615                 errno = ENOSYS;
616                 return -1;
617         }
618
619         talloc_free(tmp_ctx);
620
621         shadow_copy2_data->num_volumes = 0;
622         shadow_copy2_data->labels      = NULL;
623
624         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
625                 SHADOW_COPY_LABEL *tlabels;
626
627                 /* ignore names not of the right form in the snapshot directory */
628                 if (!shadow_copy2_match_name(d->d_name)) {
629                         continue;
630                 }
631
632                 if (!labels) {
633                         /* the caller doesn't want the labels */
634                         shadow_copy2_data->num_volumes++;
635                         continue;
636                 }
637
638                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
639                                          shadow_copy2_data->labels,
640                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
641                 if (tlabels == NULL) {
642                         DEBUG(0,("shadow_copy2: out of memory\n"));
643                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
644                         return -1;
645                 }
646
647                 strlcpy(tlabels[shadow_copy2_data->num_volumes], d->d_name, sizeof(*tlabels));
648                 shadow_copy2_data->num_volumes++;
649                 shadow_copy2_data->labels = tlabels;
650         }
651
652         SMB_VFS_NEXT_CLOSEDIR(handle,p);
653         return 0;
654 }
655
656 /* VFS operations structure */
657
658 static vfs_op_tuple shadow_copy2_ops[] = {
659         {SMB_VFS_OP(shadow_copy2_opendir),  SMB_VFS_OP_OPENDIR,  SMB_VFS_LAYER_TRANSPARENT},
660
661         /* directory operations */
662         {SMB_VFS_OP(shadow_copy2_mkdir),       SMB_VFS_OP_MKDIR,       SMB_VFS_LAYER_TRANSPARENT},
663         {SMB_VFS_OP(shadow_copy2_rmdir),       SMB_VFS_OP_RMDIR,       SMB_VFS_LAYER_TRANSPARENT},
664
665         /* xattr and flags operations */
666         {SMB_VFS_OP(shadow_copy2_chflags),     SMB_VFS_OP_CHFLAGS,     SMB_VFS_LAYER_TRANSPARENT},
667         {SMB_VFS_OP(shadow_copy2_getxattr),    SMB_VFS_OP_GETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
668         {SMB_VFS_OP(shadow_copy2_lgetxattr),   SMB_VFS_OP_LGETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
669         {SMB_VFS_OP(shadow_copy2_listxattr),   SMB_VFS_OP_LISTXATTR,   SMB_VFS_LAYER_TRANSPARENT},
670         {SMB_VFS_OP(shadow_copy2_removexattr), SMB_VFS_OP_REMOVEXATTR, SMB_VFS_LAYER_TRANSPARENT},
671         {SMB_VFS_OP(shadow_copy2_lremovexattr),SMB_VFS_OP_LREMOVEXATTR,SMB_VFS_LAYER_TRANSPARENT},
672         {SMB_VFS_OP(shadow_copy2_setxattr),    SMB_VFS_OP_SETXATTR,    SMB_VFS_LAYER_TRANSPARENT},
673         {SMB_VFS_OP(shadow_copy2_lsetxattr),   SMB_VFS_OP_LSETXATTR,   SMB_VFS_LAYER_TRANSPARENT},
674
675         /* File operations */
676         {SMB_VFS_OP(shadow_copy2_open),       SMB_VFS_OP_OPEN,     SMB_VFS_LAYER_TRANSPARENT},
677         {SMB_VFS_OP(shadow_copy2_rename),     SMB_VFS_OP_RENAME,   SMB_VFS_LAYER_TRANSPARENT},
678         {SMB_VFS_OP(shadow_copy2_stat),       SMB_VFS_OP_STAT,     SMB_VFS_LAYER_TRANSPARENT},
679         {SMB_VFS_OP(shadow_copy2_lstat),      SMB_VFS_OP_LSTAT,    SMB_VFS_LAYER_TRANSPARENT},
680         {SMB_VFS_OP(shadow_copy2_fstat),      SMB_VFS_OP_FSTAT,    SMB_VFS_LAYER_TRANSPARENT},
681         {SMB_VFS_OP(shadow_copy2_unlink),     SMB_VFS_OP_UNLINK,   SMB_VFS_LAYER_TRANSPARENT},
682         {SMB_VFS_OP(shadow_copy2_chmod),      SMB_VFS_OP_CHMOD,    SMB_VFS_LAYER_TRANSPARENT},
683         {SMB_VFS_OP(shadow_copy2_chown),      SMB_VFS_OP_CHOWN,    SMB_VFS_LAYER_TRANSPARENT},
684         {SMB_VFS_OP(shadow_copy2_chdir),      SMB_VFS_OP_CHDIR,    SMB_VFS_LAYER_TRANSPARENT},
685         {SMB_VFS_OP(shadow_copy2_ntimes),     SMB_VFS_OP_NTIMES,   SMB_VFS_LAYER_TRANSPARENT},
686         {SMB_VFS_OP(shadow_copy2_symlink),    SMB_VFS_OP_SYMLINK,  SMB_VFS_LAYER_TRANSPARENT},
687         {SMB_VFS_OP(shadow_copy2_readlink),   SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT},
688         {SMB_VFS_OP(shadow_copy2_link),       SMB_VFS_OP_LINK,     SMB_VFS_LAYER_TRANSPARENT},
689         {SMB_VFS_OP(shadow_copy2_mknod),      SMB_VFS_OP_MKNOD,    SMB_VFS_LAYER_TRANSPARENT},
690         {SMB_VFS_OP(shadow_copy2_realpath),   SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT},
691         {SMB_VFS_OP(shadow_copy2_connectpath), SMB_VFS_OP_CONNECTPATH, SMB_VFS_LAYER_OPAQUE},
692
693         /* NT File ACL operations */
694         {SMB_VFS_OP(shadow_copy2_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
695
696         /* POSIX ACL operations */
697         {SMB_VFS_OP(shadow_copy2_chmod_acl), SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT},
698
699         /* special shadown copy op */
700         {SMB_VFS_OP(shadow_copy2_get_shadow_copy2_data), 
701          SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
702
703         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
704 };
705
706 NTSTATUS vfs_shadow_copy2_init(void);
707 NTSTATUS vfs_shadow_copy2_init(void)
708 {
709         NTSTATUS ret;
710
711         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2", shadow_copy2_ops);
712
713         if (!NT_STATUS_IS_OK(ret))
714                 return ret;
715
716         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
717         if (vfs_shadow_copy2_debug_level == -1) {
718                 vfs_shadow_copy2_debug_level = DBGC_VFS;
719                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
720                         "vfs_shadow_copy2_init"));
721         } else {
722                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
723                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
724         }
725
726         return ret;
727 }