s3-vfs: include smbd/smbd.h in vfs modules.
[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  * Copyright (C) Ed Plese            2009
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *  
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *  
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include "includes.h"
23 #include "smbd/smbd.h"
24 #include "system/filesys.h"
25
26 /*
27
28   This is a 2nd implemetation of a shadow copy module for exposing
29   snapshots to windows clients as shadow copies. This version has the
30   following features:
31
32      1) you don't need to populate your shares with symlinks to the
33      snapshots. This can be very important when you have thousands of
34      shares, or use [homes]
35
36      2) the inode number of the files is altered so it is different
37      from the original. This allows the 'restore' button to work
38      without a sharing violation
39
40      3) shadow copy results can be sorted before being sent to the
41      client.  This is beneficial for filesystems that don't read
42      directories alphabetically (the default unix).
43
44      4) vanity naming for snapshots. Snapshots can be named in any
45      format compatible with str[fp]time conversions.
46
47      5) time stamps in snapshot names can be represented in localtime
48      rather than UTC.
49
50   Module options:
51
52       shadow:snapdir = <directory where snapshots are kept>
53
54       This is the directory containing the @GMT-* snapshot directories. If it is an absolute
55       path it is used as-is. If it is a relative path, then it is taken relative to the mount
56       point of the filesystem that the root of this share is on
57
58       shadow:basedir = <base directory that snapshots are from>
59
60       This is an optional parameter that specifies the directory that
61       the snapshots are relative to. It defaults to the filesystem
62       mount point
63
64       shadow:fixinodes = yes/no
65
66       If you enable shadow:fixinodes then this module will modify the
67       apparent inode number of files in the snapshot directories using
68       a hash of the files path. This is needed for snapshot systems
69       where the snapshots have the same device:inode number as the
70       original files (such as happens with GPFS snapshots). If you
71       don't set this option then the 'restore' button in the shadow
72       copy UI will fail with a sharing violation.
73
74       shadow:sort = asc/desc, or not specified for unsorted (default)
75
76       This is an optional parameter that specifies that the shadow
77       copy directories should be sorted before sending them to the
78       client.  This can be beneficial as unix filesystems are usually
79       not listed alphabetically sorted.  If enabled, you typically
80       want to specify descending order.
81
82       shadow:format = <format specification for snapshot names>
83
84       This is an optional parameter that specifies the format
85       specification for the naming of snapshots.  The format must
86       be compatible with the conversion specifications recognized
87       by str[fp]time.  The default value is "@GMT-%Y.%m.%d-%H.%M.%S".
88
89       shadow:localtime = yes/no (default is no)
90
91       This is an optional parameter that indicates whether the
92       snapshot names are in UTC/GMT or the local time.
93
94
95   The following command would generate a correctly formatted directory name
96   for use with the default parameters:
97      date -u +@GMT-%Y.%m.%d-%H.%M.%S
98   
99  */
100
101 static int vfs_shadow_copy2_debug_level = DBGC_VFS;
102
103 #undef DBGC_CLASS
104 #define DBGC_CLASS vfs_shadow_copy2_debug_level
105
106 #define GMT_NAME_LEN 24 /* length of a @GMT- name */
107 #define SHADOW_COPY2_GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
108
109 #define SHADOW_COPY2_DEFAULT_SORT NULL
110 #define SHADOW_COPY2_DEFAULT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
111 #define SHADOW_COPY2_DEFAULT_LOCALTIME false
112
113 /*
114   make very sure it is one of our special names 
115  */
116 static inline bool shadow_copy2_match_name(const char *name, const char **gmt_start)
117 {
118         unsigned year, month, day, hr, min, sec;
119         const char *p;
120         if (gmt_start) {
121                 (*gmt_start) = NULL;
122         }
123         p = strstr_m(name, "@GMT-");
124         if (p == NULL) return false;
125         if (p > name && p[-1] != '/') return False;
126         if (sscanf(p, "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", &year, &month,
127                    &day, &hr, &min, &sec) != 6) {
128                 return False;
129         }
130         if (p[24] != 0 && p[24] != '/') {
131                 return False;
132         }
133         if (gmt_start) {
134                 (*gmt_start) = p;
135         }
136         return True;
137 }
138
139 static char *shadow_copy2_snapshot_to_gmt(TALLOC_CTX *mem_ctx,
140                                 vfs_handle_struct *handle, const char *name)
141 {
142         struct tm timestamp;
143         time_t timestamp_t;
144         char gmt[GMT_NAME_LEN + 1];
145         const char *fmt;
146
147         fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
148                                    "format", SHADOW_COPY2_DEFAULT_FORMAT);
149
150         ZERO_STRUCT(timestamp);
151         if (strptime(name, fmt, &timestamp) == NULL) {
152                 DEBUG(10, ("shadow_copy2_snapshot_to_gmt: no match %s: %s\n",
153                            fmt, name));
154                 return NULL;
155         }
156
157         DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n", fmt, name));
158         if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
159                          SHADOW_COPY2_DEFAULT_LOCALTIME))
160         {
161                 timestamp.tm_isdst = -1;
162                 timestamp_t = mktime(&timestamp);
163                 gmtime_r(&timestamp_t, &timestamp);
164         }
165         strftime(gmt, sizeof(gmt), SHADOW_COPY2_GMT_FORMAT, &timestamp);
166
167         return talloc_strdup(mem_ctx, gmt);
168 }
169
170 /*
171   shadow copy paths can also come into the server in this form:
172
173     /foo/bar/@GMT-XXXXX/some/file
174
175   This function normalises the filename to be of the form:
176
177     @GMT-XXXX/foo/bar/some/file
178  */
179 static const char *shadow_copy2_normalise_path(TALLOC_CTX *mem_ctx, const char *path, const char *gmt_start)
180 {
181         char *pcopy;
182         char buf[GMT_NAME_LEN];
183         size_t prefix_len;
184
185         if (path == gmt_start) {
186                 return path;
187         }
188
189         prefix_len = gmt_start - path - 1;
190
191         DEBUG(10, ("path=%s, gmt_start=%s, prefix_len=%d\n", path, gmt_start,
192                    (int)prefix_len));
193
194         /*
195          * We've got a/b/c/@GMT-YYYY.MM.DD-HH.MM.SS/d/e. convert to
196          * @GMT-YYYY.MM.DD-HH.MM.SS/a/b/c/d/e before further
197          * processing. As many VFS calls provide a const char *,
198          * unfortunately we have to make a copy.
199          */
200
201         pcopy = talloc_strdup(talloc_tos(), path);
202         if (pcopy == NULL) {
203                 return NULL;
204         }
205
206         gmt_start = pcopy + prefix_len;
207
208         /*
209          * Copy away "@GMT-YYYY.MM.DD-HH.MM.SS"
210          */
211         memcpy(buf, gmt_start+1, GMT_NAME_LEN);
212
213         /*
214          * Make space for it including a trailing /
215          */
216         memmove(pcopy + GMT_NAME_LEN + 1, pcopy, prefix_len);
217
218         /*
219          * Move in "@GMT-YYYY.MM.DD-HH.MM.SS/" at the beginning again
220          */
221         memcpy(pcopy, buf, GMT_NAME_LEN);
222         pcopy[GMT_NAME_LEN] = '/';
223
224         DEBUG(10, ("shadow_copy2_normalise_path: %s -> %s\n", path, pcopy));
225
226         return pcopy;
227 }
228
229 /*
230   convert a name to the shadow directory
231  */
232
233 #define _SHADOW2_NEXT(op, args, rtype, eret, extra) do { \
234         const char *name = fname; \
235         const char *gmt_start; \
236         if (shadow_copy2_match_name(fname, &gmt_start)) {       \
237                 char *name2; \
238                 rtype ret; \
239                 name2 = convert_shadow2_name(handle, fname, gmt_start); \
240                 if (name2 == NULL) { \
241                         errno = EINVAL; \
242                         return eret; \
243                 } \
244                 name = name2; \
245                 ret = SMB_VFS_NEXT_ ## op args; \
246                 talloc_free(name2); \
247                 if (ret != eret) extra; \
248                 return ret; \
249         } else { \
250                 return SMB_VFS_NEXT_ ## op args; \
251         } \
252 } while (0)
253
254 #define _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, extra) do { \
255         const char *gmt_start; \
256         if (shadow_copy2_match_name(smb_fname->base_name, &gmt_start)) { \
257                 char *name2; \
258                 char *smb_base_name_tmp = NULL; \
259                 rtype ret; \
260                 name2 = convert_shadow2_name(handle, smb_fname->base_name, gmt_start); \
261                 if (name2 == NULL) { \
262                         errno = EINVAL; \
263                         return eret; \
264                 } \
265                 smb_base_name_tmp = smb_fname->base_name; \
266                 smb_fname->base_name = name2; \
267                 ret = SMB_VFS_NEXT_ ## op args; \
268                 smb_fname->base_name = smb_base_name_tmp; \
269                 talloc_free(name2); \
270                 if (ret != eret) extra; \
271                 return ret; \
272         } else { \
273                 return SMB_VFS_NEXT_ ## op args; \
274         } \
275 } while (0)
276
277 /*
278   convert a name to the shadow directory: NTSTATUS-specific handling
279  */
280
281 #define _SHADOW2_NTSTATUS_NEXT(op, args, eret, extra) do { \
282         const char *name = fname; \
283         const char *gmt_start; \
284         if (shadow_copy2_match_name(fname, &gmt_start)) {       \
285                 char *name2; \
286                 NTSTATUS ret; \
287                 name2 = convert_shadow2_name(handle, fname, gmt_start); \
288                 if (name2 == NULL) { \
289                         errno = EINVAL; \
290                         return eret; \
291                 } \
292                 name = name2; \
293                 ret = SMB_VFS_NEXT_ ## op args; \
294                 talloc_free(name2); \
295                 if (!NT_STATUS_EQUAL(ret, eret)) extra; \
296                 return ret; \
297         } else { \
298                 return SMB_VFS_NEXT_ ## op args; \
299         } \
300 } while (0)
301
302 #define SHADOW2_NTSTATUS_NEXT(op, args, eret) _SHADOW2_NTSTATUS_NEXT(op, args, eret, )
303
304 #define SHADOW2_NEXT(op, args, rtype, eret) _SHADOW2_NEXT(op, args, rtype, eret, )
305
306 #define SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret) _SHADOW2_NEXT_SMB_FNAME(op, args, rtype, eret, )
307
308 #define SHADOW2_NEXT2(op, args) do { \
309         const char *gmt_start1, *gmt_start2; \
310         if (shadow_copy2_match_name(oldname, &gmt_start1) || \
311             shadow_copy2_match_name(newname, &gmt_start2)) {    \
312                 errno = EROFS; \
313                 return -1; \
314         } else { \
315                 return SMB_VFS_NEXT_ ## op args; \
316         } \
317 } while (0)
318
319 #define SHADOW2_NEXT2_SMB_FNAME(op, args) do { \
320         const char *gmt_start1, *gmt_start2; \
321         if (shadow_copy2_match_name(smb_fname_src->base_name, &gmt_start1) || \
322             shadow_copy2_match_name(smb_fname_dst->base_name, &gmt_start2)) { \
323                 errno = EROFS; \
324                 return -1; \
325         } else { \
326                 return SMB_VFS_NEXT_ ## op args; \
327         } \
328 } while (0)
329
330
331 /*
332   find the mount point of a filesystem
333  */
334 static char *find_mount_point(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
335 {
336         char *path = talloc_strdup(mem_ctx, handle->conn->connectpath);
337         dev_t dev;
338         struct stat st;
339         char *p;
340
341         if (stat(path, &st) != 0) {
342                 talloc_free(path);
343                 return NULL;
344         }
345
346         dev = st.st_dev;
347
348         while ((p = strrchr(path, '/')) && p > path) {
349                 *p = 0;
350                 if (stat(path, &st) != 0) {
351                         talloc_free(path);
352                         return NULL;
353                 }
354                 if (st.st_dev != dev) {
355                         *p = '/';
356                         break;
357                 }
358         }
359
360         return path;    
361 }
362
363 /*
364   work out the location of the snapshot for this share
365  */
366 static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
367 {
368         const char *snapdir;
369         char *mount_point;
370         const char *ret;
371
372         snapdir = lp_parm_const_string(SNUM(handle->conn), "shadow", "snapdir", NULL);
373         if (snapdir == NULL) {
374                 return NULL;
375         }
376         /* if its an absolute path, we're done */
377         if (*snapdir == '/') {
378                 return snapdir;
379         }
380
381         /* other its relative to the filesystem mount point */
382         mount_point = find_mount_point(mem_ctx, handle);
383         if (mount_point == NULL) {
384                 return NULL;
385         }
386
387         ret = talloc_asprintf(mem_ctx, "%s/%s", mount_point, snapdir);
388         talloc_free(mount_point);
389         return ret;
390 }
391
392 /*
393   work out the location of the base directory for snapshots of this share
394  */
395 static const char *shadow_copy2_find_basedir(TALLOC_CTX *mem_ctx, vfs_handle_struct *handle)
396 {
397         const char *basedir = lp_parm_const_string(SNUM(handle->conn), "shadow", "basedir", NULL);
398
399         /* other its the filesystem mount point */
400         if (basedir == NULL) {
401                 basedir = find_mount_point(mem_ctx, handle);
402         }
403
404         return basedir;
405 }
406
407 /*
408   convert a filename from a share relative path, to a path in the
409   snapshot directory
410  */
411 static char *convert_shadow2_name(vfs_handle_struct *handle, const char *fname, const char *gmt_path)
412 {
413         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
414         const char *snapdir, *relpath, *baseoffset, *basedir;
415         size_t baselen;
416         char *ret, *prefix;
417
418         struct tm timestamp;
419         time_t timestamp_t;
420         char snapshot[MAXPATHLEN];
421         const char *fmt;
422
423         fmt = lp_parm_const_string(SNUM(handle->conn), "shadow",
424                                    "format", SHADOW_COPY2_DEFAULT_FORMAT);
425
426         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
427         if (snapdir == NULL) {
428                 DEBUG(2,("no snapdir found for share at %s\n", handle->conn->connectpath));
429                 talloc_free(tmp_ctx);
430                 return NULL;
431         }
432
433         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
434         if (basedir == NULL) {
435                 DEBUG(2,("no basedir found for share at %s\n", handle->conn->connectpath));
436                 talloc_free(tmp_ctx);
437                 return NULL;
438         }
439
440         prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
441         if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
442                 /* this looks like as we have already normalized it, leave it untouched*/
443                 talloc_free(tmp_ctx);
444                 return talloc_strdup(handle->data, fname);
445         }
446
447         if (strncmp(fname, "@GMT-", 5) != 0) {
448                 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
449                 if (fname == NULL) {
450                         talloc_free(tmp_ctx);
451                         return NULL;
452                 }
453         }
454
455         ZERO_STRUCT(timestamp);
456         relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
457         if (relpath == NULL) {
458                 talloc_free(tmp_ctx);
459                 return NULL;
460         }
461
462         /* relpath is the remaining portion of the path after the @GMT-xxx */
463
464         if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
465                          SHADOW_COPY2_DEFAULT_LOCALTIME))
466         {
467                 timestamp_t = timegm(&timestamp);
468                 localtime_r(&timestamp_t, &timestamp);
469         }
470
471         strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
472
473         baselen = strlen(basedir);
474         baseoffset = handle->conn->connectpath + baselen;
475
476         /* some sanity checks */
477         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
478             (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
479                 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
480                          basedir, handle->conn->connectpath));
481                 talloc_free(tmp_ctx);
482                 return NULL;
483         }
484
485         if (*relpath == '/') relpath++;
486         if (*baseoffset == '/') baseoffset++;
487
488         ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
489                               snapdir, 
490                               snapshot,
491                               baseoffset, 
492                               relpath);
493         DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
494         talloc_free(tmp_ctx);
495         return ret;
496 }
497
498
499 /*
500   simple string hash
501  */
502 static uint32 string_hash(const char *s)
503 {
504         uint32 n = 0;
505         while (*s) {
506                 n = ((n << 5) + n) ^ (uint32)(*s++);
507         }
508         return n;
509 }
510
511 /*
512   modify a sbuf return to ensure that inodes in the shadow directory
513   are different from those in the main directory
514  */
515 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
516 {
517         if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
518                 /* some snapshot systems, like GPFS, return the name
519                    device:inode for the snapshot files as the current
520                    files. That breaks the 'restore' button in the shadow copy
521                    GUI, as the client gets a sharing violation.
522
523                    This is a crude way of allowing both files to be
524                    open at once. It has a slight chance of inode
525                    number collision, but I can't see a better approach
526                    without significant VFS changes
527                 */
528                 uint32_t shash = string_hash(fname) & 0xFF000000;
529                 if (shash == 0) {
530                         shash = 1;
531                 }
532                 sbuf->st_ex_ino ^= shash;
533         }
534 }
535
536 static int shadow_copy2_rename(vfs_handle_struct *handle,
537                                const struct smb_filename *smb_fname_src,
538                                const struct smb_filename *smb_fname_dst)
539 {
540         if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
541                 errno = EXDEV;
542                 return -1;
543         }
544         SHADOW2_NEXT2_SMB_FNAME(RENAME,
545                                 (handle, smb_fname_src, smb_fname_dst));
546 }
547
548 static int shadow_copy2_symlink(vfs_handle_struct *handle,
549                                 const char *oldname, const char *newname)
550 {
551         SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
552 }
553
554 static int shadow_copy2_link(vfs_handle_struct *handle,
555                           const char *oldname, const char *newname)
556 {
557         SHADOW2_NEXT2(LINK, (handle, oldname, newname));
558 }
559
560 static int shadow_copy2_open(vfs_handle_struct *handle,
561                              struct smb_filename *smb_fname, files_struct *fsp,
562                              int flags, mode_t mode)
563 {
564         SHADOW2_NEXT_SMB_FNAME(OPEN,
565                                (handle, smb_fname, fsp, flags, mode),
566                                int, -1);
567 }
568
569 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
570                           const char *fname, const char *mask, uint32 attr)
571 {
572         SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
573 }
574
575 static int shadow_copy2_stat(vfs_handle_struct *handle,
576                              struct smb_filename *smb_fname)
577 {
578         _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
579                                 convert_sbuf(handle, smb_fname->base_name,
580                                              &smb_fname->st));
581 }
582
583 static int shadow_copy2_lstat(vfs_handle_struct *handle,
584                               struct smb_filename *smb_fname)
585 {
586         _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
587                                 convert_sbuf(handle, smb_fname->base_name,
588                                              &smb_fname->st));
589 }
590
591 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
592 {
593         int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
594         if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
595                 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
596         }
597         return ret;
598 }
599
600 static int shadow_copy2_unlink(vfs_handle_struct *handle,
601                                const struct smb_filename *smb_fname_in)
602 {
603         struct smb_filename *smb_fname = NULL;
604         NTSTATUS status;
605
606         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
607         if (!NT_STATUS_IS_OK(status)) {
608                 errno = map_errno_from_nt_status(status);
609                 return -1;
610         }
611
612         SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
613 }
614
615 static int shadow_copy2_chmod(vfs_handle_struct *handle,
616                        const char *fname, mode_t mode)
617 {
618         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
619 }
620
621 static int shadow_copy2_chown(vfs_handle_struct *handle,
622                        const char *fname, uid_t uid, gid_t gid)
623 {
624         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
625 }
626
627 static int shadow_copy2_chdir(vfs_handle_struct *handle,
628                        const char *fname)
629 {
630         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
631 }
632
633 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
634                                const struct smb_filename *smb_fname_in,
635                                struct smb_file_time *ft)
636 {
637         struct smb_filename *smb_fname = NULL;
638         NTSTATUS status;
639
640         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
641         if (!NT_STATUS_IS_OK(status)) {
642                 errno = map_errno_from_nt_status(status);
643                 return -1;
644         }
645
646         SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
647 }
648
649 static int shadow_copy2_readlink(vfs_handle_struct *handle,
650                                  const char *fname, char *buf, size_t bufsiz)
651 {
652         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
653 }
654
655 static int shadow_copy2_mknod(vfs_handle_struct *handle,
656                        const char *fname, mode_t mode, SMB_DEV_T dev)
657 {
658         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
659 }
660
661 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
662                             const char *fname)
663 {
664         const char *gmt;
665
666         if (shadow_copy2_match_name(fname, &gmt)
667             && (gmt[GMT_NAME_LEN] == '\0')) {
668                 char *copy;
669
670                 copy = talloc_strdup(talloc_tos(), fname);
671                 if (copy == NULL) {
672                         errno = ENOMEM;
673                         return NULL;
674                 }
675
676                 copy[gmt - fname] = '.';
677                 copy[gmt - fname + 1] = '\0';
678
679                 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
680                 SHADOW2_NEXT(REALPATH, (handle, name), char *,
681                              NULL);
682         }
683         SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
684 }
685
686 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
687                                             const char *fname)
688 {
689         TALLOC_CTX *tmp_ctx;
690         const char *snapdir, *baseoffset, *basedir, *gmt_start;
691         size_t baselen;
692         char *ret;
693
694         DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
695
696         if (!shadow_copy2_match_name(fname, &gmt_start)) {
697                 return handle->conn->connectpath;
698         }
699
700         /*
701          * We have to create a real temporary context because we have
702          * to put our result on talloc_tos(). Thus we can't use a
703          * talloc_stackframe() here.
704          */
705         tmp_ctx = talloc_new(talloc_tos());
706
707         fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
708         if (fname == NULL) {
709                 TALLOC_FREE(tmp_ctx);
710                 return NULL;
711         }
712
713         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
714         if (snapdir == NULL) {
715                 DEBUG(2,("no snapdir found for share at %s\n",
716                          handle->conn->connectpath));
717                 TALLOC_FREE(tmp_ctx);
718                 return NULL;
719         }
720
721         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
722         if (basedir == NULL) {
723                 DEBUG(2,("no basedir found for share at %s\n",
724                          handle->conn->connectpath));
725                 TALLOC_FREE(tmp_ctx);
726                 return NULL;
727         }
728
729         baselen = strlen(basedir);
730         baseoffset = handle->conn->connectpath + baselen;
731
732         /* some sanity checks */
733         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
734             (handle->conn->connectpath[baselen] != 0
735              && handle->conn->connectpath[baselen] != '/')) {
736                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
737                          "parent of %s\n", basedir,
738                          handle->conn->connectpath));
739                 TALLOC_FREE(tmp_ctx);
740                 return NULL;
741         }
742
743         if (*baseoffset == '/') baseoffset++;
744
745         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
746                               snapdir,
747                               GMT_NAME_LEN, fname,
748                               baseoffset);
749         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
750         TALLOC_FREE(tmp_ctx);
751         return ret;
752 }
753
754 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
755                                const char *fname, uint32 security_info,
756                                struct security_descriptor **ppdesc)
757 {
758         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
759 }
760
761 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
762 {
763         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
764 }
765
766 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
767 {
768         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
769 }
770
771 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
772                                 unsigned int flags)
773 {
774         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
775 }
776
777 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
778                                   const char *fname, const char *aname, void *value, size_t size)
779 {
780         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
781 }
782
783 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
784                                       const char *fname, const char *aname, void *value, size_t size)
785 {
786         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
787 }
788
789 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
790                                       char *list, size_t size)
791 {
792         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
793 }
794
795 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
796                                     const char *aname)
797 {
798         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
799 }
800
801 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
802                                      const char *aname)
803 {
804         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
805 }
806
807 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
808                                  const char *aname, const void *value, size_t size, int flags)
809 {
810         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
811 }
812
813 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
814                                   const char *aname, const void *value, size_t size, int flags)
815 {
816         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
817 }
818
819 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
820                            const char *fname, mode_t mode)
821 {
822         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
823 }
824
825 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
826 {
827         return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
828 }
829
830 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
831 {
832         return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
833 }
834
835 /*
836   sort the shadow copy data in ascending or descending order
837  */
838 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
839                                    SHADOW_COPY_DATA *shadow_copy2_data)
840 {
841         int (*cmpfunc)(const void *, const void *);
842         const char *sort;
843
844         sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
845                                     "sort", SHADOW_COPY2_DEFAULT_SORT);
846         if (sort == NULL) {
847                 return;
848         }
849
850         if (strcmp(sort, "asc") == 0) {
851                 cmpfunc = shadow_copy2_label_cmp_asc;
852         } else if (strcmp(sort, "desc") == 0) {
853                 cmpfunc = shadow_copy2_label_cmp_desc;
854         } else {
855                 return;
856         }
857
858         if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
859             shadow_copy2_data->labels)
860         {
861                 TYPESAFE_QSORT(shadow_copy2_data->labels,
862                                shadow_copy2_data->num_volumes,
863                                cmpfunc);
864         }
865
866         return;
867 }
868
869 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
870                                               files_struct *fsp, 
871                                               SHADOW_COPY_DATA *shadow_copy2_data, 
872                                               bool labels)
873 {
874         SMB_STRUCT_DIR *p;
875         const char *snapdir;
876         SMB_STRUCT_DIRENT *d;
877         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
878         char *snapshot;
879
880         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
881         if (snapdir == NULL) {
882                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
883                          handle->conn->connectpath));
884                 errno = EINVAL;
885                 talloc_free(tmp_ctx);
886                 return -1;
887         }
888
889         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
890
891         if (!p) {
892                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
893                          " - %s\n", snapdir, strerror(errno)));
894                 talloc_free(tmp_ctx);
895                 errno = ENOSYS;
896                 return -1;
897         }
898
899         shadow_copy2_data->num_volumes = 0;
900         shadow_copy2_data->labels      = NULL;
901
902         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
903                 SHADOW_COPY_LABEL *tlabels;
904
905                 /* ignore names not of the right form in the snapshot directory */
906                 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
907                                                         d->d_name);
908                 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
909                          d->d_name, snapshot));
910                 if (!snapshot) {
911                         continue;
912                 }
913
914                 if (!labels) {
915                         /* the caller doesn't want the labels */
916                         shadow_copy2_data->num_volumes++;
917                         continue;
918                 }
919
920                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
921                                          shadow_copy2_data->labels,
922                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
923                 if (tlabels == NULL) {
924                         DEBUG(0,("shadow_copy2: out of memory\n"));
925                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
926                         talloc_free(tmp_ctx);
927                         return -1;
928                 }
929
930                 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
931                         sizeof(*tlabels));
932                 talloc_free(snapshot);
933
934                 shadow_copy2_data->num_volumes++;
935                 shadow_copy2_data->labels = tlabels;
936         }
937
938         SMB_VFS_NEXT_CLOSEDIR(handle,p);
939
940         shadow_copy2_sort_data(handle, shadow_copy2_data);
941
942         talloc_free(tmp_ctx);
943         return 0;
944 }
945
946 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
947         .opendir = shadow_copy2_opendir,
948         .mkdir = shadow_copy2_mkdir,
949         .rmdir = shadow_copy2_rmdir,
950         .chflags = shadow_copy2_chflags,
951         .getxattr = shadow_copy2_getxattr,
952         .lgetxattr = shadow_copy2_lgetxattr,
953         .listxattr = shadow_copy2_listxattr,
954         .removexattr = shadow_copy2_removexattr,
955         .lremovexattr = shadow_copy2_lremovexattr,
956         .setxattr = shadow_copy2_setxattr,
957         .lsetxattr = shadow_copy2_lsetxattr,
958         .open = shadow_copy2_open,
959         .rename = shadow_copy2_rename,
960         .stat = shadow_copy2_stat,
961         .lstat = shadow_copy2_lstat,
962         .fstat = shadow_copy2_fstat,
963         .unlink = shadow_copy2_unlink,
964         .chmod = shadow_copy2_chmod,
965         .chown = shadow_copy2_chown,
966         .chdir = shadow_copy2_chdir,
967         .ntimes = shadow_copy2_ntimes,
968         .symlink = shadow_copy2_symlink,
969         .vfs_readlink = shadow_copy2_readlink,
970         .link = shadow_copy2_link,
971         .mknod = shadow_copy2_mknod,
972         .realpath = shadow_copy2_realpath,
973         .connectpath = shadow_copy2_connectpath,
974         .get_nt_acl = shadow_copy2_get_nt_acl,
975         .chmod_acl = shadow_copy2_chmod_acl,
976         .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
977 };
978
979 NTSTATUS vfs_shadow_copy2_init(void);
980 NTSTATUS vfs_shadow_copy2_init(void)
981 {
982         NTSTATUS ret;
983
984         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
985                                &vfs_shadow_copy2_fns);
986
987         if (!NT_STATUS_IS_OK(ret))
988                 return ret;
989
990         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
991         if (vfs_shadow_copy2_debug_level == -1) {
992                 vfs_shadow_copy2_debug_level = DBGC_VFS;
993                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
994                         "vfs_shadow_copy2_init"));
995         } else {
996                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
997                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
998         }
999
1000         return ret;
1001 }