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 }