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
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c
          +++ new/usr/src/cmd/ndmpd/ndmp/ndmpd_chkpnt.c
   1    1  /*
   2    2   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3    3   * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
   4    4   * Copyright (c) 2013 Steven Hartland. All rights reserved.
   5    5   * Copyright (c) 2016 Martin Matuska. All rights reserved.
        6 + * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
   6    7   */
   7    8  
   8    9  /*
   9   10   * BSD 3 Clause License
  10   11   *
  11   12   * Copyright (c) 2007, The Storage Networking Industry Association.
  12   13   *
  13   14   * Redistribution and use in source and binary forms, with or without
  14   15   * modification, are permitted provided that the following conditions
  15   16   * are met:
  16   17   *      - Redistributions of source code must retain the above copyright
  17   18   *        notice, this list of conditions and the following disclaimer.
  18   19   *
  19   20   *      - Redistributions in binary form must reproduce the above copyright
  20   21   *        notice, this list of conditions and the following disclaimer in
  21   22   *        the documentation and/or other materials provided with the
  22   23   *        distribution.
  23   24   *
  24   25   *      - Neither the name of The Storage Networking Industry Association (SNIA)
  25   26   *        nor the names of its contributors may be used to endorse or promote
  26   27   *        products derived from this software without specific prior written
  27   28   *        permission.
  28   29   *
  29   30   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  30   31   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  31   32   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  
    | 
      ↓ open down ↓ | 
    16 lines elided | 
    
      ↑ open up ↑ | 
  
  32   33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  33   34   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  34   35   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  35   36   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  36   37   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  37   38   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  38   39   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  39   40   * POSSIBILITY OF SUCH DAMAGE.
  40   41   */
  41   42  
       43 +#include <syslog.h>
  42   44  #include <stdio.h>
  43   45  #include <string.h>
       46 +#include <sys/mount.h>
  44   47  #include "ndmpd.h"
  45   48  #include <libzfs.h>
  46   49  
  47      -typedef struct snap_param {
  48      -        char *snp_name;
  49      -        boolean_t snp_found;
  50      -} snap_param_t;
  51      -
  52      -static int cleanup_fd = -1;
  53      -
  54   50  /*
  55      - * ndmp_has_backup
  56      - *
  57      - * Call backup function which looks for backup snapshot.
  58      - * This is a callback function used with zfs_iter_snapshots.
  59      - *
  60      - * Parameters:
  61      - *   zhp (input) - ZFS handle pointer
  62      - *   data (output) - 0 - no backup snapshot
  63      - *                   1 - has backup snapshot
  64      - *
  65      - * Returns:
  66      - *   0: on success
  67      - *  -1: otherwise
  68      - */
  69      -static int
  70      -ndmp_has_backup(zfs_handle_t *zhp, void *data)
  71      -{
  72      -        const char *name;
  73      -        snap_param_t *chp = (snap_param_t *)data;
  74      -
  75      -        name = zfs_get_name(zhp);
  76      -        if (name == NULL ||
  77      -            strstr(name, chp->snp_name) == NULL) {
  78      -                zfs_close(zhp);
  79      -                return (-1);
  80      -        }
  81      -
  82      -        chp->snp_found = 1;
  83      -        zfs_close(zhp);
  84      -
  85      -        return (0);
  86      -}
  87      -
  88      -/*
  89      - * ndmp_has_backup_snapshot
  90      - *
  91      - * Returns TRUE if the volume has an active backup snapshot, otherwise,
  92      - * returns FALSE.
  93      - *
  94      - * Parameters:
  95      - *   volname (input) - name of the volume
  96      - *
  97      - * Returns:
  98      - *   0: on success
  99      - *  -1: otherwise
 100      - */
 101      -static int
 102      -ndmp_has_backup_snapshot(char *volname, char *jobname)
 103      -{
 104      -        zfs_handle_t *zhp;
 105      -        snap_param_t snp;
 106      -        char chname[ZFS_MAX_DATASET_NAME_LEN];
 107      -
 108      -        (void) mutex_lock(&zlib_mtx);
 109      -        if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
 110      -                NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname);
 111      -                (void) mutex_unlock(&zlib_mtx);
 112      -                return (-1);
 113      -        }
 114      -
 115      -        snp.snp_found = 0;
 116      -        (void) snprintf(chname, ZFS_MAX_DATASET_NAME_LEN, "@%s", jobname);
 117      -        snp.snp_name = chname;
 118      -
 119      -        (void) zfs_iter_snapshots(zhp, B_FALSE, ndmp_has_backup, &snp);
 120      -        zfs_close(zhp);
 121      -        (void) mutex_unlock(&zlib_mtx);
 122      -
 123      -        return (snp.snp_found);
 124      -}
 125      -
 126      -/*
 127      - * ndmp_create_snapshot
 128      - *
 129      - * This function will parse the path to get the real volume name.
 130      - * It will then create a snapshot based on volume and job name.
 131      - * This function should be called before the NDMP backup is started.
 132      - *
 133      - * Parameters:
 134      - *   vol_name (input) - name of the volume
 135      - *
 136      - * Returns:
 137      - *   0: on success
 138      - *   -1: otherwise
 139      - */
 140      -int
 141      -ndmp_create_snapshot(char *vol_name, char *jname)
 142      -{
 143      -        char vol[ZFS_MAX_DATASET_NAME_LEN];
 144      -
 145      -        if (vol_name == 0 ||
 146      -            get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
 147      -                return (0);
 148      -
 149      -        /*
 150      -         * If there is an old snapshot left from the previous
 151      -         * backup it could be stale one and it must be
 152      -         * removed before using it.
 153      -         */
 154      -        if (ndmp_has_backup_snapshot(vol, jname))
 155      -                (void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL);
 156      -
 157      -        return (snapshot_create(vol, jname, B_FALSE, B_TRUE));
 158      -}
 159      -
 160      -/*
 161      - * ndmp_remove_snapshot
 162      - *
 163      - * This function will parse the path to get the real volume name.
 164      - * It will then remove the snapshot for that volume and job name.
 165      - * This function should be called after NDMP backup is finished.
 166      - *
 167      - * Parameters:
 168      - *   vol_name (input) - name of the volume
 169      - *
 170      - * Returns:
 171      - *   0: on success
 172      - *   -1: otherwise
 173      - */
 174      -int
 175      -ndmp_remove_snapshot(char *vol_name, char *jname)
 176      -{
 177      -        char vol[ZFS_MAX_DATASET_NAME_LEN];
 178      -
 179      -        if (vol_name == 0 ||
 180      -            get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
 181      -                return (0);
 182      -
 183      -        return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL));
 184      -}
 185      -
 186      -/*
 187   51   * Put a hold on snapshot
 188   52   */
 189   53  int
 190      -snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
       54 +snapshot_hold(char *volname, char *snapname, char *jname)
 191   55  {
 192   56          zfs_handle_t *zhp;
 193   57          char *p;
 194   58  
 195   59          if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
 196      -                NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
       60 +                syslog(LOG_ERR, "Cannot open volume %s.", volname);
 197   61                  return (-1);
 198   62          }
 199      -
 200      -        if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV,
 201      -            O_RDWR|O_EXCL)) < 0) {
 202      -                NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno);
 203      -                zfs_close(zhp);
 204      -                return (-1);
 205      -        }
 206      -
 207   63          p = strchr(snapname, '@') + 1;
 208      -        if (zfs_hold(zhp, p, jname, recursive, cleanup_fd) != 0) {
 209      -                NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p);
       64 +        /*
       65 +         * The -1 tells the lower levels there are no snapshots
       66 +         * to clean up.
       67 +         */
       68 +        if (zfs_hold(zhp, p, jname, B_FALSE, -1) != 0) {
       69 +                syslog(LOG_ERR, "Cannot hold snapshot %s", p);
 210   70                  zfs_close(zhp);
 211   71                  return (-1);
 212   72          }
 213   73          zfs_close(zhp);
 214   74          return (0);
 215   75  }
 216   76  
 217   77  int
 218      -snapshot_release(char *volname, char *snapname, char *jname,
 219      -    boolean_t recursive)
       78 +snapshot_release(char *volname, char *snapname, char *jname)
 220   79  {
 221   80          zfs_handle_t *zhp;
 222   81          char *p;
 223   82          int rv = 0;
 224   83  
 225   84          if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
 226      -                NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
       85 +                syslog(LOG_ERR, "Cannot open volume %s", volname);
 227   86                  return (-1);
 228   87          }
 229   88  
 230   89          p = strchr(snapname, '@') + 1;
 231      -        if (zfs_release(zhp, p, jname, recursive) != 0) {
 232      -                NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
       90 +        if (zfs_release(zhp, p, jname, B_FALSE) != 0) {
       91 +                syslog(LOG_DEBUG, "Cannot release snapshot %s", p);
 233   92                  rv = -1;
 234   93          }
 235      -        if (cleanup_fd != -1) {
 236      -                (void) close(cleanup_fd);
 237      -                cleanup_fd = -1;
 238      -        }
 239   94          zfs_close(zhp);
 240   95          return (rv);
 241   96  }
 242   97  
 243   98  /*
 244      - * Create a snapshot on the volume
       99 + * Create a snapshot, put a hold on it, clone it, and mount it in a
      100 + * well known location for so the backup process can traverse its
      101 + * directory tree structure.
 245  102   */
 246  103  int
 247      -snapshot_create(char *volname, char *jname, boolean_t recursive,
 248      -    boolean_t hold)
      104 +backup_dataset_create(ndmp_lbr_params_t *nlp)
 249  105  {
 250      -        char snapname[ZFS_MAX_DATASET_NAME_LEN];
      106 +        char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
      107 +        char *slash;
 251  108          int rv;
 252  109  
 253      -        if (!volname || !*volname)
      110 +        if (nlp == NULL) {
 254  111                  return (-1);
      112 +        }
 255  113  
 256      -        (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
 257      -            "%s@%s", volname, jname);
      114 +        (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
      115 +        /*
      116 +         * Pull out the pool name component from the volname
      117 +         * to use it to build snapshot and clone names.
      118 +         */
      119 +        slash = strchr(zpoolname, '/');
      120 +        if (slash != NULL) {
      121 +                *slash = '\0';
      122 +        }
 258  123  
      124 +        (void) snprintf(nlp->nlp_clonename, sizeof (nlp->nlp_clonename),
      125 +            "%s/%s", zpoolname, nlp->nlp_job_name);
      126 +
 259  127          (void) mutex_lock(&zlib_mtx);
 260      -        if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
 261      -            == -1) {
 262      -                if (errno == EEXIST) {
      128 +
      129 +        /*
      130 +         * If "checkpoint" is not enabled, create the normal
      131 +         * snapshot and continue normal backup.  If it is
      132 +         * enabled, the "checkpoint" name has been already set
      133 +         * so we just have to clone it.
      134 +         */
      135 +        if (!NLP_ISCHKPNTED(nlp)) {
      136 +                (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
      137 +                    "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
      138 +
      139 +                if ((rv = zfs_snapshot(zlibh, nlp->nlp_snapname,
      140 +                    B_FALSE, NULL)) != 0) {
      141 +                        if (errno == EEXIST) {
      142 +                                (void) mutex_unlock(&zlib_mtx);
      143 +                                return (0);
      144 +                        }
      145 +                        syslog(LOG_ERR,
      146 +                            "backup_dataset_create: %s failed (err=%d): %s",
      147 +                            nlp->nlp_snapname, errno,
      148 +                            libzfs_error_description(zlibh));
 263  149                          (void) mutex_unlock(&zlib_mtx);
 264      -                        return (0);
      150 +                        return (rv);
 265  151                  }
 266      -                NDMP_LOG(LOG_DEBUG,
 267      -                    "snapshot_create: %s failed (err=%d): %s",
 268      -                    snapname, errno, libzfs_error_description(zlibh));
 269      -                (void) mutex_unlock(&zlib_mtx);
 270      -                return (rv);
      152 +                if (snapshot_hold(nlp->nlp_vol,
      153 +                    nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
      154 +                        syslog(LOG_DEBUG,
      155 +                            "backup_dataset_create: %s "
      156 +                            "hold failed (err=%d): %s",
      157 +                            nlp->nlp_snapname,
      158 +                            errno, libzfs_error_description(zlibh));
      159 +                        (void) mutex_unlock(&zlib_mtx);
      160 +                        return (-1);
      161 +                }
      162 +                syslog(LOG_DEBUG,
      163 +                    "Using %s NdmpBackup snapshot for backup",
      164 +                    nlp->nlp_snapname);
      165 +
 271  166          }
 272      -        if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) {
 273      -                NDMP_LOG(LOG_DEBUG,
 274      -                    "snapshot_create: %s hold failed (err=%d): %s",
 275      -                    snapname, errno, libzfs_error_description(zlibh));
      167 +
      168 +        if (ndmp_clone_snapshot(nlp) != 0) {
      169 +                syslog(LOG_ERR,
      170 +                    "backup_dataset_create: %s clone failed (err=%d): %s",
      171 +                    nlp->nlp_snapname, errno, libzfs_error_description(zlibh));
 276  172                  (void) mutex_unlock(&zlib_mtx);
 277  173                  return (-1);
 278  174          }
 279      -
 280  175          (void) mutex_unlock(&zlib_mtx);
 281  176          return (0);
 282  177  }
 283  178  
 284  179  /*
 285      - * Remove and release the backup snapshot
      180 + * Unmount, release, and destroy the snapshot created for backup.
 286  181   */
 287  182  int
 288      -snapshot_destroy(char *volname, char *jname, boolean_t recursive,
 289      -    boolean_t hold, int *zfs_err)
      183 +backup_dataset_destroy(ndmp_lbr_params_t *nlp)
 290  184  {
 291      -        char snapname[ZFS_MAX_DATASET_NAME_LEN];
 292      -        zfs_handle_t *zhp;
 293      -        zfs_type_t ztype;
 294      -        char *namep;
      185 +        char zpoolname[ZFS_MAX_DATASET_NAME_LEN];
      186 +        char *slash;
      187 +        zfs_handle_t *vol_zhp;
      188 +        zfs_handle_t *cln_zhp;
 295  189          int err;
      190 +        int rv = 0;
 296  191  
 297      -        if (zfs_err)
 298      -                *zfs_err = 0;
 299      -
 300      -        if (!volname || !*volname)
      192 +        if (nlp == NULL) {
      193 +                syslog(LOG_DEBUG,
      194 +                    "nlp NULL in backup_dataset_destroy");
 301  195                  return (-1);
      196 +        }
 302  197  
 303      -        if (recursive) {
 304      -                ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
 305      -                namep = volname;
 306      -        } else {
 307      -                (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
 308      -                    "%s@%s", volname, jname);
 309      -                namep = snapname;
 310      -                ztype = ZFS_TYPE_SNAPSHOT;
      198 +        (void) strlcpy(zpoolname, nlp->nlp_vol, sizeof (zpoolname));
      199 +        slash = strchr(zpoolname, '/');
      200 +        if (slash != NULL) {
      201 +                *slash = '\0';
 311  202          }
 312  203  
      204 +        if (!NLP_ISCHKPNTED(nlp)) {
      205 +                (void) snprintf(nlp->nlp_snapname, sizeof (nlp->nlp_snapname),
      206 +                    "%s@%s", nlp->nlp_vol, nlp->nlp_job_name);
      207 +        }
      208 +
      209 +
      210 +        syslog(LOG_DEBUG, "Snapname in backup_dataset_destroy is [%s]",
      211 +            nlp->nlp_snapname);
      212 +
      213 +        /*
      214 +         * Destroy using this sequence
      215 +         * zfs release <volume>@<jname>
      216 +         * zfs destroy <pool>/<jname>
      217 +         * zfs destroy <pool>/<volume>@<jname>
      218 +         */
 313  219          (void) mutex_lock(&zlib_mtx);
 314      -        if (hold &&
 315      -            snapshot_release(volname, namep, jname, recursive) != 0) {
 316      -                NDMP_LOG(LOG_DEBUG,
 317      -                    "snapshot_destroy: %s release failed (err=%d): %s",
 318      -                    namep, errno, libzfs_error_description(zlibh));
      220 +
      221 +        /*
      222 +         * Release the normal snapshot but don't try to
      223 +         * release if it's a "checkpoint" because the hold
      224 +         * wasn't put on it to begin with.
      225 +         */
      226 +        if (!NLP_ISCHKPNTED(nlp)) {
      227 +                if (snapshot_release(nlp->nlp_vol,
      228 +                    nlp->nlp_snapname, NDMP_RCF_BASENAME) != 0) {
      229 +                        syslog(LOG_DEBUG,
      230 +                            "backup_dataset_destroy: %s "
      231 +                            "release failed (err=%d): %s",
      232 +                            nlp->nlp_clonename, errno,
      233 +                            libzfs_error_description(zlibh));
      234 +                        (void) mutex_unlock(&zlib_mtx);
      235 +                        return (-1);
      236 +                }
      237 +        } else {
      238 +                syslog(LOG_DEBUG, "Checkpointed dataset not held "
      239 +                    "will not release [%s]", nlp->nlp_snapname);
      240 +        }
      241 +
      242 +        /*
      243 +         * Open the clone to get descriptor
      244 +         */
      245 +        if ((cln_zhp = zfs_open(zlibh, nlp->nlp_clonename,
      246 +            ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
      247 +                syslog(LOG_ERR,
      248 +                    "backup_dataset_destroy: open %s failed",
      249 +                    nlp->nlp_clonename);
 319  250                  (void) mutex_unlock(&zlib_mtx);
 320  251                  return (-1);
 321  252          }
 322  253  
 323      -        if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
 324      -                NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
 325      -                    namep);
      254 +        /*
      255 +         * Open the mounted clone to get descriptor for unmount
      256 +         */
      257 +        if ((vol_zhp = zfs_open(zlibh, nlp->nlp_vol,
      258 +            ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM)) == NULL) {
      259 +                syslog(LOG_ERR,
      260 +                    "backup_dataset_destroy: open %s failed [while trying "
      261 +                    "to destroy]", nlp->nlp_vol);
      262 +                zfs_close(cln_zhp);
 326  263                  (void) mutex_unlock(&zlib_mtx);
 327  264                  return (-1);
 328  265          }
 329  266  
 330      -        if (recursive) {
 331      -                err = zfs_destroy_snaps(zhp, jname, B_TRUE);
 332      -        } else {
 333      -                err = zfs_destroy(zhp, B_TRUE);
      267 +        /*
      268 +         * This unmounts the clone which was just traversed for backup
      269 +         */
      270 +        if ((err = zfs_unmount(cln_zhp, NULL, 0)) != 0) {
      271 +                syslog(LOG_INFO, "failed to unmount [%s]", nlp->nlp_clonename);
      272 +                rv = -1;
      273 +                goto _out;
 334  274          }
 335  275  
      276 +        /*
      277 +         * This destroys the clone
      278 +         */
      279 +        err = zfs_destroy(cln_zhp, B_TRUE);
 336  280          if (err) {
 337      -                NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
 338      -                    namep,
 339      -                    recursive,
      281 +                syslog(LOG_ERR, "%s destroy: %d; %s; %s",
      282 +                    nlp->nlp_clonename,
 340  283                      libzfs_errno(zlibh),
 341  284                      libzfs_error_action(zlibh),
 342  285                      libzfs_error_description(zlibh));
      286 +                rv = -1;
      287 +                goto _out;
      288 +        }
 343  289  
 344      -                if (zfs_err)
 345      -                        *zfs_err = err;
      290 +        /*
      291 +         * This destroys the snapshot of the current backup - but,
      292 +         * don't destroy it if it is an "checkpoint" from AutoSync
      293 +         * or HPR.
      294 +         */
      295 +        if (!NLP_ISCHKPNTED(nlp)) {
      296 +                if ((err = zfs_destroy_snaps(vol_zhp,
      297 +                    nlp->nlp_job_name, B_TRUE))) {
      298 +                        syslog(LOG_ERR, "%s destroy: %d; %s; %s",
      299 +                            nlp->nlp_job_name,
      300 +                            libzfs_errno(zlibh),
      301 +                            libzfs_error_action(zlibh),
      302 +                            libzfs_error_description(zlibh));
      303 +                        rv = -1;
      304 +                        syslog(LOG_DEBUG, "Destroy [%s]", nlp->nlp_snapname);
      305 +                        goto _out;
      306 +                }
      307 +        } else {
      308 +                syslog(LOG_DEBUG, "Checkpointed checkpoint will not destroy [%s]",
      309 +                    nlp->nlp_snapname);
 346  310          }
 347  311  
 348      -        zfs_close(zhp);
      312 +_out:
      313 +        zfs_close(vol_zhp);
      314 +        zfs_close(cln_zhp);
 349  315          (void) mutex_unlock(&zlib_mtx);
 350  316  
 351      -        return (0);
      317 +        /*
      318 +         * The zfs_clone() call will have mounted the snapshot
      319 +         * in the file system at this point - so clean it up.
      320 +         */
      321 +        if (rv == 0) {
      322 +                if (rmdir(nlp->nlp_mountpoint) != 0) {
      323 +                        syslog(LOG_ERR,
      324 +                            "Failed to remove mount point [%s]",
      325 +                            nlp->nlp_mountpoint);
      326 +                        return (-1);
      327 +                }
      328 +        }
      329 +
      330 +        return (rv);
 352  331  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX