Print this page
NEX-13374 NDMP should be able to backup unmounted ZFS filesystems
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Revert "NEX-5801 Snapshots left over after failed backups"
This reverts commit f182fb95f09036db71fbfc6f0a6b90469b761f21.
NEX-5801 Snapshots left over after failed backups
Reviewed by: Rick Mesta <rick.mesta@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-2911 NDMP logging should use syslog and is too chatty

*** 1,10 **** --- 1,11 ---- /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2015 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2016 Martin Matuska. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. All rights reserved. */ /* * BSD 3 Clause License *
*** 37,352 **** * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <string.h> #include "ndmpd.h" #include <libzfs.h> - typedef struct snap_param { - char *snp_name; - boolean_t snp_found; - } snap_param_t; - - static int cleanup_fd = -1; - /* - * ndmp_has_backup - * - * Call backup function which looks for backup snapshot. - * This is a callback function used with zfs_iter_snapshots. - * - * Parameters: - * zhp (input) - ZFS handle pointer - * data (output) - 0 - no backup snapshot - * 1 - has backup snapshot - * - * Returns: - * 0: on success - * -1: otherwise - */ - static int - ndmp_has_backup(zfs_handle_t *zhp, void *data) - { - const char *name; - snap_param_t *chp = (snap_param_t *)data; - - name = zfs_get_name(zhp); - if (name == NULL || - strstr(name, chp->snp_name) == NULL) { - zfs_close(zhp); - return (-1); - } - - chp->snp_found = 1; - zfs_close(zhp); - - return (0); - } - - /* - * ndmp_has_backup_snapshot - * - * Returns TRUE if the volume has an active backup snapshot, otherwise, - * returns FALSE. - * - * Parameters: - * volname (input) - name of the volume - * - * Returns: - * 0: on success - * -1: otherwise - */ - static int - ndmp_has_backup_snapshot(char *volname, char *jobname) - { - zfs_handle_t *zhp; - snap_param_t snp; - char chname[ZFS_MAX_DATASET_NAME_LEN]; - - (void) mutex_lock(&zlib_mtx); - if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) { - NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname); - (void) mutex_unlock(&zlib_mtx); - return (-1); - } - - snp.snp_found = 0; - (void) snprintf(chname, ZFS_MAX_DATASET_NAME_LEN, "@%s", jobname); - snp.snp_name = chname; - - (void) zfs_iter_snapshots(zhp, B_FALSE, ndmp_has_backup, &snp); - zfs_close(zhp); - (void) mutex_unlock(&zlib_mtx); - - return (snp.snp_found); - } - - /* - * ndmp_create_snapshot - * - * This function will parse the path to get the real volume name. - * It will then create a snapshot based on volume and job name. - * This function should be called before the NDMP backup is started. - * - * Parameters: - * vol_name (input) - name of the volume - * - * Returns: - * 0: on success - * -1: otherwise - */ - int - ndmp_create_snapshot(char *vol_name, char *jname) - { - char vol[ZFS_MAX_DATASET_NAME_LEN]; - - if (vol_name == 0 || - get_zfsvolname(vol, sizeof (vol), vol_name) == -1) - return (0); - - /* - * If there is an old snapshot left from the previous - * backup it could be stale one and it must be - * removed before using it. - */ - if (ndmp_has_backup_snapshot(vol, jname)) - (void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL); - - return (snapshot_create(vol, jname, B_FALSE, B_TRUE)); - } - - /* - * ndmp_remove_snapshot - * - * This function will parse the path to get the real volume name. - * It will then remove the snapshot for that volume and job name. - * This function should be called after NDMP backup is finished. - * - * Parameters: - * vol_name (input) - name of the volume - * - * Returns: - * 0: on success - * -1: otherwise - */ - int - ndmp_remove_snapshot(char *vol_name, char *jname) - { - char vol[ZFS_MAX_DATASET_NAME_LEN]; - - if (vol_name == 0 || - get_zfsvolname(vol, sizeof (vol), vol_name) == -1) - return (0); - - return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL)); - } - - /* * Put a hold on snapshot */ int ! snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive) { zfs_handle_t *zhp; char *p; if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) { ! NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname); return (-1); } - - if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV, - O_RDWR|O_EXCL)) < 0) { - NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno); - zfs_close(zhp); - return (-1); - } - p = strchr(snapname, '@') + 1; ! if (zfs_hold(zhp, p, jname, recursive, cleanup_fd) != 0) { ! NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p); zfs_close(zhp); return (-1); } zfs_close(zhp); return (0); } int ! snapshot_release(char *volname, char *snapname, char *jname, ! boolean_t recursive) { zfs_handle_t *zhp; char *p; int rv = 0; if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) { ! NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname); return (-1); } p = strchr(snapname, '@') + 1; ! if (zfs_release(zhp, p, jname, recursive) != 0) { ! NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p); rv = -1; } - if (cleanup_fd != -1) { - (void) close(cleanup_fd); - cleanup_fd = -1; - } zfs_close(zhp); return (rv); } /* ! * Create a snapshot on the volume */ int ! snapshot_create(char *volname, char *jname, boolean_t recursive, ! boolean_t hold) { ! char snapname[ZFS_MAX_DATASET_NAME_LEN]; int rv; ! if (!volname || !*volname) return (-1); ! (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN, ! "%s@%s", volname, jname); (void) mutex_lock(&zlib_mtx); ! if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL)) ! == -1) { if (errno == EEXIST) { (void) mutex_unlock(&zlib_mtx); return (0); } ! NDMP_LOG(LOG_DEBUG, ! "snapshot_create: %s failed (err=%d): %s", ! snapname, errno, libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (rv); } ! if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) { ! NDMP_LOG(LOG_DEBUG, ! "snapshot_create: %s hold failed (err=%d): %s", ! snapname, errno, libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (-1); } (void) mutex_unlock(&zlib_mtx); return (0); } /* ! * Remove and release the backup snapshot */ int ! snapshot_destroy(char *volname, char *jname, boolean_t recursive, ! boolean_t hold, int *zfs_err) { ! char snapname[ZFS_MAX_DATASET_NAME_LEN]; ! zfs_handle_t *zhp; ! zfs_type_t ztype; ! char *namep; int err; ! if (zfs_err) ! *zfs_err = 0; ! ! if (!volname || !*volname) return (-1); ! if (recursive) { ! ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM; ! namep = volname; ! } else { ! (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN, ! "%s@%s", volname, jname); ! namep = snapname; ! ztype = ZFS_TYPE_SNAPSHOT; } (void) mutex_lock(&zlib_mtx); ! if (hold && ! snapshot_release(volname, namep, jname, recursive) != 0) { ! NDMP_LOG(LOG_DEBUG, ! "snapshot_destroy: %s release failed (err=%d): %s", ! namep, errno, libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (-1); } ! if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) { ! NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed", ! namep); (void) mutex_unlock(&zlib_mtx); return (-1); } ! if (recursive) { ! err = zfs_destroy_snaps(zhp, jname, B_TRUE); ! } else { ! err = zfs_destroy(zhp, B_TRUE); } if (err) { ! NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s", ! namep, ! recursive, libzfs_errno(zlibh), libzfs_error_action(zlibh), libzfs_error_description(zlibh)); ! if (zfs_err) ! *zfs_err = err; } ! zfs_close(zhp); (void) mutex_unlock(&zlib_mtx); ! return (0); } --- 38,331 ---- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ + #include <syslog.h> #include <stdio.h> #include <string.h> + #include <sys/mount.h> #include "ndmpd.h" #include <libzfs.h> /* * Put a hold on snapshot */ int ! snapshot_hold(char *volname, char *snapname, char *jname) { zfs_handle_t *zhp; char *p; if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) { ! syslog(LOG_ERR, "Cannot open volume %s.", volname); return (-1); } p = strchr(snapname, '@') + 1; ! /* ! * The -1 tells the lower levels there are no snapshots ! * to clean up. ! */ ! if (zfs_hold(zhp, p, jname, B_FALSE, -1) != 0) { ! syslog(LOG_ERR, "Cannot hold snapshot %s", p); zfs_close(zhp); return (-1); } zfs_close(zhp); return (0); } int ! snapshot_release(char *volname, char *snapname, char *jname) { zfs_handle_t *zhp; char *p; int rv = 0; if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) { ! syslog(LOG_ERR, "Cannot open volume %s", volname); return (-1); } p = strchr(snapname, '@') + 1; ! if (zfs_release(zhp, p, jname, B_FALSE) != 0) { ! syslog(LOG_DEBUG, "Cannot release snapshot %s", p); rv = -1; } zfs_close(zhp); return (rv); } /* ! * Create a snapshot, put a hold on it, clone it, and mount it in a ! * well known location for so the backup process can traverse its ! * directory tree structure. */ int ! backup_dataset_create(ndmp_lbr_params_t *nlp) { ! char zpoolname[ZFS_MAX_DATASET_NAME_LEN]; ! char *slash; int rv; ! if (nlp == NULL) { return (-1); + } ! (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname)); ! /* ! * Pull out the pool name component from the volname ! * to use it to build snapshot and clone names. ! */ ! slash = strchr(zpoolname, '/'); ! if (slash != NULL) { ! *slash = '\0'; ! } + (void) snprintf(nlp->nlp_clonename, sizeof (nlp->nlp_clonename), + "%s/%s", zpoolname, nlp->nlp_job_name); + (void) mutex_lock(&zlib_mtx); ! ! /* ! * If "checkpoint" is not enabled, create the normal ! * snapshot and continue normal backup. If it is ! * enabled, the "checkpoint" name has been already set ! * so we just have to clone it. ! */ ! if (!NLP_ISCHKPNTED(nlp)) { ! (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname), ! "%s@%s", nlp->nlp_vol, nlp->nlp_job_name); ! ! if ((rv = zfs_snapshot(zlibh, nlp->nlp_snapname, ! B_FALSE, NULL)) != 0) { if (errno == EEXIST) { (void) mutex_unlock(&zlib_mtx); return (0); } ! syslog(LOG_ERR, ! "backup_dataset_create: %s failed (err=%d): %s", ! nlp->nlp_snapname, errno, ! libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (rv); } ! if (snapshot_hold(nlp->nlp_vol, ! nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) { ! syslog(LOG_DEBUG, ! "backup_dataset_create: %s " ! "hold failed (err=%d): %s", ! nlp->nlp_snapname, ! errno, libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (-1); } + syslog(LOG_DEBUG, + "Using %s NdmpBackup snapshot for backup", + nlp->nlp_snapname); + } + + if (ndmp_clone_snapshot(nlp) != 0) { + syslog(LOG_ERR, + "backup_dataset_create: %s clone failed (err=%d): %s", + nlp->nlp_snapname, errno, libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); + return (-1); + } + (void) mutex_unlock(&zlib_mtx); return (0); } /* ! * Unmount, release, and destroy the snapshot created for backup. */ int ! backup_dataset_destroy(ndmp_lbr_params_t *nlp) { ! char zpoolname[ZFS_MAX_DATASET_NAME_LEN]; ! char *slash; ! zfs_handle_t *vol_zhp; ! zfs_handle_t *cln_zhp; int err; + int rv = 0; ! if (nlp == NULL) { ! syslog(LOG_DEBUG, ! "nlp NULL in backup_dataset_destroy"); return (-1); + } ! (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname)); ! slash = strchr(zpoolname, '/'); ! if (slash != NULL) { ! *slash = '\0'; } + if (!NLP_ISCHKPNTED(nlp)) { + (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname), + "%s@%s", nlp->nlp_vol, nlp->nlp_job_name); + } + + + syslog(LOG_DEBUG, "Snapname in backup_dataset_destroy is [%s]", + nlp->nlp_snapname); + + /* + * Destroy using this sequence + * zfs release <volume>@<jname> + * zfs destroy <pool>/<jname> + * zfs destroy <pool>/<volume>@<jname> + */ (void) mutex_lock(&zlib_mtx); ! ! /* ! * Release the normal snapshot but don't try to ! * release if it's a "checkpoint" because the hold ! * wasn't put on it to begin with. ! */ ! if (!NLP_ISCHKPNTED(nlp)) { ! if (snapshot_release(nlp->nlp_vol, ! nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) { ! syslog(LOG_DEBUG, ! "backup_dataset_destroy: %s " ! "release failed (err=%d): %s", ! nlp->nlp_clonename, errno, ! libzfs_error_description(zlibh)); (void) mutex_unlock(&zlib_mtx); return (-1); } + } else { + syslog(LOG_DEBUG, "Checkpointed dataset not held " + "will not release [%s]", nlp->nlp_snapname); + } ! /* ! * Open the clone to get descriptor ! */ ! if ((cln_zhp = zfs_open(zlibh, nlp->nlp_clonename, ! ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) { ! syslog(LOG_ERR, ! "backup_dataset_destroy: open %s failed", ! nlp->nlp_clonename); (void) mutex_unlock(&zlib_mtx); return (-1); } ! /* ! * Open the mounted clone to get descriptor for unmount ! */ ! if ((vol_zhp = zfs_open(zlibh, nlp->nlp_vol, ! ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) { ! syslog(LOG_ERR, ! "backup_dataset_destroy: open %s failed [while trying " ! "to destroy]", nlp->nlp_vol); ! zfs_close(cln_zhp); ! (void) mutex_unlock(&zlib_mtx); ! return (-1); } + /* + * This unmounts the clone which was just traversed for backup + */ + if ((err = zfs_unmount(cln_zhp, NULL, 0)) != 0) { + syslog(LOG_INFO, "failed to unmount [%s]", nlp->nlp_clonename); + rv = -1; + goto _out; + } + + /* + * This destroys the clone + */ + err = zfs_destroy(cln_zhp, B_TRUE); if (err) { ! syslog(LOG_ERR, "%s destroy: %d; %s; %s", ! nlp->nlp_clonename, libzfs_errno(zlibh), libzfs_error_action(zlibh), libzfs_error_description(zlibh)); + rv = -1; + goto _out; + } ! /* ! * This destroys the snapshot of the current backup - but, ! * don't destroy it if it is an "checkpoint" from AutoSync ! * or HPR. ! */ ! if (!NLP_ISCHKPNTED(nlp)) { ! if ((err = zfs_destroy_snaps(vol_zhp, ! nlp->nlp_job_name, B_TRUE))) { ! syslog(LOG_ERR, "%s destroy: %d; %s; %s", ! nlp->nlp_job_name, ! libzfs_errno(zlibh), ! libzfs_error_action(zlibh), ! libzfs_error_description(zlibh)); ! rv = -1; ! syslog(LOG_DEBUG, "Destroy [%s]", nlp->nlp_snapname); ! goto _out; } + } else { + syslog(LOG_DEBUG, "Checkpointed checkpoint will not destroy [%s]", + nlp->nlp_snapname); + } ! _out: ! zfs_close(vol_zhp); ! zfs_close(cln_zhp); (void) mutex_unlock(&zlib_mtx); ! /* ! * The zfs_clone() call will have mounted the snapshot ! * in the file system at this point - so clean it up. ! */ ! if (rv == 0) { ! if (rmdir(nlp->nlp_mountpoint) != 0) { ! syslog(LOG_ERR, ! "Failed to remove mount point [%s]", ! nlp->nlp_mountpoint); ! return (-1); ! } ! } ! ! return (rv); }