1 /*
   2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
   4  * Copyright (c) 2013 Steven Hartland. All rights reserved.
   5  * Copyright (c) 2016 Martin Matuska. All rights reserved.
   6  */
   7 
   8 /*
   9  * BSD 3 Clause License
  10  *
  11  * Copyright (c) 2007, The Storage Networking Industry Association.
  12  *
  13  * Redistribution and use in source and binary forms, with or without
  14  * modification, are permitted provided that the following conditions
  15  * are met:
  16  *      - Redistributions of source code must retain the above copyright
  17  *        notice, this list of conditions and the following disclaimer.
  18  *
  19  *      - Redistributions in binary form must reproduce the above copyright
  20  *        notice, this list of conditions and the following disclaimer in
  21  *        the documentation and/or other materials provided with the
  22  *        distribution.
  23  *
  24  *      - Neither the name of The Storage Networking Industry Association (SNIA)
  25  *        nor the names of its contributors may be used to endorse or promote
  26  *        products derived from this software without specific prior written
  27  *        permission.
  28  *
  29  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  30  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  32  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  33  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  39  * POSSIBILITY OF SUCH DAMAGE.
  40  */
  41 
  42 #include <stdio.h>
  43 #include <string.h>
  44 #include "ndmpd.h"
  45 #include <libzfs.h>
  46 
  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 /*
  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  * Put a hold on snapshot
 188  */
 189 int
 190 snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
 191 {
 192         zfs_handle_t *zhp;
 193         char *p;
 194 
 195         if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
 196                 NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
 197                 return (-1);
 198         }
 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         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);
 210                 zfs_close(zhp);
 211                 return (-1);
 212         }
 213         zfs_close(zhp);
 214         return (0);
 215 }
 216 
 217 int
 218 snapshot_release(char *volname, char *snapname, char *jname,
 219     boolean_t recursive)
 220 {
 221         zfs_handle_t *zhp;
 222         char *p;
 223         int rv = 0;
 224 
 225         if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
 226                 NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
 227                 return (-1);
 228         }
 229 
 230         p = strchr(snapname, '@') + 1;
 231         if (zfs_release(zhp, p, jname, recursive) != 0) {
 232                 NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
 233                 rv = -1;
 234         }
 235         if (cleanup_fd != -1) {
 236                 (void) close(cleanup_fd);
 237                 cleanup_fd = -1;
 238         }
 239         zfs_close(zhp);
 240         return (rv);
 241 }
 242 
 243 /*
 244  * Create a snapshot on the volume
 245  */
 246 int
 247 snapshot_create(char *volname, char *jname, boolean_t recursive,
 248     boolean_t hold)
 249 {
 250         char snapname[ZFS_MAX_DATASET_NAME_LEN];
 251         int rv;
 252 
 253         if (!volname || !*volname)
 254                 return (-1);
 255 
 256         (void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
 257             "%s@%s", volname, jname);
 258 
 259         (void) mutex_lock(&zlib_mtx);
 260         if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
 261             == -1) {
 262                 if (errno == EEXIST) {
 263                         (void) mutex_unlock(&zlib_mtx);
 264                         return (0);
 265                 }
 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);
 271         }
 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));
 276                 (void) mutex_unlock(&zlib_mtx);
 277                 return (-1);
 278         }
 279 
 280         (void) mutex_unlock(&zlib_mtx);
 281         return (0);
 282 }
 283 
 284 /*
 285  * Remove and release the backup snapshot
 286  */
 287 int
 288 snapshot_destroy(char *volname, char *jname, boolean_t recursive,
 289     boolean_t hold, int *zfs_err)
 290 {
 291         char snapname[ZFS_MAX_DATASET_NAME_LEN];
 292         zfs_handle_t *zhp;
 293         zfs_type_t ztype;
 294         char *namep;
 295         int err;
 296 
 297         if (zfs_err)
 298                 *zfs_err = 0;
 299 
 300         if (!volname || !*volname)
 301                 return (-1);
 302 
 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;
 311         }
 312 
 313         (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));
 319                 (void) mutex_unlock(&zlib_mtx);
 320                 return (-1);
 321         }
 322 
 323         if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
 324                 NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
 325                     namep);
 326                 (void) mutex_unlock(&zlib_mtx);
 327                 return (-1);
 328         }
 329 
 330         if (recursive) {
 331                 err = zfs_destroy_snaps(zhp, jname, B_TRUE);
 332         } else {
 333                 err = zfs_destroy(zhp, B_TRUE);
 334         }
 335 
 336         if (err) {
 337                 NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
 338                     namep,
 339                     recursive,
 340                     libzfs_errno(zlibh),
 341                     libzfs_error_action(zlibh),
 342                     libzfs_error_description(zlibh));
 343 
 344                 if (zfs_err)
 345                         *zfs_err = err;
 346         }
 347 
 348         zfs_close(zhp);
 349         (void) mutex_unlock(&zlib_mtx);
 350 
 351         return (0);
 352 }