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