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