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