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