VERSION: Bump version up to 3.6.0rc1.
[Samba.git] / source3 / modules / onefs_shadow_copy.c
blob29ee9e1a225dcaae864f3187464e9b8faee8dbfe
1 /*
2 * Unix SMB/CIFS implementation.
4 * OneFS shadow copy implementation that utilizes the file system's native
5 * snapshot support. This file does all of the heavy lifting.
7 * Copyright (C) Dave Richards, 2007
8 * Copyright (C) Tim Prouty, 2009
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
24 #include "smbd/smbd.h"
25 #include <ifs/ifs_syscalls.h>
26 #include <sys/types.h>
27 #include <sys/isi_enc.h>
28 #include <sys/module.h>
29 #include <sys/stat.h>
30 #include <sys/syscall.h>
31 #include <sys/time.h>
32 #include <dirent.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <search.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
42 #include "onefs_shadow_copy.h"
44 /* Copied from ../include/proto.h */
45 void become_root(void);
46 void unbecome_root(void);
48 #define SNAPSHOT_DIRECTORY ".snapshot"
50 #define MAX_VERSIONS 64
52 /**
53 * A snapshot object.
55 * During snapshot enumeration, snapshots are represented by snapshot objects
56 * and are stored in a snapshot set. The snapshot object represents one
57 * snapshot within the set. An important thing to note about the set is that
58 * the key of the snapshot object is the tv_sec component of the is_time
59 * member. What this means is that we only store one snapshot for each
60 * second. If multiple snapshots were created within the same second, we'll
61 * keep the earliest one and ignore the rest. Thus, not all snapshots are
62 * necessarily retained.
64 struct osc_snapshot {
65 char * is_name;
66 struct timespec is_time;
67 struct osc_snapshot * is_next;
70 /**
71 * A snapshot context object.
73 * Snapshot contexts are used to pass information throughout the snapshot
74 * enumeration routines. As a result, snapshot contexts are stored on the
75 * stack and are both created and destroyed within a single API function.
77 struct osc_snapshot_ctx {
78 void * osc_set;
79 struct timespec osc_mtime;
82 /**
83 * A directory context.
85 * Directory contexts are the underlying data structured used to enumerate
86 * snapshot versions. An opendir()-, readdir()- and closedir()-like interface
87 * is provided that utilizes directory contexts. At the API level, directory
88 * contexts are passed around as void pointers. Directory contexts are
89 * allocated on the heap and their lifetime is dictated by the calling
90 * routine.
92 struct osc_directory_ctx {
93 size_t idc_pos;
94 size_t idc_len;
95 size_t idc_size;
96 char ** idc_version;
99 /**
100 * Return a file descriptor to the STF names directory.
102 * Opens the STF names directory and returns a file descriptor to it.
103 * Subsequent calls return the same value (avoiding the need to re-open the
104 * directory repeatedly). Caveat caller: don't close the file descriptor or
105 * you will be shot!
107 static int
108 osc_get_names_directory_fd(void)
110 static int fd = -1;
112 if (fd == -1) {
113 become_root();
114 fd = pctl2_lin_open(STF_NAMES_LIN, HEAD_SNAPID, O_RDONLY);
115 unbecome_root();
118 return fd;
122 * Compare two time values.
124 * Accepts two struct timespecs and compares the tv_sec components of these
125 * values. It returns -1 if the first value preceeds the second, 0 if they
126 * are equal and +1 if the first values succeeds the second.
128 static int
129 osc_time_compare(const struct timespec *tsp1, const struct timespec *tsp2)
131 return (tsp1->tv_sec < tsp2->tv_sec) ? -1 :
132 (tsp1->tv_sec > tsp2->tv_sec) ? +1 : 0;
136 * Compare two timespec values.
138 * Compares two timespec values. It returns -1 if the first value preceeds
139 * the second, 0 if they are equal and +1 if the first values succeeds the
140 * second.
142 static int
143 osc_timespec_compare(const struct timespec *tsp1, const struct timespec *tsp2)
145 return (tsp1->tv_sec < tsp2->tv_sec) ? -1 :
146 (tsp1->tv_sec > tsp2->tv_sec) ? +1 :
147 (tsp1->tv_nsec < tsp2->tv_nsec) ? -1 :
148 (tsp1->tv_nsec > tsp2->tv_nsec) ? +1 : 0;
152 * Determine whether a timespec value is zero.
154 * Return 1 if the struct timespec provided is zero and 0 otherwise.
156 static int
157 osc_timespec_is_zero(const struct timespec *tsp)
159 return (tsp->tv_sec == 0) &&
160 (tsp->tv_nsec == 0);
164 * Create a snapshot object.
166 * Allocates and initializes a new snapshot object. In addition to allocating
167 * space for the snapshot object itself, space is allocated for the snapshot
168 * name. Both the name and time are then copied to the new object.
170 static struct osc_snapshot *
171 osc_snapshot_create(const char *name, const struct timespec *tsp)
173 struct osc_snapshot *isp;
175 isp = malloc(sizeof *isp);
176 if (isp == NULL)
177 goto out;
179 isp->is_name = malloc(strlen(name) + 1);
180 if (isp->is_name == NULL) {
181 free(isp);
182 isp = NULL;
183 goto out;
186 strcpy(isp->is_name, name);
187 isp->is_time = *tsp;
188 isp->is_next = NULL;
190 out:
191 return isp;
195 * Destroy a snapshot object.
197 * Frees both the name and the snapshot object itself. Appropriate NULL
198 * checking is performed because counting on free to do so is immoral.
200 static void
201 osc_snapshot_destroy(struct osc_snapshot *isp)
203 if (isp != NULL) {
204 if (isp->is_name != NULL)
205 free(isp->is_name);
206 free(isp);
211 * Destroy all snapshots in the snapshot list.
213 * Calls osc_snapshot_destroy() on each snapshot in the list.
215 static void
216 osc_snapshot_destroy_list(struct osc_snapshot *isp)
218 struct osc_snapshot *tmp;
220 while (isp != NULL) {
221 tmp = isp;
222 isp = isp->is_next;
223 osc_snapshot_destroy(tmp);
228 * Compare two snapshot objects.
230 * Compare two snapshot objects. It is really just a wrapper for
231 * osc_time_compare(), which compare the time value of the two snapshots.
232 * N.B. time value in this context refers only to the tv_sec component.
234 static int
235 osc_snapshot_compare(const void *vp1, const void *vp2)
237 const struct osc_snapshot *isp1 = vp1;
238 const struct osc_snapshot *isp2 = vp2;
240 return -osc_time_compare(&isp1->is_time, &isp2->is_time);
244 * Insert a snapshot into the snapshot set.
246 * Inserts a new snapshot into the snapshot set. The key for snapshots is
247 * their creation time (it's actually the seconds portion of the creation
248 * time). If a duplicate snapshot is found in the set, the new snapshot is
249 * added to a linked list of snapshots for that second.
251 static void
252 osc_snapshot_insert(struct osc_snapshot_ctx *oscp, const char *name,
253 const struct timespec *tsp, int *errorp)
255 struct osc_snapshot *isp1;
256 struct osc_snapshot **ispp;
258 isp1 = osc_snapshot_create(name, tsp);
259 if (isp1 == NULL) {
260 *errorp = 1;
261 return;
264 ispp = tsearch(isp1, &oscp->osc_set, osc_snapshot_compare);
265 if (ispp != NULL) {
266 struct osc_snapshot *isp2 = *ispp;
268 /* If this is the only snapshot for this second, we're done. */
269 if (isp2 == isp1)
270 return;
272 /* Collision: add the new snapshot to the list. */
273 isp1->is_next = isp2->is_next;
274 isp2->is_next = isp1;
276 } else
277 *errorp = 1;
282 * Process the next snapshot.
284 * Called for (almost) every entry in a .snapshot directory, ("." and ".." are
285 * ignored in osc_process_snapshot_directory()). All other entries are passed
286 * to osc_process_snapshot(), however. These entries can fall into one of two
287 * categories: snapshot names and snapshot aliases. We only care about
288 * snapshot names (as aliases are just redundant entries). Once it verifies
289 * that name represents a valid snapshot name, it calls fstat() to get the
290 * creation time of the snapshot and then calls osc_snapshot_insert() to add
291 * this entry to the snapshot set.
293 static void
294 osc_process_snapshot(struct osc_snapshot_ctx *oscp, const char *name,
295 int *errorp)
297 int fd;
298 struct stf_stat stf_stat;
299 struct stat stbuf;
301 fd = osc_get_names_directory_fd();
302 if (fd == -1)
303 goto out;
305 fd = enc_openat(fd, name, ENC_DEFAULT, O_RDONLY);
306 if (fd == -1)
307 goto out;
309 memset(&stf_stat, 0, sizeof stf_stat);
310 if (ifs_snap_stat(fd, &stf_stat) == -1)
311 goto out;
313 if (stf_stat.sf_type != SF_STF)
314 goto out;
316 if (fstat(fd, &stbuf) == -1)
317 goto out;
319 osc_snapshot_insert(oscp, name, &stbuf.st_birthtimespec, errorp);
321 out:
322 if (fd != -1)
323 close(fd);
327 * Process a snapshot directory.
329 * Opens the snapshot directory and calls osc_process_snapshot() for each
330 * entry. (Well ok, "." and ".." are ignored.) The goal here is to add all
331 * snapshots in the directory to the snapshot set.
333 static void
334 osc_process_snapshot_directory(struct osc_snapshot_ctx *oscp, int *errorp)
336 int fd;
337 struct stat stbuf;
338 DIR *dirp;
339 struct dirent *dp;
341 fd = osc_get_names_directory_fd();
342 if (fd == -1)
343 goto out;
345 if (fstat(fd, &stbuf) == -1)
346 goto out;
348 dirp = opendir(SNAPSHOT_DIRECTORY);
349 if (dirp == NULL)
350 goto out;
352 for (;;) {
353 dp = readdir(dirp);
354 if (dp == NULL)
355 break;
357 if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' ||
358 (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
359 continue;
361 osc_process_snapshot(oscp, dp->d_name, errorp);
362 if (*errorp)
363 break;
366 closedir(dirp);
368 if (!*errorp)
369 oscp->osc_mtime = stbuf.st_mtimespec;
371 out:
372 return;
376 * Initialize a snapshot context object.
378 * Clears all members of the context object.
380 static void
381 osc_snapshot_ctx_init(struct osc_snapshot_ctx *oscp)
383 memset(oscp, 0, sizeof *oscp);
387 * Desoy a snapshot context object.
389 * Frees all snapshots associated with the snapshot context and then calls
390 * osc_snapshot_ctx_init() to re-initialize the context object.
392 static void
393 osc_snapshot_ctx_clean(struct osc_snapshot_ctx *oscp)
395 struct osc_snapshot *tmp;
397 while (oscp->osc_set != NULL) {
398 tmp = *(void **)oscp->osc_set;
399 tdelete(tmp, &oscp->osc_set, osc_snapshot_compare);
400 osc_snapshot_destroy_list(tmp);
403 osc_snapshot_ctx_init(oscp);
407 * Return the "global" snapshot context.
409 * We maintain a single open snapshot context. Return a pointer to it.
411 static struct osc_snapshot_ctx *
412 osc_get_snapshot_ctx(void)
414 static struct osc_snapshot_ctx osc = { 0, { 0, 0 } };
416 return &osc;
420 * Determine whether a snapshot context is still valid.
422 * "Valid" in this context means "reusable". We can re-use a previous
423 * snapshot context iff we successfully built a previous snapshot context
424 * and no snapshots have been created or deleted since we did so.
425 * A "names" directory exists within our snapshot
426 * implementation in which all snapshot names are entered. Each time a
427 * snapshot is created or deleted, an entry must be added or removed.
428 * When this happens the modification time on the "names" directory
429 * changes. Therefore, a snapshot context is valid iff the context
430 * pointer is non-NULL, the cached modification time is non-zero
431 * (zero means uninitialized), and the modification time of the "names"
432 * directory matches the cached value.
434 static int
435 osc_snapshot_ctx_is_valid(struct osc_snapshot_ctx *oscp)
437 int fd;
438 struct stat stbuf;
440 if (oscp == NULL)
441 return 0;
443 if (osc_timespec_is_zero(&oscp->osc_mtime))
444 return 0;
446 fd = osc_get_names_directory_fd();
447 if (fd == -1)
448 return 0;
450 if (fstat(fd, &stbuf) == -1)
451 return 0;
453 if (osc_timespec_compare(&oscp->osc_mtime, &stbuf.st_mtimespec) != 0)
454 return 0;
456 return 1;
460 * Create and initialize a directory context.
462 * Allocates a directory context from the heap and initializes it.
464 static struct osc_directory_ctx *
465 osc_directory_ctx_create(void)
467 struct osc_directory_ctx *idcp;
469 idcp = malloc(sizeof *idcp);
470 if (idcp != NULL)
471 memset(idcp, 0, sizeof *idcp);
473 return idcp;
477 * Destroy a directory context.
479 * Frees any versions associated with the directory context and then frees the
480 * context itself.
482 static void
483 osc_directory_ctx_destroy(struct osc_directory_ctx *idcp)
485 int i;
487 if (idcp == NULL)
488 return;
490 for (i = 0; i < idcp->idc_len; i++)
491 free(idcp->idc_version[i]);
493 free(idcp);
497 * Expand the size of a directory context's version list.
499 * If osc_directory_ctx_append_version() detects that the version list is too
500 * small to accomodate a new version string, it called
501 * osc_directory_ctx_expand_version_list() to expand the version list.
503 static void
504 osc_directory_ctx_expand_version_list(struct osc_snapshot_ctx *oscp,
505 struct osc_directory_ctx *idcp, int *errorp)
507 size_t size;
508 char **cpp;
510 size = idcp->idc_size * 2 ?: 1;
512 cpp = realloc(idcp->idc_version, size * sizeof (char *));
513 if (cpp == NULL) {
514 *errorp = 1;
515 return;
518 idcp->idc_size = size;
519 idcp->idc_version = cpp;
523 * Append a new version to a directory context.
525 * Appends a snapshot version to the
526 * directory context's version list.
528 static void
529 osc_directory_ctx_append_version(struct osc_snapshot_ctx *oscp,
530 struct osc_directory_ctx *idcp, const struct timespec *tsp, int *errorp)
532 char *cp;
533 struct tm *tmp;
534 char text[64];
536 if (idcp->idc_len >= MAX_VERSIONS)
537 return;
539 if (idcp->idc_len >= idcp->idc_size) {
540 osc_directory_ctx_expand_version_list(oscp, idcp, errorp);
541 if (*errorp)
542 return;
545 tmp = gmtime(&tsp->tv_sec);
546 if (tmp == NULL) {
547 *errorp = 1;
548 return;
551 snprintf(text, sizeof text,
552 "@GMT-%04u.%02u.%02u-%02u.%02u.%02u",
553 tmp->tm_year + 1900,
554 tmp->tm_mon + 1,
555 tmp->tm_mday,
556 tmp->tm_hour,
557 tmp->tm_min,
558 tmp->tm_sec);
560 cp = malloc(strlen(text) + 1);
561 if (cp == NULL) {
562 *errorp = 1;
563 return;
566 strcpy(cp, text);
568 idcp->idc_version[idcp->idc_len++] = cp;
572 * Make a directory context from a snapshot context.
574 * Once a snapshot context has been completely filled-in,
575 * osc_make_directory_ctx() is used to build a directory context from it. The
576 * idea here is to create version for each snapshot in the snapshot set.
578 static void
579 osc_make_directory_ctx(struct osc_snapshot_ctx *oscp,
580 struct osc_directory_ctx *idcp, int *errorp)
582 static void
583 walk(const void *vp, VISIT v, int level)
585 const struct osc_snapshot *isp;
587 if ((v != postorder && v != leaf) || *errorp)
588 return;
590 isp = *(const struct osc_snapshot **)(u_long)vp;
592 osc_directory_ctx_append_version(oscp, idcp, &isp->is_time,
593 errorp);
596 twalk(oscp->osc_set, walk);
600 * Open a version directory.
602 * Opens a version directory. What this really means is that
603 * osc_version_opendir() returns a handle to a directory context, which can be
604 * used to retrieve version strings.
606 void *
607 osc_version_opendir(void)
609 int error = 0;
610 struct osc_directory_ctx *idcp;
611 struct osc_snapshot_ctx *oscp;
613 idcp = osc_directory_ctx_create();
614 if (idcp == NULL)
615 goto error_out;
617 oscp = osc_get_snapshot_ctx();
619 if (!osc_snapshot_ctx_is_valid(oscp)) {
620 osc_snapshot_ctx_clean(oscp);
621 osc_process_snapshot_directory(oscp, &error);
622 if (error)
623 goto error_out;
626 osc_make_directory_ctx(oscp, idcp, &error);
627 if (error)
628 goto error_out;
630 goto out;
632 error_out:
633 if (idcp != NULL) {
634 osc_directory_ctx_destroy(idcp);
635 idcp = NULL;
638 out:
639 return (void *)idcp;
643 * Read the next version directory entry.
645 * Returns the name of the next version in the version directory, or NULL if
646 * we're at the end of the directory. What this really does is return the
647 * next version from the version list stored in the directory context.
649 char *
650 osc_version_readdir(void *vp)
652 struct osc_directory_ctx *idcp = vp;
654 if (idcp == NULL)
655 return NULL;
657 if (idcp->idc_pos >= idcp->idc_len)
658 return NULL;
660 return idcp->idc_version[idcp->idc_pos++];
664 * Close the version directory.
666 * Destroys the underlying directory context.
668 void
669 osc_version_closedir(void *vp)
671 struct osc_directory_ctx *idcp = vp;
673 if (idcp != NULL)
674 osc_directory_ctx_destroy(idcp);
678 * Canonicalize a path.
680 * Converts paths of the form @GMT-.. to paths of the form ../.snapshot/..
681 * It's not the prettiest routine I've ever written, but what the heck?
683 char *
684 osc_canonicalize_path(const char *path, char *snap_component)
686 int error = 0;
687 struct osc_snapshot_ctx *oscp;
688 struct tm tm;
689 int n;
690 struct osc_snapshot is;
691 struct osc_snapshot **ispp;
692 struct osc_snapshot *isp;
693 char *cpath = NULL;
694 char *cpath2 = NULL;
695 const char *snap_component_orig = snap_component;
696 struct stat sb;
698 oscp = osc_get_snapshot_ctx();
700 if (!osc_snapshot_ctx_is_valid(oscp)) {
701 osc_snapshot_ctx_clean(oscp);
702 osc_process_snapshot_directory(oscp, &error);
703 if (error)
704 goto out;
707 memset(&tm, 0, sizeof tm);
708 n = sscanf(snap_component,
709 "@GMT-%4u.%2u.%2u-%2u.%2u.%2u",
710 &tm.tm_year,
711 &tm.tm_mon,
712 &tm.tm_mday,
713 &tm.tm_hour,
714 &tm.tm_min,
715 &tm.tm_sec);
716 if (n != 6)
717 goto out;
719 tm.tm_year -= 1900;
720 tm.tm_mon -= 1;
722 is.is_name = NULL;
723 is.is_time.tv_sec = timegm(&tm);
724 is.is_time.tv_nsec = 0;
726 ispp = tfind(&is, &oscp->osc_set, osc_snapshot_compare);
727 if (ispp == NULL)
728 goto out;
729 isp = *ispp;
731 /* Determine the path after "@GMT-..." */
732 while (*snap_component != '/' && *snap_component != '\0')
733 snap_component++;
735 while (*snap_component == '/')
736 snap_component++;
738 cpath = malloc(strlen(SNAPSHOT_DIRECTORY) + strlen(isp->is_name) +
739 strlen(path) + 3);
741 if (cpath == NULL)
742 goto out;
745 * Use the first snapshot that has a successful stat for the requested
746 * path.
748 while (true) {
750 sprintf(cpath, "%s/%s", SNAPSHOT_DIRECTORY, isp->is_name);
752 /* Append path before "@GMT-..." */
753 if (snap_component_orig != path) {
754 strcat(cpath, "/");
755 strncat(cpath, path, snap_component_orig - path);
758 /* Append path after "@GMT-..." */
759 if (*snap_component != '\0') {
760 strcat(cpath, "/");
761 strcat(cpath, snap_component);
764 /* If there is a valid snapshot for this file, we're done. */
765 if (stat(cpath, &sb) == 0)
766 break;
768 /* Try the next snapshot. If this was the last one, give up. */
769 isp = isp->is_next;
770 if (isp == NULL)
771 break;
773 /* If the realloc fails, give up. */
774 cpath2 = realloc(cpath, strlen(SNAPSHOT_DIRECTORY) +
775 strlen(isp->is_name) + strlen(path) + 3);
776 if (cpath2 == NULL)
777 break;
778 cpath = cpath2;
781 out:
782 return cpath;