Shadow Copy with ZFS

Alison Winters alisonw at sgi.com
Tue Feb 6 15:40:51 GMT 2007


Hi Ed,

I've kinda-sorta been half looking at the same thing for SGI's XVM
volume manager snapshots.  I think there is room for improvement in the
existing shadow copy module - perhaps we should have an smb.conf option
indicating the location of and the naming convention of the mounted
snapshots.  Do you think that'd be a viable approach?

Alison

Ed Plese wrote:
> 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
> 
> 
> ------------------------------------------------------------------------
> 
> 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