From 9d29f7ea1794380bb1a381570298f52cb4d53041 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Wed, 30 Jan 2013 14:42:46 +0100 Subject: [PATCH] vfs: add vfs_shell_snap module The shell_snap VFS module plumbs into the snapshot (aka shadow-copy) management paths used by Samba's File Server Remote VSS Protocol (FSRVP) server. The following shell callouts may be configured in smb.conf: shell_snap: check path command - Called when an FSRVP client wishes to check whether a given share supports snapshot create/delete requests. - The command is called with a single argument. - The command must return 0 if is capable of being snapshotted. shell_snap: create command - Called when an FSRVP client wishes to create a snapshot. - The command is called with a single argument. - The command must return 0 status if the snapshot was successfully taken. - The command must output the path of the newly created snapshot to stdout. shell_snap: delete command - Called when an FSRVP client wishes to delete a snapshot. - The command is called with and arguments. - The command must return 0 status if the snapshot was successfully removed. Signed-off-by: David Disseldorp Reviewed-by: Jeremy Allison --- source3/modules/vfs_shell_snap.c | 201 +++++++++++++++++++++++++++++++ source3/modules/wscript_build | 8 ++ source3/wscript | 2 +- 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 source3/modules/vfs_shell_snap.c diff --git a/source3/modules/vfs_shell_snap.c b/source3/modules/vfs_shell_snap.c new file mode 100644 index 00000000000..a8c29254419 --- /dev/null +++ b/source3/modules/vfs_shell_snap.c @@ -0,0 +1,201 @@ +/* + * Module for snapshot management using shell callouts + * + * Copyright (C) David Disseldorp 2013-2015 + * + * 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 . + */ + +#include "includes.h" +#include "include/ntioctl.h" +#include "system/filesys.h" +#include "smbd/smbd.h" + +/* + * Check whether a path can be shadow copied. Return the base volume, allowing + * the caller to determine if multiple paths lie on the same base volume. + */ +static NTSTATUS shell_snap_check_path(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + const char *service_path, + char **base_volume) +{ + NTSTATUS status; + const char *cmd; + char *cmd_run; + int ret; + TALLOC_CTX *tmp_ctx; + + cmd = lp_parm_const_string(handle->conn->params->service, + "shell_snap", "check path command", ""); + if ((cmd == NULL) || (strlen(cmd) == 0)) { + DEBUG(0, + ("\"shell_snap:check path command\" not configured\n")); + status = NT_STATUS_NOT_SUPPORTED; + goto err_out; + } + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + /* add service path argument */ + cmd_run = talloc_asprintf(tmp_ctx, "%s %s", cmd, service_path); + if (cmd_run == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_tmp_free; + } + + ret = smbrun(cmd_run, NULL); + if (ret != 0) { + DEBUG(0, ("%s failed with %d\n", cmd_run, ret)); + status = NT_STATUS_NOT_SUPPORTED; + goto err_tmp_free; + } + + /* assume the service path is the base volume */ + *base_volume = talloc_strdup(mem_ctx, service_path); + if (*base_volume == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_tmp_free; + } + status = NT_STATUS_OK; +err_tmp_free: + talloc_free(tmp_ctx); +err_out: + return status; +} + +static NTSTATUS shell_snap_create(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + const char *base_volume, + time_t *tstamp, + bool rw, + char **base_path, + char **snap_path) +{ + const char *cmd; + char *cmd_run; + char **qlines; + int numlines, ret; + int fd = -1; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + + cmd = lp_parm_const_string(handle->conn->params->service, + "shell_snap", "create command", ""); + if ((cmd == NULL) || (strlen(cmd) == 0)) { + DEBUG(1, ("\"shell_snap:create command\" not configured\n")); + status = NT_STATUS_NOT_SUPPORTED; + goto err_out; + } + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_out; + } + + /* add base vol argument */ + cmd_run = talloc_asprintf(tmp_ctx, "%s %s", cmd, base_volume); + if (cmd_run == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_tmp_free; + } + + ret = smbrun(cmd_run, &fd); + talloc_free(cmd_run); + if (ret != 0) { + if (fd != -1) { + close(fd); + } + status = NT_STATUS_UNSUCCESSFUL; + goto err_tmp_free; + } + + numlines = 0; + qlines = fd_lines_load(fd, &numlines, PATH_MAX + 1, tmp_ctx); + close(fd); + + /* script must return the snapshot path as a single line */ + if ((numlines == 0) || (qlines == NULL) || (qlines[0] == NULL)) { + status = NT_STATUS_UNSUCCESSFUL; + goto err_tmp_free; + } + + *base_path = talloc_strdup(mem_ctx, base_volume); + if (*base_path == NULL) { + status = NT_STATUS_NO_MEMORY; + goto err_tmp_free; + } + *snap_path = talloc_strdup(mem_ctx, qlines[0]); + if (*snap_path == NULL) { + status = NT_STATUS_NO_MEMORY; + talloc_free(*base_path); + goto err_tmp_free; + } + + status = NT_STATUS_OK; +err_tmp_free: + talloc_free(tmp_ctx); +err_out: + return status; +} + +static NTSTATUS shell_snap_delete(struct vfs_handle_struct *handle, + TALLOC_CTX *mem_ctx, + char *base_path, + char *snap_path) +{ + const char *cmd; + char *cmd_run; + int ret; + + cmd = lp_parm_const_string(handle->conn->params->service, + "shell_snap", "delete command", ""); + if ((cmd == NULL) || (strlen(cmd) == 0)) { + DEBUG(1, ("\"shell_snap:delete command\" not configured\n")); + return NT_STATUS_NOT_SUPPORTED; + } + + /* add base path and snap path arguments */ + cmd_run = talloc_asprintf(mem_ctx, "%s %s %s", + cmd, base_path, snap_path); + if (cmd_run == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ret = smbrun(cmd_run, NULL); + talloc_free(cmd_run); + if (ret != 0) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +static struct vfs_fn_pointers shell_snap_fns = { + .snap_check_path_fn = shell_snap_check_path, + .snap_create_fn = shell_snap_create, + .snap_delete_fn = shell_snap_delete, +}; + +NTSTATUS vfs_shell_snap_init(void); +NTSTATUS vfs_shell_snap_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, + "shell_snap", &shell_snap_fns); +} diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build index 018cbe91378..da2959378ab 100644 --- a/source3/modules/wscript_build +++ b/source3/modules/wscript_build @@ -469,6 +469,14 @@ bld.SAMBA3_MODULE('vfs_btrfs', internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_btrfs'), enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_btrfs')) +bld.SAMBA3_MODULE('vfs_shell_snap', + subsystem='vfs', + source='vfs_shell_snap.c', + deps='samba-util', + init_function='', + internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_shell_snap'), + enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_shell_snap')) + bld.SAMBA3_SUBSYSTEM('perfcount', source='', deps='smbd_base') diff --git a/source3/wscript b/source3/wscript index 4029813beb2..1134e31fc7e 100644 --- a/source3/wscript +++ b/source3/wscript @@ -1582,7 +1582,7 @@ main() { auth_script vfs_readahead vfs_xattr_tdb vfs_posix_eadb vfs_streams_xattr vfs_streams_depot vfs_acl_xattr vfs_acl_tdb vfs_smb_traffic_analyzer vfs_preopen vfs_catia vfs_scannedonly - vfs_media_harmony vfs_unityed_media vfs_fruit + vfs_media_harmony vfs_unityed_media vfs_fruit vfs_shell_snap vfs_commit vfs_worm vfs_crossrename vfs_linux_xfs_sgid