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