Shadow Copy with ZFS
Ed Plese
ed at edplese.com
Wed Feb 7 03:45:15 GMT 2007
I've been working on a VFS module for Samba that allows the shadow copy
functionality to work better with Solaris and specifically ZFS snapshots.
The current shadow_copy VFS module works but has the following
limitations:
* Each ZFS snapshot in .zfs/snapshot must be symlinked to the root of
the share which complicates snapshot creation and deletion.
* Snapshots are not sorted by date in the Microsoft Shadow Copy Client
GUI. The existing shadow_copy module shares this problem when using
certain filesystems.
The attached patch against 3.0.23d creates a new shadow_copy_zfs module
based off the shadow_copy module that fixes the two above issues.
The limitations of the patch are:
* Snapshots must be named in a particular fashion similar to the
original shadow_copy module. Take a snapshot like:
# zfs snapshot pool/fs at GMT-`date -u +%Y.%m.%d-%H.%M.%S`
* The path of the Samba share must be at the root of a ZFS filesystem.
Example usage in smb.conf is:
[testshare]
comment = Shadow Copy Share
writable = yes
path = /pool/fs
vfs objects = shadow_copy_zfs
shadow_copy_zfs: sort = desc
Is there any desire to have something like this merged into Samba? If so,
would it be better to go about adding this functionality by creating a
more generalized module with customizable snapshot paths instead of a
ZFS-specific module?
Thanks,
Ed Plese
-------------- next part --------------
diff -Nur samba-3.0.23d/source/Makefile.in samba-3.0.23d-zfs/source/Makefile.in
--- samba-3.0.23d/source/Makefile.in 2006-11-14 08:42:15.000000000 -0600
+++ samba-3.0.23d-zfs/source/Makefile.in 2006-12-12 11:40:22.000000000 -0600
@@ -372,6 +372,7 @@
VFS_CAP_OBJ = modules/vfs_cap.o
VFS_EXPAND_MSDFS_OBJ = modules/vfs_expand_msdfs.o
VFS_SHADOW_COPY_OBJ = modules/vfs_shadow_copy.o
+VFS_SHADOW_COPY_ZFS_OBJ = modules/vfs_shadow_copy_zfs.o
VFS_AFSACL_OBJ = modules/vfs_afsacl.o
VFS_CATIA_OBJ = modules/vfs_catia.o
@@ -1352,6 +1353,11 @@
@$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_SHADOW_COPY_OBJ:.o=. at PICSUFFIX@) \
@SONAMEFLAG@`basename $@`
+bin/shadow_copy_zfs. at SHLIBEXT@: $(VFS_SHADOW_COPY_ZFS_OBJ:.o=. at PICSUFFIX@)
+ @echo "Building plugin $@"
+ @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_SHADOW_COPY_ZFS_OBJ:.o=. at PICSUFFIX@) \
+ @SONAMEFLAG@`basename $@`
+
bin/cap. at SHLIBEXT@: $(VFS_CAP_OBJ:.o=. at PICSUFFIX@)
@echo "Building plugin $@"
@$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_CAP_OBJ:.o=. at PICSUFFIX@) \
diff -Nur samba-3.0.23d/source/configure samba-3.0.23d-zfs/source/configure
--- samba-3.0.23d/source/configure 2006-11-15 09:14:25.000000000 -0600
+++ samba-3.0.23d-zfs/source/configure 2006-12-12 11:40:22.000000000 -0600
@@ -4787,7 +4787,7 @@
default_static_modules="pdb_smbpasswd pdb_tdbsam rpc_lsa rpc_samr rpc_reg rpc_lsa_ds rpc_wks rpc_svcctl rpc_ntsvcs rpc_net rpc_netdfs rpc_srv rpc_spoolss rpc_eventlog auth_sam auth_unix auth_winbind auth_server auth_domain auth_builtin"
-default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy charset_CP850 charset_CP437 auth_script"
+default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy vfs_shadow_copy_zfs charset_CP850 charset_CP437 auth_script"
if test "x$developer" = xyes; then
default_static_modules="$default_static_modules rpc_echo"
@@ -50459,6 +50459,43 @@
fi
+ echo "$as_me:$LINENO: checking how to build vfs_shadow_copy_zfs" >&5
+echo $ECHO_N "checking how to build vfs_shadow_copy_zfs... $ECHO_C" >&6
+ if test "$MODULE_vfs_shadow_copy_zfs"; then
+ DEST=$MODULE_vfs_shadow_copy_zfs
+ elif test "$MODULE_vfs" -a "$MODULE_DEFAULT_vfs_shadow_copy_zfs"; then
+ DEST=$MODULE_vfs
+ else
+ DEST=$MODULE_DEFAULT_vfs_shadow_copy_zfs
+ fi
+
+ if test x"$DEST" = xSHARED; then
+
+cat >>confdefs.h <<\_ACEOF
+#define vfs_shadow_copy_zfs_init init_module
+_ACEOF
+
+ VFS_MODULES="$VFS_MODULES "bin/shadow_copy_zfs.$SHLIBEXT""
+ echo "$as_me:$LINENO: result: shared" >&5
+echo "${ECHO_T}shared" >&6
+
+ string_shared_modules="$string_shared_modules vfs_shadow_copy_zfs"
+ elif test x"$DEST" = xSTATIC; then
+ init_static_modules_vfs="$init_static_modules_vfs vfs_shadow_copy_zfs_init();"
+ decl_static_modules_vfs="$decl_static_modules_vfs extern NTSTATUS vfs_shadow_copy_zfs_init(void);"
+ string_static_modules="$string_static_modules vfs_shadow_copy_zfs"
+ VFS_STATIC="$VFS_STATIC \$(VFS_SHADOW_COPY_ZFS_OBJ)"
+
+
+ echo "$as_me:$LINENO: result: static" >&5
+echo "${ECHO_T}static" >&6
+ else
+ string_ignored_modules="$string_ignored_modules vfs_shadow_copy_zfs"
+ echo "$as_me:$LINENO: result: not" >&5
+echo "${ECHO_T}not" >&6
+ fi
+
+
echo "$as_me:$LINENO: checking how to build vfs_afsacl" >&5
echo $ECHO_N "checking how to build vfs_afsacl... $ECHO_C" >&6
if test "$MODULE_vfs_afsacl"; then
diff -Nur samba-3.0.23d/source/configure.in samba-3.0.23d-zfs/source/configure.in
--- samba-3.0.23d/source/configure.in 2006-11-14 08:42:15.000000000 -0600
+++ samba-3.0.23d-zfs/source/configure.in 2006-12-12 11:40:22.000000000 -0600
@@ -581,7 +581,7 @@
default_static_modules="pdb_smbpasswd pdb_tdbsam rpc_lsa rpc_samr rpc_reg rpc_lsa_ds rpc_wks rpc_svcctl rpc_ntsvcs rpc_net rpc_netdfs rpc_srv rpc_spoolss rpc_eventlog auth_sam auth_unix auth_winbind auth_server auth_domain auth_builtin"
dnl These are preferably build shared, and static if dlopen() is not available
-default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy charset_CP850 charset_CP437 auth_script"
+default_shared_modules="vfs_recycle vfs_audit vfs_extd_audit vfs_full_audit vfs_netatalk vfs_fake_perms vfs_default_quota vfs_readonly vfs_cap vfs_expand_msdfs vfs_shadow_copy vfs_shadow_copy_zfs charset_CP850 charset_CP437 auth_script"
if test "x$developer" = xyes; then
default_static_modules="$default_static_modules rpc_echo"
@@ -5596,6 +5596,7 @@
SMB_MODULE(vfs_cap, \$(VFS_CAP_OBJ), "bin/cap.$SHLIBEXT", VFS)
SMB_MODULE(vfs_expand_msdfs, \$(VFS_EXPAND_MSDFS_OBJ), "bin/expand_msdfs.$SHLIBEXT", VFS)
SMB_MODULE(vfs_shadow_copy, \$(VFS_SHADOW_COPY_OBJ), "bin/shadow_copy.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_shadow_copy_zfs, \$(VFS_SHADOW_COPY_ZFS_OBJ), "bin/shadow_copy_zfs.$SHLIBEXT", VFS)
SMB_MODULE(vfs_afsacl, \$(VFS_AFSACL_OBJ), "bin/afsacl.$SHLIBEXT", VFS)
SMB_MODULE(vfs_catia, \$(VFS_CATIA_OBJ), "bin/catia.$SHLIBEXT", VFS)
SMB_SUBSYSTEM(VFS,smbd/vfs.o)
diff -Nur samba-3.0.23d/source/include/config.h.in samba-3.0.23d-zfs/source/include/config.h.in
--- samba-3.0.23d/source/include/config.h.in 2006-11-15 09:14:22.000000000 -0600
+++ samba-3.0.23d-zfs/source/include/config.h.in 2006-12-12 11:40:22.000000000 -0600
@@ -2485,5 +2485,8 @@
/* Whether to build vfs_shadow_copy as shared module */
#undef vfs_shadow_copy_init
+/* Whether to build vfs_shadow_copy_zfs as shared module */
+#undef vfs_shadow_copy_zfs_init
+
/* Define to `unsigned short' if <sys/types.h> does not define. */
#undef wchar_t
diff -Nur samba-3.0.23d/source/modules/vfs_shadow_copy_zfs.c samba-3.0.23d-zfs/source/modules/vfs_shadow_copy_zfs.c
--- samba-3.0.23d/source/modules/vfs_shadow_copy_zfs.c 1969-12-31 18:00:00.000000000 -0600
+++ samba-3.0.23d-zfs/source/modules/vfs_shadow_copy_zfs.c 2006-12-15 08:07:07.000000000 -0600
@@ -0,0 +1,252 @@
+/*
+ * implementation of an Shadow Copy module for use with ZFS snapshots
+ *
+ * Copyright (C) Stefan Metzmacher 2003-2004
+ *
+ * Modifications for use with ZFS added by Ed Plese <ed at edplese.com>.
+ *
+ * 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"
+
+/*
+ * This module is based off of the original shadow_copy module with
+ * changes to search for the snapshot directories in a subdirectory of the
+ * share rather than the root of the share. This allows ZFS snapshots
+ * to be used as shadow copies without the need to create symbolic links
+ * to them.
+ *
+ * ZFS snapshots must be created with names similar in form to the original.
+ *
+ * Creating a snapshot is as simple as:
+ *
+ * # zfs snapshot pool/fs at GMT-`date -u +%Y.%m.%d-%H.%M.%S`
+ *
+ * Note: Files must differ to be displayed via Windows Explorer!
+ * Directories are always displayed...
+*/
+
+static int vfs_shadow_copy_zfs_debug_level = DBGC_VFS;
+
+#undef DBGC_CLASS
+#define DBGC_CLASS vfs_shadow_copy_zfs_debug_level
+
+#define SHADOW_COPY_ZFS_PREFIX "@GMT-"
+
+static int shadow_copy_zfs_label_cmp_asc(const void *x, const void *y)
+{
+ return strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
+}
+
+static int shadow_copy_zfs_label_cmp_desc(const void *x, const void *y)
+{
+ return -strncmp((char *)x, (char *)y, sizeof(SHADOW_COPY_LABEL));
+}
+
+static void shadow_copy_zfs_sort_data(vfs_handle_struct *handle, SHADOW_COPY_DATA *shadow_copy_data)
+{
+ const char *tmp_str = NULL;
+
+ if (shadow_copy_data && shadow_copy_data->num_volumes > 0 &&
+ shadow_copy_data->labels) {
+
+ tmp_str = lp_parm_const_string(SNUM(handle->conn),
+ "shadow_copy_zfs", "sort", "");
+
+ if (strcmp(tmp_str, "asc") == 0) {
+ qsort(shadow_copy_data->labels,
+ shadow_copy_data->num_volumes,
+ sizeof(SHADOW_COPY_LABEL),
+ shadow_copy_zfs_label_cmp_asc);
+
+ } else if (strcmp(tmp_str, "desc") == 0) {
+ qsort(shadow_copy_data->labels,
+ shadow_copy_data->num_volumes,
+ sizeof(SHADOW_COPY_LABEL),
+ shadow_copy_zfs_label_cmp_desc);
+ }
+ }
+
+ return;
+}
+
+static BOOL shadow_copy_zfs_match_snapshot_name(const char *name)
+{
+ /* ignore the leading @ in SHADOW_COPY_ZFS_PREFIX */
+ if (strncmp(SHADOW_COPY_ZFS_PREFIX+1, name, sizeof(SHADOW_COPY_ZFS_PREFIX)-2) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+static BOOL shadow_copy_zfs_match_file_name(const char *name)
+{
+ if (strncmp(SHADOW_COPY_ZFS_PREFIX, name, sizeof(SHADOW_COPY_ZFS_PREFIX)-1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+static SMB_STRUCT_DIR *shadow_copy_zfs_opendir(vfs_handle_struct *handle, connection_struct *conn, const char *fname, const char *mask, uint32 attr)
+{
+ char modpath[FILENAME_MAX];
+
+ if (shadow_copy_zfs_match_file_name(fname)) {
+ snprintf(modpath, FILENAME_MAX, ".zfs/snapshot/%s", &fname[1]);
+ } else {
+ snprintf(modpath, FILENAME_MAX, "%s", fname);
+ }
+
+ DEBUG(10, ("shadow_copy_zfs_opendir: [%s] -> [%s]\n", fname, modpath));
+
+ SMB_STRUCT_DIR *p = SMB_VFS_NEXT_OPENDIR(handle,conn,modpath,mask,attr);
+
+ if (!p) {
+ DEBUG(2, ("shadow_copy_zfs_opendir: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n", modpath));
+ return NULL;
+ }
+
+ return p;
+}
+
+static int shadow_copy_zfs_get_shadow_copy_data(vfs_handle_struct *handle, files_struct *fsp, SHADOW_COPY_DATA *shadow_copy_data, BOOL labels)
+{
+ char modpath[FILENAME_MAX];
+
+ snprintf(modpath, FILENAME_MAX, "%s/.zfs/snapshot", fsp->conn->connectpath);
+ DEBUG(10, ("shadow_copy_zfs_get_shadow_copy_data: [%s] -> [%s]\n",
+ fsp->conn->connectpath, modpath));
+ SMB_STRUCT_DIR *p = SMB_VFS_NEXT_OPENDIR(handle, fsp->conn, modpath,
+ NULL, 0);
+
+ shadow_copy_data->num_volumes = 0;
+ shadow_copy_data->labels = NULL;
+
+ if (!p) {
+ DEBUG(2, ("shadow_copy_zfs_get_shadow_copy_data: SMB_VFS_NEXT_OPENDIR() failed for [%s]\n", modpath));
+ return -1;
+ }
+
+ while (True) {
+ SHADOW_COPY_LABEL *tlabels;
+ SMB_STRUCT_DIRENT *d;
+
+ d = SMB_VFS_NEXT_READDIR(handle, fsp->conn, p);
+ if (d == NULL) {
+ break;
+ }
+
+ if (!shadow_copy_zfs_match_snapshot_name(d->d_name)) {
+ DEBUG(10, ("shadow_copy_zfs_get_shadow_copy_data: ignore [%s]\n", d->d_name));
+ continue;
+ }
+
+ DEBUG(10, ("shadow_copy_zfs_get_shadow_copy_data: not ignore [%s]\n", d->d_name));
+
+ 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(2, ("shadow_copy_zfs_get_shadow_copy_data: Out of memory\n"));
+ SMB_VFS_NEXT_CLOSEDIR(handle, fsp->conn, p);
+ return -1;
+ }
+
+ snprintf(tlabels[shadow_copy_data->num_volumes++], sizeof(*tlabels), "@%s", d->d_name);
+
+ shadow_copy_data->labels = tlabels;
+ }
+
+ shadow_copy_zfs_sort_data(handle, shadow_copy_data);
+
+ SMB_VFS_NEXT_CLOSEDIR(handle, fsp->conn, p);
+ return 0;
+}
+
+static int shadow_copy_zfs_stat(vfs_handle_struct *handle, connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ char modpath[FILENAME_MAX];
+
+ if (shadow_copy_zfs_match_file_name(fname)) {
+ snprintf(modpath, FILENAME_MAX, ".zfs/snapshot/%s", &fname[1]);
+ } else {
+ snprintf(modpath, FILENAME_MAX, "%s", fname);
+ }
+
+ DEBUG(10, ("shadow_copy_zfs_stat: [%s] -> [%s]\n", fname, modpath));
+
+ return SMB_VFS_NEXT_STAT(handle, conn, modpath, sbuf);
+}
+
+static int shadow_copy_zfs_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
+{
+ char modpath[FILENAME_MAX];
+
+ if (shadow_copy_zfs_match_file_name(fname)) {
+ snprintf(modpath, FILENAME_MAX, ".zfs/snapshot/%s", &fname[1]);
+ } else {
+ snprintf(modpath, FILENAME_MAX, "%s", fname);
+ }
+
+ DEBUG(10, ("shadow_copy_zfs_open: [%s] -> [%s]\n", fname, modpath));
+
+ return SMB_VFS_NEXT_OPEN(handle, conn, modpath, flags, mode);
+}
+
+/* VFS operations structure */
+
+static vfs_op_tuple shadow_copy_zfs_ops[] = {
+ {SMB_VFS_OP(shadow_copy_zfs_opendir),
+ SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy_zfs_stat),
+ SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy_zfs_open),
+ SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT},
+ {SMB_VFS_OP(shadow_copy_zfs_get_shadow_copy_data),
+ SMB_VFS_OP_GET_SHADOW_COPY_DATA,SMB_VFS_LAYER_OPAQUE},
+ {SMB_VFS_OP(NULL),
+ SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_shadow_copy_zfs_init(void)
+{
+ NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+ "shadow_copy_zfs", shadow_copy_zfs_ops);
+
+ if (!NT_STATUS_IS_OK(ret))
+ return ret;
+
+ vfs_shadow_copy_zfs_debug_level = debug_add_class("shadow_copy_zfs");
+ if (vfs_shadow_copy_zfs_debug_level == -1) {
+ vfs_shadow_copy_zfs_debug_level = DBGC_VFS;
+ DEBUG(2, ("%s: Couldn't register custom debugging class!\n",
+ "vfs_shadow_copy_zfs_init"));
+ } else {
+ DEBUG(10, ("%s: Debug class number of '%s': %d\n",
+ "vfs_shadow_copy_zfs_init", "shadow_copy_zfs",
+ vfs_shadow_copy_zfs_debug_level));
+ }
+
+ return ret;
+}
More information about the samba-technical
mailing list