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);
  }