diff options
-rw-r--r-- | source3/Makefile.in | 5 | ||||
-rw-r--r-- | source3/configure.in | 3 | ||||
-rw-r--r-- | source3/modules/onefs_shadow_copy.c | 782 | ||||
-rw-r--r-- | source3/modules/onefs_shadow_copy.h | 32 | ||||
-rw-r--r-- | source3/modules/vfs_onefs_shadow_copy.c | 717 |
5 files changed, 1538 insertions, 1 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 9bac7191fa..6ff90fc815 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -667,6 +667,7 @@ VFS_SMB_TRAFFIC_ANALYZER_OBJ = modules/vfs_smb_traffic_analyzer.o VFS_ONEFS_OBJ = modules/vfs_onefs.o modules/onefs_acl.o modules/onefs_system.o \ modules/onefs_open.o modules/onefs_streams.o modules/onefs_dir.c \ modules/onefs_cbrl.o +VFS_ONEFS_SHADOW_COPY_OBJ = modules/vfs_onefs_shadow_copy.o modules/onefs_shadow_copy.o PERFCOUNT_ONEFS_OBJ = modules/perfcount_onefs.o PERFCOUNT_TEST_OBJ = modules/perfcount_test.o @@ -2559,6 +2560,10 @@ bin/onefs.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_ONEFS_OBJ) @echo "Building plugin $@" @$(SHLD_MODULE) $(VFS_ONEFS_OBJ) @ONEFS_LIBS@ +bin/onefs_shadow_copy.@SHLIBEXT@: $(BINARY_PREREQS) $(VFS_ONEFS_SHADOW_COPY_OBJ) + @echo "Building plugin $@" + @$(SHLD_MODULE) $(VFS_ONEFS_SHADOW_COPY_OBJ) + bin/pc_onefs.@SHLIBEXT@: $(BINARY_PREREQS) $(PERFCOUNT_ONEFS_OBJ) @echo "Building plugin $@" @$(SHLD_MODULE) $(PERFCOUNT_ONEFS_OBJ) diff --git a/source3/configure.in b/source3/configure.in index 57d475fcc5..b163a9dbb8 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -1084,7 +1084,7 @@ AC_TRY_LINK([#include <isi_version/isi_version.h>], echo $samba_cv_HAVE_ONEFS if test x"$samba_cv_HAVE_ONEFS" = x"yes"; then AC_DEFINE(HAVE_ONEFS,1,[Whether building on Isilon OneFS]) - default_shared_modules="$default_shared_modules vfs_onefs perfcount_onefs" + default_shared_modules="$default_shared_modules vfs_onefs vfs_onefs_shadow_copy perfcount_onefs" ONEFS_LIBS="-lisi_acl -lisi_ecs -lisi_event -lisi_util" # Need to also add general libs for oplocks support save_LIBS="$save_LIBS -lisi_ecs -lisi_event -lisi_util -ldevstat" @@ -6178,6 +6178,7 @@ SMB_MODULE(vfs_acl_xattr, \$(VFS_ACL_XATTR_OBJ), "bin/acl_xattr.$SHLIBEXT", VFS) SMB_MODULE(vfs_acl_tdb, \$(VFS_ACL_TDB_OBJ), "bin/acl_tdb.$SHLIBEXT", VFS) SMB_MODULE(vfs_smb_traffic_analyzer, \$(VFS_SMB_TRAFFIC_ANALYZER_OBJ), "bin/smb_traffic_analyzer.$SHLIBEXT", VFS) SMB_MODULE(vfs_onefs, \$(VFS_ONEFS), "bin/onefs.$SHLIBEXT", VFS) +SMB_MODULE(vfs_onefs_shadow_copy, \$(VFS_ONEFS_SHADOW_COPY), "bin/onefs_shadow_copy.$SHLIBEXT", VFS) SMB_SUBSYSTEM(VFS,smbd/vfs.o) diff --git a/source3/modules/onefs_shadow_copy.c b/source3/modules/onefs_shadow_copy.c new file mode 100644 index 0000000000..5b02534715 --- /dev/null +++ b/source3/modules/onefs_shadow_copy.c @@ -0,0 +1,782 @@ +/* + * Unix SMB/CIFS implementation. + * + * OneFS shadow copy implementation that utilizes the file system's native + * snapshot support. This file does all of the heavy lifting. + * + * Copyright (C) Dave Richards, 2007 + * Copyright (C) Tim Prouty, 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <ifs/ifs_syscalls.h> +#include <sys/types.h> +#include <sys/isi_enc.h> +#include <sys/module.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/time.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <search.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "onefs_shadow_copy.h" + +/* Copied from ../include/proto.h */ +void become_root(void); +void unbecome_root(void); + +#define SNAPSHOT_DIRECTORY ".snapshot" + +#define MAX_VERSIONS 64 + +/** + * A snapshot object. + * + * During snapshot enumeration, snapshots are represented by snapshot objects + * and are stored in a snapshot set. The snapshot object represents one + * snapshot within the set. An important thing to note about the set is that + * the key of the snapshot object is the tv_sec component of the is_time + * member. What this means is that we only store one snapshot for each + * second. If multiple snapshots were created within the same second, we'll + * keep the earliest one and ignore the rest. Thus, not all snapshots are + * necessarily retained. + */ +struct osc_snapshot { + char * is_name; + struct timespec is_time; + struct osc_snapshot * is_next; +}; + +/** + * A snapshot context object. + * + * Snapshot contexts are used to pass information throughout the snapshot + * enumeration routines. As a result, snapshot contexts are stored on the + * stack and are both created and destroyed within a single API function. + */ +struct osc_snapshot_ctx { + void * osc_set; + struct timespec osc_mtime; +}; + +/** + * A directory context. + * + * Directory contexts are the underlying data structured used to enumerate + * snapshot versions. An opendir()-, readdir()- and closedir()-like interface + * is provided that utilizes directory contexts. At the API level, directory + * contexts are passed around as void pointers. Directory contexts are + * allocated on the heap and their lifetime is dictated by the calling + * routine. + */ +struct osc_directory_ctx { + size_t idc_pos; + size_t idc_len; + size_t idc_size; + char ** idc_version; +}; + +/** + * Return a file descriptor to the STF names directory. + * + * Opens the STF names directory and returns a file descriptor to it. + * Subsequent calls return the same value (avoiding the need to re-open the + * directory repeatedly). Caveat caller: don't close the file descriptor or + * you will be shot! + */ +static int +osc_get_names_directory_fd(void) +{ + static int fd = -1; + + if (fd == -1) { + become_root(); + fd = pctl2_lin_open(STF_NAMES_LIN, HEAD_SNAPID, O_RDONLY); + unbecome_root(); + } + + return fd; +} + +/** + * Compare two time values. + * + * Accepts two struct timespecs and compares the tv_sec components of these + * values. It returns -1 if the first value preceeds the second, 0 if they + * are equal and +1 if the first values succeeds the second. + */ +static int +osc_time_compare(const struct timespec *tsp1, const struct timespec *tsp2) +{ + return (tsp1->tv_sec < tsp2->tv_sec) ? -1 : + (tsp1->tv_sec > tsp2->tv_sec) ? +1 : 0; +} + +/** + * Compare two timespec values. + * + * Compares two timespec values. It returns -1 if the first value preceeds + * the second, 0 if they are equal and +1 if the first values succeeds the + * second. + */ +static int +osc_timespec_compare(const struct timespec *tsp1, const struct timespec *tsp2) +{ + return (tsp1->tv_sec < tsp2->tv_sec) ? -1 : + (tsp1->tv_sec > tsp2->tv_sec) ? +1 : + (tsp1->tv_nsec < tsp2->tv_nsec) ? -1 : + (tsp1->tv_nsec > tsp2->tv_nsec) ? +1 : 0; +} + +/** + * Determine whether a timespec value is zero. + * + * Return 1 if the struct timespec provided is zero and 0 otherwise. + */ +static int +osc_timespec_is_zero(const struct timespec *tsp) +{ + return (tsp->tv_sec == 0) && + (tsp->tv_nsec == 0); +} + +/** + * Create a snapshot object. + * + * Allocates and initializes a new snapshot object. In addition to allocating + * space for the snapshot object itself, space is allocated for the snapshot + * name. Both the name and time are then copied to the new object. + */ +static struct osc_snapshot * +osc_snapshot_create(const char *name, const struct timespec *tsp) +{ + struct osc_snapshot *isp; + + isp = malloc(sizeof *isp); + if (isp == NULL) + goto out; + + isp->is_name = malloc(strlen(name) + 1); + if (isp->is_name == NULL) { + free(isp); + isp = NULL; + goto out; + } + + strcpy(isp->is_name, name); + isp->is_time = *tsp; + isp->is_next = NULL; + + out: + return isp; +} + +/** + * Destroy a snapshot object. + * + * Frees both the name and the snapshot object itself. Appropriate NULL + * checking is performed because counting on free to do so is immoral. + */ +static void +osc_snapshot_destroy(struct osc_snapshot *isp) +{ + if (isp != NULL) { + if (isp->is_name != NULL) + free(isp->is_name); + free(isp); + } +} + +/** + * Destroy all snapshots in the snapshot list. + * + * Calls osc_snapshot_destroy() on each snapshot in the list. + */ +static void +osc_snapshot_destroy_list(struct osc_snapshot *isp) +{ + struct osc_snapshot *tmp; + + while (isp != NULL) { + tmp = isp; + isp = isp->is_next; + osc_snapshot_destroy(tmp); + } +} + +/** + * Compare two snapshot objects. + * + * Compare two snapshot objects. It is really just a wrapper for + * osc_time_compare(), which compare the time value of the two snapshots. + * N.B. time value in this context refers only to the tv_sec component. + */ +static int +osc_snapshot_compare(const void *vp1, const void *vp2) +{ + const struct osc_snapshot *isp1 = vp1; + const struct osc_snapshot *isp2 = vp2; + + return -osc_time_compare(&isp1->is_time, &isp2->is_time); +} + +/** + * Insert a snapshot into the snapshot set. + * + * Inserts a new snapshot into the snapshot set. The key for snapshots is + * their creation time (it's actually the seconds portion of the creation + * time). If a duplicate snapshot is found in the set, the new snapshot is + * added to a linked list of snapshots for that second. + */ +static void +osc_snapshot_insert(struct osc_snapshot_ctx *oscp, const char *name, + const struct timespec *tsp, int *errorp) +{ + struct osc_snapshot *isp1; + struct osc_snapshot **ispp; + + isp1 = osc_snapshot_create(name, tsp); + if (isp1 == NULL) { + *errorp = 1; + return; + } + + ispp = tsearch(isp1, &oscp->osc_set, osc_snapshot_compare); + if (ispp != NULL) { + struct osc_snapshot *isp2 = *ispp; + + /* If this is the only snapshot for this second, we're done. */ + if (isp2 == isp1) + return; + + /* Collision: add the new snapshot to the list. */ + isp1->is_next = isp2->is_next; + isp2->is_next = isp1; + + } else + *errorp = 1; + +} + +/** + * Process the next snapshot. + * + * Called for (almost) every entry in a .snapshot directory, ("." and ".." are + * ignored in osc_process_snapshot_directory()). All other entries are passed + * to osc_process_snapshot(), however. These entries can fall into one of two + * categories: snapshot names and snapshot aliases. We only care about + * snapshot names (as aliases are just redundant entries). Once it verifies + * that name represents a valid snapshot name, it calls fstat() to get the + * creation time of the snapshot and then calls osc_snapshot_insert() to add + * this entry to the snapshot set. + */ +static void +osc_process_snapshot(struct osc_snapshot_ctx *oscp, const char *name, + int *errorp) +{ + int fd; + struct stf_stat stf_stat; + struct stat stbuf; + + fd = osc_get_names_directory_fd(); + if (fd == -1) + goto out; + + fd = enc_openat(fd, name, ENC_DEFAULT, O_RDONLY); + if (fd == -1) + goto out; + + memset(&stf_stat, 0, sizeof stf_stat); + if (ifs_snap_stat(fd, &stf_stat) == -1) + goto out; + + if (stf_stat.sf_type != SF_STF) + goto out; + + if (fstat(fd, &stbuf) == -1) + goto out; + + osc_snapshot_insert(oscp, name, &stbuf.st_birthtimespec, errorp); + + out: + if (fd != -1) + close(fd); +} + +/** + * Process a snapshot directory. + * + * Opens the snapshot directory and calls osc_process_snapshot() for each + * entry. (Well ok, "." and ".." are ignored.) The goal here is to add all + * snapshots in the directory to the snapshot set. + */ +static void +osc_process_snapshot_directory(struct osc_snapshot_ctx *oscp, int *errorp) +{ + int fd; + struct stat stbuf; + DIR *dirp; + struct dirent *dp; + + fd = osc_get_names_directory_fd(); + if (fd == -1) + goto out; + + if (fstat(fd, &stbuf) == -1) + goto out; + + dirp = opendir(SNAPSHOT_DIRECTORY); + if (dirp == NULL) + goto out; + + for (;;) { + dp = readdir(dirp); + if (dp == NULL) + break; + + if (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || + (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) + continue; + + osc_process_snapshot(oscp, dp->d_name, errorp); + if (*errorp) + break; + } + + closedir(dirp); + + if (!*errorp) + oscp->osc_mtime = stbuf.st_mtimespec; + + out: + return; +} + +/** + * Initialize a snapshot context object. + * + * Clears all members of the context object. + */ +static void +osc_snapshot_ctx_init(struct osc_snapshot_ctx *oscp) +{ + memset(oscp, 0, sizeof *oscp); +} + +/** + * Desoy a snapshot context object. + * + * Frees all snapshots associated with the snapshot context and then calls + * osc_snapshot_ctx_init() to re-initialize the context object. + */ +static void +osc_snapshot_ctx_clean(struct osc_snapshot_ctx *oscp) +{ + struct osc_snapshot *tmp; + + while (oscp->osc_set != NULL) { + tmp = *(void **)oscp->osc_set; + tdelete(tmp, &oscp->osc_set, osc_snapshot_compare); + osc_snapshot_destroy_list(tmp); + } + + osc_snapshot_ctx_init(oscp); +} + +/** + * Return the "global" snapshot context. + * + * We maintain a single open snapshot context. Return a pointer to it. + */ +static struct osc_snapshot_ctx * +osc_get_snapshot_ctx(void) +{ + static struct osc_snapshot_ctx osc = { 0, { 0, 0 } }; + + return &osc; +} + +/** + * Determine whether a snapshot context is still valid. + * + * "Valid" in this context means "reusable". We can re-use a previous + * snapshot context iff we successfully built a previous snapshot context + * and no snapshots have been created or deleted since we did so. + * A "names" directory exists within our snapshot + * implementation in which all snapshot names are entered. Each time a + * snapshot is created or deleted, an entry must be added or removed. + * When this happens the modification time on the "names" directory + * changes. Therefore, a snapshot context is valid iff the context + * pointer is non-NULL, the cached modification time is non-zero + * (zero means uninitialized), and the modification time of the "names" + * directory matches the cached value. + */ +static int +osc_snapshot_ctx_is_valid(struct osc_snapshot_ctx *oscp) +{ + int fd; + struct stat stbuf; + + if (oscp == NULL) + return 0; + + if (osc_timespec_is_zero(&oscp->osc_mtime)) + return 0; + + fd = osc_get_names_directory_fd(); + if (fd == -1) + return 0; + + if (fstat(fd, &stbuf) == -1) + return 0; + + if (osc_timespec_compare(&oscp->osc_mtime, &stbuf.st_mtimespec) != 0) + return 0; + + return 1; +} + +/** + * Create and initialize a directory context. + * + * Allocates a directory context from the heap and initializes it. + */ +static struct osc_directory_ctx * +osc_directory_ctx_create(void) +{ + struct osc_directory_ctx *idcp; + + idcp = malloc(sizeof *idcp); + if (idcp != NULL) + memset(idcp, 0, sizeof *idcp); + + return idcp; +} + +/** + * Destroy a directory context. + * + * Frees any versions associated with the directory context and then frees the + * context itself. + */ +static void +osc_directory_ctx_destroy(struct osc_directory_ctx *idcp) +{ + int i; + + if (idcp == NULL) + return; + + for (i = 0; i < idcp->idc_len; i++) + free(idcp->idc_version[i]); + + free(idcp); +} + +/** + * Expand the size of a directory context's version list. + * + * If osc_directory_ctx_append_version() detects that the version list is too + * small to accomodate a new version string, it called + * osc_directory_ctx_expand_version_list() to expand the version list. + */ +static void +osc_directory_ctx_expand_version_list(struct osc_snapshot_ctx *oscp, + struct osc_directory_ctx *idcp, int *errorp) +{ + size_t size; + char **cpp; + + size = idcp->idc_size * 2 ?: 1; + + cpp = realloc(idcp->idc_version, size * sizeof (char *)); + if (cpp == NULL) { + *errorp = 1; + return; + } + + idcp->idc_size = size; + idcp->idc_version = cpp; +} + +/** + * Append a new version to a directory context. + * + * Appends a snapshot version to the + * directory context's version list. + */ +static void +osc_directory_ctx_append_version(struct osc_snapshot_ctx *oscp, + struct osc_directory_ctx *idcp, const struct timespec *tsp, int *errorp) +{ + char *cp; + struct tm *tmp; + char text[64]; + + if (idcp->idc_len >= MAX_VERSIONS) + return; + + if (idcp->idc_len >= idcp->idc_size) { + osc_directory_ctx_expand_version_list(oscp, idcp, errorp); + if (*errorp) + return; + } + + tmp = gmtime(&tsp->tv_sec); + if (tmp == NULL) { + *errorp = 1; + return; + } + + snprintf(text, sizeof text, + "@GMT-%04u.%02u.%02u-%02u.%02u.%02u", + tmp->tm_year + 1900, + tmp->tm_mon + 1, + tmp->tm_mday, + tmp->tm_hour, + tmp->tm_min, + tmp->tm_sec); + + cp = malloc(strlen(text) + 1); + if (cp == NULL) { + *errorp = 1; + return; + } + + strcpy(cp, text); + + idcp->idc_version[idcp->idc_len++] = cp; +} + +/** + * Make a directory context from a snapshot context. + * + * Once a snapshot context has been completely filled-in, + * osc_make_directory_ctx() is used to build a directory context from it. The + * idea here is to create version for each snapshot in the snapshot set. + */ +static void +osc_make_directory_ctx(struct osc_snapshot_ctx *oscp, + struct osc_directory_ctx *idcp, int *errorp) +{ + static void + walk(const void *vp, VISIT v, int level) + { + const struct osc_snapshot *isp; + + if ((v != postorder && v != leaf) || *errorp) + return; + + isp = *(const struct osc_snapshot **)(u_long)vp; + + osc_directory_ctx_append_version(oscp, idcp, &isp->is_time, + errorp); + } + + twalk(oscp->osc_set, walk); +} + +/** + * Open a version directory. + * + * Opens a version directory. What this really means is that + * osc_version_opendir() returns a handle to a directory context, which can be + * used to retrieve version strings. + */ +void * +osc_version_opendir(void) +{ + int error = 0; + struct osc_directory_ctx *idcp; + struct osc_snapshot_ctx *oscp; + + idcp = osc_directory_ctx_create(); + if (idcp == NULL) + goto error_out; + + oscp = osc_get_snapshot_ctx(); + + if (!osc_snapshot_ctx_is_valid(oscp)) { + osc_snapshot_ctx_clean(oscp); + osc_process_snapshot_directory(oscp, &error); + if (error) + goto error_out; + } + + osc_make_directory_ctx(oscp, idcp, &error); + if (error) + goto error_out; + + goto out; + + error_out: + if (idcp != NULL) { + osc_directory_ctx_destroy(idcp); + idcp = NULL; + } + + out: + return (void *)idcp; +} + +/** + * Read the next version directory entry. + * + * Returns the name of the next version in the version directory, or NULL if + * we're at the end of the directory. What this really does is return the + * next version from the version list stored in the directory context. + */ +char * +osc_version_readdir(void *vp) +{ + struct osc_directory_ctx *idcp = vp; + + if (idcp == NULL) + return NULL; + + if (idcp->idc_pos >= idcp->idc_len) + return NULL; + + return idcp->idc_version[idcp->idc_pos++]; +} + +/** + * Close the version directory. + * + * Destroys the underlying directory context. + */ +void +osc_version_closedir(void *vp) +{ + struct osc_directory_ctx *idcp = vp; + + if (idcp != NULL) + osc_directory_ctx_destroy(idcp); +} + +/** + * Canonicalize a path. + * + * Converts paths of the form @GMT-.. to paths of the form ../.snapshot/.. + * It's not the prettiest routine I've ever written, but what the heck? + */ +char * +osc_canonicalize_path(const char *path, char *snap_component) +{ + int error = 0; + struct osc_snapshot_ctx *oscp; + struct tm tm; + int n; + struct osc_snapshot is; + struct osc_snapshot **ispp; + struct osc_snapshot *isp; + char *cpath = NULL; + char *cpath2 = NULL; + const char *snap_component_orig = snap_component; + struct stat sb; + + oscp = osc_get_snapshot_ctx(); + + if (!osc_snapshot_ctx_is_valid(oscp)) { + osc_snapshot_ctx_clean(oscp); + osc_process_snapshot_directory(oscp, &error); + if (error) + goto out; + } + + memset(&tm, 0, sizeof tm); + n = sscanf(snap_component, + "@GMT-%4u.%2u.%2u-%2u.%2u.%2u", + &tm.tm_year, + &tm.tm_mon, + &tm.tm_mday, + &tm.tm_hour, + &tm.tm_min, + &tm.tm_sec); + if (n != 6) + goto out; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + is.is_name = NULL; + is.is_time.tv_sec = timegm(&tm); + is.is_time.tv_nsec = 0; + + ispp = tfind(&is, &oscp->osc_set, osc_snapshot_compare); + if (ispp == NULL) + goto out; + isp = *ispp; + + /* Determine the path after "@GMT-..." */ + while (*snap_component != '/' && *snap_component != '\0') + snap_component++; + + while (*snap_component == '/') + snap_component++; + + cpath = malloc(strlen(SNAPSHOT_DIRECTORY) + strlen(isp->is_name) + + strlen(path) + 3); + + if (cpath == NULL) + goto out; + + /* + * Use the first snapshot that has a successful stat for the requested + * path. + */ + while (true) { + + sprintf(cpath, "%s/%s", SNAPSHOT_DIRECTORY, isp->is_name); + + /* Append path before "@GMT-..." */ + if (snap_component_orig != path) { + strcat(cpath, "/"); + strncat(cpath, path, snap_component_orig - path); + } + + /* Append path after "@GMT-..." */ + if (*snap_component != '\0') { + strcat(cpath, "/"); + strcat(cpath, snap_component); + } + + /* If there is a valid snapshot for this file, we're done. */ + if (stat(cpath, &sb) == 0) + break; + + /* Try the next snapshot. If this was the last one, give up. */ + isp = isp->is_next; + if (isp == NULL) + break; + + /* If the realloc fails, give up. */ + cpath2 = realloc(cpath, strlen(SNAPSHOT_DIRECTORY) + + strlen(isp->is_name) + strlen(path) + 3); + if (cpath2 == NULL) + break; + cpath = cpath2; + } + + out: + return cpath; +} diff --git a/source3/modules/onefs_shadow_copy.h b/source3/modules/onefs_shadow_copy.h new file mode 100644 index 0000000000..6415a4be1a --- /dev/null +++ b/source3/modules/onefs_shadow_copy.h @@ -0,0 +1,32 @@ +/* + * Unix SMB/CIFS implementation. + * + * OneFS shadow copy implementation that utilizes the file system's native + * snapshot support. + * + * Copyright (C) Dave Richards, 2007 + * Copyright (C) Tim Prouty, 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef ONEFS_SHADOW_COPY_H +#define ONEFS_SHADOW_COPY_H + +void *osc_version_opendir(void); +char *osc_version_readdir(void *vp); +void osc_version_closedir(void *vp); +char *osc_canonicalize_path(const char *path, char *snap_component); + +#endif /* ONEFS_SHADOW_COPY_H */ diff --git a/source3/modules/vfs_onefs_shadow_copy.c b/source3/modules/vfs_onefs_shadow_copy.c new file mode 100644 index 0000000000..28bc0c5081 --- /dev/null +++ b/source3/modules/vfs_onefs_shadow_copy.c @@ -0,0 +1,717 @@ +/* + * OneFS shadow copy implementation that utilizes the file system's native + * snapshot support. This is based on the original shadow copy module from + * 2004. + * + * Copyright (C) Stefan Metzmacher 2003-2004 + * Copyright (C) Tim Prouty 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" +#include "onefs_shadow_copy.h" + +static int vfs_onefs_shadow_copy_debug_level = DBGC_VFS; + +#undef DBGC_CLASS +#define DBGC_CLASS vfs_onefs_shadow_copy_debug_level + +#define SHADOW_COPY_PREFIX "@GMT-" +#define SHADOW_COPY_SAMPLE "@GMT-2004.02.18-15.44.00" + +bool +shadow_copy_match_name(const char *name, char **snap_component) +{ + uint32 i = 0; + char delim[] = SHADOW_COPY_PREFIX; + char* start; + + start = strstr( name, delim ); + + /* + * The name could have SHADOW_COPY_PREFIX in it so we need to keep + * trying until we get something that is the full length of the + * SHADOW_COPY_SAMPLE. + */ + while (start != NULL) { + + DEBUG(10,("Processing %s\n", name)); + + /* size / correctness check */ + *snap_component = start; + for ( i = sizeof(SHADOW_COPY_PREFIX); + i < sizeof(SHADOW_COPY_SAMPLE); i++) { + if (start[i] == '/') { + if (i == sizeof(SHADOW_COPY_SAMPLE) - 1) + return true; + else + break; + } else if (start[i] == '\0') + return (i == sizeof(SHADOW_COPY_SAMPLE) - 1); + } + + start = strstr( start, delim ); + } + + return false; +} + +static int +onefs_shadow_copy_get_shadow_copy_data(vfs_handle_struct *handle, + files_struct *fsp, + SHADOW_COPY_DATA *shadow_copy_data, + bool labels) +{ + void *p = osc_version_opendir(); + char *snap_component = NULL; + shadow_copy_data->num_volumes = 0; + shadow_copy_data->labels = NULL; + + if (!p) { + DEBUG(0, ("shadow_copy_get_shadow_copy_data: osc_opendir() " + "failed for [%s]\n",fsp->conn->connectpath)); + return -1; + } + + while (true) { + SHADOW_COPY_LABEL *tlabels; + char *d; + + d = osc_version_readdir(p); + if (d == NULL) + break; + + if (!shadow_copy_match_name(d, &snap_component)) { + DEBUG(10,("shadow_copy_get_shadow_copy_data: ignore " + "[%s]\n",d)); + continue; + } + + DEBUG(7,("shadow_copy_get_shadow_copy_data: not ignore " + "[%s]\n",d)); + + if (!labels) { + shadow_copy_data->num_volumes++; + continue; + } + + tlabels = (SHADOW_COPY_LABEL *)TALLOC_REALLOC( + shadow_copy_data->mem_ctx, + shadow_copy_data->labels, + (shadow_copy_data->num_volumes+1) * + sizeof(SHADOW_COPY_LABEL)); + + if (tlabels == NULL) { + DEBUG(0,("shadow_copy_get_shadow_copy_data: Out of " + "memory\n")); + osc_version_closedir(p); + return -1; + } + + snprintf(tlabels[shadow_copy_data->num_volumes++], + sizeof(*tlabels), "%s",d); + + shadow_copy_data->labels = tlabels; + } + + osc_version_closedir(p); + + return 0; +} + +#define SHADOW_NEXT(op, args, rtype) do { \ + char *cpath = NULL; \ + char *snap_component = NULL; \ + rtype ret; \ + if (shadow_copy_match_name(path, &snap_component)) \ + cpath = osc_canonicalize_path(path, snap_component); \ + ret = SMB_VFS_NEXT_ ## op args; \ + SAFE_FREE(cpath); \ + return ret; \ + } while (0) \ + + + +static uint64_t +onefs_shadow_copy_disk_free(vfs_handle_struct *handle, const char *path, + bool small_query, uint64_t *bsize, uint64_t *dfree, + uint64_t *dsize) +{ + + SHADOW_NEXT(DISK_FREE, + (handle, cpath ?: path, small_query, bsize, dfree, dsize), + uint64_t); + +} + +static int +onefs_shadow_copy_statvfs(struct vfs_handle_struct *handle, const char *path, + struct vfs_statvfs_struct *statbuf) +{ + SHADOW_NEXT(STATVFS, + (handle, cpath ?: path, statbuf), + int); +} + +static SMB_STRUCT_DIR * +onefs_shadow_copy_opendir(vfs_handle_struct *handle, const char *path, + const char *mask, uint32_t attr) +{ + SHADOW_NEXT(OPENDIR, + (handle, cpath ?: path, mask, attr), + SMB_STRUCT_DIR *); +} + +static int +onefs_shadow_copy_mkdir(vfs_handle_struct *handle, const char *path, + mode_t mode) +{ + SHADOW_NEXT(MKDIR, + (handle, cpath ?: path, mode), + int); +} + +static int +onefs_shadow_copy_rmdir(vfs_handle_struct *handle, const char *path) +{ + SHADOW_NEXT(RMDIR, + (handle, cpath ?: path), + int); +} + +static int +onefs_shadow_copy_open(vfs_handle_struct *handle, const char *path, + files_struct *fsp, int flags, mode_t mode) +{ + SHADOW_NEXT(OPEN, + (handle, cpath ?: path, fsp, flags, mode), + int); +} + +static NTSTATUS +onefs_shadow_copy_create_file(vfs_handle_struct *handle, + struct smb_request *req, + uint16_t root_dir_fid, + const char *path, + uint32_t create_file_flags, + uint32_t access_mask, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t file_attributes, + uint32_t oplock_request, + uint64_t allocation_size, + struct security_descriptor *sd, + struct ea_list *ea_list, + files_struct **result, + int *pinfo, + SMB_STRUCT_STAT *psbuf) +{ + SHADOW_NEXT(CREATE_FILE, + (handle, req, root_dir_fid, cpath ?: path, + create_file_flags, access_mask, share_access, + create_disposition, create_options, file_attributes, + oplock_request, allocation_size, sd, ea_list, result, + pinfo, psbuf), + NTSTATUS); +} + +/** + * XXX: macro-ize + */ +static int +onefs_shadow_copy_rename(vfs_handle_struct *handle, const char *old_name, + const char *new_name) +{ + char *old_cpath = NULL; + char *old_snap_component = NULL; + char *new_cpath = NULL; + char *new_snap_component = NULL; + int ret; + + if (shadow_copy_match_name(old_name, &old_snap_component)) + old_cpath = osc_canonicalize_path(old_name, old_snap_component); + + if (shadow_copy_match_name(new_name, &new_snap_component)) + new_cpath = osc_canonicalize_path(new_name, new_snap_component); + + ret = SMB_VFS_NEXT_RENAME(handle, old_cpath ?: old_name, + new_cpath ?: new_name); + + SAFE_FREE(old_cpath); + SAFE_FREE(new_cpath); + + return ret; +} + +static int +onefs_shadow_copy_stat(vfs_handle_struct *handle, const char *path, + SMB_STRUCT_STAT *sbuf) +{ + SHADOW_NEXT(STAT, + (handle, cpath ?: path, sbuf), + int); +} + +static int +onefs_shadow_copy_lstat(vfs_handle_struct *handle, const char *path, + SMB_STRUCT_STAT *sbuf) +{ + SHADOW_NEXT(LSTAT, + (handle, cpath ?: path, sbuf), + int); +} + +static int +onefs_shadow_copy_unlink(vfs_handle_struct *handle, const char *path) +{ + SHADOW_NEXT(UNLINK, + (handle, cpath ?: path), + int); +} + +static int +onefs_shadow_copy_chmod(vfs_handle_struct *handle, const char *path, + mode_t mode) +{ + SHADOW_NEXT(CHMOD, + (handle, cpath ?: path, mode), + int); +} + +static int +onefs_shadow_copy_chown(vfs_handle_struct *handle, const char *path, + uid_t uid, gid_t gid) +{ + SHADOW_NEXT(CHOWN, + (handle, cpath ?: path, uid, gid), + int); +} + +static int +onefs_shadow_copy_lchown(vfs_handle_struct *handle, const char *path, + uid_t uid, gid_t gid) +{ + SHADOW_NEXT(LCHOWN, + (handle, cpath ?: path, uid, gid), + int); +} + +static int +onefs_shadow_copy_chdir(vfs_handle_struct *handle, const char *path) +{ + SHADOW_NEXT(CHDIR, + (handle, cpath ?: path), + int); +} + +static int +onefs_shadow_copy_ntimes(vfs_handle_struct *handle, const char *path, + struct smb_file_time *ft) +{ + SHADOW_NEXT(NTIMES, + (handle, cpath ?: path, ft), + int); + +} + +/** + * XXX: macro-ize + */ +static bool +onefs_shadow_copy_symlink(vfs_handle_struct *handle, + const char *oldpath, const char *newpath) +{ + char *old_cpath = NULL; + char *old_snap_component = NULL; + char *new_cpath = NULL; + char *new_snap_component = NULL; + bool ret; + + if (shadow_copy_match_name(oldpath, &old_snap_component)) + old_cpath = osc_canonicalize_path(oldpath, old_snap_component); + + if (shadow_copy_match_name(newpath, &new_snap_component)) + new_cpath = osc_canonicalize_path(newpath, new_snap_component); + + ret = SMB_VFS_NEXT_SYMLINK(handle, old_cpath ?: oldpath, + new_cpath ?: newpath); + + SAFE_FREE(old_cpath); + SAFE_FREE(new_cpath); + + return ret; +} + +static bool +onefs_shadow_copy_readlink(vfs_handle_struct *handle, const char *path, + char *buf, size_t bufsiz) +{ + SHADOW_NEXT(READLINK, + (handle, cpath ?: path, buf, bufsiz), + bool); +} + +/** + * XXX: macro-ize + */ +static int +onefs_shadow_copy_link(vfs_handle_struct *handle, const char *oldpath, + const char *newpath) +{ + char *old_cpath = NULL; + char *old_snap_component = NULL; + char *new_cpath = NULL; + char *new_snap_component = NULL; + int ret; + + if (shadow_copy_match_name(oldpath, &old_snap_component)) + old_cpath = osc_canonicalize_path(oldpath, old_snap_component); + + if (shadow_copy_match_name(newpath, &new_snap_component)) + new_cpath = osc_canonicalize_path(newpath, new_snap_component); + + ret = SMB_VFS_NEXT_LINK(handle, old_cpath ?: oldpath, + new_cpath ?: newpath); + + SAFE_FREE(old_cpath); + SAFE_FREE(new_cpath); + + return ret; +} + +static int +onefs_shadow_copy_mknod(vfs_handle_struct *handle, const char *path, + mode_t mode, SMB_DEV_T dev) +{ + SHADOW_NEXT(MKNOD, + (handle, cpath ?: path, mode, dev), + int); +} + +static char * +onefs_shadow_copy_realpath(vfs_handle_struct *handle, const char *path, + char *resolved_path) +{ + SHADOW_NEXT(REALPATH, + (handle, cpath ?: path, resolved_path), + char *); +} + +static int onefs_shadow_copy_chflags(struct vfs_handle_struct *handle, + const char *path, unsigned int flags) +{ + SHADOW_NEXT(CHFLAGS, + (handle, cpath ?: path, flags), + int); +} + +static NTSTATUS +onefs_shadow_copy_streaminfo(struct vfs_handle_struct *handle, + struct files_struct *fsp, + const char *path, + TALLOC_CTX *mem_ctx, + unsigned int *num_streams, + struct stream_struct **streams) +{ + SHADOW_NEXT(STREAMINFO, + (handle, fsp, cpath ?: path, mem_ctx, num_streams, + streams), + NTSTATUS); +} + +static int +onefs_shadow_copy_get_real_filename(struct vfs_handle_struct *handle, + const char *full_path, + const char *path, + TALLOC_CTX *mem_ctx, + char **found_name) +{ + SHADOW_NEXT(GET_REAL_FILENAME, + (handle, full_path, cpath ?: path, mem_ctx, found_name), + int); +} + +static NTSTATUS +onefs_shadow_copy_get_nt_acl(struct vfs_handle_struct *handle, + const char *path, uint32 security_info, + struct security_descriptor **ppdesc) +{ + SHADOW_NEXT(GET_NT_ACL, + (handle, cpath ?: path, security_info, ppdesc), + NTSTATUS); +} + +static int +onefs_shadow_copy_chmod_acl(vfs_handle_struct *handle, const char *path, + mode_t mode) +{ + SHADOW_NEXT(CHMOD_ACL, + (handle, cpath ?: path, mode), + int); +} + +static SMB_ACL_T +onefs_shadow_copy_sys_acl_get_file(vfs_handle_struct *handle, + const char *path, SMB_ACL_TYPE_T type) +{ + SHADOW_NEXT(SYS_ACL_GET_FILE, + (handle, cpath ?: path, type), + SMB_ACL_T); +} + +static int +onefs_shadow_copy_sys_acl_set_file(vfs_handle_struct *handle, const char *path, + SMB_ACL_TYPE_T type, SMB_ACL_T theacl) +{ + SHADOW_NEXT(SYS_ACL_SET_FILE, + (handle, cpath ?: path, type, theacl), + int); +} + +static int +onefs_shadow_copy_sys_acl_delete_def_file(vfs_handle_struct *handle, + const char *path) +{ + SHADOW_NEXT(SYS_ACL_DELETE_DEF_FILE, + (handle, cpath ?: path), + int); +} + +static ssize_t +onefs_shadow_copy_getxattr(vfs_handle_struct *handle, const char *path, + const char *name, void *value, size_t size) +{ + SHADOW_NEXT(GETXATTR, + (handle, cpath ?: path, name, value, size), + ssize_t); +} + +static ssize_t +onefs_shadow_copy_lgetxattr(vfs_handle_struct *handle, const char *path, + const char *name, void *value, size_t size) +{ + SHADOW_NEXT(LGETXATTR, + (handle, cpath ?: path, name, value, size), + ssize_t); +} + +static ssize_t +onefs_shadow_copy_listxattr(vfs_handle_struct *handle, const char *path, + char *list, size_t size) +{ + SHADOW_NEXT(LISTXATTR, + (handle, cpath ?: path, list, size), + ssize_t); +} + +static ssize_t +onefs_shadow_copy_llistxattr(vfs_handle_struct *handle, const char *path, + char *list, size_t size) +{ + SHADOW_NEXT(LLISTXATTR, + (handle, cpath ?: path, list, size), + ssize_t); +} + +static int +onefs_shadow_copy_removexattr(vfs_handle_struct *handle, const char *path, + const char *name) +{ + SHADOW_NEXT(REMOVEXATTR, + (handle, cpath ?: path, name), + int); +} + +static int +onefs_shadow_copy_lremovexattr(vfs_handle_struct *handle, const char *path, + const char *name) +{ + SHADOW_NEXT(LREMOVEXATTR, + (handle, cpath ?: path, name), + int); +} + +static int +onefs_shadow_copy_setxattr(vfs_handle_struct *handle, const char *path, + const char *name, const void *value, size_t size, + int flags) +{ + SHADOW_NEXT(SETXATTR, + (handle, cpath ?: path, name, value, size, flags), + int); +} + +static int +onefs_shadow_copy_lsetxattr(vfs_handle_struct *handle, const char *path, + const char *name, const void *value, size_t size, + int flags) +{ + SHADOW_NEXT(LSETXATTR, + (handle, cpath ?: path, name, value, size, flags), + int); +} + +static bool +onefs_shadow_copy_is_offline(struct vfs_handle_struct *handle, + const char *path, SMB_STRUCT_STAT *sbuf) +{ + SHADOW_NEXT(IS_OFFLINE, + (handle, cpath ?: path, sbuf), + bool); +} + +static int +onefs_shadow_copy_set_offline(struct vfs_handle_struct *handle, + const char *path) +{ + SHADOW_NEXT(SET_OFFLINE, + (handle, cpath ?: path), + int); +} + +/* VFS operations structure */ + +static vfs_op_tuple onefs_shadow_copy_ops[] = { + + /* Disk operations */ + + {SMB_VFS_OP(onefs_shadow_copy_disk_free), SMB_VFS_OP_DISK_FREE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_get_shadow_copy_data), + SMB_VFS_OP_GET_SHADOW_COPY_DATA, SMB_VFS_LAYER_OPAQUE}, + {SMB_VFS_OP(onefs_shadow_copy_statvfs), SMB_VFS_OP_STATVFS, + SMB_VFS_LAYER_TRANSPARENT}, + + /* Directory operations */ + + {SMB_VFS_OP(onefs_shadow_copy_opendir), SMB_VFS_OP_OPENDIR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_mkdir), SMB_VFS_OP_MKDIR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_rmdir), SMB_VFS_OP_RMDIR, + SMB_VFS_LAYER_TRANSPARENT}, + + /* File operations */ + + {SMB_VFS_OP(onefs_shadow_copy_open), SMB_VFS_OP_OPEN, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_create_file), SMB_VFS_OP_CREATE_FILE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_rename), SMB_VFS_OP_RENAME, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_stat), SMB_VFS_OP_STAT, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_stat), SMB_VFS_OP_STAT, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_lstat), SMB_VFS_OP_LSTAT, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_unlink), SMB_VFS_OP_UNLINK, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_chmod), SMB_VFS_OP_CHMOD, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_chown), SMB_VFS_OP_CHOWN, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_lchown), SMB_VFS_OP_LCHOWN, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_chdir), SMB_VFS_OP_CHDIR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_ntimes), SMB_VFS_OP_NTIMES, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_symlink), SMB_VFS_OP_SYMLINK, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_readlink), SMB_VFS_OP_READLINK, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_link), SMB_VFS_OP_LINK, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_mknod), SMB_VFS_OP_MKNOD, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_realpath), SMB_VFS_OP_REALPATH, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_chflags), SMB_VFS_OP_CHFLAGS, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_streaminfo), SMB_VFS_OP_STREAMINFO, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_get_real_filename), + SMB_VFS_OP_GET_REAL_FILENAME, SMB_VFS_LAYER_TRANSPARENT}, + + /* NT File ACL operations */ + + {SMB_VFS_OP(onefs_shadow_copy_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT}, + + /* POSIX ACL operations */ + + {SMB_VFS_OP(onefs_shadow_copy_chmod_acl), SMB_VFS_OP_CHMOD_ACL, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_sys_acl_get_file), + SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_sys_acl_set_file), + SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_sys_acl_delete_def_file), + SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT}, + + /* EA operations. */ + + {SMB_VFS_OP(onefs_shadow_copy_getxattr), SMB_VFS_OP_GETXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_lgetxattr), SMB_VFS_OP_LGETXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_listxattr), SMB_VFS_OP_LISTXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_llistxattr), SMB_VFS_OP_LLISTXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_removexattr), SMB_VFS_OP_REMOVEXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_lremovexattr), SMB_VFS_OP_LREMOVEXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_setxattr), SMB_VFS_OP_SETXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_lsetxattr), SMB_VFS_OP_LSETXATTR, + SMB_VFS_LAYER_TRANSPARENT}, + + /* offline operations */ + {SMB_VFS_OP(onefs_shadow_copy_is_offline), SMB_VFS_OP_IS_OFFLINE, + SMB_VFS_LAYER_TRANSPARENT}, + {SMB_VFS_OP(onefs_shadow_copy_set_offline), SMB_VFS_OP_SET_OFFLINE, + SMB_VFS_LAYER_TRANSPARENT}, + + {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +NTSTATUS vfs_shadow_copy_init(void) +{ + NTSTATUS ret; + + ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, + "onefs_shadow_copy", + onefs_shadow_copy_ops); + + if (!NT_STATUS_IS_OK(ret)) + return ret; + + vfs_onefs_shadow_copy_debug_level = debug_add_class("onefs_shadow_copy"); + + if (vfs_onefs_shadow_copy_debug_level == -1) { + vfs_onefs_shadow_copy_debug_level = DBGC_VFS; + DEBUG(0, ("Couldn't register custom debugging class!\n")); + } else { + DEBUG(10, ("Debug class number of 'onefs_shadow_copy': %d\n", + vfs_onefs_shadow_copy_debug_level)); + } + + return ret; +} |