1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  14  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  15  * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
  16  */
  17 
  18 #ifndef _KERNEL
  19 #include <assert.h>
  20 #include <stdio.h>
  21 #include <stdlib.h>
  22 #include <strings.h>
  23 #include <umem.h>
  24 #include <stddef.h>
  25 #include <zlib.h>
  26 #include <libnvpair.h>
  27 #else
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/sysmacros.h>
  31 #include <sys/nvpair.h>
  32 
  33 #include <util/sscanf.h>
  34 #endif
  35 
  36 #include <sys/zfs_context.h>
  37 #include <sys/zfs_ioctl.h>
  38 #include <sys/zio_compress.h>
  39 #include <sys/zio_checksum.h>
  40 #include "zfs_fletcher.h"
  41 #include "zfs_sendrecv.h"
  42 
  43 #ifndef _KERNEL
  44 #ifndef SET_ERROR
  45 #define SET_ERROR(err) (err)
  46 #endif
  47 #endif
  48 
  49 static int
  50 zfs_mem_alloc(void **data, size_t data_sz)
  51 {
  52 #ifdef _KERNEL
  53                 *data = kmem_zalloc(data_sz, KM_SLEEP);
  54 #else
  55                 if ((*data = calloc(1, data_sz)) == NULL)
  56                         return (SET_ERROR(ENOMEM));
  57 #endif
  58                 return (0);
  59 }
  60 
  61 /* ARGSUSED */
  62 static void
  63 zfs_mem_free(void *data, size_t data_sz)
  64 {
  65 #ifdef _KERNEL
  66                 kmem_free(data, data_sz);
  67 #else
  68                 free(data);
  69 #endif
  70 }
  71 
  72 typedef struct fsavl_node {
  73         avl_node_t fn_node;
  74         nvlist_t *fn_nvfs;
  75         char *fn_snapname;
  76         uint64_t fn_guid;
  77 } fsavl_node_t;
  78 
  79 /*
  80  * Routines for dealing with the AVL tree of fs-nvlists
  81  */
  82 
  83 static int
  84 fsavl_compare(const void *arg1, const void *arg2)
  85 {
  86         const fsavl_node_t *fn1 = arg1;
  87         const fsavl_node_t *fn2 = arg2;
  88 
  89         if (fn1->fn_guid > fn2->fn_guid)
  90                 return (+1);
  91         else if (fn1->fn_guid < fn2->fn_guid)
  92                 return (-1);
  93         else
  94                 return (0);
  95 }
  96 
  97 /*
  98  * Given the GUID of a snapshot, find its containing filesystem and
  99  * (optionally) name.
 100  */
 101 nvlist_t *
 102 fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
 103 {
 104         fsavl_node_t fn_find;
 105         fsavl_node_t *fn;
 106 
 107         fn_find.fn_guid = snapguid;
 108 
 109         fn = avl_find(avl, &fn_find, NULL);
 110         if (fn != NULL) {
 111                 if (snapname != NULL)
 112                         *snapname = fn->fn_snapname;
 113 
 114                 return (fn->fn_nvfs);
 115         }
 116 
 117         return (NULL);
 118 }
 119 
 120 void
 121 fsavl_destroy(avl_tree_t *avl)
 122 {
 123         fsavl_node_t *fn;
 124         void *cookie;
 125 
 126         if (avl == NULL)
 127                 return;
 128 
 129         cookie = NULL;
 130         while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
 131                 zfs_mem_free(fn, sizeof (fsavl_node_t));
 132 
 133         avl_destroy(avl);
 134 
 135         zfs_mem_free(avl, sizeof (avl_tree_t));
 136 }
 137 
 138 static int
 139 fsavl_create_nodes(avl_tree_t *fsavl, nvlist_t *nvfs)
 140 {
 141         int err;
 142         fsavl_node_t *fn, fn_find;
 143         nvlist_t *snaps = NULL;
 144         nvpair_t *snapelem = NULL;
 145         uint64_t guid;
 146 
 147         err = nvlist_lookup_nvlist(nvfs, "snaps", &snaps);
 148         if (err != 0)
 149                 return (SET_ERROR(err));
 150 
 151         while ((snapelem = nvlist_next_nvpair(snaps, snapelem)) != NULL) {
 152                 err = nvpair_value_uint64(snapelem, &guid);
 153                 if (err != 0)
 154                         return (SET_ERROR(err));
 155 
 156                 /*
 157                  * Note: if there are multiple snaps with the
 158                  * same GUID, we ignore all but one.
 159                  */
 160                 fn_find.fn_guid = guid;
 161                 if (avl_find(fsavl, &fn_find, NULL) != NULL)
 162                         continue;
 163 
 164                 err = zfs_mem_alloc((void **)&fn, sizeof (fsavl_node_t));
 165                 if (err != 0)
 166                         return (err);
 167 
 168                 fn->fn_nvfs = nvfs;
 169                 fn->fn_snapname = nvpair_name(snapelem);
 170                 fn->fn_guid = guid;
 171 
 172                 avl_add(fsavl, fn);
 173         }
 174 
 175         return (0);
 176 }
 177 
 178 /*
 179  * Given an nvlist, produce an avl tree of snapshots, ordered by guid
 180  */
 181 int
 182 fsavl_create(nvlist_t *fss, avl_tree_t **fsavl_result)
 183 {
 184         int err;
 185         avl_tree_t *fsavl;
 186         nvpair_t *fselem = NULL;
 187 
 188         err = zfs_mem_alloc((void **)&fsavl, sizeof (avl_tree_t));
 189         if (err != 0)
 190                 return (err);
 191 
 192         avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
 193             offsetof(fsavl_node_t, fn_node));
 194 
 195         while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
 196                 nvlist_t *nvfs = NULL;
 197 
 198                 err = nvpair_value_nvlist(fselem, &nvfs);
 199                 if (err != 0)
 200                         break;
 201 
 202                 err = fsavl_create_nodes(fsavl, nvfs);
 203                 if (err != 0)
 204                         break;
 205         }
 206 
 207         if (err != 0)
 208                 fsavl_destroy(fsavl);
 209         else
 210                 *fsavl_result = fsavl;
 211 
 212         return (err);
 213 }
 214 
 215 int
 216 zfs_send_resume_token_to_nvlist_impl(const char *token, nvlist_t **result_nvl)
 217 {
 218         int err;
 219         unsigned int version;
 220         int nread, len;
 221         uint64_t checksum, packed_len;
 222         unsigned char *compressed = NULL;
 223         void *packed = NULL;
 224 
 225         /*
 226          * Decode token header, which is:
 227          *   <token version>-<checksum of payload>-<uncompressed payload length>
 228          * Note that the only supported token version is 1.
 229          */
 230         nread = sscanf(token, "%u-%llx-%llx-",
 231             &version, (unsigned long long *)&checksum,
 232             (unsigned long long *)&packed_len);
 233         if (nread != 3)
 234                 return (SET_ERROR(EINVAL));
 235 
 236         if (version != ZFS_SEND_RESUME_TOKEN_VERSION)
 237                 return (SET_ERROR(ENOTSUP));
 238 
 239         /* convert hexadecimal representation to binary */
 240         token = strrchr(token, '-') + 1;
 241         len = strlen(token) / 2;
 242         err = zfs_mem_alloc((void **)&compressed, len + 1);
 243         if (err != 0)
 244                 return (err);
 245 
 246         for (int i = 0; i < len; i++) {
 247                 nread = sscanf(token + i * 2, "%2hhx", compressed + i);
 248                 if (nread != 1) {
 249                         zfs_mem_free(compressed, len + 1);
 250                         return (SET_ERROR(EBADMSG));
 251                 }
 252         }
 253 
 254         /* verify checksum */
 255         zio_cksum_t cksum;
 256         fletcher_4_native(compressed, len, NULL, &cksum);
 257         if (cksum.zc_word[0] != checksum) {
 258                 zfs_mem_free(compressed, len + 1);
 259                 return (SET_ERROR(ECKSUM));
 260         }
 261 
 262         /* uncompress */
 263         err = zfs_mem_alloc(&packed, packed_len);
 264         if (err != 0) {
 265                 zfs_mem_free(compressed, len + 1);
 266                 return (err);
 267         }
 268 
 269 #ifdef _KERNEL
 270         err = gzip_decompress(compressed, packed, len, packed_len, 0);
 271 #else
 272         uLongf packed_len_long = packed_len;
 273         if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
 274             packed_len_long != packed_len)
 275                 err = -1;
 276 #endif
 277 
 278         if (err != 0) {
 279                 zfs_mem_free(packed, packed_len);
 280                 zfs_mem_free(compressed, len + 1);
 281                 return (SET_ERROR(ENOSR));
 282         }
 283 
 284         /* unpack nvlist */
 285         nvlist_t *nv = NULL;
 286 #ifdef _KERNEL
 287         err = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
 288 #else
 289         err = nvlist_unpack(packed, packed_len, &nv, 0);
 290 #endif
 291 
 292         zfs_mem_free(packed, packed_len);
 293         zfs_mem_free(compressed, len + 1);
 294         if (err != 0)
 295                 return (SET_ERROR(ENODATA));
 296 
 297         *result_nvl = nv;
 298         return (0);
 299 }