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