]>
Commit | Line | Data |
---|---|---|
bf78d5b2 RM |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
2cdc8995 | 3 | * Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc. |
bf78d5b2 | 4 | * Copyright 2010 Sun Microsystems, Inc. |
bf78d5b2 | 5 | * |
54207d4b | 6 | * GRUB is free software; you can redistribute it and/or modify |
bf78d5b2 RM |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
54207d4b | 11 | * GRUB is distributed in the hope that it will be useful, |
bf78d5b2 RM |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
54207d4b | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
bf78d5b2 RM |
18 | */ |
19 | /* | |
20 | * The zfs plug-in routines for GRUB are: | |
21 | * | |
22 | * zfs_mount() - locates a valid uberblock of the root pool and reads | |
23 | * in its MOS at the memory address MOS. | |
24 | * | |
25 | * zfs_open() - locates a plain file object by following the MOS | |
26 | * and places its dnode at the memory address DNODE. | |
27 | * | |
28 | * zfs_read() - read in the data blocks pointed by the DNODE. | |
29 | * | |
30 | */ | |
31 | ||
32 | #include <grub/err.h> | |
33 | #include <grub/file.h> | |
34 | #include <grub/mm.h> | |
35 | #include <grub/misc.h> | |
36 | #include <grub/disk.h> | |
37 | #include <grub/partition.h> | |
38 | #include <grub/dl.h> | |
39 | #include <grub/types.h> | |
40 | #include <grub/zfs/zfs.h> | |
41 | #include <grub/zfs/zio.h> | |
42 | #include <grub/zfs/dnode.h> | |
43 | #include <grub/zfs/uberblock_impl.h> | |
44 | #include <grub/zfs/vdev_impl.h> | |
45 | #include <grub/zfs/zio_checksum.h> | |
46 | #include <grub/zfs/zap_impl.h> | |
47 | #include <grub/zfs/zap_leaf.h> | |
48 | #include <grub/zfs/zfs_znode.h> | |
49 | #include <grub/zfs/dmu.h> | |
50 | #include <grub/zfs/dmu_objset.h> | |
51 | #include <grub/zfs/sa_impl.h> | |
52 | #include <grub/zfs/dsl_dir.h> | |
53 | #include <grub/zfs/dsl_dataset.h> | |
290766fb | 54 | #include <grub/deflate.h> |
1bc7cc1b | 55 | #include <grub/crypto.h> |
bf78d5b2 | 56 | |
e745cf0c VS |
57 | GRUB_MOD_LICENSE ("GPLv3+"); |
58 | ||
bf78d5b2 RM |
59 | #define ZPOOL_PROP_BOOTFS "bootfs" |
60 | ||
61 | #define MIN(a,b) (((a) < (b)) ? (a) : (b)) | |
62 | ||
63 | /* | |
64 | * For nvlist manipulation. (from nvpair.h) | |
65 | */ | |
66 | #define NV_ENCODE_NATIVE 0 | |
67 | #define NV_ENCODE_XDR 1 | |
68 | #define NV_BIG_ENDIAN 0 | |
69 | #define NV_LITTLE_ENDIAN 1 | |
70 | #define DATA_TYPE_UINT64 8 | |
71 | #define DATA_TYPE_STRING 9 | |
72 | #define DATA_TYPE_NVLIST 19 | |
73 | #define DATA_TYPE_NVLIST_ARRAY 20 | |
74 | ||
75 | #ifndef GRUB_UTIL | |
76 | static grub_dl_t my_mod; | |
77 | #endif | |
78 | ||
79 | #define P2PHASE(x, align) ((x) & ((align) - 1)) | |
80 | #define DVA_OFFSET_TO_PHYS_SECTOR(offset) \ | |
81 | ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT) | |
82 | ||
83 | /* | |
84 | * FAT ZAP data structures | |
85 | */ | |
86 | #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ | |
87 | #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n)))) | |
88 | #define CHAIN_END 0xffff /* end of the chunk chain */ | |
89 | ||
90 | /* | |
91 | * The amount of space within the chunk available for the array is: | |
92 | * chunk size - space for type (1) - space for next pointer (2) | |
93 | */ | |
94 | #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) | |
95 | ||
96 | #define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5) | |
97 | #define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs)) | |
98 | #define LEAF_HASH(bs, h) \ | |
99 | ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \ | |
100 | ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len))) | |
101 | ||
102 | /* | |
103 | * The amount of space available for chunks is: | |
104 | * block size shift - hash entry size (2) * number of hash | |
105 | * entries - header space (2*chunksize) | |
106 | */ | |
107 | #define ZAP_LEAF_NUMCHUNKS(bs) \ | |
108 | (((1<<bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(bs)) / \ | |
109 | ZAP_LEAF_CHUNKSIZE - 2) | |
110 | ||
111 | /* | |
112 | * The chunks start immediately after the hash table. The end of the | |
113 | * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a | |
114 | * chunk_t. | |
115 | */ | |
116 | #define ZAP_LEAF_CHUNK(l, bs, idx) \ | |
117 | ((zap_leaf_chunk_t *)(l->l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx] | |
118 | #define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry) | |
119 | ||
120 | ||
121 | /* | |
122 | * Decompression Entry - lzjb | |
123 | */ | |
124 | #ifndef NBBY | |
125 | #define NBBY 8 | |
126 | #endif | |
127 | ||
1bc7cc1b VS |
128 | enum grub_zfs_algo |
129 | { | |
130 | GRUB_ZFS_ALGO_CCM, | |
131 | GRUB_ZFS_ALGO_GCM, | |
132 | }; | |
133 | ||
134 | struct grub_zfs_key | |
135 | { | |
136 | grub_uint64_t algo; | |
137 | grub_uint8_t enc_nonce[13]; | |
138 | grub_uint8_t unused[3]; | |
139 | grub_uint8_t enc_key[48]; | |
140 | grub_uint8_t unknown_purpose_nonce[13]; | |
141 | grub_uint8_t unused2[3]; | |
142 | grub_uint8_t unknown_purpose_key[48]; | |
143 | }; | |
144 | ||
2cdc8995 VS |
145 | struct grub_zfs_wrap_key |
146 | { | |
147 | struct grub_zfs_wrap_key *next; | |
148 | grub_uint64_t key[GRUB_ZFS_MAX_KEYLEN / 8]; | |
149 | }; | |
150 | ||
bf78d5b2 RM |
151 | extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t); |
152 | ||
153 | typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start, | |
154 | grub_size_t s_len, grub_size_t d_len); | |
155 | typedef struct decomp_entry | |
156 | { | |
157 | char *name; | |
158 | zfs_decomp_func_t *decomp_func; | |
159 | } decomp_entry_t; | |
160 | ||
161 | typedef struct dnode_end | |
162 | { | |
163 | dnode_phys_t dn; | |
164 | grub_zfs_endian_t endian; | |
165 | } dnode_end_t; | |
166 | ||
fdfde32a VS |
167 | struct grub_zfs_device_desc |
168 | { | |
39db1a3f | 169 | enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type; |
fdfde32a | 170 | grub_uint64_t id; |
8ff84951 VS |
171 | grub_uint64_t guid; |
172 | ||
173 | /* Valid only for non-leafs. */ | |
174 | unsigned n_children; | |
175 | struct grub_zfs_device_desc *children; | |
176 | ||
39db1a3f VS |
177 | /* Valid only for RAIDZ. */ |
178 | unsigned nparity; | |
ce8ca56e | 179 | unsigned ashift; |
39db1a3f | 180 | |
8ff84951 VS |
181 | /* Valid only for leaf devices. */ |
182 | grub_device_t dev; | |
fdfde32a VS |
183 | grub_disk_addr_t vdev_phys_sector; |
184 | uberblock_t current_uberblock; | |
8ff84951 | 185 | int original; |
fdfde32a VS |
186 | }; |
187 | ||
1bc7cc1b VS |
188 | struct subvolume |
189 | { | |
190 | dnode_end_t mdn; | |
191 | grub_uint64_t obj; | |
192 | grub_uint64_t case_insensitive; | |
193 | grub_crypto_cipher_handle_t cipher; | |
194 | }; | |
195 | ||
bf78d5b2 RM |
196 | struct grub_zfs_data |
197 | { | |
198 | /* cache for a file block of the currently zfs_open()-ed file */ | |
199 | char *file_buf; | |
200 | grub_uint64_t file_start; | |
201 | grub_uint64_t file_end; | |
202 | ||
203 | /* cache for a dnode block */ | |
204 | dnode_phys_t *dnode_buf; | |
205 | dnode_phys_t *dnode_mdn; | |
206 | grub_uint64_t dnode_start; | |
207 | grub_uint64_t dnode_end; | |
208 | grub_zfs_endian_t dnode_endian; | |
209 | ||
bf78d5b2 | 210 | dnode_end_t mos; |
bf78d5b2 | 211 | dnode_end_t dnode; |
1bc7cc1b | 212 | struct subvolume subvol; |
bf78d5b2 | 213 | |
fdfde32a VS |
214 | struct grub_zfs_device_desc *devices_attached; |
215 | unsigned n_devices_attached; | |
216 | unsigned n_devices_allocated; | |
eb03b552 | 217 | struct grub_zfs_device_desc *device_original; |
fdfde32a | 218 | |
8ff84951 VS |
219 | uberblock_t current_uberblock; |
220 | ||
fdfde32a VS |
221 | int mounted; |
222 | grub_uint64_t guid; | |
bf78d5b2 RM |
223 | }; |
224 | ||
2cdc8995 VS |
225 | static struct grub_zfs_wrap_key *zfs_wrap_keys; |
226 | ||
227 | grub_err_t | |
228 | grub_zfs_add_key (grub_uint8_t *key_in) | |
229 | { | |
230 | struct grub_zfs_wrap_key *key; | |
231 | key = grub_malloc (sizeof (*key)); | |
232 | if (!key) | |
233 | return grub_errno; | |
234 | grub_memcpy (key->key, key_in, GRUB_ZFS_MAX_KEYLEN); | |
235 | key->next = zfs_wrap_keys; | |
236 | zfs_wrap_keys = key; | |
237 | return GRUB_ERR_NONE; | |
238 | } | |
239 | ||
290766fb VS |
240 | static grub_err_t |
241 | zlib_decompress (void *s, void *d, | |
242 | grub_size_t slen, grub_size_t dlen) | |
243 | { | |
244 | if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0) | |
245 | return grub_errno; | |
246 | return GRUB_ERR_NONE; | |
247 | } | |
248 | ||
b632b404 VS |
249 | static grub_err_t |
250 | zle_decompress (void *s, void *d, | |
251 | grub_size_t slen, grub_size_t dlen) | |
252 | { | |
253 | grub_uint8_t *iptr, *optr; | |
254 | grub_size_t clen; | |
255 | for (iptr = s, optr = d; iptr < (grub_uint8_t *) s + slen | |
256 | && optr < (grub_uint8_t *) d + dlen;) | |
257 | { | |
258 | if (*iptr & 0x80) | |
259 | clen = ((*iptr) & 0x7f) + 0x41; | |
260 | else | |
261 | clen = ((*iptr) & 0x3f) + 1; | |
262 | if ((grub_ssize_t) clen > (grub_uint8_t *) d + dlen - optr) | |
263 | clen = (grub_uint8_t *) d + dlen - optr; | |
264 | if (*iptr & 0x40 || *iptr & 0x80) | |
265 | { | |
266 | grub_memset (optr, 0, clen); | |
267 | iptr++; | |
268 | optr += clen; | |
269 | continue; | |
270 | } | |
271 | if ((grub_ssize_t) clen > (grub_uint8_t *) s + slen - iptr - 1) | |
272 | clen = (grub_uint8_t *) s + slen - iptr - 1; | |
273 | grub_memcpy (optr, iptr + 1, clen); | |
274 | optr += clen; | |
275 | iptr += clen + 1; | |
276 | } | |
277 | if (optr < (grub_uint8_t *) d + dlen) | |
278 | grub_memset (optr, 0, (grub_uint8_t *) d + dlen - optr); | |
279 | return GRUB_ERR_NONE; | |
280 | } | |
281 | ||
7d4e39d6 | 282 | static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { |
bf78d5b2 RM |
283 | {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ |
284 | {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ | |
285 | {"off", NULL}, /* ZIO_COMPRESS_OFF */ | |
286 | {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ | |
287 | {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ | |
290766fb VS |
288 | {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ |
289 | {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ | |
290 | {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ | |
291 | {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ | |
292 | {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ | |
293 | {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ | |
294 | {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ | |
295 | {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ | |
296 | {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ | |
b632b404 | 297 | {"zle", zle_decompress}, /* ZIO_COMPRESS_ZLE */ |
bf78d5b2 RM |
298 | }; |
299 | ||
300 | static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, | |
301 | void *buf, struct grub_zfs_data *data); | |
302 | ||
303 | /* | |
304 | * Our own version of log2(). Same thing as highbit()-1. | |
305 | */ | |
306 | static int | |
307 | zfs_log2 (grub_uint64_t num) | |
308 | { | |
309 | int i = 0; | |
310 | ||
311 | while (num > 1) | |
312 | { | |
313 | i++; | |
314 | num = num >> 1; | |
315 | } | |
316 | ||
317 | return (i); | |
318 | } | |
319 | ||
320 | /* Checksum Functions */ | |
321 | static void | |
322 | zio_checksum_off (const void *buf __attribute__ ((unused)), | |
323 | grub_uint64_t size __attribute__ ((unused)), | |
324 | grub_zfs_endian_t endian __attribute__ ((unused)), | |
325 | zio_cksum_t * zcp) | |
326 | { | |
327 | ZIO_SET_CHECKSUM (zcp, 0, 0, 0, 0); | |
328 | } | |
329 | ||
330 | /* Checksum Table and Values */ | |
7d4e39d6 | 331 | static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { |
bf78d5b2 RM |
332 | {NULL, 0, 0, "inherit"}, |
333 | {NULL, 0, 0, "on"}, | |
334 | {zio_checksum_off, 0, 0, "off"}, | |
335 | {zio_checksum_SHA256, 1, 1, "label"}, | |
336 | {zio_checksum_SHA256, 1, 1, "gang_header"}, | |
337 | {NULL, 0, 0, "zilog"}, | |
338 | {fletcher_2, 0, 0, "fletcher2"}, | |
339 | {fletcher_4, 1, 0, "fletcher4"}, | |
340 | {zio_checksum_SHA256, 1, 0, "SHA256"}, | |
341 | {NULL, 0, 0, "zilog2"}, | |
1bc7cc1b | 342 | {zio_checksum_SHA256, 1, 0, "SHA256+MAC"}, |
bf78d5b2 RM |
343 | }; |
344 | ||
345 | /* | |
346 | * zio_checksum_verify: Provides support for checksum verification. | |
347 | * | |
348 | * Fletcher2, Fletcher4, and SHA256 are supported. | |
349 | * | |
350 | */ | |
351 | static grub_err_t | |
352 | zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, | |
1bc7cc1b VS |
353 | grub_zfs_endian_t endian, |
354 | char *buf, grub_size_t size) | |
bf78d5b2 RM |
355 | { |
356 | zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; | |
357 | zio_checksum_info_t *ci = &zio_checksum_table[checksum]; | |
358 | zio_cksum_t actual_cksum, expected_cksum; | |
359 | ||
360 | if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) | |
361 | { | |
362 | grub_dprintf ("zfs", "unknown checksum function %d\n", checksum); | |
363 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
1bc7cc1b | 364 | "unknown checksum function %d", checksum); |
bf78d5b2 RM |
365 | } |
366 | ||
367 | if (ci->ci_eck) | |
368 | { | |
369 | expected_cksum = zec->zec_cksum; | |
370 | zec->zec_cksum = zc; | |
371 | ci->ci_func (buf, size, endian, &actual_cksum); | |
372 | zec->zec_cksum = expected_cksum; | |
373 | zc = expected_cksum; | |
374 | } | |
375 | else | |
376 | ci->ci_func (buf, size, endian, &actual_cksum); | |
377 | ||
1bc7cc1b VS |
378 | if (grub_memcmp (&actual_cksum, &zc, |
379 | checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0) | |
bf78d5b2 | 380 | { |
39db1a3f VS |
381 | grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name); |
382 | grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n", | |
bf78d5b2 RM |
383 | (unsigned long long) actual_cksum.zc_word[0], |
384 | (unsigned long long) actual_cksum.zc_word[1], | |
385 | (unsigned long long) actual_cksum.zc_word[2], | |
386 | (unsigned long long) actual_cksum.zc_word[3]); | |
39db1a3f | 387 | grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n", |
bf78d5b2 RM |
388 | (unsigned long long) zc.zc_word[0], |
389 | (unsigned long long) zc.zc_word[1], | |
390 | (unsigned long long) zc.zc_word[2], | |
391 | (unsigned long long) zc.zc_word[3]); | |
392 | return grub_error (GRUB_ERR_BAD_FS, "checksum verification failed"); | |
393 | } | |
394 | ||
395 | return GRUB_ERR_NONE; | |
396 | } | |
397 | ||
398 | /* | |
399 | * vdev_uberblock_compare takes two uberblock structures and returns an integer | |
400 | * indicating the more recent of the two. | |
401 | * Return Value = 1 if ub2 is more recent | |
402 | * Return Value = -1 if ub1 is more recent | |
403 | * The most recent uberblock is determined using its transaction number and | |
404 | * timestamp. The uberblock with the highest transaction number is | |
405 | * considered "newer". If the transaction numbers of the two blocks match, the | |
406 | * timestamps are compared to determine the "newer" of the two. | |
407 | */ | |
408 | static int | |
409 | vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2) | |
410 | { | |
411 | grub_zfs_endian_t ub1_endian, ub2_endian; | |
412 | if (grub_zfs_to_cpu64 (ub1->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) | |
413 | ub1_endian = LITTLE_ENDIAN; | |
414 | else | |
415 | ub1_endian = BIG_ENDIAN; | |
416 | if (grub_zfs_to_cpu64 (ub2->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) | |
417 | ub2_endian = LITTLE_ENDIAN; | |
418 | else | |
419 | ub2_endian = BIG_ENDIAN; | |
420 | ||
421 | if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) | |
422 | < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) | |
423 | return (-1); | |
424 | if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) | |
425 | > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) | |
426 | return (1); | |
427 | ||
428 | if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) | |
429 | < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) | |
430 | return (-1); | |
431 | if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) | |
432 | > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) | |
433 | return (1); | |
434 | ||
435 | return (0); | |
436 | } | |
437 | ||
438 | /* | |
439 | * Three pieces of information are needed to verify an uberblock: the magic | |
440 | * number, the version number, and the checksum. | |
441 | * | |
442 | * Currently Implemented: version number, magic number | |
443 | * Need to Implement: checksum | |
444 | * | |
445 | */ | |
446 | static grub_err_t | |
5bbd28b8 | 447 | uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset) |
bf78d5b2 RM |
448 | { |
449 | uberblock_t *uber = &ub->ubp_uberblock; | |
450 | grub_err_t err; | |
451 | grub_zfs_endian_t endian = UNKNOWN_ENDIAN; | |
452 | zio_cksum_t zc; | |
453 | ||
454 | if (grub_zfs_to_cpu64 (uber->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC | |
455 | && grub_zfs_to_cpu64 (uber->ub_version, LITTLE_ENDIAN) > 0 | |
456 | && grub_zfs_to_cpu64 (uber->ub_version, LITTLE_ENDIAN) <= SPA_VERSION) | |
457 | endian = LITTLE_ENDIAN; | |
458 | ||
459 | if (grub_zfs_to_cpu64 (uber->ub_magic, BIG_ENDIAN) == UBERBLOCK_MAGIC | |
460 | && grub_zfs_to_cpu64 (uber->ub_version, BIG_ENDIAN) > 0 | |
461 | && grub_zfs_to_cpu64 (uber->ub_version, BIG_ENDIAN) <= SPA_VERSION) | |
462 | endian = BIG_ENDIAN; | |
463 | ||
464 | if (endian == UNKNOWN_ENDIAN) | |
465 | return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic"); | |
466 | ||
467 | grub_memset (&zc, 0, sizeof (zc)); | |
468 | ||
469 | zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian); | |
470 | err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian, | |
471 | (char *) ub, UBERBLOCK_SIZE); | |
472 | ||
473 | return err; | |
474 | } | |
475 | ||
476 | /* | |
477 | * Find the best uberblock. | |
478 | * Return: | |
479 | * Success - Pointer to the best uberblock. | |
480 | * Failure - NULL | |
481 | */ | |
482 | static uberblock_phys_t * | |
483 | find_bestub (uberblock_phys_t * ub_array, grub_disk_addr_t sector) | |
484 | { | |
485 | uberblock_phys_t *ubbest = NULL; | |
486 | int i; | |
487 | grub_disk_addr_t offset; | |
488 | grub_err_t err = GRUB_ERR_NONE; | |
489 | ||
490 | for (i = 0; i < (VDEV_UBERBLOCK_RING >> VDEV_UBERBLOCK_SHIFT); i++) | |
491 | { | |
492 | offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE | |
493 | + (i << VDEV_UBERBLOCK_SHIFT); | |
494 | ||
495 | err = uberblock_verify (&ub_array[i], offset); | |
496 | if (err) | |
497 | { | |
498 | grub_errno = GRUB_ERR_NONE; | |
499 | continue; | |
500 | } | |
501 | if (ubbest == NULL | |
502 | || vdev_uberblock_compare (&(ub_array[i].ubp_uberblock), | |
503 | &(ubbest->ubp_uberblock)) > 0) | |
504 | ubbest = &ub_array[i]; | |
505 | } | |
506 | if (!ubbest) | |
507 | grub_errno = err; | |
508 | ||
509 | return (ubbest); | |
510 | } | |
511 | ||
512 | static inline grub_size_t | |
513 | get_psize (blkptr_t * bp, grub_zfs_endian_t endian) | |
514 | { | |
515 | return ((((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) >> 16) & 0xffff) + 1) | |
516 | << SPA_MINBLOCKSHIFT); | |
517 | } | |
518 | ||
519 | static grub_uint64_t | |
fdfde32a | 520 | dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian) |
bf78d5b2 RM |
521 | { |
522 | grub_dprintf ("zfs", "dva=%llx, %llx\n", | |
523 | (unsigned long long) dva->dva_word[0], | |
524 | (unsigned long long) dva->dva_word[1]); | |
525 | return grub_zfs_to_cpu64 ((dva)->dva_word[1], | |
526 | endian) << SPA_MINBLOCKSHIFT; | |
527 | } | |
528 | ||
fdfde32a VS |
529 | static grub_err_t |
530 | zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist) | |
531 | { | |
532 | grub_err_t err; | |
533 | ||
534 | *nvlist = grub_malloc (VDEV_PHYS_SIZE); | |
8ff84951 VS |
535 | if (!diskdesc->dev) |
536 | return grub_error (GRUB_ERR_BAD_FS, "member drive unknown"); | |
537 | ||
fdfde32a | 538 | /* Read in the vdev name-value pair list (112K). */ |
8ff84951 | 539 | err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0, |
fdfde32a VS |
540 | VDEV_PHYS_SIZE, *nvlist); |
541 | if (err) | |
542 | { | |
543 | grub_free (*nvlist); | |
544 | *nvlist = 0; | |
545 | return err; | |
546 | } | |
547 | return GRUB_ERR_NONE; | |
548 | } | |
549 | ||
550 | static grub_err_t | |
eb03b552 VS |
551 | fill_vdev_info_real (struct grub_zfs_data *data, |
552 | const char *nvlist, | |
8ff84951 VS |
553 | struct grub_zfs_device_desc *fill, |
554 | struct grub_zfs_device_desc *insert) | |
fdfde32a | 555 | { |
8ff84951 | 556 | char *type; |
fdfde32a VS |
557 | |
558 | type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); | |
559 | ||
560 | if (!type) | |
561 | return grub_errno; | |
8ff84951 VS |
562 | |
563 | if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id))) | |
564 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); | |
565 | ||
566 | if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid))) | |
567 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); | |
568 | ||
c3b87afc VS |
569 | if (grub_strcmp (type, VDEV_TYPE_DISK) == 0 |
570 | || grub_strcmp (type, VDEV_TYPE_FILE) == 0) | |
fdfde32a | 571 | { |
8ff84951 VS |
572 | fill->type = DEVICE_LEAF; |
573 | ||
574 | if (!fill->dev && fill->guid == insert->guid) | |
575 | { | |
576 | fill->dev = insert->dev; | |
577 | fill->vdev_phys_sector = insert->vdev_phys_sector; | |
578 | fill->current_uberblock = insert->current_uberblock; | |
579 | fill->original = insert->original; | |
8563e2a6 VS |
580 | if (!data->device_original) |
581 | data->device_original = fill; | |
8ff84951 | 582 | } |
fdfde32a VS |
583 | |
584 | return GRUB_ERR_NONE; | |
585 | } | |
8ff84951 | 586 | |
39db1a3f VS |
587 | if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0 |
588 | || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0) | |
8ff84951 VS |
589 | { |
590 | int nelm, i; | |
591 | ||
39db1a3f VS |
592 | if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0) |
593 | fill->type = DEVICE_MIRROR; | |
594 | else | |
595 | { | |
596 | grub_uint64_t par; | |
597 | fill->type = DEVICE_RAIDZ; | |
598 | if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par)) | |
599 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity"); | |
600 | fill->nparity = par; | |
ce8ca56e VS |
601 | if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par)) |
602 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz ashift"); | |
603 | fill->ashift = par; | |
39db1a3f | 604 | } |
8ff84951 VS |
605 | |
606 | nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist, ZPOOL_CONFIG_CHILDREN); | |
607 | ||
608 | if (nelm <= 0) | |
609 | return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV"); | |
610 | ||
39db1a3f VS |
611 | if (!fill->children) |
612 | { | |
613 | fill->n_children = nelm; | |
614 | ||
615 | fill->children = grub_zalloc (fill->n_children | |
616 | * sizeof (fill->children[0])); | |
617 | } | |
8ff84951 VS |
618 | |
619 | for (i = 0; i < nelm; i++) | |
620 | { | |
621 | char *child; | |
622 | grub_err_t err; | |
623 | ||
624 | child = grub_zfs_nvlist_lookup_nvlist_array | |
625 | (nvlist, ZPOOL_CONFIG_CHILDREN, i); | |
626 | ||
eb03b552 | 627 | err = fill_vdev_info_real (data, child, &fill->children[i], insert); |
8ff84951 VS |
628 | |
629 | grub_free (child); | |
630 | ||
631 | if (err) | |
632 | return err; | |
633 | } | |
634 | return GRUB_ERR_NONE; | |
635 | } | |
636 | ||
fdfde32a VS |
637 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported", |
638 | type); | |
639 | } | |
640 | ||
8ff84951 VS |
641 | static grub_err_t |
642 | fill_vdev_info (struct grub_zfs_data *data, | |
643 | char *nvlist, struct grub_zfs_device_desc *diskdesc) | |
644 | { | |
645 | grub_uint64_t id; | |
646 | unsigned i; | |
647 | ||
648 | if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id)) | |
649 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); | |
650 | ||
651 | for (i = 0; i < data->n_devices_attached; i++) | |
652 | if (data->devices_attached[i].id == id) | |
eb03b552 | 653 | return fill_vdev_info_real (data, nvlist, &data->devices_attached[i], |
8ff84951 VS |
654 | diskdesc); |
655 | ||
656 | data->n_devices_attached++; | |
657 | if (data->n_devices_attached > data->n_devices_allocated) | |
658 | { | |
659 | void *tmp; | |
660 | data->n_devices_allocated = 2 * data->n_devices_attached + 1; | |
661 | data->devices_attached | |
662 | = grub_realloc (tmp = data->devices_attached, | |
663 | data->n_devices_allocated | |
664 | * sizeof (data->devices_attached[0])); | |
665 | if (!data->devices_attached) | |
666 | { | |
667 | data->devices_attached = tmp; | |
668 | return grub_errno; | |
669 | } | |
670 | } | |
671 | ||
672 | grub_memset (&data->devices_attached[data->n_devices_attached - 1], | |
673 | 0, sizeof (data->devices_attached[data->n_devices_attached - 1])); | |
674 | ||
eb03b552 | 675 | return fill_vdev_info_real (data, nvlist, |
8ff84951 VS |
676 | &data->devices_attached[data->n_devices_attached - 1], |
677 | diskdesc); | |
678 | } | |
679 | ||
fdfde32a VS |
680 | /* |
681 | * Check the disk label information and retrieve needed vdev name-value pairs. | |
682 | * | |
683 | */ | |
684 | static grub_err_t | |
685 | check_pool_label (struct grub_zfs_data *data, | |
8ff84951 | 686 | struct grub_zfs_device_desc *diskdesc) |
fdfde32a VS |
687 | { |
688 | grub_uint64_t pool_state, txg = 0; | |
689 | char *nvlist; | |
690 | #if 0 | |
691 | char *nv; | |
692 | #endif | |
8ff84951 | 693 | grub_uint64_t poolguid; |
fdfde32a VS |
694 | grub_uint64_t version; |
695 | int found; | |
696 | grub_err_t err; | |
697 | ||
698 | err = zfs_fetch_nvlist (diskdesc, &nvlist); | |
699 | if (err) | |
700 | return err; | |
701 | ||
702 | grub_dprintf ("zfs", "check 2 passed\n"); | |
703 | ||
704 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, | |
705 | &pool_state); | |
706 | if (! found) | |
707 | { | |
708 | grub_free (nvlist); | |
709 | if (! grub_errno) | |
710 | grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found"); | |
711 | return grub_errno; | |
712 | } | |
713 | grub_dprintf ("zfs", "check 3 passed\n"); | |
714 | ||
715 | if (pool_state == POOL_STATE_DESTROYED) | |
716 | { | |
717 | grub_free (nvlist); | |
718 | return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed"); | |
719 | } | |
720 | grub_dprintf ("zfs", "check 4 passed\n"); | |
721 | ||
722 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg); | |
723 | if (!found) | |
724 | { | |
725 | grub_free (nvlist); | |
726 | if (! grub_errno) | |
727 | grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found"); | |
728 | return grub_errno; | |
729 | } | |
730 | grub_dprintf ("zfs", "check 6 passed\n"); | |
731 | ||
732 | /* not an active device */ | |
733 | if (txg == 0) | |
734 | { | |
735 | grub_free (nvlist); | |
736 | return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active"); | |
737 | } | |
738 | grub_dprintf ("zfs", "check 7 passed\n"); | |
739 | ||
740 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION, | |
741 | &version); | |
742 | if (! found) | |
743 | { | |
744 | grub_free (nvlist); | |
745 | if (! grub_errno) | |
746 | grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found"); | |
747 | return grub_errno; | |
748 | } | |
749 | grub_dprintf ("zfs", "check 8 passed\n"); | |
750 | ||
751 | if (version > SPA_VERSION) | |
752 | { | |
753 | grub_free (nvlist); | |
754 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
755 | "too new version %llu > %llu", | |
756 | (unsigned long long) version, | |
757 | (unsigned long long) SPA_VERSION); | |
758 | } | |
759 | grub_dprintf ("zfs", "check 9 passed\n"); | |
760 | ||
8ff84951 VS |
761 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID, |
762 | &(diskdesc->guid)); | |
fdfde32a VS |
763 | if (! found) |
764 | { | |
765 | grub_free (nvlist); | |
766 | if (! grub_errno) | |
767 | grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found"); | |
768 | return grub_errno; | |
769 | } | |
770 | ||
771 | found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, | |
772 | &poolguid); | |
773 | if (! found) | |
774 | { | |
775 | grub_free (nvlist); | |
776 | if (! grub_errno) | |
777 | grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found"); | |
778 | return grub_errno; | |
779 | } | |
780 | ||
781 | grub_dprintf ("zfs", "check 11 passed\n"); | |
782 | ||
783 | if (data->mounted && data->guid != poolguid) | |
784 | return grub_error (GRUB_ERR_BAD_FS, "another zpool"); | |
785 | else | |
786 | data->guid = poolguid; | |
fdfde32a | 787 | |
8ff84951 VS |
788 | { |
789 | char *nv; | |
790 | nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); | |
fdfde32a | 791 | |
8ff84951 VS |
792 | if (!nv) |
793 | { | |
794 | grub_free (nvlist); | |
795 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree"); | |
796 | } | |
797 | err = fill_vdev_info (data, nv, diskdesc); | |
798 | if (err) | |
799 | { | |
800 | grub_free (nvlist); | |
801 | return err; | |
802 | } | |
803 | } | |
804 | grub_dprintf ("zfs", "check 10 passed\n"); | |
805 | ||
806 | grub_free (nvlist); | |
fdfde32a VS |
807 | |
808 | return GRUB_ERR_NONE; | |
809 | } | |
810 | ||
811 | static grub_err_t | |
8ff84951 VS |
812 | scan_disk (grub_device_t dev, struct grub_zfs_data *data, |
813 | int original) | |
fdfde32a VS |
814 | { |
815 | int label = 0; | |
816 | uberblock_phys_t *ub_array, *ubbest = NULL; | |
817 | vdev_boot_header_t *bh; | |
818 | grub_err_t err; | |
819 | int vdevnum; | |
820 | struct grub_zfs_device_desc desc; | |
821 | ||
822 | ub_array = grub_malloc (VDEV_UBERBLOCK_RING); | |
823 | if (!ub_array) | |
824 | return grub_errno; | |
825 | ||
826 | bh = grub_malloc (VDEV_BOOT_HEADER_SIZE); | |
827 | if (!bh) | |
828 | { | |
829 | grub_free (ub_array); | |
830 | return grub_errno; | |
831 | } | |
832 | ||
833 | vdevnum = VDEV_LABELS; | |
834 | ||
8ff84951 VS |
835 | desc.dev = dev; |
836 | desc.original = original; | |
fdfde32a VS |
837 | |
838 | /* Don't check back labels on CDROM. */ | |
8ff84951 | 839 | if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN) |
fdfde32a VS |
840 | vdevnum = VDEV_LABELS / 2; |
841 | ||
842 | for (label = 0; ubbest == NULL && label < vdevnum; label++) | |
843 | { | |
fdfde32a VS |
844 | desc.vdev_phys_sector |
845 | = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT) | |
846 | + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT) | |
8ff84951 | 847 | + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk) |
fdfde32a VS |
848 | - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)); |
849 | ||
850 | /* Read in the uberblock ring (128K). */ | |
8ff84951 | 851 | err = grub_disk_read (dev->disk, desc.vdev_phys_sector |
fdfde32a VS |
852 | + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT), |
853 | 0, VDEV_UBERBLOCK_RING, (char *) ub_array); | |
854 | if (err) | |
855 | { | |
856 | grub_errno = GRUB_ERR_NONE; | |
857 | continue; | |
858 | } | |
859 | grub_dprintf ("zfs", "label ok %d\n", label); | |
860 | ||
861 | ubbest = find_bestub (ub_array, desc.vdev_phys_sector); | |
862 | if (!ubbest) | |
863 | { | |
864 | grub_dprintf ("zfs", "No uberblock found\n"); | |
865 | grub_errno = GRUB_ERR_NONE; | |
866 | continue; | |
867 | } | |
868 | ||
869 | grub_memmove (&(desc.current_uberblock), | |
870 | &ubbest->ubp_uberblock, sizeof (uberblock_t)); | |
8ff84951 VS |
871 | if (original) |
872 | grub_memmove (&(data->current_uberblock), | |
873 | &ubbest->ubp_uberblock, sizeof (uberblock_t)); | |
fdfde32a | 874 | |
8ff84951 | 875 | err = check_pool_label (data, &desc); |
fdfde32a VS |
876 | if (err) |
877 | { | |
878 | grub_errno = GRUB_ERR_NONE; | |
879 | continue; | |
880 | } | |
881 | #if 0 | |
882 | if (find_best_root && | |
883 | vdev_uberblock_compare (&ubbest->ubp_uberblock, | |
884 | &(current_uberblock)) <= 0) | |
885 | continue; | |
886 | #endif | |
887 | grub_free (ub_array); | |
888 | grub_free (bh); | |
889 | return GRUB_ERR_NONE; | |
890 | } | |
891 | ||
892 | grub_free (ub_array); | |
893 | grub_free (bh); | |
894 | ||
895 | return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label"); | |
896 | } | |
897 | ||
898 | static grub_err_t | |
39db1a3f | 899 | scan_devices (struct grub_zfs_data *data) |
fdfde32a | 900 | { |
fdfde32a VS |
901 | auto int hook (const char *name); |
902 | int hook (const char *name) | |
903 | { | |
904 | grub_device_t dev; | |
905 | grub_err_t err; | |
fdfde32a VS |
906 | dev = grub_device_open (name); |
907 | if (!dev) | |
908 | return 0; | |
909 | if (!dev->disk) | |
910 | { | |
911 | grub_device_close (dev); | |
912 | return 0; | |
913 | } | |
8ff84951 | 914 | err = scan_disk (dev, data, 0); |
fdfde32a VS |
915 | if (err == GRUB_ERR_BAD_FS) |
916 | { | |
917 | grub_device_close (dev); | |
918 | grub_errno = GRUB_ERR_NONE; | |
919 | return 0; | |
920 | } | |
921 | if (err) | |
922 | { | |
923 | grub_device_close (dev); | |
924 | grub_print_error (); | |
925 | return 0; | |
926 | } | |
fdfde32a | 927 | |
39db1a3f | 928 | return 0; |
fdfde32a VS |
929 | } |
930 | grub_device_iterate (hook); | |
fdfde32a VS |
931 | return GRUB_ERR_NONE; |
932 | } | |
933 | ||
17744004 VS |
934 | static inline void |
935 | xor (grub_uint64_t *a, const grub_uint64_t *b, grub_size_t s) | |
936 | { | |
937 | s /= sizeof (grub_uint64_t); | |
938 | while (s--) | |
939 | *a++ ^= *b++; | |
940 | } | |
941 | ||
95f2e860 VS |
942 | /* x**y. */ |
943 | static grub_uint8_t powx[255 * 2]; | |
944 | /* Such an s that x**s = y */ | |
945 | static int powx_inv[256]; | |
946 | static const grub_uint8_t poly = 0x1d; | |
947 | ||
948 | /* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */ | |
949 | static inline void | |
950 | xor_out (void *a_in, const void *b_in, grub_size_t s, | |
951 | int known_idx, int recovery_pow) | |
952 | { | |
953 | int add; | |
954 | grub_uint8_t *a = a_in; | |
955 | const grub_uint8_t *b = b_in; | |
956 | ||
957 | /* Simple xor. */ | |
958 | if (known_idx == 0 || recovery_pow == 0) | |
959 | { | |
960 | xor (a_in, b_in, s); | |
961 | return; | |
962 | } | |
963 | add = (known_idx * recovery_pow) % 255; | |
964 | for (;s--; b++, a++) | |
965 | if (*b) | |
966 | *a ^= powx[powx_inv[*b] + add]; | |
967 | } | |
968 | ||
969 | static inline grub_uint8_t | |
970 | gf_mul (grub_uint8_t a, grub_uint8_t b) | |
971 | { | |
972 | if (a == 0 || b == 0) | |
973 | return 0; | |
974 | return powx[powx_inv[a] + powx_inv[b]]; | |
975 | } | |
976 | ||
c2fd16ca | 977 | static inline grub_err_t |
95f2e860 VS |
978 | recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs, |
979 | const unsigned *powers, | |
980 | const int *idx) | |
981 | { | |
c2fd16ca | 982 | grub_dprintf ("zfs", "recovering %u bufers\n", nbufs); |
95f2e860 VS |
983 | /* Now we have */ |
984 | /* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/ | |
c2fd16ca | 985 | /* Let's invert the matrix in question. */ |
95f2e860 VS |
986 | switch (nbufs) |
987 | { | |
988 | /* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */ | |
989 | case 1: | |
990 | { | |
991 | int add; | |
992 | grub_uint8_t *a; | |
993 | if (powers[0] == 0 || idx[0] == 0) | |
c2fd16ca | 994 | return GRUB_ERR_NONE; |
95f2e860 VS |
995 | add = 255 - ((powers[0] * idx[0]) % 255); |
996 | for (a = bufs[0]; s--; a++) | |
997 | if (*a) | |
998 | *a = powx[powx_inv[*a] + add]; | |
c2fd16ca | 999 | return GRUB_ERR_NONE; |
95f2e860 | 1000 | } |
c2fd16ca | 1001 | /* Case 2x2: Let's use the determinant formula. */ |
95f2e860 VS |
1002 | case 2: |
1003 | { | |
1004 | grub_uint8_t det, det_inv; | |
c2fd16ca | 1005 | grub_uint8_t matrixinv[2][2]; |
95f2e860 VS |
1006 | unsigned i; |
1007 | /* The determinant is: */ | |
1008 | det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255] | |
1009 | ^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]); | |
c2fd16ca VS |
1010 | if (det == 0) |
1011 | return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); | |
95f2e860 | 1012 | det_inv = powx[255 - powx_inv[det]]; |
c2fd16ca VS |
1013 | matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv); |
1014 | matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv); | |
1015 | matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv); | |
1016 | matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv); | |
95f2e860 VS |
1017 | for (i = 0; i < s; i++) |
1018 | { | |
c2fd16ca VS |
1019 | grub_uint8_t b0, b1; |
1020 | b0 = bufs[0][i]; | |
1021 | b1 = bufs[1][i]; | |
1022 | ||
1023 | bufs[0][i] = (gf_mul (b0, matrixinv[0][0]) | |
1024 | ^ gf_mul (b1, matrixinv[0][1])); | |
1025 | bufs[1][i] = (gf_mul (b0, matrixinv[1][0]) | |
1026 | ^ gf_mul (b1, matrixinv[1][1])); | |
1027 | } | |
1028 | return GRUB_ERR_NONE; | |
1029 | } | |
1030 | /* Otherwise use Gauss. */ | |
1031 | default: | |
1032 | { | |
1033 | grub_uint8_t matrix1[nbufs][nbufs], matrix2[nbufs][nbufs]; | |
1034 | int i, j, k; | |
1035 | ||
1036 | for (i = 0; i < nbufs; i++) | |
1037 | for (j = 0; j < nbufs; j++) | |
1038 | matrix1[i][j] = powx[(powers[i] * idx[j]) % 255]; | |
1039 | for (i = 0; i < nbufs; i++) | |
1040 | for (j = 0; j < nbufs; j++) | |
1041 | matrix2[i][j] = 0; | |
1042 | for (i = 0; i < nbufs; i++) | |
1043 | matrix2[i][i] = 1; | |
1044 | ||
1045 | for (i = 0; i < nbufs; i++) | |
1046 | { | |
1047 | grub_uint8_t mul; | |
1048 | for (j = i; j < nbufs; j++) | |
1049 | if (matrix1[i][j]) | |
1050 | break; | |
1051 | if (j == nbufs) | |
1052 | return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); | |
1053 | if (j != i) | |
1054 | { | |
1055 | int xchng; | |
1056 | xchng = j; | |
1057 | for (j = 0; j < nbufs; j++) | |
1058 | { | |
1059 | grub_uint8_t t; | |
1060 | t = matrix1[xchng][j]; | |
1061 | matrix1[xchng][j] = matrix1[i][j]; | |
1062 | matrix1[i][j] = t; | |
1063 | } | |
1064 | for (j = 0; j < nbufs; j++) | |
1065 | { | |
1066 | grub_uint8_t t; | |
1067 | t = matrix2[xchng][j]; | |
1068 | matrix2[xchng][j] = matrix2[i][j]; | |
1069 | matrix2[i][j] = t; | |
1070 | } | |
1071 | } | |
1072 | mul = powx[255 - powx_inv[matrix1[i][i]]]; | |
1073 | for (j = 0; j < nbufs; j++) | |
1074 | matrix1[i][j] = gf_mul (matrix1[i][j], mul); | |
1075 | for (j = 0; j < nbufs; j++) | |
1076 | matrix2[i][j] = gf_mul (matrix2[i][j], mul); | |
1077 | for (j = i + 1; j < nbufs; j++) | |
1078 | { | |
1079 | mul = matrix1[j][i]; | |
1080 | for (k = 0; k < nbufs; k++) | |
1081 | matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); | |
1082 | for (k = 0; k < nbufs; k++) | |
1083 | matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); | |
1084 | } | |
1085 | } | |
1086 | for (i = nbufs - 1; i >= 0; i--) | |
1087 | { | |
1088 | for (j = 0; j < i; j++) | |
1089 | { | |
1090 | grub_uint8_t mul; | |
1091 | mul = matrix1[j][i]; | |
1092 | for (k = 0; k < nbufs; k++) | |
1093 | matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); | |
1094 | for (k = 0; k < nbufs; k++) | |
1095 | matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); | |
1096 | } | |
1097 | } | |
95f2e860 | 1098 | |
c2fd16ca VS |
1099 | for (i = 0; i < (int) s; i++) |
1100 | { | |
1101 | grub_uint8_t b[nbufs]; | |
1102 | for (j = 0; j < nbufs; j++) | |
1103 | b[j] = bufs[j][i]; | |
1104 | for (j = 0; j < nbufs; j++) | |
1105 | { | |
1106 | bufs[j][i] = 0; | |
1107 | for (k = 0; k < nbufs; k++) | |
1108 | bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]); | |
1109 | } | |
95f2e860 | 1110 | } |
c2fd16ca | 1111 | return GRUB_ERR_NONE; |
95f2e860 | 1112 | } |
c2fd16ca | 1113 | } |
95f2e860 VS |
1114 | } |
1115 | ||
8ff84951 | 1116 | static grub_err_t |
39db1a3f | 1117 | read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, |
aca5aefc | 1118 | grub_size_t len, void *buf) |
8ff84951 VS |
1119 | { |
1120 | switch (desc->type) | |
1121 | { | |
1122 | case DEVICE_LEAF: | |
39db1a3f VS |
1123 | { |
1124 | grub_uint64_t sector; | |
1125 | sector = DVA_OFFSET_TO_PHYS_SECTOR (offset); | |
1126 | if (!desc->dev) | |
1127 | { | |
1128 | return grub_error (GRUB_ERR_BAD_FS, "member drive unknown"); | |
1129 | } | |
1130 | /* read in a data block */ | |
1131 | return grub_disk_read (desc->dev->disk, sector, 0, len, buf); | |
1132 | } | |
8ff84951 VS |
1133 | case DEVICE_MIRROR: |
1134 | { | |
f8d82408 | 1135 | grub_err_t err = GRUB_ERR_NONE; |
8ff84951 | 1136 | unsigned i; |
925d998e VS |
1137 | if (desc->n_children <= 0) |
1138 | return grub_error (GRUB_ERR_BAD_FS, | |
1139 | "non-positive number of mirror children"); | |
8ff84951 VS |
1140 | for (i = 0; i < desc->n_children; i++) |
1141 | { | |
aca5aefc | 1142 | err = read_device (offset, &desc->children[i], |
8ff84951 VS |
1143 | len, buf); |
1144 | if (!err) | |
1145 | break; | |
1146 | grub_errno = GRUB_ERR_NONE; | |
1147 | } | |
1148 | return (grub_errno = err); | |
1149 | } | |
39db1a3f VS |
1150 | case DEVICE_RAIDZ: |
1151 | { | |
bfff320c | 1152 | unsigned c = 0; |
ce8ca56e VS |
1153 | grub_uint64_t high; |
1154 | grub_uint64_t devn; | |
17744004 VS |
1155 | grub_uint64_t m; |
1156 | grub_uint32_t s, orig_s; | |
1157 | void *orig_buf = buf; | |
1158 | grub_size_t orig_len = len; | |
95f2e860 VS |
1159 | grub_uint8_t *recovery_buf[4]; |
1160 | grub_size_t recovery_len[4]; | |
1161 | int recovery_idx[4]; | |
1162 | unsigned failed_devices = 0; | |
1163 | int idx, orig_idx; | |
ce8ca56e | 1164 | |
cf5ba824 | 1165 | if (desc->nparity < 1 || desc->nparity > 3) |
aca5aefc VS |
1166 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, |
1167 | "raidz%d is not supported", desc->nparity); | |
1168 | ||
17744004 VS |
1169 | orig_s = (((len + (1 << desc->ashift) - 1) >> desc->ashift) |
1170 | + (desc->n_children - desc->nparity) - 1); | |
1171 | s = orig_s; | |
aca5aefc | 1172 | |
ce8ca56e VS |
1173 | high = grub_divmod64 ((offset >> desc->ashift), |
1174 | desc->n_children, &m); | |
17744004 VS |
1175 | if (desc->nparity == 2) |
1176 | c = 2; | |
cf5ba824 VS |
1177 | if (desc->nparity == 3) |
1178 | c = 3; | |
95f2e860 VS |
1179 | if (((len + (1 << desc->ashift) - 1) >> desc->ashift) |
1180 | >= (desc->n_children - desc->nparity)) | |
1181 | idx = (desc->n_children - desc->nparity - 1); | |
1182 | else | |
1183 | idx = ((len + (1 << desc->ashift) - 1) >> desc->ashift) - 1; | |
1184 | orig_idx = idx; | |
39db1a3f VS |
1185 | while (len > 0) |
1186 | { | |
1187 | grub_size_t csize; | |
ce8ca56e | 1188 | grub_uint32_t bsize; |
39db1a3f | 1189 | grub_err_t err; |
aca5aefc VS |
1190 | bsize = s / (desc->n_children - desc->nparity); |
1191 | ||
17744004 VS |
1192 | if (desc->nparity == 1 |
1193 | && ((offset >> (desc->ashift + 11)) & 1) == c) | |
1194 | c++; | |
aca5aefc | 1195 | |
17744004 VS |
1196 | high = grub_divmod64 ((offset >> desc->ashift) + c, |
1197 | desc->n_children, &devn); | |
ce8ca56e | 1198 | csize = bsize << desc->ashift; |
39db1a3f VS |
1199 | if (csize > len) |
1200 | csize = len; | |
ce8ca56e | 1201 | |
bfff320c | 1202 | grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T |
aca5aefc VS |
1203 | "+%u (%" PRIxGRUB_SIZE ", %" PRIxGRUB_UINT32_T |
1204 | ") -> (0x%" PRIxGRUB_UINT64_T ", 0x%" | |
11e50e92 | 1205 | PRIxGRUB_UINT64_T ")\n", |
aca5aefc | 1206 | offset >> desc->ashift, c, len, bsize, high, |
ce8ca56e VS |
1207 | devn); |
1208 | err = read_device ((high << desc->ashift) | |
1209 | | (offset & ((1 << desc->ashift) - 1)), | |
1210 | &desc->children[devn], | |
aca5aefc | 1211 | csize, buf); |
c2fd16ca | 1212 | if (err && failed_devices < desc->nparity) |
17744004 | 1213 | { |
95f2e860 VS |
1214 | recovery_buf[failed_devices] = buf; |
1215 | recovery_len[failed_devices] = csize; | |
1216 | recovery_idx[failed_devices] = idx; | |
1217 | failed_devices++; | |
17744004 VS |
1218 | grub_errno = err = 0; |
1219 | } | |
39db1a3f VS |
1220 | if (err) |
1221 | return err; | |
aca5aefc | 1222 | |
bfff320c | 1223 | c++; |
95f2e860 | 1224 | idx--; |
ce8ca56e | 1225 | s--; |
bfff320c | 1226 | buf = (char *) buf + csize; |
39db1a3f | 1227 | len -= csize; |
39db1a3f | 1228 | } |
95f2e860 | 1229 | if (failed_devices) |
17744004 | 1230 | { |
95f2e860 VS |
1231 | unsigned redundancy_pow[4]; |
1232 | unsigned cur_redundancy_pow = 0; | |
1233 | unsigned n_redundancy = 0; | |
1234 | unsigned i, j; | |
c2fd16ca | 1235 | grub_err_t err; |
95f2e860 VS |
1236 | |
1237 | /* Compute mul. x**s has a period of 255. */ | |
1238 | if (powx[0] == 0) | |
1239 | { | |
1240 | grub_uint8_t cur = 1; | |
1241 | for (i = 0; i < 255; i++) | |
1242 | { | |
1243 | powx[i] = cur; | |
1244 | powx[i + 255] = cur; | |
1245 | powx_inv[cur] = i; | |
1246 | if (cur & 0x80) | |
1247 | cur = (cur << 1) ^ poly; | |
1248 | else | |
1249 | cur <<= 1; | |
1250 | } | |
1251 | } | |
1252 | ||
1253 | /* Read redundancy data. */ | |
1254 | for (n_redundancy = 0, cur_redundancy_pow = 0; | |
1255 | n_redundancy < failed_devices; | |
1256 | cur_redundancy_pow++) | |
1257 | { | |
95f2e860 VS |
1258 | high = grub_divmod64 ((offset >> desc->ashift) |
1259 | + cur_redundancy_pow | |
1260 | + ((desc->nparity == 1) | |
1261 | && ((offset >> (desc->ashift + 11)) | |
1262 | & 1)), | |
1263 | desc->n_children, &devn); | |
1264 | err = read_device ((high << desc->ashift) | |
1265 | | (offset & ((1 << desc->ashift) - 1)), | |
1266 | &desc->children[devn], | |
1267 | recovery_len[n_redundancy], | |
1268 | recovery_buf[n_redundancy]); | |
1269 | /* Ignore error if we may still have enough devices. */ | |
1270 | if (err && n_redundancy + desc->nparity - cur_redundancy_pow - 1 | |
1271 | >= failed_devices) | |
1272 | { | |
1273 | grub_errno = GRUB_ERR_NONE; | |
1274 | continue; | |
1275 | } | |
1276 | if (err) | |
1277 | return err; | |
1278 | redundancy_pow[n_redundancy] = cur_redundancy_pow; | |
1279 | n_redundancy++; | |
1280 | } | |
1281 | /* Now xor-our the parts we already know. */ | |
17744004 VS |
1282 | buf = orig_buf; |
1283 | len = orig_len; | |
1284 | s = orig_s; | |
95f2e860 VS |
1285 | idx = orig_idx; |
1286 | ||
17744004 VS |
1287 | while (len > 0) |
1288 | { | |
1289 | grub_size_t csize; | |
1290 | csize = ((s / (desc->n_children - desc->nparity)) | |
1291 | << desc->ashift); | |
1292 | if (csize > len) | |
1293 | csize = len; | |
1294 | ||
95f2e860 VS |
1295 | for (j = 0; j < failed_devices; j++) |
1296 | if (buf == recovery_buf[j]) | |
1297 | break; | |
1298 | ||
1299 | if (j == failed_devices) | |
1300 | for (j = 0; j < failed_devices; j++) | |
1301 | xor_out (recovery_buf[j], buf, | |
1302 | csize < recovery_len[j] ? csize : recovery_len[j], | |
1303 | idx, redundancy_pow[j]); | |
17744004 VS |
1304 | |
1305 | s--; | |
1306 | buf = (char *) buf + csize; | |
1307 | len -= csize; | |
95f2e860 VS |
1308 | idx--; |
1309 | } | |
1310 | for (i = 0; i < failed_devices | |
1311 | && recovery_len[i] == recovery_len[0]; | |
1312 | i++); | |
1313 | /* Since the chunks have variable length handle the last block | |
1314 | separately. */ | |
1315 | if (i != failed_devices) | |
1316 | { | |
1317 | grub_uint8_t *tmp_recovery_buf[4]; | |
1318 | for (j = 0; j < i; j++) | |
1319 | tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[j] - 1; | |
c2fd16ca VS |
1320 | err = recovery (tmp_recovery_buf, 1, i, redundancy_pow, |
1321 | recovery_idx); | |
1322 | if (err) | |
1323 | return err; | |
95f2e860 | 1324 | } |
c2fd16ca VS |
1325 | err = recovery (recovery_buf, recovery_len[failed_devices - 1], |
1326 | failed_devices, redundancy_pow, recovery_idx); | |
1327 | if (err) | |
1328 | return err; | |
17744004 VS |
1329 | } |
1330 | return GRUB_ERR_NONE; | |
bfff320c | 1331 | } |
8ff84951 | 1332 | } |
39db1a3f | 1333 | return grub_error (GRUB_ERR_BAD_FS, "unsupported device type"); |
8ff84951 VS |
1334 | } |
1335 | ||
fdfde32a VS |
1336 | static grub_err_t |
1337 | read_dva (const dva_t *dva, | |
1338 | grub_zfs_endian_t endian, struct grub_zfs_data *data, | |
1339 | void *buf, grub_size_t len) | |
1340 | { | |
39db1a3f | 1341 | grub_uint64_t offset; |
fdfde32a VS |
1342 | unsigned i; |
1343 | grub_err_t err; | |
1344 | int try = 0; | |
8ff84951 | 1345 | offset = dva_get_offset (dva, endian); |
fdfde32a | 1346 | |
39db1a3f | 1347 | for (try = 0; try < 2; try++) |
fdfde32a VS |
1348 | { |
1349 | for (i = 0; i < data->n_devices_attached; i++) | |
1350 | if (data->devices_attached[i].id == DVA_GET_VDEV (dva)) | |
39db1a3f | 1351 | { |
aca5aefc | 1352 | err = read_device (offset, &data->devices_attached[i], len, buf); |
39db1a3f VS |
1353 | if (!err) |
1354 | return GRUB_ERR_NONE; | |
1355 | break; | |
1356 | } | |
1357 | if (try == 1) | |
1358 | break; | |
1359 | err = scan_devices (data); | |
fdfde32a VS |
1360 | if (err) |
1361 | return err; | |
1362 | } | |
39db1a3f | 1363 | return err; |
fdfde32a | 1364 | } |
bf78d5b2 RM |
1365 | |
1366 | /* | |
1367 | * Read a block of data based on the gang block address dva, | |
1368 | * and put its data in buf. | |
1369 | * | |
1370 | */ | |
1371 | static grub_err_t | |
1372 | zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf, | |
1373 | struct grub_zfs_data *data) | |
1374 | { | |
1375 | zio_gbh_phys_t *zio_gb; | |
bf78d5b2 RM |
1376 | unsigned i; |
1377 | grub_err_t err; | |
1378 | zio_cksum_t zc; | |
1379 | ||
1380 | grub_memset (&zc, 0, sizeof (zc)); | |
1381 | ||
1382 | zio_gb = grub_malloc (SPA_GANGBLOCKSIZE); | |
1383 | if (!zio_gb) | |
1384 | return grub_errno; | |
1385 | grub_dprintf ("zfs", endian == LITTLE_ENDIAN ? "little-endian gang\n" | |
1386 | :"big-endian gang\n"); | |
bf78d5b2 | 1387 | |
fdfde32a | 1388 | err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE); |
bf78d5b2 RM |
1389 | if (err) |
1390 | { | |
1391 | grub_free (zio_gb); | |
1392 | return err; | |
1393 | } | |
1394 | ||
1395 | /* XXX */ | |
1396 | /* self checksuming the gang block header */ | |
1397 | ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva), | |
1398 | dva_get_offset (dva, endian), bp->blk_birth, 0); | |
1399 | err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, endian, | |
1400 | (char *) zio_gb, SPA_GANGBLOCKSIZE); | |
1401 | if (err) | |
1402 | { | |
1403 | grub_free (zio_gb); | |
1404 | return err; | |
1405 | } | |
1406 | ||
1407 | endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; | |
1408 | ||
1409 | for (i = 0; i < SPA_GBH_NBLKPTRS; i++) | |
1410 | { | |
1411 | if (zio_gb->zg_blkptr[i].blk_birth == 0) | |
1412 | continue; | |
1413 | ||
1414 | err = zio_read_data (&zio_gb->zg_blkptr[i], endian, buf, data); | |
1415 | if (err) | |
1416 | { | |
1417 | grub_free (zio_gb); | |
1418 | return err; | |
1419 | } | |
1420 | buf = (char *) buf + get_psize (&zio_gb->zg_blkptr[i], endian); | |
1421 | } | |
1422 | grub_free (zio_gb); | |
1423 | return GRUB_ERR_NONE; | |
1424 | } | |
1425 | ||
1426 | /* | |
1427 | * Read in a block of raw data to buf. | |
1428 | */ | |
1429 | static grub_err_t | |
1430 | zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, | |
1431 | struct grub_zfs_data *data) | |
1432 | { | |
1433 | int i, psize; | |
1434 | grub_err_t err = GRUB_ERR_NONE; | |
1435 | ||
1436 | psize = get_psize (bp, endian); | |
1437 | ||
1438 | /* pick a good dva from the block pointer */ | |
1439 | for (i = 0; i < SPA_DVAS_PER_BP; i++) | |
1440 | { | |
bf78d5b2 RM |
1441 | if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) |
1442 | continue; | |
1443 | ||
1444 | if ((grub_zfs_to_cpu64 (bp->blk_dva[i].dva_word[1], endian)>>63) & 1) | |
1445 | err = zio_read_gang (bp, endian, &bp->blk_dva[i], buf, data); | |
1446 | else | |
fdfde32a | 1447 | err = read_dva (&bp->blk_dva[i], endian, data, buf, psize); |
bf78d5b2 RM |
1448 | if (!err) |
1449 | return GRUB_ERR_NONE; | |
1450 | grub_errno = GRUB_ERR_NONE; | |
1451 | } | |
1452 | ||
1453 | if (!err) | |
1454 | err = grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA"); | |
1455 | grub_errno = err; | |
1456 | ||
1457 | return err; | |
1458 | } | |
1459 | ||
1bc7cc1b VS |
1460 | static grub_err_t |
1461 | grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher, | |
1462 | grub_uint8_t *out, const grub_uint8_t *in, | |
1463 | grub_size_t psize, | |
1464 | void *mac_out, const void *nonce, | |
1465 | unsigned l, unsigned m) | |
1466 | { | |
1467 | grub_uint8_t iv[16]; | |
1468 | grub_uint8_t mul[16]; | |
1469 | grub_uint32_t mac[4]; | |
1470 | unsigned i, j; | |
1471 | grub_err_t err; | |
1472 | ||
1473 | grub_memcpy (iv + 1, nonce, 15 - l); | |
1474 | ||
1475 | iv[0] = (l - 1) | (((m-2) / 2) << 3); | |
1476 | for (j = 0; j < l; j++) | |
1477 | iv[15 - j] = psize >> (8 * j); | |
1478 | err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16); | |
1479 | if (err) | |
1480 | return err; | |
1481 | ||
1482 | iv[0] = l - 1; | |
1483 | ||
1484 | for (i = 0; i < (psize + 15) / 16; i++) | |
1485 | { | |
1486 | grub_size_t csize; | |
1487 | csize = 16; | |
1488 | if (csize > psize - 16 * i) | |
1489 | csize = psize - 16 * i; | |
1490 | for (j = 0; j < l; j++) | |
1491 | iv[15 - j] = (i + 1) >> (8 * j); | |
1492 | err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); | |
1493 | if (err) | |
1494 | return err; | |
1495 | grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize); | |
1496 | grub_crypto_xor (mac, mac, out + 16 * i, csize); | |
1497 | err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16); | |
1498 | if (err) | |
1499 | return err; | |
1500 | } | |
1501 | for (j = 0; j < l; j++) | |
1502 | iv[15 - j] = 0; | |
1503 | err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); | |
1504 | if (err) | |
1505 | return err; | |
1506 | if (mac_out) | |
1507 | grub_crypto_xor (mac_out, mac, mul, m); | |
1508 | return GRUB_ERR_NONE; | |
1509 | } | |
1510 | ||
bf78d5b2 RM |
1511 | /* |
1512 | * Read in a block of data, verify its checksum, decompress if needed, | |
1513 | * and put the uncompressed data in buf. | |
1514 | */ | |
1515 | static grub_err_t | |
1bc7cc1b | 1516 | zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, |
bf78d5b2 RM |
1517 | grub_size_t *size, struct grub_zfs_data *data) |
1518 | { | |
1519 | grub_size_t lsize, psize; | |
1bc7cc1b | 1520 | unsigned int comp, encrypted; |
bf78d5b2 RM |
1521 | char *compbuf = NULL; |
1522 | grub_err_t err; | |
1523 | zio_cksum_t zc = bp->blk_cksum; | |
1524 | grub_uint32_t checksum; | |
1525 | ||
1526 | *buf = NULL; | |
1527 | ||
1528 | checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; | |
290766fb | 1529 | comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff; |
1bc7cc1b | 1530 | encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3); |
bf78d5b2 RM |
1531 | lsize = (BP_IS_HOLE(bp) ? 0 : |
1532 | (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1) | |
1533 | << SPA_MINBLOCKSHIFT)); | |
1534 | psize = get_psize (bp, endian); | |
1535 | ||
1536 | if (size) | |
1537 | *size = lsize; | |
1538 | ||
1539 | if (comp >= ZIO_COMPRESS_FUNCTIONS) | |
1540 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
1541 | "compression algorithm %u not supported\n", (unsigned int) comp); | |
1542 | ||
1543 | if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL) | |
1544 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
1545 | "compression algorithm %s not supported\n", decomp_table[comp].name); | |
1546 | ||
1547 | if (comp != ZIO_COMPRESS_OFF) | |
1548 | { | |
1bc7cc1b | 1549 | compbuf = grub_malloc (ALIGN_UP (psize, 16)); |
bf78d5b2 RM |
1550 | if (! compbuf) |
1551 | return grub_errno; | |
1552 | } | |
1553 | else | |
1554 | compbuf = *buf = grub_malloc (lsize); | |
1555 | ||
1556 | grub_dprintf ("zfs", "endian = %d\n", endian); | |
1557 | err = zio_read_data (bp, endian, compbuf, data); | |
1558 | if (err) | |
1559 | { | |
1560 | grub_free (compbuf); | |
1561 | *buf = NULL; | |
1562 | return err; | |
1563 | } | |
1bc7cc1b | 1564 | grub_memset (compbuf, 0, ALIGN_UP (psize, 16) - psize); |
bf78d5b2 | 1565 | |
1bc7cc1b VS |
1566 | err = zio_checksum_verify (zc, checksum, endian, |
1567 | compbuf, psize); | |
bf78d5b2 RM |
1568 | if (err) |
1569 | { | |
1570 | grub_dprintf ("zfs", "incorrect checksum\n"); | |
1571 | grub_free (compbuf); | |
1572 | *buf = NULL; | |
1573 | return err; | |
1574 | } | |
1575 | ||
1bc7cc1b VS |
1576 | if (encrypted) |
1577 | { | |
1578 | grub_uint32_t mac[4]; | |
1579 | unsigned i; | |
1580 | grub_uint32_t sw[4]; | |
1581 | ||
1582 | grub_memcpy (sw, &(bp)->blk_dva[encrypted], 16); | |
1583 | for (i = 0; i < 4; i++) | |
1584 | sw[i] = grub_cpu_to_be32 (grub_zfs_to_cpu32 (sw[i], endian)); | |
1585 | ||
1586 | if (!data->subvol.cipher) | |
1587 | { | |
1588 | grub_free (compbuf); | |
1589 | *buf = NULL; | |
1590 | return grub_error (GRUB_ERR_ACCESS_DENIED, | |
1591 | "no decryption key available");; | |
1592 | } | |
1593 | err = grub_ccm_decrypt (data->subvol.cipher, | |
1594 | (grub_uint8_t *) compbuf, | |
1595 | (grub_uint8_t *) compbuf, | |
1596 | psize, mac, | |
1597 | sw + 1, 3, 12); | |
1598 | if (err) | |
1599 | { | |
1600 | grub_free (compbuf); | |
1601 | *buf = NULL; | |
1602 | return err; | |
1603 | } | |
1604 | ||
1605 | for (i = 0; i < 3; i++) | |
1606 | if (grub_zfs_to_cpu32 (((grub_uint32_t *) &zc + 5)[i], endian) | |
1607 | != grub_be_to_cpu32 (mac[i])) | |
1608 | { | |
1609 | grub_free (compbuf); | |
1610 | *buf = NULL; | |
1611 | return grub_error (GRUB_ERR_BAD_FS, "MAC verification failed"); | |
1612 | } | |
1613 | } | |
1614 | ||
bf78d5b2 RM |
1615 | if (comp != ZIO_COMPRESS_OFF) |
1616 | { | |
1617 | *buf = grub_malloc (lsize); | |
1618 | if (!*buf) | |
1619 | { | |
1620 | grub_free (compbuf); | |
1621 | return grub_errno; | |
1622 | } | |
1623 | ||
1624 | err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize); | |
1625 | grub_free (compbuf); | |
1626 | if (err) | |
1627 | { | |
1628 | grub_free (*buf); | |
1629 | *buf = NULL; | |
1630 | return err; | |
1631 | } | |
1632 | } | |
1633 | ||
1634 | return GRUB_ERR_NONE; | |
1635 | } | |
1636 | ||
1637 | /* | |
1638 | * Get the block from a block id. | |
1639 | * push the block onto the stack. | |
1640 | * | |
1641 | */ | |
1642 | static grub_err_t | |
1643 | dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, | |
1644 | grub_zfs_endian_t *endian_out, struct grub_zfs_data *data) | |
1645 | { | |
5bbd28b8 VS |
1646 | int level; |
1647 | grub_off_t idx; | |
bf78d5b2 RM |
1648 | blkptr_t *bp_array = dn->dn.dn_blkptr; |
1649 | int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT; | |
e7121b69 VS |
1650 | blkptr_t *bp; |
1651 | void *tmpbuf = 0; | |
bf78d5b2 RM |
1652 | grub_zfs_endian_t endian; |
1653 | grub_err_t err = GRUB_ERR_NONE; | |
1654 | ||
1655 | bp = grub_malloc (sizeof (blkptr_t)); | |
1656 | if (!bp) | |
1657 | return grub_errno; | |
1658 | ||
1659 | endian = dn->endian; | |
1660 | for (level = dn->dn.dn_nlevels - 1; level >= 0; level--) | |
1661 | { | |
1662 | grub_dprintf ("zfs", "endian = %d\n", endian); | |
1663 | idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1); | |
1664 | *bp = bp_array[idx]; | |
1665 | if (bp_array != dn->dn.dn_blkptr) | |
1666 | { | |
1667 | grub_free (bp_array); | |
1668 | bp_array = 0; | |
1669 | } | |
1670 | ||
1671 | if (BP_IS_HOLE (bp)) | |
1672 | { | |
1673 | grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec, | |
1674 | dn->endian) | |
1675 | << SPA_MINBLOCKSHIFT; | |
1676 | *buf = grub_malloc (size); | |
1677 | if (*buf) | |
1678 | { | |
1679 | err = grub_errno; | |
1680 | break; | |
1681 | } | |
1682 | grub_memset (*buf, 0, size); | |
1683 | endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; | |
1684 | break; | |
1685 | } | |
1686 | if (level == 0) | |
1687 | { | |
1688 | grub_dprintf ("zfs", "endian = %d\n", endian); | |
1689 | err = zio_read (bp, endian, buf, 0, data); | |
1690 | endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; | |
1691 | break; | |
1692 | } | |
1693 | grub_dprintf ("zfs", "endian = %d\n", endian); | |
e7121b69 | 1694 | err = zio_read (bp, endian, &tmpbuf, 0, data); |
bf78d5b2 RM |
1695 | endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; |
1696 | if (err) | |
1697 | break; | |
1698 | bp_array = tmpbuf; | |
1699 | } | |
1700 | if (bp_array != dn->dn.dn_blkptr) | |
1701 | grub_free (bp_array); | |
1702 | if (endian_out) | |
1703 | *endian_out = endian; | |
1704 | ||
1705 | grub_free (bp); | |
1706 | return err; | |
1707 | } | |
1708 | ||
1709 | /* | |
1710 | * mzap_lookup: Looks up property described by "name" and returns the value | |
1711 | * in "value". | |
1712 | */ | |
1713 | static grub_err_t | |
1714 | mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian, | |
1bc7cc1b | 1715 | int objsize, const char *name, grub_uint64_t * value, |
8622923b | 1716 | int case_insensitive) |
bf78d5b2 RM |
1717 | { |
1718 | int i, chunks; | |
1719 | mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; | |
1720 | ||
1721 | chunks = objsize / MZAP_ENT_LEN - 1; | |
1722 | for (i = 0; i < chunks; i++) | |
1723 | { | |
8622923b VS |
1724 | if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0) |
1725 | : (grub_strcmp (mzap_ent[i].mze_name, name) == 0)) | |
bf78d5b2 RM |
1726 | { |
1727 | *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian); | |
1728 | return GRUB_ERR_NONE; | |
1729 | } | |
1730 | } | |
1731 | ||
1732 | return grub_error (GRUB_ERR_FILE_NOT_FOUND, "couldn't find %s", name); | |
1733 | } | |
1734 | ||
1735 | static int | |
1736 | mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, | |
1737 | int NESTED_FUNC_ATTR (*hook) (const char *name, | |
1738 | grub_uint64_t val)) | |
1739 | { | |
1740 | int i, chunks; | |
1741 | mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; | |
1742 | ||
1743 | chunks = objsize / MZAP_ENT_LEN - 1; | |
1744 | for (i = 0; i < chunks; i++) | |
1745 | { | |
1746 | grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n", | |
1747 | mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value, | |
1748 | (int)mzap_ent[i].mze_cd); | |
1749 | if (hook (mzap_ent[i].mze_name, | |
1750 | grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian))) | |
1751 | return 1; | |
1752 | } | |
1753 | ||
1754 | return 0; | |
1755 | } | |
1756 | ||
1757 | static grub_uint64_t | |
8622923b VS |
1758 | zap_hash (grub_uint64_t salt, const char *name, |
1759 | int case_insensitive) | |
bf78d5b2 RM |
1760 | { |
1761 | static grub_uint64_t table[256]; | |
1762 | const grub_uint8_t *cp; | |
1763 | grub_uint8_t c; | |
1764 | grub_uint64_t crc = salt; | |
1765 | ||
1766 | if (table[128] == 0) | |
1767 | { | |
1768 | grub_uint64_t *ct; | |
1769 | int i, j; | |
1770 | for (i = 0; i < 256; i++) | |
1771 | { | |
1772 | for (ct = table + i, *ct = i, j = 8; j > 0; j--) | |
1773 | *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); | |
1774 | } | |
1775 | } | |
1776 | ||
8622923b VS |
1777 | if (case_insensitive) |
1778 | for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) | |
1779 | crc = (crc >> 8) ^ table[(crc ^ grub_toupper (c)) & 0xFF]; | |
1780 | else | |
1781 | for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) | |
1782 | crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; | |
bf78d5b2 RM |
1783 | |
1784 | /* | |
1785 | * Only use 28 bits, since we need 4 bits in the cookie for the | |
1786 | * collision differentiator. We MUST use the high bits, since | |
1787 | * those are the onces that we first pay attention to when | |
1788 | * chosing the bucket. | |
1789 | */ | |
1790 | crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); | |
1791 | ||
1792 | return (crc); | |
1793 | } | |
1794 | ||
1795 | /* | |
1796 | * Only to be used on 8-bit arrays. | |
1797 | * array_len is actual len in bytes (not encoded le_value_length). | |
1798 | * buf is null-terminated. | |
1799 | */ | |
8622923b VS |
1800 | |
1801 | static inline int | |
1802 | name_cmp (const char *s1, const char *s2, grub_size_t n, | |
1803 | int case_insensitive) | |
1804 | { | |
1805 | const char *t1 = (const char *) s1; | |
1806 | const char *t2 = (const char *) s2; | |
1807 | ||
1808 | if (!case_insensitive) | |
1809 | return grub_memcmp (t1, t2, n); | |
1810 | ||
1811 | while (n--) | |
1812 | { | |
1813 | if (grub_toupper (*t1) != grub_toupper (*t2)) | |
1814 | return (int) grub_toupper (*t1) - (int) grub_toupper (*t2); | |
1815 | ||
1816 | t1++; | |
1817 | t2++; | |
1818 | } | |
1819 | ||
1820 | return 0; | |
1821 | } | |
1822 | ||
bf78d5b2 RM |
1823 | /* XXX */ |
1824 | static int | |
1825 | zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian, | |
8622923b VS |
1826 | int blksft, int chunk, int array_len, const char *buf, |
1827 | int case_insensitive) | |
bf78d5b2 RM |
1828 | { |
1829 | int bseen = 0; | |
1830 | ||
1831 | while (bseen < array_len) | |
1832 | { | |
1833 | struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk).l_array; | |
1834 | int toread = MIN (array_len - bseen, ZAP_LEAF_ARRAY_BYTES); | |
1835 | ||
1836 | if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) | |
1837 | return (0); | |
1838 | ||
8622923b VS |
1839 | if (name_cmp ((char *) la->la_array, buf + bseen, toread, |
1840 | case_insensitive) != 0) | |
bf78d5b2 RM |
1841 | break; |
1842 | chunk = grub_zfs_to_cpu16 (la->la_next, endian); | |
1843 | bseen += toread; | |
1844 | } | |
1845 | return (bseen == array_len); | |
1846 | } | |
1847 | ||
1848 | /* XXX */ | |
1849 | static grub_err_t | |
1850 | zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft, | |
1851 | int chunk, int array_len, char *buf) | |
1852 | { | |
1853 | int bseen = 0; | |
1854 | ||
1855 | while (bseen < array_len) | |
1856 | { | |
1857 | struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk).l_array; | |
1858 | int toread = MIN (array_len - bseen, ZAP_LEAF_ARRAY_BYTES); | |
1859 | ||
1860 | if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) | |
1861 | /* Don't use grub_error because this error is to be ignored. */ | |
1862 | return GRUB_ERR_BAD_FS; | |
1863 | ||
1864 | grub_memcpy (buf + bseen,la->la_array, toread); | |
1865 | chunk = grub_zfs_to_cpu16 (la->la_next, endian); | |
1866 | bseen += toread; | |
1867 | } | |
1868 | return GRUB_ERR_NONE; | |
1869 | } | |
1870 | ||
1871 | ||
1872 | /* | |
1873 | * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the | |
1874 | * value for the property "name". | |
1875 | * | |
1876 | */ | |
1877 | /* XXX */ | |
1878 | static grub_err_t | |
1879 | zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian, | |
1880 | int blksft, grub_uint64_t h, | |
8622923b VS |
1881 | const char *name, grub_uint64_t * value, |
1882 | int case_insensitive) | |
bf78d5b2 RM |
1883 | { |
1884 | grub_uint16_t chunk; | |
1885 | struct zap_leaf_entry *le; | |
1886 | ||
1887 | /* Verify if this is a valid leaf block */ | |
1888 | if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF) | |
1889 | return grub_error (GRUB_ERR_BAD_FS, "invalid leaf type"); | |
1890 | if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) | |
1891 | return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic"); | |
1892 | ||
1893 | for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h)], endian); | |
47c3603c | 1894 | chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian)) |
bf78d5b2 RM |
1895 | { |
1896 | ||
1897 | if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) | |
1898 | return grub_error (GRUB_ERR_BAD_FS, "invalid chunk number"); | |
1899 | ||
1900 | le = ZAP_LEAF_ENTRY (l, blksft, chunk); | |
1901 | ||
1902 | /* Verify the chunk entry */ | |
1903 | if (le->le_type != ZAP_CHUNK_ENTRY) | |
1904 | return grub_error (GRUB_ERR_BAD_FS, "invalid chunk entry"); | |
1905 | ||
1906 | if (grub_zfs_to_cpu64 (le->le_hash,endian) != h) | |
1907 | continue; | |
1908 | ||
1909 | grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length); | |
1910 | ||
1911 | if (zap_leaf_array_equal (l, endian, blksft, | |
1912 | grub_zfs_to_cpu16 (le->le_name_chunk,endian), | |
1913 | grub_zfs_to_cpu16 (le->le_name_length, endian), | |
8622923b | 1914 | name, case_insensitive)) |
bf78d5b2 RM |
1915 | { |
1916 | struct zap_leaf_array *la; | |
bf78d5b2 | 1917 | |
47c3603c VS |
1918 | if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length, |
1919 | endian) != 1) | |
bf78d5b2 RM |
1920 | return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry"); |
1921 | ||
1922 | /* get the uint64_t property value */ | |
1923 | la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk).l_array; | |
bf78d5b2 RM |
1924 | |
1925 | *value = grub_be_to_cpu64 (la->la_array64); | |
1926 | ||
1927 | return GRUB_ERR_NONE; | |
1928 | } | |
1929 | } | |
1930 | ||
1931 | return grub_error (GRUB_ERR_FILE_NOT_FOUND, "couldn't find %s", name); | |
1932 | } | |
1933 | ||
1934 | ||
1935 | /* Verify if this is a fat zap header block */ | |
1936 | static grub_err_t | |
47c3603c | 1937 | zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian) |
bf78d5b2 | 1938 | { |
47c3603c | 1939 | if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC) |
bf78d5b2 RM |
1940 | return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic"); |
1941 | ||
1bc7cc1b VS |
1942 | /* if (zap->zap_flags != 0) |
1943 | return grub_error (GRUB_ERR_BAD_FS, "bad ZAP flags");*/ | |
bf78d5b2 RM |
1944 | |
1945 | if (zap->zap_salt == 0) | |
1946 | return grub_error (GRUB_ERR_BAD_FS, "bad ZAP salt"); | |
1947 | ||
1948 | return GRUB_ERR_NONE; | |
1949 | } | |
1950 | ||
1951 | /* | |
1952 | * Fat ZAP lookup | |
1953 | * | |
1954 | */ | |
1955 | /* XXX */ | |
1956 | static grub_err_t | |
1957 | fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap, | |
8622923b VS |
1958 | char *name, grub_uint64_t * value, struct grub_zfs_data *data, |
1959 | int case_insensitive) | |
bf78d5b2 | 1960 | { |
e7121b69 | 1961 | void *l; |
bf78d5b2 RM |
1962 | grub_uint64_t hash, idx, blkid; |
1963 | int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, | |
1964 | zap_dnode->endian) << DNODE_SHIFT); | |
1965 | grub_err_t err; | |
1966 | grub_zfs_endian_t leafendian; | |
1967 | ||
47c3603c | 1968 | err = zap_verify (zap, zap_dnode->endian); |
bf78d5b2 RM |
1969 | if (err) |
1970 | return err; | |
1971 | ||
8622923b | 1972 | hash = zap_hash (zap->zap_salt, name, case_insensitive); |
bf78d5b2 RM |
1973 | |
1974 | /* get block id from index */ | |
1975 | if (zap->zap_ptrtbl.zt_numblks != 0) | |
1976 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
1977 | "external pointer tables not supported"); | |
1978 | idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift); | |
47c3603c | 1979 | blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian); |
bf78d5b2 RM |
1980 | |
1981 | /* Get the leaf block */ | |
1982 | if ((1U << blksft) < sizeof (zap_leaf_phys_t)) | |
1983 | return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); | |
e7121b69 | 1984 | err = dmu_read (zap_dnode, blkid, &l, &leafendian, data); |
bf78d5b2 RM |
1985 | if (err) |
1986 | return err; | |
1987 | ||
8622923b VS |
1988 | err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value, |
1989 | case_insensitive); | |
bf78d5b2 RM |
1990 | grub_free (l); |
1991 | return err; | |
1992 | } | |
1993 | ||
1994 | /* XXX */ | |
1995 | static int | |
1996 | fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, | |
1bc7cc1b VS |
1997 | int NESTED_FUNC_ATTR (*hook) (const char *name, |
1998 | const void *val_in, | |
1999 | grub_size_t nelem, | |
2000 | grub_size_t elemsize), | |
2001 | struct grub_zfs_data *data) | |
bf78d5b2 RM |
2002 | { |
2003 | zap_leaf_phys_t *l; | |
e7121b69 | 2004 | void *l_in; |
5587329c | 2005 | grub_uint64_t idx, idx2, blkid; |
bf78d5b2 RM |
2006 | grub_uint16_t chunk; |
2007 | int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, | |
2008 | zap_dnode->endian) << DNODE_SHIFT); | |
2009 | grub_err_t err; | |
2010 | grub_zfs_endian_t endian; | |
2011 | ||
47c3603c | 2012 | if (zap_verify (zap, zap_dnode->endian)) |
bf78d5b2 RM |
2013 | return 0; |
2014 | ||
2015 | /* get block id from index */ | |
2016 | if (zap->zap_ptrtbl.zt_numblks != 0) | |
2017 | { | |
2018 | grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
2019 | "external pointer tables not supported"); | |
2020 | return 0; | |
2021 | } | |
2022 | /* Get the leaf block */ | |
2023 | if ((1U << blksft) < sizeof (zap_leaf_phys_t)) | |
2024 | { | |
2025 | grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); | |
2026 | return 0; | |
2027 | } | |
5587329c | 2028 | for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++) |
bf78d5b2 | 2029 | { |
47c3603c VS |
2030 | blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], |
2031 | zap_dnode->endian); | |
bf78d5b2 | 2032 | |
5587329c | 2033 | for (idx2 = 0; idx2 < idx; idx2++) |
1869edb5 VS |
2034 | if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))], |
2035 | zap_dnode->endian)) | |
5587329c VS |
2036 | break; |
2037 | if (idx2 != idx) | |
2038 | continue; | |
2039 | ||
e7121b69 VS |
2040 | err = dmu_read (zap_dnode, blkid, &l_in, &endian, data); |
2041 | l = l_in; | |
bf78d5b2 RM |
2042 | if (err) |
2043 | { | |
2044 | grub_errno = GRUB_ERR_NONE; | |
2045 | continue; | |
2046 | } | |
2047 | ||
2048 | /* Verify if this is a valid leaf block */ | |
2049 | if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF) | |
2050 | { | |
2051 | grub_free (l); | |
2052 | continue; | |
2053 | } | |
2054 | if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) | |
2055 | { | |
2056 | grub_free (l); | |
2057 | continue; | |
2058 | } | |
2059 | ||
2060 | for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS (blksft); chunk++) | |
2061 | { | |
2062 | char *buf; | |
bf78d5b2 | 2063 | struct zap_leaf_entry *le; |
1bc7cc1b VS |
2064 | char *val; |
2065 | grub_size_t val_length; | |
bf78d5b2 RM |
2066 | le = ZAP_LEAF_ENTRY (l, blksft, chunk); |
2067 | ||
2068 | /* Verify the chunk entry */ | |
2069 | if (le->le_type != ZAP_CHUNK_ENTRY) | |
2070 | continue; | |
2071 | ||
2072 | buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) | |
2073 | + 1); | |
47c3603c VS |
2074 | if (zap_leaf_array_get (l, endian, blksft, |
2075 | grub_zfs_to_cpu16 (le->le_name_chunk, | |
2076 | endian), | |
2077 | grub_zfs_to_cpu16 (le->le_name_length, | |
2078 | endian), buf)) | |
bf78d5b2 RM |
2079 | { |
2080 | grub_free (buf); | |
2081 | continue; | |
2082 | } | |
2083 | buf[le->le_name_length] = 0; | |
2084 | ||
1bc7cc1b VS |
2085 | val_length = ((int) le->le_value_length |
2086 | * (int) le->le_int_size); | |
2087 | val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian)); | |
2088 | if (zap_leaf_array_get (l, endian, blksft, | |
2089 | grub_zfs_to_cpu16 (le->le_value_chunk, | |
2090 | endian), | |
2091 | val_length, val)) | |
2092 | { | |
2093 | grub_free (buf); | |
2094 | grub_free (val); | |
2095 | continue; | |
2096 | } | |
bf78d5b2 | 2097 | |
1bc7cc1b | 2098 | if (hook (buf, val, le->le_value_length, le->le_int_size)) |
bf78d5b2 RM |
2099 | return 1; |
2100 | grub_free (buf); | |
1bc7cc1b | 2101 | grub_free (val); |
bf78d5b2 RM |
2102 | } |
2103 | } | |
2104 | return 0; | |
2105 | } | |
2106 | ||
bf78d5b2 RM |
2107 | /* |
2108 | * Read in the data of a zap object and find the value for a matching | |
2109 | * property name. | |
2110 | * | |
2111 | */ | |
2112 | static grub_err_t | |
2113 | zap_lookup (dnode_end_t * zap_dnode, char *name, grub_uint64_t * val, | |
8622923b | 2114 | struct grub_zfs_data *data, int case_insensitive) |
bf78d5b2 RM |
2115 | { |
2116 | grub_uint64_t block_type; | |
2117 | int size; | |
2118 | void *zapbuf; | |
2119 | grub_err_t err; | |
2120 | grub_zfs_endian_t endian; | |
2121 | ||
2122 | grub_dprintf ("zfs", "looking for '%s'\n", name); | |
2123 | ||
2124 | /* Read in the first block of the zap object data. */ | |
2125 | size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, | |
2126 | zap_dnode->endian) << SPA_MINBLOCKSHIFT; | |
2127 | err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); | |
2128 | if (err) | |
2129 | return err; | |
2130 | block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); | |
2131 | ||
2132 | grub_dprintf ("zfs", "zap read\n"); | |
2133 | ||
2134 | if (block_type == ZBT_MICRO) | |
2135 | { | |
2136 | grub_dprintf ("zfs", "micro zap\n"); | |
8622923b VS |
2137 | err = mzap_lookup (zapbuf, endian, size, name, val, |
2138 | case_insensitive); | |
bf78d5b2 RM |
2139 | grub_dprintf ("zfs", "returned %d\n", err); |
2140 | grub_free (zapbuf); | |
2141 | return err; | |
2142 | } | |
2143 | else if (block_type == ZBT_HEADER) | |
2144 | { | |
2145 | grub_dprintf ("zfs", "fat zap\n"); | |
2146 | /* this is a fat zap */ | |
8622923b VS |
2147 | err = fzap_lookup (zap_dnode, zapbuf, name, val, data, |
2148 | case_insensitive); | |
bf78d5b2 RM |
2149 | grub_dprintf ("zfs", "returned %d\n", err); |
2150 | grub_free (zapbuf); | |
2151 | return err; | |
2152 | } | |
2153 | ||
2154 | return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); | |
2155 | } | |
2156 | ||
2157 | static int | |
1bc7cc1b VS |
2158 | zap_iterate_u64 (dnode_end_t * zap_dnode, |
2159 | int NESTED_FUNC_ATTR (*hook) (const char *name, | |
2160 | grub_uint64_t val), | |
2161 | struct grub_zfs_data *data) | |
bf78d5b2 RM |
2162 | { |
2163 | grub_uint64_t block_type; | |
2164 | int size; | |
2165 | void *zapbuf; | |
2166 | grub_err_t err; | |
2167 | int ret; | |
2168 | grub_zfs_endian_t endian; | |
2169 | ||
1bc7cc1b VS |
2170 | auto int NESTED_FUNC_ATTR transform (const char *name, |
2171 | const void *val_in, | |
2172 | grub_size_t nelem, | |
2173 | grub_size_t elemsize); | |
2174 | ||
2175 | int NESTED_FUNC_ATTR transform (const char *name, | |
2176 | const void *val_in, | |
2177 | grub_size_t nelem, | |
2178 | grub_size_t elemsize) | |
2179 | { | |
2180 | if (elemsize != sizeof (grub_uint64_t) || nelem != 1) | |
2181 | return 0; | |
2182 | return hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in)); | |
2183 | } | |
2184 | ||
bf78d5b2 RM |
2185 | /* Read in the first block of the zap object data. */ |
2186 | size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; | |
2187 | err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); | |
2188 | if (err) | |
2189 | return 0; | |
2190 | block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); | |
2191 | ||
5587329c | 2192 | grub_dprintf ("zfs", "zap iterate\n"); |
bf78d5b2 RM |
2193 | |
2194 | if (block_type == ZBT_MICRO) | |
2195 | { | |
2196 | grub_dprintf ("zfs", "micro zap\n"); | |
2197 | ret = mzap_iterate (zapbuf, endian, size, hook); | |
2198 | grub_free (zapbuf); | |
2199 | return ret; | |
2200 | } | |
2201 | else if (block_type == ZBT_HEADER) | |
1bc7cc1b VS |
2202 | { |
2203 | grub_dprintf ("zfs", "fat zap\n"); | |
2204 | /* this is a fat zap */ | |
2205 | ret = fzap_iterate (zap_dnode, zapbuf, transform, data); | |
2206 | grub_free (zapbuf); | |
2207 | return ret; | |
2208 | } | |
2209 | grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); | |
2210 | return 0; | |
2211 | } | |
2212 | ||
2213 | static int | |
2214 | zap_iterate (dnode_end_t * zap_dnode, | |
2215 | int NESTED_FUNC_ATTR (*hook) (const char *name, | |
2216 | const void *val_in, | |
2217 | grub_size_t nelem, | |
2218 | grub_size_t elemsize), | |
2219 | struct grub_zfs_data *data) | |
2220 | { | |
2221 | grub_uint64_t block_type; | |
2222 | int size; | |
2223 | void *zapbuf; | |
2224 | grub_err_t err; | |
2225 | int ret; | |
2226 | grub_zfs_endian_t endian; | |
2227 | ||
2228 | /* Read in the first block of the zap object data. */ | |
2229 | size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; | |
2230 | err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); | |
2231 | if (err) | |
2232 | return 0; | |
2233 | block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); | |
2234 | ||
2235 | grub_dprintf ("zfs", "zap iterate\n"); | |
2236 | ||
2237 | if (block_type == ZBT_MICRO) | |
2238 | { | |
2239 | grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected"); | |
2240 | return 0; | |
2241 | } | |
2242 | if (block_type == ZBT_HEADER) | |
bf78d5b2 RM |
2243 | { |
2244 | grub_dprintf ("zfs", "fat zap\n"); | |
2245 | /* this is a fat zap */ | |
2246 | ret = fzap_iterate (zap_dnode, zapbuf, hook, data); | |
2247 | grub_free (zapbuf); | |
2248 | return ret; | |
2249 | } | |
2250 | grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); | |
2251 | return 0; | |
2252 | } | |
2253 | ||
2254 | ||
2255 | /* | |
2256 | * Get the dnode of an object number from the metadnode of an object set. | |
2257 | * | |
2258 | * Input | |
2259 | * mdn - metadnode to get the object dnode | |
2260 | * objnum - object number for the object dnode | |
2261 | * buf - data buffer that holds the returning dnode | |
2262 | */ | |
2263 | static grub_err_t | |
2264 | dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, | |
2265 | dnode_end_t * buf, struct grub_zfs_data *data) | |
2266 | { | |
2267 | grub_uint64_t blkid, blksz; /* the block id this object dnode is in */ | |
2268 | int epbs; /* shift of number of dnodes in a block */ | |
2269 | int idx; /* index within a block */ | |
e7121b69 | 2270 | void *dnbuf; |
bf78d5b2 RM |
2271 | grub_err_t err; |
2272 | grub_zfs_endian_t endian; | |
2273 | ||
2274 | blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, | |
2275 | mdn->endian) << SPA_MINBLOCKSHIFT; | |
2276 | epbs = zfs_log2 (blksz) - DNODE_SHIFT; | |
2277 | blkid = objnum >> epbs; | |
2278 | idx = objnum & ((1 << epbs) - 1); | |
2279 | ||
2280 | if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn, | |
2281 | sizeof (*mdn)) == 0 | |
2282 | && objnum >= data->dnode_start && objnum < data->dnode_end) | |
2283 | { | |
2284 | grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); | |
2285 | buf->endian = data->dnode_endian; | |
2286 | if (type && buf->dn.dn_type != type) | |
2287 | return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); | |
2288 | return GRUB_ERR_NONE; | |
2289 | } | |
2290 | ||
2291 | grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, | |
2292 | (unsigned long long) blkid); | |
e7121b69 | 2293 | err = dmu_read (mdn, blkid, &dnbuf, &endian, data); |
bf78d5b2 RM |
2294 | if (err) |
2295 | return err; | |
2296 | grub_dprintf ("zfs", "alive\n"); | |
2297 | ||
2298 | grub_free (data->dnode_buf); | |
2299 | grub_free (data->dnode_mdn); | |
2300 | data->dnode_mdn = grub_malloc (sizeof (*mdn)); | |
2301 | if (! data->dnode_mdn) | |
2302 | { | |
2303 | grub_errno = GRUB_ERR_NONE; | |
2304 | data->dnode_buf = 0; | |
2305 | } | |
2306 | else | |
2307 | { | |
2308 | grub_memcpy (data->dnode_mdn, mdn, sizeof (*mdn)); | |
2309 | data->dnode_buf = dnbuf; | |
2310 | data->dnode_start = blkid << epbs; | |
2311 | data->dnode_end = (blkid + 1) << epbs; | |
2312 | data->dnode_endian = endian; | |
2313 | } | |
2314 | ||
e7121b69 | 2315 | grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); |
bf78d5b2 RM |
2316 | buf->endian = endian; |
2317 | if (type && buf->dn.dn_type != type) | |
2318 | return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); | |
2319 | ||
2320 | return GRUB_ERR_NONE; | |
2321 | } | |
2322 | ||
2323 | /* | |
2324 | * Get the file dnode for a given file name where mdn is the meta dnode | |
2325 | * for this ZFS object set. When found, place the file dnode in dn. | |
2326 | * The 'path' argument will be mangled. | |
2327 | * | |
2328 | */ | |
2329 | static grub_err_t | |
1bc7cc1b VS |
2330 | dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, |
2331 | struct grub_zfs_data *data) | |
bf78d5b2 | 2332 | { |
1bc7cc1b | 2333 | grub_uint64_t objnum, version; |
bf78d5b2 RM |
2334 | char *cname, ch; |
2335 | grub_err_t err = GRUB_ERR_NONE; | |
2336 | char *path, *path_buf; | |
2337 | struct dnode_chain | |
2338 | { | |
2339 | struct dnode_chain *next; | |
2340 | dnode_end_t dn; | |
2341 | }; | |
2342 | struct dnode_chain *dnode_path = 0, *dn_new, *root; | |
2343 | ||
2344 | dn_new = grub_malloc (sizeof (*dn_new)); | |
2345 | if (! dn_new) | |
2346 | return grub_errno; | |
2347 | dn_new->next = 0; | |
2348 | dnode_path = root = dn_new; | |
2349 | ||
1bc7cc1b | 2350 | err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, |
bf78d5b2 RM |
2351 | &(dnode_path->dn), data); |
2352 | if (err) | |
2353 | { | |
2354 | grub_free (dn_new); | |
2355 | return err; | |
2356 | } | |
2357 | ||
8622923b VS |
2358 | err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version, |
2359 | data, 0); | |
bf78d5b2 RM |
2360 | if (err) |
2361 | { | |
2362 | grub_free (dn_new); | |
2363 | return err; | |
2364 | } | |
8622923b | 2365 | |
bf78d5b2 RM |
2366 | if (version > ZPL_VERSION) |
2367 | { | |
2368 | grub_free (dn_new); | |
2369 | return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version"); | |
2370 | } | |
8622923b | 2371 | |
1bc7cc1b VS |
2372 | err = zap_lookup (&(dnode_path->dn), "casesensitivity", |
2373 | &subvol->case_insensitive, | |
8622923b VS |
2374 | data, 0); |
2375 | if (err == GRUB_ERR_FILE_NOT_FOUND) | |
2376 | { | |
2377 | grub_errno = GRUB_ERR_NONE; | |
1bc7cc1b | 2378 | subvol->case_insensitive = 0; |
8622923b | 2379 | } |
8622923b VS |
2380 | |
2381 | err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0); | |
bf78d5b2 RM |
2382 | if (err) |
2383 | { | |
2384 | grub_free (dn_new); | |
2385 | return err; | |
2386 | } | |
2387 | ||
1bc7cc1b | 2388 | err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data); |
bf78d5b2 RM |
2389 | if (err) |
2390 | { | |
2391 | grub_free (dn_new); | |
2392 | return err; | |
2393 | } | |
2394 | ||
2395 | path = path_buf = grub_strdup (path_in); | |
2396 | if (!path_buf) | |
2397 | { | |
2398 | grub_free (dn_new); | |
2399 | return grub_errno; | |
2400 | } | |
2401 | ||
2402 | while (1) | |
2403 | { | |
2404 | /* skip leading slashes */ | |
2405 | while (*path == '/') | |
2406 | path++; | |
2407 | if (!*path) | |
2408 | break; | |
2409 | /* get the next component name */ | |
2410 | cname = path; | |
2411 | while (*path && *path != '/') | |
2412 | path++; | |
2413 | /* Skip dot. */ | |
2414 | if (cname + 1 == path && cname[0] == '.') | |
2415 | continue; | |
2416 | /* Handle double dot. */ | |
2417 | if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') | |
2418 | { | |
2419 | if (dn_new->next) | |
2420 | { | |
2421 | dn_new = dnode_path; | |
2422 | dnode_path = dn_new->next; | |
2423 | grub_free (dn_new); | |
2424 | } | |
2425 | else | |
2426 | { | |
2427 | err = grub_error (GRUB_ERR_FILE_NOT_FOUND, | |
2428 | "can't resolve .."); | |
2429 | break; | |
2430 | } | |
2431 | continue; | |
2432 | } | |
2433 | ||
2434 | ch = *path; | |
2435 | *path = 0; /* ensure null termination */ | |
2436 | ||
2437 | if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) | |
2438 | { | |
2439 | grub_free (path_buf); | |
2440 | return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); | |
2441 | } | |
1bc7cc1b VS |
2442 | err = zap_lookup (&(dnode_path->dn), cname, &objnum, |
2443 | data, subvol->case_insensitive); | |
bf78d5b2 RM |
2444 | if (err) |
2445 | break; | |
2446 | ||
2447 | dn_new = grub_malloc (sizeof (*dn_new)); | |
2448 | if (! dn_new) | |
2449 | { | |
2450 | err = grub_errno; | |
2451 | break; | |
2452 | } | |
2453 | dn_new->next = dnode_path; | |
2454 | dnode_path = dn_new; | |
2455 | ||
2456 | objnum = ZFS_DIRENT_OBJ (objnum); | |
1bc7cc1b | 2457 | err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data); |
bf78d5b2 RM |
2458 | if (err) |
2459 | break; | |
2460 | ||
2461 | *path = ch; | |
34c59654 VS |
2462 | if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE |
2463 | && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) | |
bf78d5b2 | 2464 | { |
5587329c | 2465 | char *sym_value; |
5587329c VS |
2466 | grub_size_t sym_sz; |
2467 | int free_symval = 0; | |
bf78d5b2 | 2468 | char *oldpath = path, *oldpathbuf = path_buf; |
5587329c | 2469 | sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys)); |
5587329c VS |
2470 | |
2471 | sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian); | |
2472 | ||
34c59654 | 2473 | if (dnode_path->dn.dn.dn_flags & 1) |
5587329c VS |
2474 | { |
2475 | grub_size_t block; | |
2476 | grub_size_t blksz; | |
2477 | blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, | |
2478 | dnode_path->dn.endian) | |
2479 | << SPA_MINBLOCKSHIFT); | |
2480 | ||
2481 | sym_value = grub_malloc (sym_sz); | |
2482 | if (!sym_value) | |
2483 | return grub_errno; | |
2484 | for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++) | |
2485 | { | |
2486 | void *t; | |
2487 | grub_size_t movesize; | |
2488 | ||
2489 | err = dmu_read (&(dnode_path->dn), block, &t, 0, data); | |
2490 | if (err) | |
2491 | return err; | |
2492 | ||
2493 | movesize = MIN (sym_sz - block * blksz, blksz); | |
2494 | ||
2495 | grub_memcpy (sym_value + block * blksz, t, movesize); | |
2496 | grub_free (t); | |
2497 | } | |
2498 | free_symval = 1; | |
2499 | } | |
2500 | path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); | |
bf78d5b2 RM |
2501 | if (!path_buf) |
2502 | { | |
2503 | grub_free (oldpathbuf); | |
2504 | return grub_errno; | |
2505 | } | |
5587329c VS |
2506 | grub_memcpy (path, sym_value, sym_sz); |
2507 | if (free_symval) | |
2508 | grub_free (sym_value); | |
2509 | path [sym_sz] = 0; | |
bf78d5b2 RM |
2510 | grub_memcpy (path + grub_strlen (path), oldpath, |
2511 | grub_strlen (oldpath) + 1); | |
2512 | ||
2513 | grub_free (oldpathbuf); | |
2514 | if (path[0] != '/') | |
2515 | { | |
2516 | dn_new = dnode_path; | |
2517 | dnode_path = dn_new->next; | |
2518 | grub_free (dn_new); | |
2519 | } | |
2520 | else while (dnode_path != root) | |
2521 | { | |
2522 | dn_new = dnode_path; | |
2523 | dnode_path = dn_new->next; | |
2524 | grub_free (dn_new); | |
2525 | } | |
2526 | } | |
34c59654 VS |
2527 | if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA) |
2528 | { | |
2529 | void *sahdrp; | |
2530 | int hdrsize; | |
2531 | ||
2532 | if (dnode_path->dn.dn.dn_bonuslen != 0) | |
2533 | { | |
2534 | sahdrp = DN_BONUS (&dnode_path->dn.dn); | |
2535 | } | |
2536 | else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) | |
2537 | { | |
2538 | blkptr_t *bp = &dnode_path->dn.dn.dn_spill; | |
2539 | ||
2540 | err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data); | |
2541 | if (err) | |
2542 | return err; | |
2543 | } | |
2544 | else | |
2545 | { | |
2546 | return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); | |
2547 | } | |
2548 | ||
2549 | hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); | |
2550 | ||
2551 | if (((grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_TYPE_OFFSET), dnode_path->dn.endian) >> 12) & 0xf) == 0xa) | |
2552 | { | |
2553 | char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET; | |
2554 | grub_size_t sym_sz = | |
2555 | grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), dnode_path->dn.endian); | |
2556 | char *oldpath = path, *oldpathbuf = path_buf; | |
2557 | path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); | |
2558 | if (!path_buf) | |
2559 | { | |
2560 | grub_free (oldpathbuf); | |
2561 | return grub_errno; | |
2562 | } | |
2563 | grub_memcpy (path, sym_value, sym_sz); | |
2564 | path [sym_sz] = 0; | |
2565 | grub_memcpy (path + grub_strlen (path), oldpath, | |
2566 | grub_strlen (oldpath) + 1); | |
2567 | ||
2568 | grub_free (oldpathbuf); | |
2569 | if (path[0] != '/') | |
2570 | { | |
2571 | dn_new = dnode_path; | |
2572 | dnode_path = dn_new->next; | |
2573 | grub_free (dn_new); | |
2574 | } | |
2575 | else while (dnode_path != root) | |
2576 | { | |
2577 | dn_new = dnode_path; | |
2578 | dnode_path = dn_new->next; | |
2579 | grub_free (dn_new); | |
2580 | } | |
2581 | } | |
2582 | } | |
bf78d5b2 RM |
2583 | } |
2584 | ||
2585 | if (!err) | |
2586 | grub_memcpy (dn, &(dnode_path->dn), sizeof (*dn)); | |
2587 | ||
2588 | while (dnode_path) | |
2589 | { | |
2590 | dn_new = dnode_path->next; | |
2591 | grub_free (dnode_path); | |
2592 | dnode_path = dn_new; | |
2593 | } | |
2594 | grub_free (path_buf); | |
2595 | return err; | |
2596 | } | |
2597 | ||
2598 | #if 0 | |
2599 | /* | |
2600 | * Get the default 'bootfs' property value from the rootpool. | |
2601 | * | |
2602 | */ | |
2603 | static grub_err_t | |
2604 | get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj, | |
2605 | struct grub_zfs_data *data) | |
2606 | { | |
2607 | grub_uint64_t objnum = 0; | |
2608 | dnode_phys_t *dn; | |
2609 | if (!dn) | |
2610 | return grub_errno; | |
2611 | ||
2612 | if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, | |
2613 | DMU_OT_OBJECT_DIRECTORY, dn, data))) | |
2614 | { | |
2615 | grub_free (dn); | |
2616 | return (grub_errno); | |
2617 | } | |
2618 | ||
2619 | /* | |
2620 | * find the object number for 'pool_props', and get the dnode | |
2621 | * of the 'pool_props'. | |
2622 | */ | |
2623 | if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data)) | |
2624 | { | |
2625 | grub_free (dn); | |
2626 | return (GRUB_ERR_BAD_FS); | |
2627 | } | |
2628 | if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data))) | |
2629 | { | |
2630 | grub_free (dn); | |
2631 | return (grub_errno); | |
2632 | } | |
2633 | if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data)) | |
2634 | { | |
2635 | grub_free (dn); | |
2636 | return (GRUB_ERR_BAD_FS); | |
2637 | } | |
2638 | ||
2639 | if (!objnum) | |
2640 | { | |
2641 | grub_free (dn); | |
2642 | return (GRUB_ERR_BAD_FS); | |
2643 | } | |
2644 | ||
2645 | *obj = objnum; | |
2646 | return (0); | |
2647 | } | |
2648 | #endif | |
2649 | /* | |
2650 | * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), | |
2651 | * e.g. pool/rootfs, or a given object number (obj), e.g. the object number | |
2652 | * of pool/rootfs. | |
2653 | * | |
2654 | * If no fsname and no obj are given, return the DSL_DIR metadnode. | |
2655 | * If fsname is given, return its metadnode and its matching object number. | |
2656 | * If only obj is given, return the metadnode for this object number. | |
2657 | * | |
2658 | */ | |
2659 | static grub_err_t | |
2660 | get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname, | |
2661 | dnode_end_t * mdn, struct grub_zfs_data *data) | |
2662 | { | |
2663 | grub_uint64_t objnum; | |
2664 | grub_err_t err; | |
2665 | ||
2666 | grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian); | |
2667 | ||
2668 | err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, | |
2669 | DMU_OT_OBJECT_DIRECTORY, mdn, data); | |
2670 | if (err) | |
2671 | return err; | |
2672 | ||
2673 | grub_dprintf ("zfs", "alive\n"); | |
2674 | ||
8622923b | 2675 | err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data, 0); |
bf78d5b2 RM |
2676 | if (err) |
2677 | return err; | |
2678 | ||
2679 | grub_dprintf ("zfs", "alive\n"); | |
2680 | ||
2681 | err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); | |
2682 | if (err) | |
2683 | return err; | |
2684 | ||
2685 | grub_dprintf ("zfs", "alive\n"); | |
2686 | ||
2687 | while (*fsname) | |
2688 | { | |
2689 | grub_uint64_t childobj; | |
2690 | char *cname, ch; | |
2691 | ||
2692 | while (*fsname == '/') | |
2693 | fsname++; | |
2694 | ||
2695 | if (! *fsname || *fsname == '@') | |
2696 | break; | |
2697 | ||
2698 | cname = fsname; | |
2699 | while (*fsname && !grub_isspace (*fsname) && *fsname != '/') | |
2700 | fsname++; | |
2701 | ch = *fsname; | |
2702 | *fsname = 0; | |
2703 | ||
2704 | childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian); | |
2705 | err = dnode_get (mosmdn, childobj, | |
2706 | DMU_OT_DSL_DIR_CHILD_MAP, mdn, data); | |
2707 | if (err) | |
2708 | return err; | |
2709 | ||
8622923b | 2710 | err = zap_lookup (mdn, cname, &objnum, data, 0); |
bf78d5b2 RM |
2711 | if (err) |
2712 | return err; | |
2713 | ||
2714 | err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); | |
2715 | if (err) | |
2716 | return err; | |
2717 | ||
2718 | *fsname = ch; | |
2719 | } | |
2720 | return GRUB_ERR_NONE; | |
2721 | } | |
2722 | ||
2723 | static grub_err_t | |
2724 | make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data) | |
2725 | { | |
e7121b69 | 2726 | void *osp; |
bf78d5b2 RM |
2727 | blkptr_t *bp; |
2728 | grub_size_t ospsize; | |
2729 | grub_err_t err; | |
2730 | ||
2731 | grub_dprintf ("zfs", "endian = %d\n", mdn->endian); | |
2732 | ||
2733 | bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp); | |
e7121b69 | 2734 | err = zio_read (bp, mdn->endian, &osp, &ospsize, data); |
bf78d5b2 RM |
2735 | if (err) |
2736 | return err; | |
2737 | if (ospsize < OBJSET_PHYS_SIZE_V14) | |
2738 | { | |
2739 | grub_free (osp); | |
2740 | return grub_error (GRUB_ERR_BAD_FS, "too small osp"); | |
2741 | } | |
2742 | ||
2743 | mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1; | |
e7121b69 VS |
2744 | grub_memmove ((char *) &(mdn->dn), |
2745 | (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); | |
bf78d5b2 RM |
2746 | grub_free (osp); |
2747 | return GRUB_ERR_NONE; | |
2748 | } | |
2749 | ||
2750 | static grub_err_t | |
1bc7cc1b VS |
2751 | dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, |
2752 | dnode_end_t * dn, int *isfs, | |
2753 | struct grub_zfs_data *data) | |
bf78d5b2 RM |
2754 | { |
2755 | char *fsname, *snapname; | |
2756 | const char *ptr_at, *filename; | |
2757 | grub_uint64_t headobj; | |
1bc7cc1b | 2758 | grub_uint64_t keychainobj; |
bf78d5b2 RM |
2759 | grub_err_t err; |
2760 | ||
1bc7cc1b VS |
2761 | auto int NESTED_FUNC_ATTR iterate_zap_key (const char *name, |
2762 | const void *val_in, | |
2763 | grub_size_t nelem, | |
2764 | grub_size_t elemsize); | |
2765 | int NESTED_FUNC_ATTR iterate_zap_key (const char *name __attribute__ ((unused)), | |
2766 | const void *val_in, | |
2767 | grub_size_t nelem, | |
2768 | grub_size_t elemsize) | |
2769 | { | |
2770 | const struct grub_zfs_key *key = val_in; | |
1bc7cc1b | 2771 | unsigned keylen; |
2cdc8995 | 2772 | struct grub_zfs_wrap_key *wrap_key; |
1bc7cc1b VS |
2773 | |
2774 | if (elemsize != 1 || nelem != sizeof (*key)) | |
2775 | { | |
2776 | grub_dprintf ("zfs", "Unexpected key length %" PRIuGRUB_SIZE | |
2777 | " x %" PRIuGRUB_SIZE "\n", nelem, elemsize); | |
2778 | return 0; | |
2779 | } | |
2780 | ||
2781 | if (grub_memcmp (key->enc_key + 32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) | |
2782 | == 0) | |
2783 | keylen = 16; | |
2784 | else if (grub_memcmp (key->enc_key + 40, "\0\0\0\0\0\0\0\0", 8) == 0) | |
2785 | keylen = 24; | |
2786 | else | |
2787 | keylen = 32; | |
2788 | ||
2cdc8995 | 2789 | for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next) |
1bc7cc1b | 2790 | { |
2cdc8995 VS |
2791 | grub_crypto_cipher_handle_t cipher; |
2792 | grub_uint8_t decrypted[32], mac[32]; | |
2793 | cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); | |
2794 | if (!cipher) | |
2795 | { | |
2796 | grub_errno = GRUB_ERR_NONE; | |
2797 | return 0; | |
2798 | } | |
2799 | err = grub_crypto_cipher_set_key (cipher, | |
2800 | (const grub_uint8_t *) wrap_key->key, | |
2801 | keylen); | |
2802 | if (err) | |
2803 | { | |
2804 | grub_errno = GRUB_ERR_NONE; | |
2805 | continue; | |
2806 | } | |
1bc7cc1b | 2807 | |
2cdc8995 VS |
2808 | err = grub_ccm_decrypt (cipher, decrypted, key->unknown_purpose_key, 32, |
2809 | mac, key->unknown_purpose_nonce, 2, 16); | |
2810 | if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) | |
2811 | != 0)) | |
2812 | { | |
2813 | grub_dprintf ("zfs", "key loading failed\n"); | |
2814 | grub_errno = GRUB_ERR_NONE; | |
2815 | continue; | |
2816 | } | |
1bc7cc1b | 2817 | |
2cdc8995 VS |
2818 | err = grub_ccm_decrypt (cipher, decrypted, key->enc_key, keylen, mac, |
2819 | key->enc_nonce, 2, 16); | |
2820 | if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0) | |
2821 | { | |
2822 | grub_dprintf ("zfs", "key loading failed\n"); | |
2823 | grub_errno = GRUB_ERR_NONE; | |
2824 | continue; | |
2825 | } | |
2826 | subvol->cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); | |
2827 | if (!subvol->cipher) | |
2828 | { | |
2829 | grub_errno = GRUB_ERR_NONE; | |
2830 | continue; | |
2831 | } | |
2832 | err = grub_crypto_cipher_set_key (subvol->cipher, decrypted, keylen); | |
2833 | if (err) | |
2834 | { | |
2835 | grub_errno = GRUB_ERR_NONE; | |
2836 | continue; | |
2837 | } | |
1bc7cc1b VS |
2838 | return 0; |
2839 | } | |
1bc7cc1b VS |
2840 | return 0; |
2841 | } | |
2842 | ||
bf78d5b2 RM |
2843 | ptr_at = grub_strchr (fullpath, '@'); |
2844 | if (! ptr_at) | |
2845 | { | |
2846 | *isfs = 1; | |
2847 | filename = 0; | |
2848 | snapname = 0; | |
2849 | fsname = grub_strdup (fullpath); | |
2850 | } | |
2851 | else | |
2852 | { | |
2853 | const char *ptr_slash = grub_strchr (ptr_at, '/'); | |
2854 | ||
2855 | *isfs = 0; | |
2856 | fsname = grub_malloc (ptr_at - fullpath + 1); | |
2857 | if (!fsname) | |
2858 | return grub_errno; | |
2859 | grub_memcpy (fsname, fullpath, ptr_at - fullpath); | |
2860 | fsname[ptr_at - fullpath] = 0; | |
2861 | if (ptr_at[1] && ptr_at[1] != '/') | |
2862 | { | |
2863 | snapname = grub_malloc (ptr_slash - ptr_at); | |
2864 | if (!snapname) | |
2865 | { | |
2866 | grub_free (fsname); | |
2867 | return grub_errno; | |
2868 | } | |
2869 | grub_memcpy (snapname, ptr_at + 1, ptr_slash - ptr_at - 1); | |
2870 | snapname[ptr_slash - ptr_at - 1] = 0; | |
2871 | } | |
2872 | else | |
2873 | snapname = 0; | |
2874 | if (ptr_slash) | |
2875 | filename = ptr_slash; | |
2876 | else | |
2877 | filename = "/"; | |
2878 | grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n", | |
2879 | fsname, snapname, filename); | |
2880 | } | |
2881 | grub_dprintf ("zfs", "alive\n"); | |
2882 | err = get_filesystem_dnode (&(data->mos), fsname, dn, data); | |
2883 | if (err) | |
2884 | { | |
2885 | grub_free (fsname); | |
2886 | grub_free (snapname); | |
2887 | return err; | |
2888 | } | |
2889 | ||
2890 | grub_dprintf ("zfs", "alive\n"); | |
2891 | ||
2892 | headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian); | |
2893 | ||
1bc7cc1b | 2894 | grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); |
bf78d5b2 | 2895 | |
1bc7cc1b VS |
2896 | err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &subvol->mdn, |
2897 | data); | |
bf78d5b2 RM |
2898 | if (err) |
2899 | { | |
2900 | grub_free (fsname); | |
2901 | grub_free (snapname); | |
2902 | return err; | |
2903 | } | |
1bc7cc1b VS |
2904 | grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); |
2905 | ||
2906 | keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian); | |
2907 | if (keychainobj) | |
2908 | { | |
2909 | dnode_end_t keychain_dn; | |
2910 | err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN, | |
2911 | &keychain_dn, data); | |
2912 | if (err) | |
2913 | { | |
2914 | grub_free (fsname); | |
2915 | grub_free (snapname); | |
2916 | return err; | |
2917 | } | |
2918 | zap_iterate (&keychain_dn, iterate_zap_key, data); | |
2919 | } | |
2920 | ||
bf78d5b2 RM |
2921 | |
2922 | if (snapname) | |
2923 | { | |
2924 | grub_uint64_t snapobj; | |
2925 | ||
1bc7cc1b | 2926 | snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian); |
bf78d5b2 RM |
2927 | |
2928 | err = dnode_get (&(data->mos), snapobj, | |
1bc7cc1b | 2929 | DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data); |
bf78d5b2 | 2930 | if (!err) |
1bc7cc1b | 2931 | err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0); |
bf78d5b2 | 2932 | if (!err) |
1bc7cc1b VS |
2933 | err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, |
2934 | &subvol->mdn, data); | |
bf78d5b2 RM |
2935 | if (err) |
2936 | { | |
2937 | grub_free (fsname); | |
2938 | grub_free (snapname); | |
2939 | return err; | |
2940 | } | |
2941 | } | |
2942 | ||
1bc7cc1b | 2943 | subvol->obj = headobj; |
bf78d5b2 | 2944 | |
1bc7cc1b | 2945 | make_mdn (&subvol->mdn, data); |
bf78d5b2 | 2946 | |
1bc7cc1b | 2947 | grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); |
bf78d5b2 RM |
2948 | |
2949 | if (*isfs) | |
2950 | { | |
2951 | grub_free (fsname); | |
2952 | grub_free (snapname); | |
2953 | return GRUB_ERR_NONE; | |
2954 | } | |
1bc7cc1b | 2955 | err = dnode_get_path (subvol, filename, dn, data); |
bf78d5b2 RM |
2956 | grub_free (fsname); |
2957 | grub_free (snapname); | |
2958 | return err; | |
2959 | } | |
2960 | ||
2961 | /* | |
2962 | * For a given XDR packed nvlist, verify the first 4 bytes and move on. | |
2963 | * | |
2964 | * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : | |
2965 | * | |
2966 | * encoding method/host endian (4 bytes) | |
2967 | * nvl_version (4 bytes) | |
2968 | * nvl_nvflag (4 bytes) | |
2969 | * encoded nvpairs: | |
2970 | * encoded size of the nvpair (4 bytes) | |
2971 | * decoded size of the nvpair (4 bytes) | |
2972 | * name string size (4 bytes) | |
2973 | * name string data (sizeof(NV_ALIGN4(string)) | |
2974 | * data type (4 bytes) | |
2975 | * # of elements in the nvpair (4 bytes) | |
2976 | * data | |
2977 | * 2 zero's for the last nvpair | |
2978 | * (end of the entire list) (8 bytes) | |
2979 | * | |
2980 | */ | |
2981 | ||
2982 | static int | |
8ff84951 VS |
2983 | nvlist_find_value (const char *nvlist, const char *name, |
2984 | int valtype, char **val, | |
bf78d5b2 RM |
2985 | grub_size_t *size_out, grub_size_t *nelm_out) |
2986 | { | |
2987 | int name_len, type, encode_size; | |
8ff84951 | 2988 | const char *nvpair, *nvp_name; |
bf78d5b2 RM |
2989 | |
2990 | /* Verify if the 1st and 2nd byte in the nvlist are valid. */ | |
2991 | /* NOTE: independently of what endianness header announces all | |
2992 | subsequent values are big-endian. */ | |
2993 | if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN | |
2994 | && nvlist[1] != NV_BIG_ENDIAN)) | |
2995 | { | |
2996 | grub_dprintf ("zfs", "incorrect nvlist header\n"); | |
2997 | grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); | |
2998 | return 0; | |
2999 | } | |
3000 | ||
3001 | /* skip the header, nvl_version, and nvl_nvflag */ | |
3002 | nvlist = nvlist + 4 * 3; | |
3003 | /* | |
3004 | * Loop thru the nvpair list | |
3005 | * The XDR representation of an integer is in big-endian byte order. | |
3006 | */ | |
3007 | while ((encode_size = grub_be_to_cpu32 (*(grub_uint32_t *) nvlist))) | |
3008 | { | |
3009 | int nelm; | |
3010 | ||
3011 | nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ | |
3012 | ||
3013 | name_len = grub_be_to_cpu32 (*(grub_uint32_t *) nvpair); | |
3014 | nvpair += 4; | |
3015 | ||
3016 | nvp_name = nvpair; | |
3017 | nvpair = nvpair + ((name_len + 3) & ~3); /* align */ | |
3018 | ||
3019 | type = grub_be_to_cpu32 (*(grub_uint32_t *) nvpair); | |
3020 | nvpair += 4; | |
3021 | ||
3022 | nelm = grub_be_to_cpu32 (*(grub_uint32_t *) nvpair); | |
3023 | if (nelm < 1) | |
3024 | return grub_error (GRUB_ERR_BAD_FS, "empty nvpair"); | |
3025 | ||
3026 | nvpair += 4; | |
3027 | ||
3028 | if ((grub_strncmp (nvp_name, name, name_len) == 0) && type == valtype) | |
3029 | { | |
8ff84951 | 3030 | *val = (char *) nvpair; |
bf78d5b2 RM |
3031 | *size_out = encode_size; |
3032 | if (nelm_out) | |
3033 | *nelm_out = nelm; | |
3034 | return 1; | |
3035 | } | |
3036 | ||
3037 | nvlist += encode_size; /* goto the next nvpair */ | |
3038 | } | |
3039 | return 0; | |
3040 | } | |
3041 | ||
3042 | int | |
8ff84951 VS |
3043 | grub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name, |
3044 | grub_uint64_t * out) | |
bf78d5b2 RM |
3045 | { |
3046 | char *nvpair; | |
3047 | grub_size_t size; | |
3048 | int found; | |
3049 | ||
3050 | found = nvlist_find_value (nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0); | |
3051 | if (!found) | |
3052 | return 0; | |
3053 | if (size < sizeof (grub_uint64_t)) | |
3054 | { | |
3055 | grub_error (GRUB_ERR_BAD_FS, "invalid uint64"); | |
3056 | return 0; | |
3057 | } | |
3058 | ||
3059 | *out = grub_be_to_cpu64 (*(grub_uint64_t *) nvpair); | |
3060 | return 1; | |
3061 | } | |
3062 | ||
3063 | char * | |
8ff84951 | 3064 | grub_zfs_nvlist_lookup_string (const char *nvlist, const char *name) |
bf78d5b2 RM |
3065 | { |
3066 | char *nvpair; | |
3067 | char *ret; | |
3068 | grub_size_t slen; | |
3069 | grub_size_t size; | |
3070 | int found; | |
3071 | ||
3072 | found = nvlist_find_value (nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0); | |
3073 | if (!found) | |
3074 | return 0; | |
3075 | if (size < 4) | |
3076 | { | |
3077 | grub_error (GRUB_ERR_BAD_FS, "invalid string"); | |
3078 | return 0; | |
3079 | } | |
3080 | slen = grub_be_to_cpu32 (*(grub_uint32_t *) nvpair); | |
3081 | if (slen > size - 4) | |
3082 | slen = size - 4; | |
3083 | ret = grub_malloc (slen + 1); | |
3084 | if (!ret) | |
3085 | return 0; | |
3086 | grub_memcpy (ret, nvpair + 4, slen); | |
3087 | ret[slen] = 0; | |
3088 | return ret; | |
3089 | } | |
3090 | ||
3091 | char * | |
8ff84951 | 3092 | grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name) |
bf78d5b2 RM |
3093 | { |
3094 | char *nvpair; | |
3095 | char *ret; | |
3096 | grub_size_t size; | |
3097 | int found; | |
3098 | ||
3099 | found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair, | |
3100 | &size, 0); | |
3101 | if (!found) | |
3102 | return 0; | |
3103 | ret = grub_zalloc (size + 3 * sizeof (grub_uint32_t)); | |
3104 | if (!ret) | |
3105 | return 0; | |
3106 | grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); | |
3107 | ||
3108 | grub_memcpy (ret + sizeof (grub_uint32_t), nvpair, size); | |
3109 | return ret; | |
3110 | } | |
3111 | ||
3112 | int | |
8ff84951 VS |
3113 | grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, |
3114 | const char *name) | |
bf78d5b2 | 3115 | { |
bf78d5b2 RM |
3116 | char *nvpair; |
3117 | grub_size_t nelm, size; | |
bf78d5b2 | 3118 | int found; |
bf78d5b2 | 3119 | |
e5c63d9d | 3120 | found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, |
bf78d5b2 RM |
3121 | &size, &nelm); |
3122 | if (! found) | |
3123 | return -1; | |
3124 | return nelm; | |
3125 | } | |
bf78d5b2 | 3126 | |
e5c63d9d | 3127 | static int |
8ff84951 | 3128 | get_nvlist_size (const char *beg, const char *limit) |
e5c63d9d | 3129 | { |
8ff84951 | 3130 | const char *ptr; |
e5c63d9d VS |
3131 | grub_uint32_t encode_size; |
3132 | ||
3133 | ptr = beg + 8; | |
bf78d5b2 | 3134 | |
e5c63d9d VS |
3135 | while (ptr < limit |
3136 | && (encode_size = grub_be_to_cpu32 (*(grub_uint32_t *) ptr))) | |
3137 | ptr += encode_size; /* goto the next nvpair */ | |
3138 | ptr += 8; | |
3139 | return (ptr > limit) ? -1 : (ptr - beg); | |
3140 | } | |
bf78d5b2 | 3141 | |
bf78d5b2 | 3142 | char * |
8ff84951 | 3143 | grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, |
bf78d5b2 RM |
3144 | grub_size_t index) |
3145 | { | |
3146 | char *nvpair, *nvpairptr; | |
3147 | int found; | |
3148 | char *ret; | |
3149 | grub_size_t size; | |
3150 | unsigned i; | |
3151 | grub_size_t nelm; | |
e5c63d9d | 3152 | int elemsize = 0; |
bf78d5b2 | 3153 | |
e5c63d9d | 3154 | found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, |
bf78d5b2 | 3155 | &size, &nelm); |
bf78d5b2 | 3156 | if (!found) |
bf78d5b2 RM |
3157 | return 0; |
3158 | if (index >= nelm) | |
bf78d5b2 | 3159 | { |
bf78d5b2 RM |
3160 | grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array"); |
3161 | return 0; | |
bf78d5b2 | 3162 | } |
bf78d5b2 | 3163 | |
bf78d5b2 | 3164 | nvpairptr = nvpair; |
bf78d5b2 | 3165 | |
bf78d5b2 | 3166 | for (i = 0; i < index; i++) |
bf78d5b2 | 3167 | { |
e5c63d9d VS |
3168 | int r; |
3169 | r = get_nvlist_size (nvpairptr, nvpair + size); | |
bf78d5b2 | 3170 | |
e5c63d9d VS |
3171 | if (r < 0) |
3172 | { | |
3173 | grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); | |
3174 | return NULL; | |
3175 | } | |
3176 | nvpairptr += r; | |
bf78d5b2 | 3177 | } |
bf78d5b2 | 3178 | |
e5c63d9d VS |
3179 | elemsize = get_nvlist_size (nvpairptr, nvpair + size); |
3180 | ||
3181 | if (elemsize < 0) | |
bf78d5b2 | 3182 | { |
bf78d5b2 RM |
3183 | grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); |
3184 | return 0; | |
bf78d5b2 | 3185 | } |
bf78d5b2 | 3186 | |
e5c63d9d | 3187 | ret = grub_zalloc (elemsize + sizeof (grub_uint32_t)); |
bf78d5b2 RM |
3188 | if (!ret) |
3189 | return 0; | |
3190 | grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); | |
bf78d5b2 | 3191 | |
e5c63d9d | 3192 | grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, elemsize); |
bf78d5b2 RM |
3193 | return ret; |
3194 | } | |
3195 | ||
8ff84951 VS |
3196 | static void |
3197 | unmount_device (struct grub_zfs_device_desc *desc) | |
3198 | { | |
3199 | unsigned i; | |
3200 | switch (desc->type) | |
3201 | { | |
3202 | case DEVICE_LEAF: | |
3203 | if (!desc->original && desc->dev) | |
3204 | grub_device_close (desc->dev); | |
3205 | return; | |
39db1a3f | 3206 | case DEVICE_RAIDZ: |
8ff84951 VS |
3207 | case DEVICE_MIRROR: |
3208 | for (i = 0; i < desc->n_children; i++) | |
3209 | unmount_device (&desc->children[i]); | |
3210 | return; | |
3211 | } | |
bf78d5b2 RM |
3212 | } |
3213 | ||
3214 | static void | |
3215 | zfs_unmount (struct grub_zfs_data *data) | |
3216 | { | |
8ff84951 VS |
3217 | unsigned i; |
3218 | for (i = 0; i < data->n_devices_attached; i++) | |
3219 | unmount_device (&data->devices_attached[i]); | |
f8d82408 | 3220 | grub_free (data->devices_attached); |
bf78d5b2 RM |
3221 | grub_free (data->dnode_buf); |
3222 | grub_free (data->dnode_mdn); | |
3223 | grub_free (data->file_buf); | |
3224 | grub_free (data); | |
3225 | } | |
3226 | ||
3227 | /* | |
3228 | * zfs_mount() locates a valid uberblock of the root pool and read in its MOS | |
3229 | * to the memory address MOS. | |
3230 | * | |
3231 | */ | |
3232 | static struct grub_zfs_data * | |
3233 | zfs_mount (grub_device_t dev) | |
3234 | { | |
3235 | struct grub_zfs_data *data = 0; | |
bf78d5b2 | 3236 | grub_err_t err; |
bf78d5b2 RM |
3237 | objset_phys_t *osp = 0; |
3238 | grub_size_t ospsize; | |
fdfde32a VS |
3239 | grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN; |
3240 | uberblock_t *ub; | |
bf78d5b2 RM |
3241 | |
3242 | if (! dev->disk) | |
3243 | { | |
3244 | grub_error (GRUB_ERR_BAD_DEVICE, "not a disk"); | |
3245 | return 0; | |
3246 | } | |
3247 | ||
fdfde32a | 3248 | data = grub_zalloc (sizeof (*data)); |
bf78d5b2 RM |
3249 | if (!data) |
3250 | return 0; | |
bf78d5b2 RM |
3251 | #if 0 |
3252 | /* if it's our first time here, zero the best uberblock out */ | |
3253 | if (data->best_drive == 0 && data->best_part == 0 && find_best_root) | |
3254 | grub_memset (¤t_uberblock, 0, sizeof (uberblock_t)); | |
3255 | #endif | |
3256 | ||
fdfde32a VS |
3257 | data->n_devices_allocated = 16; |
3258 | data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) | |
3259 | * data->n_devices_allocated); | |
3260 | data->n_devices_attached = 0; | |
8ff84951 | 3261 | err = scan_disk (dev, data, 1); |
fdfde32a | 3262 | if (err) |
bf78d5b2 RM |
3263 | { |
3264 | zfs_unmount (data); | |
fdfde32a | 3265 | return NULL; |
bf78d5b2 RM |
3266 | } |
3267 | ||
8ff84951 | 3268 | ub = &(data->current_uberblock); |
fdfde32a VS |
3269 | ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, |
3270 | LITTLE_ENDIAN) == UBERBLOCK_MAGIC | |
3271 | ? LITTLE_ENDIAN : BIG_ENDIAN); | |
3272 | ||
3273 | err = zio_read (&ub->ub_rootbp, ub_endian, | |
3274 | (void **) &osp, &ospsize, data); | |
3275 | if (err) | |
bf78d5b2 RM |
3276 | { |
3277 | zfs_unmount (data); | |
fdfde32a | 3278 | return NULL; |
bf78d5b2 RM |
3279 | } |
3280 | ||
fdfde32a | 3281 | if (ospsize < OBJSET_PHYS_SIZE_V14) |
bf78d5b2 | 3282 | { |
fdfde32a | 3283 | grub_error (GRUB_ERR_BAD_FS, "OSP too small"); |
bf78d5b2 | 3284 | grub_free (osp); |
fdfde32a VS |
3285 | zfs_unmount (data); |
3286 | return NULL; | |
bf78d5b2 | 3287 | } |
fdfde32a VS |
3288 | |
3289 | /* Got the MOS. Save it at the memory addr MOS. */ | |
3290 | grub_memmove (&(data->mos.dn), &osp->os_meta_dnode, DNODE_SIZE); | |
3291 | data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop, | |
3292 | ub_endian) >> 63) & 1; | |
bf78d5b2 RM |
3293 | grub_free (osp); |
3294 | ||
fdfde32a VS |
3295 | data->mounted = 1; |
3296 | ||
3297 | return data; | |
bf78d5b2 RM |
3298 | } |
3299 | ||
3300 | grub_err_t | |
3301 | grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist) | |
3302 | { | |
3303 | struct grub_zfs_data *zfs; | |
3304 | grub_err_t err; | |
3305 | ||
3306 | zfs = zfs_mount (dev); | |
3307 | if (!zfs) | |
3308 | return grub_errno; | |
eb03b552 | 3309 | err = zfs_fetch_nvlist (zfs->device_original, nvlist); |
bf78d5b2 RM |
3310 | zfs_unmount (zfs); |
3311 | return err; | |
3312 | } | |
3313 | ||
3314 | static grub_err_t | |
3315 | zfs_label (grub_device_t device, char **label) | |
3316 | { | |
3317 | char *nvlist; | |
3318 | grub_err_t err; | |
3319 | struct grub_zfs_data *data; | |
3320 | ||
3321 | data = zfs_mount (device); | |
3322 | if (! data) | |
3323 | return grub_errno; | |
3324 | ||
eb03b552 | 3325 | err = zfs_fetch_nvlist (data->device_original, &nvlist); |
bf78d5b2 RM |
3326 | if (err) |
3327 | { | |
3328 | zfs_unmount (data); | |
3329 | return err; | |
3330 | } | |
3331 | ||
3332 | *label = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); | |
3333 | grub_free (nvlist); | |
3334 | zfs_unmount (data); | |
3335 | return grub_errno; | |
3336 | } | |
3337 | ||
3338 | static grub_err_t | |
3339 | zfs_uuid (grub_device_t device, char **uuid) | |
3340 | { | |
bf78d5b2 | 3341 | struct grub_zfs_data *data; |
bf78d5b2 RM |
3342 | |
3343 | *uuid = 0; | |
3344 | ||
3345 | data = zfs_mount (device); | |
3346 | if (! data) | |
3347 | return grub_errno; | |
3348 | ||
fdfde32a | 3349 | *uuid = grub_xasprintf ("%016llx", (long long unsigned) data->guid); |
bf78d5b2 RM |
3350 | zfs_unmount (data); |
3351 | if (! *uuid) | |
3352 | return grub_errno; | |
3353 | return GRUB_ERR_NONE; | |
3354 | } | |
3355 | ||
ea0df4e9 VS |
3356 | static grub_err_t |
3357 | zfs_mtime (grub_device_t device, grub_int32_t *mt) | |
3358 | { | |
3359 | struct grub_zfs_data *data; | |
3360 | grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN; | |
3361 | uberblock_t *ub; | |
3362 | ||
3363 | *mt = 0; | |
3364 | ||
3365 | data = zfs_mount (device); | |
3366 | if (! data) | |
3367 | return grub_errno; | |
3368 | ||
3369 | ub = &(data->current_uberblock); | |
3370 | ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, | |
3371 | LITTLE_ENDIAN) == UBERBLOCK_MAGIC | |
3372 | ? LITTLE_ENDIAN : BIG_ENDIAN); | |
3373 | ||
3374 | *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian); | |
3375 | zfs_unmount (data); | |
3376 | return GRUB_ERR_NONE; | |
3377 | } | |
3378 | ||
bf78d5b2 RM |
3379 | /* |
3380 | * zfs_open() locates a file in the rootpool by following the | |
3381 | * MOS and places the dnode of the file in the memory address DNODE. | |
3382 | */ | |
3383 | static grub_err_t | |
3384 | grub_zfs_open (struct grub_file *file, const char *fsfilename) | |
3385 | { | |
3386 | struct grub_zfs_data *data; | |
3387 | grub_err_t err; | |
3388 | int isfs; | |
3389 | ||
3390 | data = zfs_mount (file->device); | |
3391 | if (! data) | |
3392 | return grub_errno; | |
3393 | ||
1bc7cc1b VS |
3394 | err = dnode_get_fullpath (fsfilename, &(data->subvol), |
3395 | &(data->dnode), &isfs, data); | |
bf78d5b2 RM |
3396 | if (err) |
3397 | { | |
3398 | zfs_unmount (data); | |
3399 | return err; | |
3400 | } | |
3401 | ||
3402 | if (isfs) | |
3403 | { | |
3404 | zfs_unmount (data); | |
3405 | return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Missing @ or / separator"); | |
3406 | } | |
3407 | ||
3408 | /* We found the dnode for this file. Verify if it is a plain file. */ | |
3409 | if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) | |
3410 | { | |
3411 | zfs_unmount (data); | |
3412 | return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file"); | |
3413 | } | |
3414 | ||
3415 | /* get the file size and set the file position to 0 */ | |
3416 | ||
3417 | /* | |
3418 | * For DMU_OT_SA we will need to locate the SIZE attribute | |
3419 | * attribute, which could be either in the bonus buffer | |
3420 | * or the "spill" block. | |
3421 | */ | |
3422 | if (data->dnode.dn.dn_bonustype == DMU_OT_SA) | |
3423 | { | |
e7121b69 | 3424 | void *sahdrp; |
bf78d5b2 RM |
3425 | int hdrsize; |
3426 | ||
3427 | if (data->dnode.dn.dn_bonuslen != 0) | |
3428 | { | |
3429 | sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn); | |
3430 | } | |
3431 | else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) | |
3432 | { | |
3433 | blkptr_t *bp = &data->dnode.dn.dn_spill; | |
3434 | ||
e7121b69 | 3435 | err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data); |
bf78d5b2 RM |
3436 | if (err) |
3437 | return err; | |
3438 | } | |
3439 | else | |
3440 | { | |
3441 | return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); | |
3442 | } | |
3443 | ||
e7121b69 | 3444 | hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); |
34c59654 | 3445 | file->size = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian); |
bf78d5b2 | 3446 | } |
34c59654 | 3447 | else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE) |
bf78d5b2 RM |
3448 | { |
3449 | file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian); | |
3450 | } | |
34c59654 VS |
3451 | else |
3452 | return grub_error (GRUB_ERR_BAD_FS, "bad bonus type"); | |
bf78d5b2 RM |
3453 | |
3454 | file->data = data; | |
3455 | file->offset = 0; | |
3456 | ||
3457 | #ifndef GRUB_UTIL | |
3458 | grub_dl_ref (my_mod); | |
3459 | #endif | |
3460 | ||
3461 | return GRUB_ERR_NONE; | |
3462 | } | |
3463 | ||
3464 | static grub_ssize_t | |
3465 | grub_zfs_read (grub_file_t file, char *buf, grub_size_t len) | |
3466 | { | |
3467 | struct grub_zfs_data *data = (struct grub_zfs_data *) file->data; | |
5bbd28b8 | 3468 | grub_size_t blksz, movesize; |
bf78d5b2 RM |
3469 | grub_size_t length; |
3470 | grub_size_t read; | |
3471 | grub_err_t err; | |
3472 | ||
3473 | if (data->file_buf == NULL) | |
3474 | { | |
3475 | data->file_buf = grub_malloc (SPA_MAXBLOCKSIZE); | |
3476 | if (!data->file_buf) | |
3477 | return -1; | |
3478 | data->file_start = data->file_end = 0; | |
3479 | } | |
3480 | ||
3481 | /* | |
3482 | * If offset is in memory, move it into the buffer provided and return. | |
3483 | */ | |
3484 | if (file->offset >= data->file_start | |
3485 | && file->offset + len <= data->file_end) | |
3486 | { | |
3487 | grub_memmove (buf, data->file_buf + file->offset - data->file_start, | |
3488 | len); | |
3489 | return len; | |
3490 | } | |
3491 | ||
3492 | blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec, | |
3493 | data->dnode.endian) << SPA_MINBLOCKSHIFT; | |
3494 | ||
3495 | /* | |
3496 | * Entire Dnode is too big to fit into the space available. We | |
3497 | * will need to read it in chunks. This could be optimized to | |
3498 | * read in as large a chunk as there is space available, but for | |
3499 | * now, this only reads in one data block at a time. | |
3500 | */ | |
3501 | length = len; | |
3502 | read = 0; | |
3503 | while (length) | |
3504 | { | |
e7121b69 | 3505 | void *t; |
bf78d5b2 RM |
3506 | /* |
3507 | * Find requested blkid and the offset within that block. | |
3508 | */ | |
3509 | grub_uint64_t blkid = grub_divmod64 (file->offset + read, blksz, 0); | |
3510 | grub_free (data->file_buf); | |
3511 | data->file_buf = 0; | |
3512 | ||
e7121b69 | 3513 | err = dmu_read (&(data->dnode), blkid, &t, |
bf78d5b2 | 3514 | 0, data); |
e7121b69 | 3515 | data->file_buf = t; |
bf78d5b2 RM |
3516 | if (err) |
3517 | return -1; | |
3518 | ||
3519 | data->file_start = blkid * blksz; | |
3520 | data->file_end = data->file_start + blksz; | |
3521 | ||
5bbd28b8 | 3522 | movesize = MIN (length, data->file_end - file->offset - read); |
bf78d5b2 RM |
3523 | |
3524 | grub_memmove (buf, data->file_buf + file->offset + read | |
3525 | - data->file_start, movesize); | |
3526 | buf += movesize; | |
3527 | length -= movesize; | |
3528 | read += movesize; | |
3529 | } | |
3530 | ||
3531 | return len; | |
3532 | } | |
3533 | ||
3534 | static grub_err_t | |
3535 | grub_zfs_close (grub_file_t file) | |
3536 | { | |
3537 | zfs_unmount ((struct grub_zfs_data *) file->data); | |
3538 | ||
3539 | #ifndef GRUB_UTIL | |
3540 | grub_dl_unref (my_mod); | |
3541 | #endif | |
3542 | ||
3543 | return GRUB_ERR_NONE; | |
3544 | } | |
3545 | ||
3546 | grub_err_t | |
3547 | grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename, | |
3548 | grub_uint64_t *mdnobj) | |
3549 | { | |
3550 | struct grub_zfs_data *data; | |
3551 | grub_err_t err; | |
3552 | int isfs; | |
3553 | ||
3554 | data = zfs_mount (dev); | |
3555 | if (! data) | |
3556 | return grub_errno; | |
3557 | ||
1bc7cc1b VS |
3558 | err = dnode_get_fullpath (fsfilename, &(data->subvol), |
3559 | &(data->dnode), &isfs, data); | |
3560 | *mdnobj = data->subvol.obj; | |
bf78d5b2 RM |
3561 | zfs_unmount (data); |
3562 | return err; | |
3563 | } | |
3564 | ||
3565 | static void | |
3566 | fill_fs_info (struct grub_dirhook_info *info, | |
3567 | dnode_end_t mdn, struct grub_zfs_data *data) | |
3568 | { | |
3569 | grub_err_t err; | |
3570 | dnode_end_t dn; | |
3571 | grub_uint64_t objnum; | |
3572 | grub_uint64_t headobj; | |
3573 | ||
3574 | grub_memset (info, 0, sizeof (*info)); | |
3575 | ||
3576 | info->dir = 1; | |
3577 | ||
3578 | if (mdn.dn.dn_type == DMU_OT_DSL_DIR) | |
3579 | { | |
3580 | headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian); | |
3581 | ||
3582 | err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data); | |
3583 | if (err) | |
3584 | { | |
3585 | grub_dprintf ("zfs", "failed here\n"); | |
3586 | return; | |
3587 | } | |
3588 | } | |
3589 | make_mdn (&mdn, data); | |
3590 | err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, | |
3591 | &dn, data); | |
3592 | if (err) | |
3593 | { | |
3594 | grub_dprintf ("zfs", "failed here\n"); | |
3595 | return; | |
3596 | } | |
3597 | ||
8622923b | 3598 | err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0); |
bf78d5b2 RM |
3599 | if (err) |
3600 | { | |
3601 | grub_dprintf ("zfs", "failed here\n"); | |
3602 | return; | |
3603 | } | |
3604 | ||
3605 | err = dnode_get (&mdn, objnum, 0, &dn, data); | |
3606 | if (err) | |
3607 | { | |
3608 | grub_dprintf ("zfs", "failed here\n"); | |
3609 | return; | |
3610 | } | |
3611 | ||
34c59654 VS |
3612 | if (dn.dn.dn_bonustype == DMU_OT_SA) |
3613 | { | |
3614 | void *sahdrp; | |
3615 | int hdrsize; | |
3616 | ||
3617 | if (dn.dn.dn_bonuslen != 0) | |
3618 | { | |
3619 | sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn); | |
3620 | } | |
3621 | else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) | |
3622 | { | |
3623 | blkptr_t *bp = &dn.dn.dn_spill; | |
3624 | ||
3625 | err = zio_read (bp, dn.endian, &sahdrp, NULL, data); | |
3626 | if (err) | |
3627 | return; | |
3628 | } | |
3629 | else | |
3630 | { | |
3631 | grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); | |
3632 | return; | |
3633 | } | |
3634 | ||
3635 | hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); | |
3636 | info->mtimeset = 1; | |
3637 | info->mtime = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); | |
3638 | } | |
3639 | ||
3640 | if (dn.dn.dn_bonustype == DMU_OT_ZNODE) | |
3641 | { | |
3642 | info->mtimeset = 1; | |
3643 | info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); | |
3644 | } | |
bf78d5b2 RM |
3645 | return; |
3646 | } | |
3647 | ||
3648 | static grub_err_t | |
3649 | grub_zfs_dir (grub_device_t device, const char *path, | |
3650 | int (*hook) (const char *, const struct grub_dirhook_info *)) | |
3651 | { | |
3652 | struct grub_zfs_data *data; | |
3653 | grub_err_t err; | |
3654 | int isfs; | |
3655 | auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val); | |
3656 | auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, | |
3657 | grub_uint64_t val); | |
3658 | auto int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, | |
3659 | grub_uint64_t val); | |
3660 | ||
3661 | int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val) | |
3662 | { | |
3663 | struct grub_dirhook_info info; | |
3664 | dnode_end_t dn; | |
3665 | grub_memset (&info, 0, sizeof (info)); | |
3666 | ||
1bc7cc1b | 3667 | dnode_get (&(data->subvol.mdn), val, 0, &dn, data); |
34c59654 VS |
3668 | |
3669 | if (dn.dn.dn_bonustype == DMU_OT_SA) | |
3670 | { | |
3671 | void *sahdrp; | |
3672 | int hdrsize; | |
3673 | ||
3674 | if (dn.dn.dn_bonuslen != 0) | |
3675 | { | |
3676 | sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn); | |
3677 | } | |
3678 | else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) | |
3679 | { | |
3680 | blkptr_t *bp = &dn.dn.dn_spill; | |
3681 | ||
3682 | err = zio_read (bp, dn.endian, &sahdrp, NULL, data); | |
3683 | if (err) | |
3684 | { | |
3685 | grub_print_error (); | |
3686 | return 0; | |
3687 | } | |
3688 | } | |
3689 | else | |
3690 | { | |
3691 | grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); | |
3692 | grub_print_error (); | |
3693 | return 0; | |
3694 | } | |
3695 | ||
3696 | hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); | |
3697 | info.mtimeset = 1; | |
3698 | info.mtime = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); | |
1bc7cc1b | 3699 | info.case_insensitive = data->subvol.case_insensitive; |
34c59654 VS |
3700 | } |
3701 | ||
3702 | if (dn.dn.dn_bonustype == DMU_OT_ZNODE) | |
3703 | { | |
3704 | info.mtimeset = 1; | |
3705 | info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], | |
3706 | dn.endian); | |
3707 | } | |
3708 | info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); | |
3709 | grub_dprintf ("zfs", "type=%d, name=%s\n", | |
bf78d5b2 RM |
3710 | (int)dn.dn.dn_type, (char *)name); |
3711 | return hook (name, &info); | |
3712 | } | |
3713 | ||
3714 | int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, grub_uint64_t val) | |
3715 | { | |
3716 | struct grub_dirhook_info info; | |
3717 | dnode_end_t mdn; | |
3718 | err = dnode_get (&(data->mos), val, 0, &mdn, data); | |
3719 | if (err) | |
3720 | return 0; | |
3721 | if (mdn.dn.dn_type != DMU_OT_DSL_DIR) | |
3722 | return 0; | |
3723 | ||
3724 | fill_fs_info (&info, mdn, data); | |
3725 | return hook (name, &info); | |
3726 | } | |
3727 | int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, grub_uint64_t val) | |
3728 | { | |
3729 | struct grub_dirhook_info info; | |
3730 | char *name2; | |
3731 | int ret; | |
3732 | dnode_end_t mdn; | |
3733 | ||
3734 | err = dnode_get (&(data->mos), val, 0, &mdn, data); | |
3735 | if (err) | |
3736 | return 0; | |
3737 | ||
3738 | if (mdn.dn.dn_type != DMU_OT_DSL_DATASET) | |
3739 | return 0; | |
3740 | ||
3741 | fill_fs_info (&info, mdn, data); | |
3742 | ||
3743 | name2 = grub_malloc (grub_strlen (name) + 2); | |
3744 | name2[0] = '@'; | |
3745 | grub_memcpy (name2 + 1, name, grub_strlen (name) + 1); | |
3746 | ret = hook (name2, &info); | |
3747 | grub_free (name2); | |
3748 | return ret; | |
3749 | } | |
3750 | ||
3751 | data = zfs_mount (device); | |
3752 | if (! data) | |
3753 | return grub_errno; | |
1bc7cc1b | 3754 | err = dnode_get_fullpath (path, &(data->subvol), &(data->dnode), &isfs, data); |
bf78d5b2 RM |
3755 | if (err) |
3756 | { | |
3757 | zfs_unmount (data); | |
3758 | return err; | |
3759 | } | |
3760 | if (isfs) | |
3761 | { | |
3762 | grub_uint64_t childobj, headobj; | |
3763 | grub_uint64_t snapobj; | |
3764 | dnode_end_t dn; | |
3765 | struct grub_dirhook_info info; | |
3766 | ||
3767 | fill_fs_info (&info, data->dnode, data); | |
3768 | hook ("@", &info); | |
3769 | ||
3770 | childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian); | |
3771 | headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian); | |
3772 | err = dnode_get (&(data->mos), childobj, | |
3773 | DMU_OT_DSL_DIR_CHILD_MAP, &dn, data); | |
3774 | if (err) | |
3775 | { | |
3776 | zfs_unmount (data); | |
3777 | return err; | |
3778 | } | |
3779 | ||
1bc7cc1b | 3780 | zap_iterate_u64 (&dn, iterate_zap_fs, data); |
bf78d5b2 RM |
3781 | |
3782 | err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); | |
3783 | if (err) | |
3784 | { | |
3785 | zfs_unmount (data); | |
3786 | return err; | |
3787 | } | |
3788 | ||
3789 | snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian); | |
3790 | ||
3791 | err = dnode_get (&(data->mos), snapobj, | |
3792 | DMU_OT_DSL_DS_SNAP_MAP, &dn, data); | |
3793 | if (err) | |
3794 | { | |
3795 | zfs_unmount (data); | |
3796 | return err; | |
3797 | } | |
3798 | ||
1bc7cc1b | 3799 | zap_iterate_u64 (&dn, iterate_zap_snap, data); |
bf78d5b2 RM |
3800 | } |
3801 | else | |
3802 | { | |
3803 | if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) | |
3804 | { | |
3805 | zfs_unmount (data); | |
3806 | return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); | |
3807 | } | |
1bc7cc1b | 3808 | zap_iterate_u64 (&(data->dnode), iterate_zap, data); |
bf78d5b2 RM |
3809 | } |
3810 | zfs_unmount (data); | |
3811 | return grub_errno; | |
3812 | } | |
3813 | ||
3814 | static struct grub_fs grub_zfs_fs = { | |
3815 | .name = "zfs", | |
3816 | .dir = grub_zfs_dir, | |
3817 | .open = grub_zfs_open, | |
3818 | .read = grub_zfs_read, | |
3819 | .close = grub_zfs_close, | |
3820 | .label = zfs_label, | |
3821 | .uuid = zfs_uuid, | |
ea0df4e9 | 3822 | .mtime = zfs_mtime, |
bf78d5b2 RM |
3823 | .next = 0 |
3824 | }; | |
3825 | ||
3826 | GRUB_MOD_INIT (zfs) | |
3827 | { | |
fe79a8ad | 3828 | COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE); |
bf78d5b2 RM |
3829 | grub_fs_register (&grub_zfs_fs); |
3830 | #ifndef GRUB_UTIL | |
3831 | my_mod = mod; | |
3832 | #endif | |
3833 | } | |
3834 | ||
3835 | GRUB_MOD_FINI (zfs) | |
3836 | { | |
3837 | grub_fs_unregister (&grub_zfs_fs); | |
3838 | } |