e3c3f9f3a06b8de3a85fcb8336e3badc0b205a98
[samba.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, *prefix;
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         prefix = talloc_asprintf(tmp_ctx, "%s/@GMT-", snapdir);
439         if (strncmp(fname, prefix, (talloc_get_size(prefix)-1)) == 0) {
440                 /* this looks like as we have already normalized it, leave it untouched*/
441                 talloc_free(tmp_ctx);
442                 return talloc_strdup(handle->data, fname);
443         }
444
445         if (strncmp(fname, "@GMT-", 5) != 0) {
446                 fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_path);
447                 if (fname == NULL) {
448                         talloc_free(tmp_ctx);
449                         return NULL;
450                 }
451         }
452
453         ZERO_STRUCT(timestamp);
454         relpath = strptime(fname, SHADOW_COPY2_GMT_FORMAT, &timestamp);
455         if (relpath == NULL) {
456                 talloc_free(tmp_ctx);
457                 return NULL;
458         }
459
460         /* relpath is the remaining portion of the path after the @GMT-xxx */
461
462         if (lp_parm_bool(SNUM(handle->conn), "shadow", "localtime",
463                          SHADOW_COPY2_DEFAULT_LOCALTIME))
464         {
465                 timestamp_t = timegm(&timestamp);
466                 localtime_r(&timestamp_t, &timestamp);
467         }
468
469         strftime(snapshot, MAXPATHLEN, fmt, &timestamp);
470
471         baselen = strlen(basedir);
472         baseoffset = handle->conn->connectpath + baselen;
473
474         /* some sanity checks */
475         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
476             (handle->conn->connectpath[baselen] != 0 && handle->conn->connectpath[baselen] != '/')) {
477                 DEBUG(0,("convert_shadow2_name: basedir %s is not a parent of %s\n",
478                          basedir, handle->conn->connectpath));
479                 talloc_free(tmp_ctx);
480                 return NULL;
481         }
482
483         if (*relpath == '/') relpath++;
484         if (*baseoffset == '/') baseoffset++;
485
486         ret = talloc_asprintf(handle->data, "%s/%s/%s/%s",
487                               snapdir, 
488                               snapshot,
489                               baseoffset, 
490                               relpath);
491         DEBUG(6,("convert_shadow2_name: '%s' -> '%s'\n", fname, ret));
492         talloc_free(tmp_ctx);
493         return ret;
494 }
495
496
497 /*
498   simple string hash
499  */
500 static uint32 string_hash(const char *s)
501 {
502         uint32 n = 0;
503         while (*s) {
504                 n = ((n << 5) + n) ^ (uint32)(*s++);
505         }
506         return n;
507 }
508
509 /*
510   modify a sbuf return to ensure that inodes in the shadow directory
511   are different from those in the main directory
512  */
513 static void convert_sbuf(vfs_handle_struct *handle, const char *fname, SMB_STRUCT_STAT *sbuf)
514 {
515         if (lp_parm_bool(SNUM(handle->conn), "shadow", "fixinodes", False)) {           
516                 /* some snapshot systems, like GPFS, return the name
517                    device:inode for the snapshot files as the current
518                    files. That breaks the 'restore' button in the shadow copy
519                    GUI, as the client gets a sharing violation.
520
521                    This is a crude way of allowing both files to be
522                    open at once. It has a slight chance of inode
523                    number collision, but I can't see a better approach
524                    without significant VFS changes
525                 */
526                 uint32_t shash = string_hash(fname) & 0xFF000000;
527                 if (shash == 0) {
528                         shash = 1;
529                 }
530                 sbuf->st_ex_ino ^= shash;
531         }
532 }
533
534 static int shadow_copy2_rename(vfs_handle_struct *handle,
535                                const struct smb_filename *smb_fname_src,
536                                const struct smb_filename *smb_fname_dst)
537 {
538         if (shadow_copy2_match_name(smb_fname_src->base_name, NULL)) {
539                 errno = EXDEV;
540                 return -1;
541         }
542         SHADOW2_NEXT2_SMB_FNAME(RENAME,
543                                 (handle, smb_fname_src, smb_fname_dst));
544 }
545
546 static int shadow_copy2_symlink(vfs_handle_struct *handle,
547                                 const char *oldname, const char *newname)
548 {
549         SHADOW2_NEXT2(SYMLINK, (handle, oldname, newname));
550 }
551
552 static int shadow_copy2_link(vfs_handle_struct *handle,
553                           const char *oldname, const char *newname)
554 {
555         SHADOW2_NEXT2(LINK, (handle, oldname, newname));
556 }
557
558 static int shadow_copy2_open(vfs_handle_struct *handle,
559                              struct smb_filename *smb_fname, files_struct *fsp,
560                              int flags, mode_t mode)
561 {
562         SHADOW2_NEXT_SMB_FNAME(OPEN,
563                                (handle, smb_fname, fsp, flags, mode),
564                                int, -1);
565 }
566
567 static SMB_STRUCT_DIR *shadow_copy2_opendir(vfs_handle_struct *handle,
568                           const char *fname, const char *mask, uint32 attr)
569 {
570         SHADOW2_NEXT(OPENDIR, (handle, name, mask, attr), SMB_STRUCT_DIR *, NULL);
571 }
572
573 static int shadow_copy2_stat(vfs_handle_struct *handle,
574                              struct smb_filename *smb_fname)
575 {
576         _SHADOW2_NEXT_SMB_FNAME(STAT, (handle, smb_fname), int, -1,
577                                 convert_sbuf(handle, smb_fname->base_name,
578                                              &smb_fname->st));
579 }
580
581 static int shadow_copy2_lstat(vfs_handle_struct *handle,
582                               struct smb_filename *smb_fname)
583 {
584         _SHADOW2_NEXT_SMB_FNAME(LSTAT, (handle, smb_fname), int, -1,
585                                 convert_sbuf(handle, smb_fname->base_name,
586                                              &smb_fname->st));
587 }
588
589 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
590 {
591         int ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
592         if (ret == 0 && shadow_copy2_match_name(fsp->fsp_name->base_name, NULL)) {
593                 convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
594         }
595         return ret;
596 }
597
598 static int shadow_copy2_unlink(vfs_handle_struct *handle,
599                                const struct smb_filename *smb_fname_in)
600 {
601         struct smb_filename *smb_fname = NULL;
602         NTSTATUS status;
603
604         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
605         if (!NT_STATUS_IS_OK(status)) {
606                 errno = map_errno_from_nt_status(status);
607                 return -1;
608         }
609
610         SHADOW2_NEXT_SMB_FNAME(UNLINK, (handle, smb_fname), int, -1);
611 }
612
613 static int shadow_copy2_chmod(vfs_handle_struct *handle,
614                        const char *fname, mode_t mode)
615 {
616         SHADOW2_NEXT(CHMOD, (handle, name, mode), int, -1);
617 }
618
619 static int shadow_copy2_chown(vfs_handle_struct *handle,
620                        const char *fname, uid_t uid, gid_t gid)
621 {
622         SHADOW2_NEXT(CHOWN, (handle, name, uid, gid), int, -1);
623 }
624
625 static int shadow_copy2_chdir(vfs_handle_struct *handle,
626                        const char *fname)
627 {
628         SHADOW2_NEXT(CHDIR, (handle, name), int, -1);
629 }
630
631 static int shadow_copy2_ntimes(vfs_handle_struct *handle,
632                                const struct smb_filename *smb_fname_in,
633                                struct smb_file_time *ft)
634 {
635         struct smb_filename *smb_fname = NULL;
636         NTSTATUS status;
637
638         status = copy_smb_filename(talloc_tos(), smb_fname_in, &smb_fname);
639         if (!NT_STATUS_IS_OK(status)) {
640                 errno = map_errno_from_nt_status(status);
641                 return -1;
642         }
643
644         SHADOW2_NEXT_SMB_FNAME(NTIMES, (handle, smb_fname, ft), int, -1);
645 }
646
647 static int shadow_copy2_readlink(vfs_handle_struct *handle,
648                                  const char *fname, char *buf, size_t bufsiz)
649 {
650         SHADOW2_NEXT(READLINK, (handle, name, buf, bufsiz), int, -1);
651 }
652
653 static int shadow_copy2_mknod(vfs_handle_struct *handle,
654                        const char *fname, mode_t mode, SMB_DEV_T dev)
655 {
656         SHADOW2_NEXT(MKNOD, (handle, name, mode, dev), int, -1);
657 }
658
659 static char *shadow_copy2_realpath(vfs_handle_struct *handle,
660                             const char *fname)
661 {
662         const char *gmt;
663
664         if (shadow_copy2_match_name(fname, &gmt)
665             && (gmt[GMT_NAME_LEN] == '\0')) {
666                 char *copy;
667
668                 copy = talloc_strdup(talloc_tos(), fname);
669                 if (copy == NULL) {
670                         errno = ENOMEM;
671                         return NULL;
672                 }
673
674                 copy[gmt - fname] = '.';
675                 copy[gmt - fname + 1] = '\0';
676
677                 DEBUG(10, ("calling NEXT_REALPATH with %s\n", copy));
678                 SHADOW2_NEXT(REALPATH, (handle, name), char *,
679                              NULL);
680         }
681         SHADOW2_NEXT(REALPATH, (handle, name), char *, NULL);
682 }
683
684 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
685                                             const char *fname)
686 {
687         TALLOC_CTX *tmp_ctx;
688         const char *snapdir, *baseoffset, *basedir, *gmt_start;
689         size_t baselen;
690         char *ret;
691
692         DEBUG(10, ("shadow_copy2_connectpath called with %s\n", fname));
693
694         if (!shadow_copy2_match_name(fname, &gmt_start)) {
695                 return handle->conn->connectpath;
696         }
697
698         /*
699          * We have to create a real temporary context because we have
700          * to put our result on talloc_tos(). Thus we can't use a
701          * talloc_stackframe() here.
702          */
703         tmp_ctx = talloc_new(talloc_tos());
704
705         fname = shadow_copy2_normalise_path(tmp_ctx, fname, gmt_start);
706         if (fname == NULL) {
707                 TALLOC_FREE(tmp_ctx);
708                 return NULL;
709         }
710
711         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
712         if (snapdir == NULL) {
713                 DEBUG(2,("no snapdir found for share at %s\n",
714                          handle->conn->connectpath));
715                 TALLOC_FREE(tmp_ctx);
716                 return NULL;
717         }
718
719         basedir = shadow_copy2_find_basedir(tmp_ctx, handle);
720         if (basedir == NULL) {
721                 DEBUG(2,("no basedir found for share at %s\n",
722                          handle->conn->connectpath));
723                 TALLOC_FREE(tmp_ctx);
724                 return NULL;
725         }
726
727         baselen = strlen(basedir);
728         baseoffset = handle->conn->connectpath + baselen;
729
730         /* some sanity checks */
731         if (strncmp(basedir, handle->conn->connectpath, baselen) != 0 ||
732             (handle->conn->connectpath[baselen] != 0
733              && handle->conn->connectpath[baselen] != '/')) {
734                 DEBUG(0,("shadow_copy2_connectpath: basedir %s is not a "
735                          "parent of %s\n", basedir,
736                          handle->conn->connectpath));
737                 TALLOC_FREE(tmp_ctx);
738                 return NULL;
739         }
740
741         if (*baseoffset == '/') baseoffset++;
742
743         ret = talloc_asprintf(talloc_tos(), "%s/%.*s/%s",
744                               snapdir,
745                               GMT_NAME_LEN, fname,
746                               baseoffset);
747         DEBUG(6,("shadow_copy2_connectpath: '%s' -> '%s'\n", fname, ret));
748         TALLOC_FREE(tmp_ctx);
749         return ret;
750 }
751
752 static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
753                                const char *fname, uint32 security_info,
754                                struct security_descriptor **ppdesc)
755 {
756         SHADOW2_NTSTATUS_NEXT(GET_NT_ACL, (handle, name, security_info, ppdesc), NT_STATUS_ACCESS_DENIED);
757 }
758
759 static int shadow_copy2_mkdir(vfs_handle_struct *handle,  const char *fname, mode_t mode)
760 {
761         SHADOW2_NEXT(MKDIR, (handle, name, mode), int, -1);
762 }
763
764 static int shadow_copy2_rmdir(vfs_handle_struct *handle,  const char *fname)
765 {
766         SHADOW2_NEXT(RMDIR, (handle, name), int, -1);
767 }
768
769 static int shadow_copy2_chflags(vfs_handle_struct *handle, const char *fname,
770                                 unsigned int flags)
771 {
772         SHADOW2_NEXT(CHFLAGS, (handle, name, flags), int, -1);
773 }
774
775 static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
776                                   const char *fname, const char *aname, void *value, size_t size)
777 {
778         SHADOW2_NEXT(GETXATTR, (handle, name, aname, value, size), ssize_t, -1);
779 }
780
781 static ssize_t shadow_copy2_lgetxattr(vfs_handle_struct *handle,
782                                       const char *fname, const char *aname, void *value, size_t size)
783 {
784         SHADOW2_NEXT(LGETXATTR, (handle, name, aname, value, size), ssize_t, -1);
785 }
786
787 static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle, const char *fname, 
788                                       char *list, size_t size)
789 {
790         SHADOW2_NEXT(LISTXATTR, (handle, name, list, size), ssize_t, -1);
791 }
792
793 static int shadow_copy2_removexattr(struct vfs_handle_struct *handle, const char *fname, 
794                                     const char *aname)
795 {
796         SHADOW2_NEXT(REMOVEXATTR, (handle, name, aname), int, -1);
797 }
798
799 static int shadow_copy2_lremovexattr(struct vfs_handle_struct *handle, const char *fname, 
800                                      const char *aname)
801 {
802         SHADOW2_NEXT(LREMOVEXATTR, (handle, name, aname), int, -1);
803 }
804
805 static int shadow_copy2_setxattr(struct vfs_handle_struct *handle, const char *fname, 
806                                  const char *aname, const void *value, size_t size, int flags)
807 {
808         SHADOW2_NEXT(SETXATTR, (handle, name, aname, value, size, flags), int, -1);
809 }
810
811 static int shadow_copy2_lsetxattr(struct vfs_handle_struct *handle, const char *fname, 
812                                   const char *aname, const void *value, size_t size, int flags)
813 {
814         SHADOW2_NEXT(LSETXATTR, (handle, name, aname, value, size, flags), int, -1);
815 }
816
817 static int shadow_copy2_chmod_acl(vfs_handle_struct *handle,
818                            const char *fname, mode_t mode)
819 {
820         SHADOW2_NEXT(CHMOD_ACL, (handle, name, mode), int, -1);
821 }
822
823 static int shadow_copy2_label_cmp_asc(const void *x, const void *y)
824 {
825         return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
826 }
827
828 static int shadow_copy2_label_cmp_desc(const void *x, const void *y)
829 {
830         return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
831 }
832
833 /*
834   sort the shadow copy data in ascending or descending order
835  */
836 static void shadow_copy2_sort_data(vfs_handle_struct *handle,
837                                    SHADOW_COPY_DATA *shadow_copy2_data)
838 {
839         int (*cmpfunc)(const void *, const void *);
840         const char *sort;
841
842         sort = lp_parm_const_string(SNUM(handle->conn), "shadow",
843                                     "sort", SHADOW_COPY2_DEFAULT_SORT);
844         if (sort == NULL) {
845                 return;
846         }
847
848         if (strcmp(sort, "asc") == 0) {
849                 cmpfunc = shadow_copy2_label_cmp_asc;
850         } else if (strcmp(sort, "desc") == 0) {
851                 cmpfunc = shadow_copy2_label_cmp_desc;
852         } else {
853                 return;
854         }
855
856         if (shadow_copy2_data && shadow_copy2_data->num_volumes > 0 &&
857             shadow_copy2_data->labels)
858         {
859                 TYPESAFE_QSORT(shadow_copy2_data->labels,
860                                shadow_copy2_data->num_volumes,
861                                cmpfunc);
862         }
863
864         return;
865 }
866
867 static int shadow_copy2_get_shadow_copy2_data(vfs_handle_struct *handle, 
868                                               files_struct *fsp, 
869                                               SHADOW_COPY_DATA *shadow_copy2_data, 
870                                               bool labels)
871 {
872         SMB_STRUCT_DIR *p;
873         const char *snapdir;
874         SMB_STRUCT_DIRENT *d;
875         TALLOC_CTX *tmp_ctx = talloc_new(handle->data);
876         char *snapshot;
877
878         snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle);
879         if (snapdir == NULL) {
880                 DEBUG(0,("shadow:snapdir not found for %s in get_shadow_copy_data\n",
881                          handle->conn->connectpath));
882                 errno = EINVAL;
883                 talloc_free(tmp_ctx);
884                 return -1;
885         }
886
887         p = SMB_VFS_NEXT_OPENDIR(handle, snapdir, NULL, 0);
888
889         if (!p) {
890                 DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
891                          " - %s\n", snapdir, strerror(errno)));
892                 talloc_free(tmp_ctx);
893                 errno = ENOSYS;
894                 return -1;
895         }
896
897         shadow_copy2_data->num_volumes = 0;
898         shadow_copy2_data->labels      = NULL;
899
900         while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
901                 SHADOW_COPY_LABEL *tlabels;
902
903                 /* ignore names not of the right form in the snapshot directory */
904                 snapshot = shadow_copy2_snapshot_to_gmt(tmp_ctx, handle,
905                                                         d->d_name);
906                 DEBUG(6,("shadow_copy2_get_shadow_copy2_data: %s -> %s\n",
907                          d->d_name, snapshot));
908                 if (!snapshot) {
909                         continue;
910                 }
911
912                 if (!labels) {
913                         /* the caller doesn't want the labels */
914                         shadow_copy2_data->num_volumes++;
915                         continue;
916                 }
917
918                 tlabels = talloc_realloc(shadow_copy2_data->mem_ctx,
919                                          shadow_copy2_data->labels,
920                                          SHADOW_COPY_LABEL, shadow_copy2_data->num_volumes+1);
921                 if (tlabels == NULL) {
922                         DEBUG(0,("shadow_copy2: out of memory\n"));
923                         SMB_VFS_NEXT_CLOSEDIR(handle, p);
924                         talloc_free(tmp_ctx);
925                         return -1;
926                 }
927
928                 strlcpy(tlabels[shadow_copy2_data->num_volumes], snapshot,
929                         sizeof(*tlabels));
930                 talloc_free(snapshot);
931
932                 shadow_copy2_data->num_volumes++;
933                 shadow_copy2_data->labels = tlabels;
934         }
935
936         SMB_VFS_NEXT_CLOSEDIR(handle,p);
937
938         shadow_copy2_sort_data(handle, shadow_copy2_data);
939
940         talloc_free(tmp_ctx);
941         return 0;
942 }
943
944 static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
945         .opendir = shadow_copy2_opendir,
946         .mkdir = shadow_copy2_mkdir,
947         .rmdir = shadow_copy2_rmdir,
948         .chflags = shadow_copy2_chflags,
949         .getxattr = shadow_copy2_getxattr,
950         .lgetxattr = shadow_copy2_lgetxattr,
951         .listxattr = shadow_copy2_listxattr,
952         .removexattr = shadow_copy2_removexattr,
953         .lremovexattr = shadow_copy2_lremovexattr,
954         .setxattr = shadow_copy2_setxattr,
955         .lsetxattr = shadow_copy2_lsetxattr,
956         .open = shadow_copy2_open,
957         .rename = shadow_copy2_rename,
958         .stat = shadow_copy2_stat,
959         .lstat = shadow_copy2_lstat,
960         .fstat = shadow_copy2_fstat,
961         .unlink = shadow_copy2_unlink,
962         .chmod = shadow_copy2_chmod,
963         .chown = shadow_copy2_chown,
964         .chdir = shadow_copy2_chdir,
965         .ntimes = shadow_copy2_ntimes,
966         .symlink = shadow_copy2_symlink,
967         .vfs_readlink = shadow_copy2_readlink,
968         .link = shadow_copy2_link,
969         .mknod = shadow_copy2_mknod,
970         .realpath = shadow_copy2_realpath,
971         .connectpath = shadow_copy2_connectpath,
972         .get_nt_acl = shadow_copy2_get_nt_acl,
973         .chmod_acl = shadow_copy2_chmod_acl,
974         .get_shadow_copy_data = shadow_copy2_get_shadow_copy2_data,
975 };
976
977 NTSTATUS vfs_shadow_copy2_init(void);
978 NTSTATUS vfs_shadow_copy2_init(void)
979 {
980         NTSTATUS ret;
981
982         ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "shadow_copy2",
983                                &vfs_shadow_copy2_fns);
984
985         if (!NT_STATUS_IS_OK(ret))
986                 return ret;
987
988         vfs_shadow_copy2_debug_level = debug_add_class("shadow_copy2");
989         if (vfs_shadow_copy2_debug_level == -1) {
990                 vfs_shadow_copy2_debug_level = DBGC_VFS;
991                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
992                         "vfs_shadow_copy2_init"));
993         } else {
994                 DEBUG(10, ("%s: Debug class number of '%s': %d\n", 
995                         "vfs_shadow_copy2_init","shadow_copy2",vfs_shadow_copy2_debug_level));
996         }
997
998         return ret;
999 }