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