2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
4 * Copyright 2008 Sun Microsystems, Inc.
5 * Copyright (C) 2009 Free Software Foundation, Inc.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * The zfs plug-in routines for GRUB are:
24 * zfs_mount() - locates a valid uberblock of the root pool and reads
25 * in its MOS at the memory address MOS.
27 * zfs_open() - locates a plain file object by following the MOS
28 * and places its dnode at the memory address DNODE.
30 * zfs_read() - read in the data blocks pointed by the DNODE.
35 #include <grub/file.h>
37 #include <grub/misc.h>
38 #include <grub/disk.h>
40 #include <grub/types.h>
41 #include <grub/zfs/zfs.h>
42 #include <grub/zfs/zio.h>
43 #include <grub/zfs/dnode.h>
44 #include <grub/zfs/uberblock_impl.h>
45 #include <grub/zfs/vdev_impl.h>
46 #include <grub/zfs/zio_checksum.h>
47 #include <grub/zfs/zap_impl.h>
48 #include <grub/zfs/zap_leaf.h>
49 #include <grub/zfs/zfs_znode.h>
50 #include <grub/zfs/dmu.h>
51 #include <grub/zfs/dmu_objset.h>
52 #include <grub/zfs/dsl_dir.h>
53 #include <grub/zfs/dsl_dataset.h>
55 #define ZPOOL_PROP_BOOTFS "bootfs"
57 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
60 * For nvlist manipulation. (from nvpair.h)
62 #define NV_ENCODE_NATIVE 0
63 #define NV_ENCODE_XDR 1
64 #define NV_BIG_ENDIAN 0
65 #define NV_LITTLE_ENDIAN 1
66 #define DATA_TYPE_UINT64 8
67 #define DATA_TYPE_STRING 9
68 #define DATA_TYPE_NVLIST 19
69 #define DATA_TYPE_NVLIST_ARRAY 20
72 static grub_dl_t my_mod
;
75 #define P2PHASE(x, align) ((x) & ((align) - 1))
76 #define DVA_OFFSET_TO_PHYS_SECTOR(offset) \
77 ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT)
80 * FAT ZAP data structures
82 #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
83 #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n))))
84 #define CHAIN_END 0xffff /* end of the chunk chain */
87 * The amount of space within the chunk available for the array is:
88 * chunk size - space for type (1) - space for next pointer (2)
90 #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3)
92 #define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5)
93 #define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs))
94 #define LEAF_HASH(bs, h) \
95 ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \
96 ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len)))
99 * The amount of space available for chunks is:
100 * block size shift - hash entry size (2) * number of hash
101 * entries - header space (2*chunksize)
103 #define ZAP_LEAF_NUMCHUNKS(bs) \
104 (((1<<bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(bs)) / \
105 ZAP_LEAF_CHUNKSIZE - 2)
108 * The chunks start immediately after the hash table. The end of the
109 * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
112 #define ZAP_LEAF_CHUNK(l, bs, idx) \
113 ((zap_leaf_chunk_t *)(l->l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx]
114 #define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry)
118 * Decompression Entry - lzjb
124 extern grub_err_t
lzjb_decompress (void *, void *, grub_size_t
, grub_size_t
);
126 typedef grub_err_t
zfs_decomp_func_t (void *s_start
, void *d_start
,
127 grub_size_t s_len
, grub_size_t d_len
);
128 typedef struct decomp_entry
131 zfs_decomp_func_t
*decomp_func
;
134 typedef struct dnode_end
137 grub_zfs_endian_t endian
;
142 /* cache for a file block of the currently zfs_open()-ed file */
144 grub_uint64_t file_start
;
145 grub_uint64_t file_end
;
147 /* cache for a dnode block */
148 dnode_phys_t
*dnode_buf
;
149 dnode_phys_t
*dnode_mdn
;
150 grub_uint64_t dnode_start
;
151 grub_uint64_t dnode_end
;
152 grub_zfs_endian_t dnode_endian
;
154 uberblock_t current_uberblock
;
162 char current_bootfs
[MAXNAMELEN
];
163 char current_bootpath
[MAXPATHLEN
];
164 char current_devid
[MAXPATHLEN
];
170 decomp_entry_t decomp_table
[ZIO_COMPRESS_FUNCTIONS
] = {
171 {"inherit", 0}, /* ZIO_COMPRESS_INHERIT */
172 {"on", lzjb_decompress
}, /* ZIO_COMPRESS_ON */
173 {"off", 0}, /* ZIO_COMPRESS_OFF */
174 {"lzjb", lzjb_decompress
}, /* ZIO_COMPRESS_LZJB */
175 {"empty", 0} /* ZIO_COMPRESS_EMPTY */
178 static grub_err_t
zio_read_data (blkptr_t
* bp
, grub_zfs_endian_t endian
,
179 void *buf
, struct grub_zfs_data
*data
);
182 * Our own version of log2(). Same thing as highbit()-1.
185 zfs_log2 (grub_uint64_t num
)
198 /* Checksum Functions */
200 zio_checksum_off (const void *buf
__attribute__ ((unused
)),
201 grub_uint64_t size
__attribute__ ((unused
)),
202 grub_zfs_endian_t endian
__attribute__ ((unused
)),
205 ZIO_SET_CHECKSUM (zcp
, 0, 0, 0, 0);
208 /* Checksum Table and Values */
209 zio_checksum_info_t zio_checksum_table
[ZIO_CHECKSUM_FUNCTIONS
] = {
210 {NULL
, 0, 0, "inherit"},
212 {zio_checksum_off
, 0, 0, "off"},
213 {zio_checksum_SHA256
, 1, 1, "label"},
214 {zio_checksum_SHA256
, 1, 1, "gang_header"},
215 {fletcher_2
, 0, 1, "zilog"},
216 {fletcher_2
, 0, 0, "fletcher2"},
217 {fletcher_4
, 1, 0, "fletcher4"},
218 {zio_checksum_SHA256
, 1, 0, "SHA256"},
222 * zio_checksum_verify: Provides support for checksum verification.
224 * Fletcher2, Fletcher4, and SHA256 are supported.
228 zio_checksum_verify (zio_cksum_t zc
, grub_uint32_t checksum
,
229 grub_zfs_endian_t endian
, char *buf
, int size
)
231 zio_block_tail_t
*zbt
= (zio_block_tail_t
*) (buf
+ size
) - 1;
232 zio_checksum_info_t
*ci
= &zio_checksum_table
[checksum
];
233 zio_cksum_t actual_cksum
, expected_cksum
;
235 if (checksum
>= ZIO_CHECKSUM_FUNCTIONS
|| ci
->ci_func
== NULL
)
237 grub_dprintf ("zfs", "unknown checksum function %d\n", checksum
);
238 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
239 "unknown checksum function %d", checksum
);
244 expected_cksum
= zbt
->zbt_cksum
;
246 ci
->ci_func (buf
, size
, endian
, &actual_cksum
);
247 zbt
->zbt_cksum
= expected_cksum
;
251 ci
->ci_func (buf
, size
, endian
, &actual_cksum
);
253 if ((actual_cksum
.zc_word
[0] != zc
.zc_word
[0])
254 || (actual_cksum
.zc_word
[1] != zc
.zc_word
[1])
255 || (actual_cksum
.zc_word
[2] != zc
.zc_word
[2])
256 || (actual_cksum
.zc_word
[3] != zc
.zc_word
[3]))
258 grub_dprintf ("zfs", "checksum %d verification failed\n", checksum
);
259 grub_dprintf ("zfs", "actual checksum %16llx %16llx %16llx %16llx\n",
260 (unsigned long long) actual_cksum
.zc_word
[0],
261 (unsigned long long) actual_cksum
.zc_word
[1],
262 (unsigned long long) actual_cksum
.zc_word
[2],
263 (unsigned long long) actual_cksum
.zc_word
[3]);
264 grub_dprintf ("zfs", "expected checksum %16llx %16llx %16llx %16llx\n",
265 (unsigned long long) zc
.zc_word
[0],
266 (unsigned long long) zc
.zc_word
[1],
267 (unsigned long long) zc
.zc_word
[2],
268 (unsigned long long) zc
.zc_word
[3]);
269 return grub_error (GRUB_ERR_BAD_FS
, "checksum verification failed");
272 return GRUB_ERR_NONE
;
276 * vdev_label_offset takes "offset" (the offset within a vdev_label) and
277 * returns its physical disk offset (starting from the beginning of the vdev).
280 * psize : Physical size of this vdev
281 * l : Label Number (0-3)
282 * offset : The offset with a vdev_label in which we want the physical
284 * sector : where physical disk offset will be put
287 vdev_label_offset (grub_uint64_t psize
, int l
, grub_uint64_t offset
,
288 grub_disk_addr_t
*sector
)
290 /* XXX Need to add back label support! */
291 if (l
>= VDEV_LABELS
/ 2 || offset
> sizeof (vdev_label_t
))
292 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
293 "back labels aren't supported yet");
295 *sector
= offset
+ l
* sizeof (vdev_label_t
) + (l
< VDEV_LABELS
/ 2 ?
298 sizeof (vdev_label_t
));
300 return GRUB_ERR_NONE
;
304 * vdev_uberblock_compare takes two uberblock structures and returns an integer
305 * indicating the more recent of the two.
306 * Return Value = 1 if ub2 is more recent
307 * Return Value = -1 if ub1 is more recent
308 * The most recent uberblock is determined using its transaction number and
309 * timestamp. The uberblock with the highest transaction number is
310 * considered "newer". If the transaction numbers of the two blocks match, the
311 * timestamps are compared to determine the "newer" of the two.
314 vdev_uberblock_compare (uberblock_t
* ub1
, uberblock_t
* ub2
)
316 grub_zfs_endian_t ub1_endian
, ub2_endian
;
317 if (grub_zfs_to_cpu64 (ub1
->ub_magic
, LITTLE_ENDIAN
) == UBERBLOCK_MAGIC
)
318 ub1_endian
= LITTLE_ENDIAN
;
320 ub1_endian
= BIG_ENDIAN
;
321 if (grub_zfs_to_cpu64 (ub2
->ub_magic
, LITTLE_ENDIAN
) == UBERBLOCK_MAGIC
)
322 ub2_endian
= LITTLE_ENDIAN
;
324 ub2_endian
= BIG_ENDIAN
;
326 if (grub_zfs_to_cpu64 (ub1
->ub_txg
, ub1_endian
)
327 < grub_zfs_to_cpu64 (ub2
->ub_txg
, ub2_endian
))
329 if (grub_zfs_to_cpu64 (ub1
->ub_txg
, ub1_endian
)
330 > grub_zfs_to_cpu64 (ub2
->ub_txg
, ub2_endian
))
333 if (grub_zfs_to_cpu64 (ub1
->ub_timestamp
, ub1_endian
)
334 < grub_zfs_to_cpu64 (ub2
->ub_timestamp
, ub2_endian
))
336 if (grub_zfs_to_cpu64 (ub1
->ub_timestamp
, ub1_endian
)
337 > grub_zfs_to_cpu64 (ub2
->ub_timestamp
, ub2_endian
))
344 * Three pieces of information are needed to verify an uberblock: the magic
345 * number, the version number, and the checksum.
347 * Currently Implemented: version number, magic number
348 * Need to Implement: checksum
352 uberblock_verify (uberblock_phys_t
* ub
, int offset
)
354 uberblock_t
*uber
= &ub
->ubp_uberblock
;
356 grub_zfs_endian_t endian
= UNKNOWN_ENDIAN
;
360 if (grub_zfs_to_cpu64 (uber
->ub_magic
, LITTLE_ENDIAN
) == UBERBLOCK_MAGIC
361 && grub_zfs_to_cpu64 (uber
->ub_version
, LITTLE_ENDIAN
) > 0
362 && grub_zfs_to_cpu64 (uber
->ub_version
, LITTLE_ENDIAN
) <= SPA_VERSION
)
363 endian
= LITTLE_ENDIAN
;
365 if (grub_zfs_to_cpu64 (uber
->ub_magic
, BIG_ENDIAN
) == UBERBLOCK_MAGIC
366 && grub_zfs_to_cpu64 (uber
->ub_version
, BIG_ENDIAN
) > 0
367 && grub_zfs_to_cpu64 (uber
->ub_version
, BIG_ENDIAN
) <= SPA_VERSION
)
370 if (endian
== UNKNOWN_ENDIAN
)
371 return grub_error (GRUB_ERR_BAD_FS
, "invalid uberblock magic");
373 grub_memset (&zc
, 0, sizeof (zc
));
375 if (endian
== BIG_ENDIAN
)
376 zc
.zc_word
[0] = grub_cpu_to_zfs64 (offset
, endian
);
378 zc
.zc_word
[0] = grub_cpu_to_zfs64 (offset
, endian
);
379 err
= zio_checksum_verify (zc
, ZIO_CHECKSUM_LABEL
, endian
,
380 (char *) ub
, UBERBLOCK_SIZE
);
386 * Find the best uberblock.
388 * Success - Pointer to the best uberblock.
391 static uberblock_phys_t
*
392 find_bestub (uberblock_phys_t
* ub_array
, int label
)
394 uberblock_phys_t
*ubbest
= NULL
;
396 grub_disk_addr_t offset
;
397 grub_err_t err
= GRUB_ERR_NONE
;
399 for (i
= 0; i
< (VDEV_UBERBLOCK_RING
>> VDEV_UBERBLOCK_SHIFT
); i
++)
401 err
= vdev_label_offset (0, label
, VDEV_UBERBLOCK_OFFSET (i
), &offset
);
406 err
= uberblock_verify (&ub_array
[i
], offset
);
409 grub_errno
= GRUB_ERR_NONE
;
413 || vdev_uberblock_compare (&(ub_array
[i
].ubp_uberblock
),
414 &(ubbest
->ubp_uberblock
)) > 0)
415 ubbest
= &ub_array
[i
];
423 static inline grub_size_t
424 get_psize (blkptr_t
* bp
, grub_zfs_endian_t endian
)
426 return ((((grub_zfs_to_cpu64 ((bp
)->blk_prop
, endian
)>>16) & 0xffff) + 1)
427 << SPA_MINBLOCKSHIFT
);
431 dva_get_offset (dva_t
* dva
, grub_zfs_endian_t endian
)
433 grub_dprintf ("zfs", "dva=%llx, %llx\n",
434 (unsigned long long) dva
->dva_word
[0],
435 (unsigned long long) dva
->dva_word
[1]);
436 return grub_zfs_to_cpu64 ((dva
)->dva_word
[1],
437 endian
) << SPA_MINBLOCKSHIFT
;
442 * Read a block of data based on the gang block address dva,
443 * and put its data in buf.
447 zio_read_gang (blkptr_t
* bp
, grub_zfs_endian_t endian
, dva_t
* dva
, void *buf
,
448 struct grub_zfs_data
*data
)
450 zio_gbh_phys_t
*zio_gb
;
451 grub_uint64_t offset
, sector
;
456 grub_memset (&zc
, 0, sizeof (zc
));
458 zio_gb
= grub_malloc (SPA_GANGBLOCKSIZE
);
461 grub_dprintf ("zfs", endian
== LITTLE_ENDIAN
? "little-endian gang\n"
462 :"big-endian gang\n");
463 offset
= dva_get_offset (dva
, endian
);
464 sector
= DVA_OFFSET_TO_PHYS_SECTOR (offset
);
465 grub_dprintf ("zfs", "offset=%llx\n", (unsigned long long) offset
);
467 /* read in the gang block header */
468 err
= grub_disk_read (data
->disk
, sector
, 0, SPA_GANGBLOCKSIZE
,
477 /* self checksuming the gang block header */
478 ZIO_SET_CHECKSUM (&zc
, DVA_GET_VDEV (dva
),
479 dva_get_offset (dva
, endian
), bp
->blk_birth
, 0);
480 err
= zio_checksum_verify (zc
, ZIO_CHECKSUM_GANG_HEADER
, endian
,
481 (char *) zio_gb
, SPA_GANGBLOCKSIZE
);
488 endian
= (grub_zfs_to_cpu64 (bp
->blk_prop
, endian
) >> 63) & 1;
490 for (i
= 0; i
< SPA_GBH_NBLKPTRS
; i
++)
492 if (zio_gb
->zg_blkptr
[i
].blk_birth
== 0)
495 if (zio_read_data (&zio_gb
->zg_blkptr
[i
], endian
, buf
, data
))
500 buf
+= get_psize (&zio_gb
->zg_blkptr
[i
], endian
);
507 * Read in a block of raw data to buf.
510 zio_read_data (blkptr_t
* bp
, grub_zfs_endian_t endian
, void *buf
,
511 struct grub_zfs_data
*data
)
514 grub_err_t err
= GRUB_ERR_NONE
;
516 psize
= get_psize (bp
, endian
);
518 /* pick a good dva from the block pointer */
519 for (i
= 0; i
< SPA_DVAS_PER_BP
; i
++)
521 grub_uint64_t offset
, sector
;
523 if (bp
->blk_dva
[i
].dva_word
[0] == 0 && bp
->blk_dva
[i
].dva_word
[1] == 0)
526 if ((grub_zfs_to_cpu64 (bp
->blk_dva
[i
].dva_word
[1], endian
)>>63) & 1)
527 err
= zio_read_gang (bp
, endian
, &bp
->blk_dva
[i
], buf
, data
);
530 /* read in a data block */
531 offset
= dva_get_offset (&bp
->blk_dva
[i
], endian
);
532 sector
= DVA_OFFSET_TO_PHYS_SECTOR (offset
);
533 err
= grub_disk_read (data
->disk
, sector
, 0, psize
, buf
);
536 return GRUB_ERR_NONE
;
537 grub_errno
= GRUB_ERR_NONE
;
541 err
= grub_error (GRUB_ERR_BAD_FS
, "couldn't find a valid DVA");
548 * Read in a block of data, verify its checksum, decompress if needed,
549 * and put the uncompressed data in buf.
552 zio_read (blkptr_t
* bp
, grub_zfs_endian_t endian
, void **buf
,
553 grub_size_t
*size
, struct grub_zfs_data
*data
)
555 grub_size_t lsize
, psize
;
559 zio_cksum_t zc
= bp
->blk_cksum
;
560 grub_uint32_t checksum
;
562 checksum
= (grub_zfs_to_cpu64((bp
)->blk_prop
, endian
) >> 40) & 0xff;
563 comp
= (grub_zfs_to_cpu64((bp
)->blk_prop
, endian
)>>32) & 0xff;
564 lsize
= (BP_IS_HOLE(bp
) ? 0 :
565 (((grub_zfs_to_cpu64 ((bp
)->blk_prop
, endian
) & 0xffff) + 1)
566 << SPA_MINBLOCKSHIFT
));
567 psize
= get_psize (bp
, endian
);
572 if ((unsigned int) comp
>= ZIO_COMPRESS_FUNCTIONS
||
573 (comp
!= ZIO_COMPRESS_OFF
&& decomp_table
[comp
].decomp_func
== NULL
))
575 grub_dprintf ("zfs", "comp=%d\n", comp
);
576 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
577 "compression algorithm not supported\n");
580 if (comp
!= ZIO_COMPRESS_OFF
)
582 compbuf
= grub_malloc (psize
);
584 return GRUB_ERR_NONE
;
587 compbuf
= *buf
= grub_malloc (lsize
);
589 grub_dprintf ("zfs", "endian = %d\n", endian
);
590 err
= zio_read_data (bp
, endian
, compbuf
, data
);
597 err
= zio_checksum_verify (zc
, checksum
, endian
, compbuf
, psize
);
600 grub_dprintf ("zfs", "incorrect checksum\n");
605 if (comp
!= ZIO_COMPRESS_OFF
)
607 *buf
= grub_malloc (lsize
);
608 err
= decomp_table
[comp
].decomp_func (compbuf
, *buf
, psize
, lsize
);
614 return GRUB_ERR_NONE
;
618 * Get the block from a block id.
619 * push the block onto the stack.
623 dmu_read (dnode_end_t
* dn
, grub_uint64_t blkid
, void **buf
,
624 grub_zfs_endian_t
*endian_out
, struct grub_zfs_data
*data
)
627 blkptr_t
*bp_array
= dn
->dn
.dn_blkptr
;
628 int epbs
= dn
->dn
.dn_indblkshift
- SPA_BLKPTRSHIFT
;
629 blkptr_t
*bp
, *tmpbuf
= 0;
630 grub_zfs_endian_t endian
;
633 bp
= grub_malloc (sizeof (blkptr_t
));
638 for (level
= dn
->dn
.dn_nlevels
- 1; level
>= 0; level
--)
640 grub_dprintf ("zfs", "endian = %d\n", endian
);
641 idx
= (blkid
>> (epbs
* level
)) & ((1 << epbs
) - 1);
643 if (bp_array
!= dn
->dn
.dn_blkptr
)
645 grub_free (bp_array
);
651 grub_size_t size
= grub_zfs_to_cpu16 (dn
->dn
.dn_datablkszsec
,
653 << SPA_MINBLOCKSHIFT
;
654 *buf
= grub_malloc (size
);
660 grub_memset (*buf
, 0, size
);
661 endian
= (grub_zfs_to_cpu64 (bp
->blk_prop
, endian
) >> 63) & 1;
666 grub_dprintf ("zfs", "endian = %d\n", endian
);
667 err
= zio_read (bp
, endian
, buf
, 0, data
);
668 endian
= (grub_zfs_to_cpu64 (bp
->blk_prop
, endian
) >> 63) & 1;
671 grub_dprintf ("zfs", "endian = %d\n", endian
);
672 err
= zio_read (bp
, endian
, (void **) &tmpbuf
, 0, data
);
673 endian
= (grub_zfs_to_cpu64 (bp
->blk_prop
, endian
) >> 63) & 1;
678 if (bp_array
!= dn
->dn
.dn_blkptr
)
679 grub_free (bp_array
);
681 *endian_out
= endian
;
688 * mzap_lookup: Looks up property described by "name" and returns the value
692 mzap_lookup (mzap_phys_t
* zapobj
, grub_zfs_endian_t endian
,
693 int objsize
, char *name
, grub_uint64_t
* value
)
696 mzap_ent_phys_t
*mzap_ent
= zapobj
->mz_chunk
;
698 chunks
= objsize
/ MZAP_ENT_LEN
- 1;
699 for (i
= 0; i
< chunks
; i
++)
701 if (grub_strcmp (mzap_ent
[i
].mze_name
, name
) == 0)
703 *value
= grub_zfs_to_cpu64 (mzap_ent
[i
].mze_value
, endian
);
704 return GRUB_ERR_NONE
;
708 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "couldn't find %s", name
);
712 mzap_iterate (mzap_phys_t
* zapobj
, grub_zfs_endian_t endian
, int objsize
,
713 int NESTED_FUNC_ATTR (*hook
) (const char *name
,
717 mzap_ent_phys_t
*mzap_ent
= zapobj
->mz_chunk
;
719 chunks
= objsize
/ MZAP_ENT_LEN
- 1;
720 for (i
= 0; i
< chunks
; i
++)
722 grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n",
723 mzap_ent
[i
].mze_name
, (long long)mzap_ent
[i
].mze_value
,
724 (int)mzap_ent
[i
].mze_cd
);
725 if (hook (mzap_ent
[i
].mze_name
,
726 grub_zfs_to_cpu64 (mzap_ent
[i
].mze_value
, endian
)))
734 zap_hash (grub_uint64_t salt
, const char *name
)
736 static grub_uint64_t table
[256];
737 const grub_uint8_t
*cp
;
739 grub_uint64_t crc
= salt
;
745 for (i
= 0; i
< 256; i
++)
747 for (ct
= table
+ i
, *ct
= i
, j
= 8; j
> 0; j
--)
748 *ct
= (*ct
>> 1) ^ (-(*ct
& 1) & ZFS_CRC64_POLY
);
752 for (cp
= (const grub_uint8_t
*) name
; (c
= *cp
) != '\0'; cp
++)
753 crc
= (crc
>> 8) ^ table
[(crc
^ c
) & 0xFF];
756 * Only use 28 bits, since we need 4 bits in the cookie for the
757 * collision differentiator. We MUST use the high bits, since
758 * those are the onces that we first pay attention to when
759 * chosing the bucket.
761 crc
&= ~((1ULL << (64 - ZAP_HASHBITS
)) - 1);
767 * Only to be used on 8-bit arrays.
768 * array_len is actual len in bytes (not encoded le_value_length).
769 * buf is null-terminated.
773 zap_leaf_array_equal (zap_leaf_phys_t
* l
, grub_zfs_endian_t endian
,
774 int blksft
, int chunk
, int array_len
, const char *buf
)
778 while (bseen
< array_len
)
780 struct zap_leaf_array
*la
= &ZAP_LEAF_CHUNK (l
, blksft
, chunk
).l_array
;
781 int toread
= MIN (array_len
- bseen
, ZAP_LEAF_ARRAY_BYTES
);
783 if (chunk
>= ZAP_LEAF_NUMCHUNKS (blksft
))
786 if (grub_memcmp (la
->la_array
, buf
+ bseen
, toread
) != 0)
788 chunk
= grub_zfs_to_cpu16 (la
->la_next
, endian
);
791 return (bseen
== array_len
);
796 zap_leaf_array_get (zap_leaf_phys_t
* l
, grub_zfs_endian_t endian
, int blksft
,
797 int chunk
, int array_len
, char *buf
)
801 while (bseen
< array_len
)
803 struct zap_leaf_array
*la
= &ZAP_LEAF_CHUNK (l
, blksft
, chunk
).l_array
;
804 int toread
= MIN (array_len
- bseen
, ZAP_LEAF_ARRAY_BYTES
);
806 if (chunk
>= ZAP_LEAF_NUMCHUNKS (blksft
))
807 /* Don't use grub_error because this error is to be ignored. */
808 return GRUB_ERR_BAD_FS
;
810 grub_memcpy (buf
+ bseen
,la
->la_array
, toread
);
811 chunk
= grub_zfs_to_cpu16 (la
->la_next
, endian
);
814 return GRUB_ERR_NONE
;
819 * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the
820 * value for the property "name".
825 zap_leaf_lookup (zap_leaf_phys_t
* l
, grub_zfs_endian_t endian
,
826 int blksft
, grub_uint64_t h
,
827 const char *name
, grub_uint64_t
* value
)
830 struct zap_leaf_entry
*le
;
831 grub_size_t tlen
= grub_strlen (name
);
833 /* Verify if this is a valid leaf block */
834 if (grub_zfs_to_cpu64 (l
->l_hdr
.lh_block_type
, endian
) != ZBT_LEAF
)
835 return grub_error (GRUB_ERR_BAD_FS
, "invalid leaf type");
836 if (grub_zfs_to_cpu32 (l
->l_hdr
.lh_magic
, endian
) != ZAP_LEAF_MAGIC
)
837 return grub_error (GRUB_ERR_BAD_FS
, "invalid leaf magic");
839 for (chunk
= grub_zfs_to_cpu16 (l
->l_hash
[LEAF_HASH (blksft
, h
)], endian
);
840 chunk
!= CHAIN_END
; chunk
= le
->le_next
)
843 if (chunk
>= ZAP_LEAF_NUMCHUNKS (blksft
))
844 return grub_error (GRUB_ERR_BAD_FS
, "invalid chunk number");
846 le
= ZAP_LEAF_ENTRY (l
, blksft
, chunk
);
848 /* Verify the chunk entry */
849 if (le
->le_type
!= ZAP_CHUNK_ENTRY
)
850 return grub_error (GRUB_ERR_BAD_FS
, "invalid chunk entry");
852 if (grub_zfs_to_cpu64 (le
->le_hash
,endian
) != h
)
855 grub_dprintf ("zfs", "fzap: length %d:%d", (int) tlen
,
856 (int) le
->le_name_length
);
858 if (zap_leaf_array_equal (l
, endian
, blksft
,
859 grub_zfs_to_cpu16 (le
->le_name_chunk
,endian
),
860 grub_zfs_to_cpu16 (le
->le_name_length
, endian
),
863 struct zap_leaf_array
*la
;
866 if (le
->le_int_size
!= 8 || le
->le_value_length
!= 1)
867 return (GRUB_ERR_BAD_FS
);
869 /* get the uint64_t property value */
870 la
= &ZAP_LEAF_CHUNK (l
, blksft
, le
->le_value_chunk
).l_array
;
873 *value
= grub_be_to_cpu64 (*(grub_uint64_t
*)la
->la_array
);
875 return GRUB_ERR_NONE
;
879 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "couldn't find %s", name
);
888 fzap_lookup (dnode_end_t
* zap_dnode
, zap_phys_t
* zap
,
889 char *name
, grub_uint64_t
* value
, struct grub_zfs_data
*data
)
892 grub_uint64_t hash
, idx
, blkid
;
893 int blksft
= zfs_log2 (grub_zfs_to_cpu16 (zap_dnode
->dn
.dn_datablkszsec
,
894 zap_dnode
->endian
) << DNODE_SHIFT
);
896 grub_zfs_endian_t leafendian
;
898 /* Verify if this is a fat zap header block */
899 if (zap
->zap_magic
!= (grub_uint64_t
) ZAP_MAGIC
)
900 return grub_error (GRUB_ERR_BAD_FS
, "bad ZAP magic");
902 if (zap
->zap_salt
== 0)
903 return grub_error (GRUB_ERR_BAD_FS
, "bad ZAP salt");
904 hash
= zap_hash (zap
->zap_salt
, name
);
906 /* get block id from index */
907 if (zap
->zap_ptrtbl
.zt_numblks
!= 0)
908 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
909 "external pointer tables not supported");
910 idx
= ZAP_HASH_IDX (hash
, zap
->zap_ptrtbl
.zt_shift
);
911 blkid
= ((grub_uint64_t
*) zap
)[idx
+ (1 << (blksft
- 3 - 1))];
913 /* Get the leaf block */
914 if ((1U << blksft
) < sizeof (zap_leaf_phys_t
))
915 return grub_error (GRUB_ERR_BAD_FS
, "ZAP leaf is too small");
916 err
= dmu_read (zap_dnode
, blkid
, (void **) &l
, &leafendian
, data
);
920 err
= zap_leaf_lookup (l
, leafendian
, blksft
, hash
, name
, value
);
927 fzap_iterate (dnode_end_t
* zap_dnode
, zap_phys_t
* zap
,
928 int NESTED_FUNC_ATTR (*hook
) (const char *name
,
930 struct grub_zfs_data
*data
)
933 grub_uint64_t idx
, blkid
;
935 int blksft
= zfs_log2 (grub_zfs_to_cpu16 (zap_dnode
->dn
.dn_datablkszsec
,
936 zap_dnode
->endian
) << DNODE_SHIFT
);
938 grub_zfs_endian_t endian
;
940 /* Verify if this is a fat zap header block */
941 if (zap
->zap_magic
!= (grub_uint64_t
) ZAP_MAGIC
)
943 grub_error (GRUB_ERR_BAD_FS
, "bad ZAP magic");
947 /* get block id from index */
948 if (zap
->zap_ptrtbl
.zt_numblks
!= 0)
950 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
951 "external pointer tables not supported");
954 /* Get the leaf block */
955 if ((1U << blksft
) < sizeof (zap_leaf_phys_t
))
957 grub_error (GRUB_ERR_BAD_FS
, "ZAP leaf is too small");
960 for (idx
= 0; idx
< zap
->zap_ptrtbl
.zt_numblks
; idx
++)
962 blkid
= ((grub_uint64_t
*) zap
)[idx
+ (1 << (blksft
- 3 - 1))];
964 err
= dmu_read (zap_dnode
, blkid
, (void **) &l
, &endian
, data
);
967 grub_errno
= GRUB_ERR_NONE
;
971 /* Verify if this is a valid leaf block */
972 if (grub_zfs_to_cpu64 (l
->l_hdr
.lh_block_type
, endian
) != ZBT_LEAF
)
977 if (grub_zfs_to_cpu32 (l
->l_hdr
.lh_magic
, endian
) != ZAP_LEAF_MAGIC
)
983 for (chunk
= 0; chunk
< ZAP_LEAF_NUMCHUNKS (blksft
); chunk
++)
986 struct zap_leaf_array
*la
;
987 struct zap_leaf_entry
*le
;
989 le
= ZAP_LEAF_ENTRY (l
, blksft
, chunk
);
991 /* Verify the chunk entry */
992 if (le
->le_type
!= ZAP_CHUNK_ENTRY
)
995 buf
= grub_malloc (grub_zfs_to_cpu16 (le
->le_name_length
, endian
)
997 if (zap_leaf_array_get (l
, endian
, blksft
, le
->le_name_chunk
,
998 le
->le_name_length
, buf
))
1003 buf
[le
->le_name_length
] = 0;
1005 if (le
->le_int_size
!= 8
1006 || grub_zfs_to_cpu16 (le
->le_value_length
, endian
) != 1)
1009 /* get the uint64_t property value */
1010 la
= &ZAP_LEAF_CHUNK (l
, blksft
, le
->le_value_chunk
).l_array
;
1011 val
= grub_be_to_cpu64 (*(grub_uint64_t
*)la
->la_array
);
1012 if (hook (buf
, val
))
1022 * Read in the data of a zap object and find the value for a matching
1027 zap_lookup (dnode_end_t
* zap_dnode
, char *name
, grub_uint64_t
* val
,
1028 struct grub_zfs_data
*data
)
1030 grub_uint64_t block_type
;
1034 grub_zfs_endian_t endian
;
1036 grub_dprintf ("zfs", "looking for '%s'\n", name
);
1038 /* Read in the first block of the zap object data. */
1039 size
= grub_zfs_to_cpu16 (zap_dnode
->dn
.dn_datablkszsec
,
1040 zap_dnode
->endian
) << SPA_MINBLOCKSHIFT
;
1041 err
= dmu_read (zap_dnode
, 0, &zapbuf
, &endian
, data
);
1044 block_type
= grub_zfs_to_cpu64 (*((grub_uint64_t
*) zapbuf
), endian
);
1046 grub_dprintf ("zfs", "zap read\n");
1048 if (block_type
== ZBT_MICRO
)
1050 grub_dprintf ("zfs", "micro zap\n");
1051 err
= (mzap_lookup (zapbuf
, endian
, size
, name
, val
));
1052 grub_dprintf ("zfs", "returned %d\n", err
);
1056 else if (block_type
== ZBT_HEADER
)
1058 grub_dprintf ("zfs", "fat zap\n");
1059 /* this is a fat zap */
1060 err
= (fzap_lookup (zap_dnode
, zapbuf
, name
, val
, data
));
1061 grub_dprintf ("zfs", "returned %d\n", err
);
1066 return grub_error (GRUB_ERR_BAD_FS
, "unknown ZAP type");
1070 zap_iterate (dnode_end_t
* zap_dnode
,
1071 int NESTED_FUNC_ATTR (*hook
) (const char *name
, grub_uint64_t val
),
1072 struct grub_zfs_data
*data
)
1074 grub_uint64_t block_type
;
1079 grub_zfs_endian_t endian
;
1081 /* Read in the first block of the zap object data. */
1082 size
= grub_zfs_to_cpu16 (zap_dnode
->dn
.dn_datablkszsec
, zap_dnode
->endian
) << SPA_MINBLOCKSHIFT
;
1083 err
= dmu_read (zap_dnode
, 0, &zapbuf
, &endian
, data
);
1086 block_type
= grub_zfs_to_cpu64 (*((grub_uint64_t
*) zapbuf
), endian
);
1088 grub_dprintf ("zfs", "zap read\n");
1090 if (block_type
== ZBT_MICRO
)
1092 grub_dprintf ("zfs", "micro zap\n");
1093 ret
= mzap_iterate (zapbuf
, endian
, size
, hook
);
1097 else if (block_type
== ZBT_HEADER
)
1099 grub_dprintf ("zfs", "fat zap\n");
1100 /* this is a fat zap */
1101 ret
= fzap_iterate (zap_dnode
, zapbuf
, hook
, data
);
1105 grub_error (GRUB_ERR_BAD_FS
, "unknown ZAP type");
1111 * Get the dnode of an object number from the metadnode of an object set.
1114 * mdn - metadnode to get the object dnode
1115 * objnum - object number for the object dnode
1116 * buf - data buffer that holds the returning dnode
1119 dnode_get (dnode_end_t
* mdn
, grub_uint64_t objnum
, grub_uint8_t type
,
1120 dnode_end_t
* buf
, struct grub_zfs_data
*data
)
1122 grub_uint64_t blkid
, blksz
; /* the block id this object dnode is in */
1123 int epbs
; /* shift of number of dnodes in a block */
1124 int idx
; /* index within a block */
1125 dnode_phys_t
*dnbuf
;
1127 grub_zfs_endian_t endian
;
1129 blksz
= grub_zfs_to_cpu16 (mdn
->dn
.dn_datablkszsec
,
1130 mdn
->endian
) << SPA_MINBLOCKSHIFT
;
1131 epbs
= zfs_log2 (blksz
) - DNODE_SHIFT
;
1132 blkid
= objnum
>> epbs
;
1133 idx
= objnum
& ((1 << epbs
) - 1);
1135 if (data
->dnode_buf
!= NULL
&& grub_memcmp (data
->dnode_mdn
, mdn
,
1137 && objnum
>= data
->dnode_start
&& objnum
< data
->dnode_end
)
1139 grub_memmove (&(buf
->dn
), &(data
->dnode_buf
)[idx
], DNODE_SIZE
);
1140 buf
->endian
= data
->dnode_endian
;
1141 if (type
&& buf
->dn
.dn_type
!= type
)
1142 return grub_error(GRUB_ERR_BAD_FS
, "incorrect dnode type");
1143 return GRUB_ERR_NONE
;
1146 grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn
->endian
,
1147 (unsigned long long) blkid
);
1148 err
= dmu_read (mdn
, blkid
, (void **) &dnbuf
, &endian
, data
);
1151 grub_dprintf ("zfs", "alive\n");
1153 grub_free (data
->dnode_buf
);
1154 grub_free (data
->dnode_mdn
);
1155 data
->dnode_mdn
= grub_malloc (sizeof (*mdn
));
1156 if (! data
->dnode_mdn
)
1158 grub_errno
= GRUB_ERR_NONE
;
1159 data
->dnode_buf
= 0;
1163 grub_memcpy (data
->dnode_mdn
, mdn
, sizeof (*mdn
));
1164 data
->dnode_buf
= dnbuf
;
1165 data
->dnode_start
= blkid
<< epbs
;
1166 data
->dnode_end
= (blkid
+ 1) << epbs
;
1167 data
->dnode_endian
= endian
;
1170 grub_memmove (&(buf
->dn
), &dnbuf
[idx
], DNODE_SIZE
);
1171 buf
->endian
= endian
;
1172 if (type
&& buf
->dn
.dn_type
!= type
)
1173 return grub_error(GRUB_ERR_BAD_FS
, "incorrect dnode type");
1175 return GRUB_ERR_NONE
;
1179 * Get the file dnode for a given file name where mdn is the meta dnode
1180 * for this ZFS object set. When found, place the file dnode in dn.
1181 * The 'path' argument will be mangled.
1185 dnode_get_path (dnode_end_t
* mdn
, const char *path_in
, dnode_end_t
* dn
,
1186 struct grub_zfs_data
*data
)
1188 grub_uint64_t objnum
, version
;
1190 grub_err_t err
= GRUB_ERR_NONE
;
1191 char *path
, *path_buf
;
1194 struct dnode_chain
*next
;
1197 struct dnode_chain
*dnode_path
= 0, *dn_new
, *root
;
1199 dn_new
= grub_malloc (sizeof (*dn_new
));
1203 dnode_path
= root
= dn_new
;
1205 err
= dnode_get (mdn
, MASTER_NODE_OBJ
, DMU_OT_MASTER_NODE
,
1206 &(dnode_path
->dn
), data
);
1213 err
= zap_lookup (&(dnode_path
->dn
), ZPL_VERSION_STR
, &version
, data
);
1219 if (version
> ZPL_VERSION
)
1222 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
, "too new ZPL version");
1225 err
= zap_lookup (&(dnode_path
->dn
), ZFS_ROOT_OBJ
, &objnum
, data
);
1232 err
= dnode_get (mdn
, objnum
, 0, &(dnode_path
->dn
), data
);
1239 path
= path_buf
= grub_strdup (path_in
);
1248 /* skip leading slashes */
1249 while (*path
== '/')
1253 /* get the next component name */
1255 while (*path
&& *path
!= '/')
1258 if (cname
+ 1 == path
&& cname
[0] == '.')
1260 /* Handle double dot. */
1261 if (cname
+ 2 == path
&& cname
[0] == '.' && cname
[1] == '.')
1265 dn_new
= dnode_path
;
1266 dnode_path
= dn_new
->next
;
1271 err
= grub_error (GRUB_ERR_FILE_NOT_FOUND
,
1272 "can't resolve ..");
1279 *path
= 0; /* ensure null termination */
1281 if (dnode_path
->dn
.dn
.dn_type
!= DMU_OT_DIRECTORY_CONTENTS
)
1283 grub_free (path_buf
);
1284 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
1286 err
= zap_lookup (&(dnode_path
->dn
), cname
, &objnum
, data
);
1290 dn_new
= grub_malloc (sizeof (*dn_new
));
1296 dn_new
->next
= dnode_path
;
1297 dnode_path
= dn_new
;
1299 objnum
= ZFS_DIRENT_OBJ (objnum
);
1300 err
= dnode_get (mdn
, objnum
, 0, &(dnode_path
->dn
), data
);
1305 if (((grub_zfs_to_cpu64(((znode_phys_t
*) &dnode_path
->dn
.dn
.dn_bonus
)->zp_mode
, dnode_path
->dn
.endian
) >> 12) & 0xf) == 0xa && ch
)
1307 char *oldpath
= path
, *oldpathbuf
= path_buf
;
1309 = grub_malloc (sizeof (dnode_path
->dn
.dn
.dn_bonus
)
1310 - sizeof (znode_phys_t
) + grub_strlen (oldpath
) + 1);
1313 grub_free (oldpathbuf
);
1317 (char *) &dnode_path
->dn
.dn
.dn_bonus
[sizeof (znode_phys_t
)],
1318 sizeof (dnode_path
->dn
.dn
.dn_bonus
) - sizeof (znode_phys_t
));
1319 path
[sizeof (dnode_path
->dn
.dn
.dn_bonus
) - sizeof (znode_phys_t
)] = 0;
1320 grub_memcpy (path
+ grub_strlen (path
), oldpath
,
1321 grub_strlen (oldpath
) + 1);
1323 grub_free (oldpathbuf
);
1326 dn_new
= dnode_path
;
1327 dnode_path
= dn_new
->next
;
1330 else while (dnode_path
!= root
)
1332 dn_new
= dnode_path
;
1333 dnode_path
= dn_new
->next
;
1340 grub_memcpy (dn
, &(dnode_path
->dn
), sizeof (*dn
));
1344 dn_new
= dnode_path
->next
;
1345 grub_free (dnode_path
);
1346 dnode_path
= dn_new
;
1348 grub_free (path_buf
);
1354 * Get the default 'bootfs' property value from the rootpool.
1358 get_default_bootfsobj (dnode_phys_t
* mosmdn
, grub_uint64_t
* obj
,
1359 struct grub_zfs_data
*data
)
1361 grub_uint64_t objnum
= 0;
1366 if ((grub_errno
= dnode_get (mosmdn
, DMU_POOL_DIRECTORY_OBJECT
,
1367 DMU_OT_OBJECT_DIRECTORY
, dn
, data
)))
1370 return (grub_errno
);
1374 * find the object number for 'pool_props', and get the dnode
1375 * of the 'pool_props'.
1377 if (zap_lookup (dn
, DMU_POOL_PROPS
, &objnum
, data
))
1380 return (GRUB_ERR_BAD_FS
);
1382 if ((grub_errno
= dnode_get (mosmdn
, objnum
, DMU_OT_POOL_PROPS
, dn
, data
)))
1385 return (grub_errno
);
1387 if (zap_lookup (dn
, ZPOOL_PROP_BOOTFS
, &objnum
, data
))
1390 return (GRUB_ERR_BAD_FS
);
1396 return (GRUB_ERR_BAD_FS
);
1404 * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
1405 * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
1408 * If no fsname and no obj are given, return the DSL_DIR metadnode.
1409 * If fsname is given, return its metadnode and its matching object number.
1410 * If only obj is given, return the metadnode for this object number.
1414 get_filesystem_dnode (dnode_end_t
* mosmdn
, char *fsname
,
1415 dnode_end_t
* mdn
, struct grub_zfs_data
*data
)
1417 grub_uint64_t objnum
;
1420 grub_dprintf ("zfs", "endian = %d\n", mosmdn
->endian
);
1422 err
= dnode_get (mosmdn
, DMU_POOL_DIRECTORY_OBJECT
,
1423 DMU_OT_OBJECT_DIRECTORY
, mdn
, data
);
1427 grub_dprintf ("zfs", "alive\n");
1429 err
= zap_lookup (mdn
, DMU_POOL_ROOT_DATASET
, &objnum
, data
);
1433 grub_dprintf ("zfs", "alive\n");
1435 err
= dnode_get (mosmdn
, objnum
, DMU_OT_DSL_DIR
, mdn
, data
);
1439 grub_dprintf ("zfs", "alive\n");
1443 grub_uint64_t childobj
;
1446 while (*fsname
== '/')
1449 if (! *fsname
|| *fsname
== '@')
1453 while (*fsname
&& !grub_isspace (*fsname
) && *fsname
!= '/')
1458 childobj
= grub_zfs_to_cpu64(((dsl_dir_phys_t
*) &(mdn
->dn
.dn_bonus
))->dd_child_dir_zapobj
, mdn
->endian
);
1459 err
= dnode_get (mosmdn
, childobj
,
1460 DMU_OT_DSL_DIR_CHILD_MAP
, mdn
, data
);
1464 err
= zap_lookup (mdn
, cname
, &objnum
, data
);
1468 err
= dnode_get (mosmdn
, objnum
, DMU_OT_DSL_DIR
, mdn
, data
);
1474 return GRUB_ERR_NONE
;
1478 make_mdn (dnode_end_t
* mdn
, struct grub_zfs_data
*data
)
1482 grub_size_t ospsize
;
1485 grub_dprintf ("zfs", "endian = %d\n", mdn
->endian
);
1487 bp
= &((dsl_dataset_phys_t
*) &(mdn
->dn
.dn_bonus
))->ds_bp
;
1488 err
= zio_read (bp
, mdn
->endian
, (void **) &osp
, &ospsize
, data
);
1491 if (ospsize
< sizeof (objset_phys_t
))
1494 return grub_error (GRUB_ERR_BAD_FS
, "too small osp");
1497 mdn
->endian
= (grub_zfs_to_cpu64 (bp
->blk_prop
, mdn
->endian
)>>63) & 1;
1498 grub_memmove ((char *) &(mdn
->dn
), (char *) &osp
->os_meta_dnode
, DNODE_SIZE
);
1500 return GRUB_ERR_NONE
;
1504 dnode_get_fullpath (const char *fullpath
, dnode_end_t
* mdn
,
1505 dnode_end_t
* dn
, int *isfs
,
1506 struct grub_zfs_data
*data
)
1508 char *fsname
, *snapname
;
1509 const char *ptr_at
, *ptr_slash
, *filename
;
1510 grub_uint64_t headobj
;
1513 ptr_at
= grub_strchr (fullpath
, '@');
1515 ptr_slash
= grub_strchr (ptr_at
, '/');
1521 fsname
= grub_strdup (fullpath
);
1526 fsname
= grub_malloc (ptr_at
- fullpath
+ 1);
1529 grub_memcpy (fsname
, fullpath
, ptr_at
- fullpath
);
1530 fsname
[ptr_at
- fullpath
] = 0;
1531 if (ptr_at
[1] && ptr_at
[1] != '/')
1533 snapname
= grub_malloc (ptr_slash
- ptr_at
);
1539 grub_memcpy (snapname
, ptr_at
+ 1, ptr_slash
- ptr_at
- 1);
1540 snapname
[ptr_slash
- ptr_at
- 1] = 0;
1545 filename
= ptr_slash
;
1548 grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n",
1549 fsname
, snapname
, filename
);
1551 grub_dprintf ("zfs", "alive\n");
1552 err
= get_filesystem_dnode (&(data
->mos
), fsname
, dn
, data
);
1556 grub_free (snapname
);
1560 grub_dprintf ("zfs", "alive\n");
1562 headobj
= grub_zfs_to_cpu64 (((dsl_dir_phys_t
*) &(dn
->dn
.dn_bonus
))->dd_head_dataset_obj
, dn
->endian
);
1564 grub_dprintf ("zfs", "endian = %d\n", mdn
->endian
);
1566 err
= dnode_get (&(data
->mos
), headobj
, DMU_OT_DSL_DATASET
, mdn
, data
);
1570 grub_free (snapname
);
1573 grub_dprintf ("zfs", "endian = %d\n", mdn
->endian
);
1577 grub_uint64_t snapobj
;
1579 snapobj
= grub_zfs_to_cpu64 (((dsl_dataset_phys_t
*) &(mdn
->dn
.dn_bonus
))->ds_snapnames_zapobj
, mdn
->endian
);
1581 err
= dnode_get (&(data
->mos
), snapobj
,
1582 DMU_OT_DSL_DS_SNAP_MAP
, mdn
, data
);
1584 err
= zap_lookup (mdn
, snapname
, &headobj
, data
);
1586 err
= dnode_get (&(data
->mos
), headobj
, DMU_OT_DSL_DATASET
, mdn
, data
);
1590 grub_free (snapname
);
1595 make_mdn (mdn
, data
);
1597 grub_dprintf ("zfs", "endian = %d\n", mdn
->endian
);
1602 grub_free (snapname
);
1603 return GRUB_ERR_NONE
;
1605 err
= dnode_get_path (mdn
, filename
, dn
, data
);
1607 grub_free (snapname
);
1612 * For a given XDR packed nvlist, verify the first 4 bytes and move on.
1614 * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) :
1616 * encoding method/host endian (4 bytes)
1617 * nvl_version (4 bytes)
1618 * nvl_nvflag (4 bytes)
1620 * encoded size of the nvpair (4 bytes)
1621 * decoded size of the nvpair (4 bytes)
1622 * name string size (4 bytes)
1623 * name string data (sizeof(NV_ALIGN4(string))
1624 * data type (4 bytes)
1625 * # of elements in the nvpair (4 bytes)
1627 * 2 zero's for the last nvpair
1628 * (end of the entire list) (8 bytes)
1637 nvlist_array (char *nvlist
, int index
)
1641 for (i
= 0; i
< index
; i
++)
1643 /* skip the header, nvl_version, and nvl_nvflag */
1644 nvlist
= nvlist
+ 4 * 2;
1646 while ((encode_size
= BSWAP_32 (*(grub_uint32_t
*) nvlist
)))
1647 nvlist
+= encode_size
; /* goto the next nvpair */
1649 nvlist
= nvlist
+ 4 * 2; /* skip the ending 2 zeros - 8 bytes */
1657 nvlist_find_value (char *nvlist
, char *name
, int valtype
, char **val
)
1659 int name_len
, type
, encode_size
;
1660 char *nvpair
, *nvp_name
;
1662 /* Verify if the 1st and 2nd byte in the nvlist are valid. */
1663 /* NOTE: independently of what endianness header announces all
1664 subsequent values are big-endian. */
1665 if (nvlist
[0] != NV_ENCODE_XDR
|| (nvlist
[1] != NV_LITTLE_ENDIAN
1666 && nvlist
[1] != NV_BIG_ENDIAN
))
1667 return grub_error (GRUB_ERR_BAD_FS
, "incorrect nvlist");
1669 /* skip the header, nvl_version, and nvl_nvflag */
1670 nvlist
= nvlist
+ 4 * 3;
1672 * Loop thru the nvpair list
1673 * The XDR representation of an integer is in big-endian byte order.
1675 while ((encode_size
= grub_be_to_cpu32 (*(grub_uint32_t
*) nvlist
)))
1679 nvpair
= nvlist
+ 4 * 2; /* skip the encode/decode size */
1681 name_len
= grub_be_to_cpu32 (*(grub_uint32_t
*) nvpair
);
1685 nvpair
= nvpair
+ ((name_len
+ 3) & ~3); /* align */
1687 type
= grub_be_to_cpu32 (*(grub_uint32_t
*) nvpair
);
1690 nelm
= grub_be_to_cpu32 (*(grub_uint32_t
*) nvpair
);
1692 return grub_error (GRUB_ERR_BAD_FS
, "empty nvpair");
1696 if ((grub_strncmp (nvp_name
, name
, name_len
) == 0) && type
== valtype
)
1699 return GRUB_ERR_NONE
;
1702 nvlist
+= encode_size
; /* goto the next nvpair */
1704 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "key %s not found", name
);
1708 nvlist_lookup_uint64 (char *nvlist
, char *name
, grub_uint64_t
* out
)
1712 err
= nvlist_find_value (nvlist
, name
, DATA_TYPE_UINT64
, &nvpair
);
1715 *out
= grub_be_to_cpu64 (*(grub_uint64_t
*) nvpair
);
1716 return GRUB_ERR_NONE
;
1720 nvlist_lookup_string (char *nvlist
, char *name
)
1727 err
= nvlist_find_value (nvlist
, name
, DATA_TYPE_STRING
, &nvpair
);
1730 slen
= grub_be_to_cpu32 (*(grub_uint32_t
*) nvpair
);
1731 ret
= grub_malloc (slen
+ 1);
1734 grub_memcpy (ret
, nvpair
+ 4, slen
);
1741 nvlist_lookup_value (char *nvlist
, char *name
, void *val
, int valtype
,
1744 grub_uint64_t
*intval
= val
;
1749 case DATA_TYPE_NVLIST
:
1750 *(void **) val
= (void *) nvpair
;
1753 case DATA_TYPE_NVLIST_ARRAY
:
1754 *(void **) val
= (void *) nvpair
;
1765 * Check if this vdev is online and is in a good state.
1768 vdev_validate (char *nv
)
1772 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_OFFLINE
, &ival
,
1773 DATA_TYPE_UINT64
, NULL
) == 0 ||
1774 nvlist_lookup_value (nv
, ZPOOL_CONFIG_FAULTED
, &ival
,
1775 DATA_TYPE_UINT64
, NULL
) == 0 ||
1776 nvlist_lookup_value (nv
, ZPOOL_CONFIG_REMOVED
, &ival
,
1777 DATA_TYPE_UINT64
, NULL
) == 0)
1778 return (GRUB_ERR_BAD_FS
);
1784 * Get a list of valid vdev pathname from the boot device.
1785 * The caller should already allocate MAXPATHLEN memory for bootpath and devid.
1788 vdev_get_bootpath (char *nv
, grub_uint64_t inguid
, char *devid
,
1793 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_TYPE
, &type
, DATA_TYPE_STRING
,
1795 return (GRUB_ERR_BAD_FS
);
1797 if (grub_strcmp (type
, VDEV_TYPE_DISK
) == 0)
1801 if (vdev_validate (nv
) != 0)
1802 return (GRUB_ERR_FILE_NOT_FOUND
);
1804 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_GUID
,
1805 &guid
, DATA_TYPE_UINT64
, NULL
) != 0)
1806 return (GRUB_ERR_FILE_NOT_FOUND
);
1809 return (GRUB_ERR_FILE_NOT_FOUND
);
1811 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_PHYS_PATH
,
1812 bootpath
, DATA_TYPE_STRING
, NULL
) != 0)
1815 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_DEVID
,
1816 devid
, DATA_TYPE_STRING
, NULL
) != 0)
1819 if (grub_strlen (bootpath
) >= MAXPATHLEN
||
1820 grub_strlen (devid
) >= MAXPATHLEN
)
1821 return (GRUB_ERR_OUT_OF_MEMORY
);
1826 else if (grub_strcmp (type
, VDEV_TYPE_MIRROR
) == 0)
1831 if (nvlist_lookup_value (nv
, ZPOOL_CONFIG_CHILDREN
, &child
,
1832 DATA_TYPE_NVLIST_ARRAY
, &nelm
))
1833 return (GRUB_ERR_BAD_FS
);
1835 for (i
= 0; i
< nelm
; i
++)
1839 child_i
= nvlist_array (child
, i
);
1840 if (vdev_get_bootpath (child_i
, inguid
, devid
, bootpath
) == 0)
1845 return (GRUB_ERR_FILE_NOT_FOUND
);
1850 grub_zfs_fetch_nvlist (struct grub_zfs_data
* data
, char **nvlist
)
1852 grub_uint64_t sector
;
1855 sector
= (data
->current_label
* sizeof (vdev_label_t
) + VDEV_SKIP_SIZE
+
1856 VDEV_BOOT_HEADER_SIZE
) >> SPA_MINBLOCKSHIFT
;
1857 *nvlist
= grub_malloc (VDEV_PHYS_SIZE
);
1858 /* Read in the vdev name-value pair list (112K). */
1859 err
= grub_disk_read (data
->disk
, sector
, 0, VDEV_PHYS_SIZE
, *nvlist
);
1862 grub_free (*nvlist
);
1866 return GRUB_ERR_NONE
;
1871 * Check the disk label information and retrieve needed vdev name-value pairs.
1875 check_pool_label (struct grub_zfs_data
*data
)
1877 grub_uint64_t pool_state
, txg
= 0;
1882 grub_uint64_t diskguid
;
1883 grub_uint64_t version
;
1886 err
= grub_zfs_fetch_nvlist (data
, &nvlist
);
1890 grub_dprintf ("zfs", "check 2 passed\n");
1892 err
= nvlist_lookup_uint64 (nvlist
, ZPOOL_CONFIG_POOL_STATE
, &pool_state
);
1898 grub_dprintf ("zfs", "check 3 passed\n");
1900 if (pool_state
== POOL_STATE_DESTROYED
)
1903 return grub_error (GRUB_ERR_BAD_FS
, "zpool is marked as destroyed");
1905 grub_dprintf ("zfs", "check 4 passed\n");
1907 err
= nvlist_lookup_uint64 (nvlist
, ZPOOL_CONFIG_POOL_TXG
, &txg
);
1913 grub_dprintf ("zfs", "check 6 passed\n");
1915 /* not an active device */
1919 return grub_error (GRUB_ERR_BAD_FS
, "zpool isn't active");
1921 grub_dprintf ("zfs", "check 7 passed\n");
1923 err
= nvlist_lookup_uint64 (nvlist
, ZPOOL_CONFIG_VERSION
, &version
);
1929 grub_dprintf ("zfs", "check 8 passed\n");
1931 if (version
> SPA_VERSION
)
1934 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
1935 "too new version %llu > %llu",
1936 (unsigned long long) version
,
1937 (unsigned long long) SPA_VERSION
);
1939 grub_dprintf ("zfs", "check 9 passed\n");
1941 if (nvlist_lookup_value (nvlist
, ZPOOL_CONFIG_VDEV_TREE
, &nv
,
1942 DATA_TYPE_NVLIST
, NULL
))
1945 return (GRUB_ERR_BAD_FS
);
1947 grub_dprintf ("zfs", "check 10 passed\n");
1950 err
= nvlist_lookup_uint64 (nvlist
, ZPOOL_CONFIG_GUID
, &diskguid
);
1956 grub_dprintf ("zfs", "check 11 passed\n");
1959 if (vdev_get_bootpath (nv
, diskguid
, outdevid
, outpath
))
1962 return (GRUB_ERR_FILE_NOT_FOUND
);
1965 grub_dprintf ("zfs", "check 12 passed\n");
1968 return GRUB_ERR_NONE
;
1972 zfs_unmount (struct grub_zfs_data
*data
)
1974 grub_free (data
->dnode_buf
);
1975 grub_free (data
->dnode_mdn
);
1976 grub_free (data
->file_buf
);
1981 * zfs_mount() locates a valid uberblock of the root pool and read in its MOS
1982 * to the memory address MOS.
1985 struct grub_zfs_data
*
1986 zfs_mount (grub_disk_t disk
)
1988 struct grub_zfs_data
*data
= 0;
1990 uberblock_phys_t
*ub_array
, *ubbest
= NULL
;
1991 vdev_boot_header_t
*bh
;
1992 objset_phys_t
*osp
= 0;
1993 grub_size_t ospsize
;
1996 char tmp_bootpath
[MAXNAMELEN
];
1997 char tmp_devid
[MAXNAMELEN
];
2000 data
= grub_malloc (sizeof (*data
));
2003 grub_memset (data
, 0, sizeof (*data
));
2005 /* if it's our first time here, zero the best uberblock out */
2006 if (data
->best_drive
== 0 && data
->best_part
== 0 && find_best_root
)
2007 grub_memset (¤t_uberblock
, 0, sizeof (uberblock_t
));
2012 ub_array
= grub_malloc (VDEV_UBERBLOCK_RING
);
2019 bh
= grub_malloc (VDEV_BOOT_HEADER_SIZE
);
2023 grub_free (ub_array
);
2028 /* XXX add back labels support? */
2029 for (label
= 0; ubbest
== NULL
&& label
< (VDEV_LABELS
/ 2); label
++)
2031 grub_uint64_t sector
= (label
* sizeof (vdev_label_t
) +
2032 VDEV_SKIP_SIZE
) >> SPA_MINBLOCKSHIFT
;
2033 grub_zfs_endian_t vdev_endian
= UNKNOWN_ENDIAN
;
2034 grub_zfs_endian_t ub_endian
= UNKNOWN_ENDIAN
;
2035 grub_dprintf ("zfs", "label %d\n", label
);
2036 err
= grub_disk_read (data
->disk
, sector
, 0, VDEV_BOOT_HEADER_SIZE
,
2040 grub_errno
= GRUB_ERR_NONE
;
2043 if ((grub_zfs_to_cpu64 (bh
->vb_magic
, LITTLE_ENDIAN
) == VDEV_BOOT_MAGIC
)
2044 && (grub_zfs_to_cpu64 (bh
->vb_version
, LITTLE_ENDIAN
)
2045 == VDEV_BOOT_VERSION
))
2047 grub_dprintf ("zfs", "little endian VDEV\n");
2048 vdev_endian
= LITTLE_ENDIAN
;
2051 if ((grub_zfs_to_cpu64 (bh
->vb_magic
, BIG_ENDIAN
) == VDEV_BOOT_MAGIC
)
2052 && (grub_zfs_to_cpu64 (bh
->vb_version
, BIG_ENDIAN
)
2053 == VDEV_BOOT_VERSION
))
2055 grub_dprintf ("zfs", "big endian VDEV\n");
2056 vdev_endian
= BIG_ENDIAN
;
2059 if (vdev_endian
== UNKNOWN_ENDIAN
)
2061 grub_dprintf ("zfs", "incorrect magic\n");
2064 sector
+= (VDEV_BOOT_HEADER_SIZE
+ VDEV_PHYS_SIZE
) >> SPA_MINBLOCKSHIFT
;
2066 /* Read in the uberblock ring (128K). */
2067 err
= grub_disk_read (data
->disk
, sector
, 0, VDEV_UBERBLOCK_RING
,
2071 grub_errno
= GRUB_ERR_NONE
;
2074 grub_dprintf ("zfs", "label ok %d\n", label
);
2076 ubbest
= find_bestub (ub_array
, label
);
2079 grub_dprintf ("zfs", "No uberblock found\n");
2080 grub_errno
= GRUB_ERR_NONE
;
2083 ub_endian
= (grub_zfs_to_cpu64 (ubbest
->ubp_uberblock
.ub_magic
,
2084 LITTLE_ENDIAN
) == UBERBLOCK_MAGIC
2085 ? LITTLE_ENDIAN
: BIG_ENDIAN
);
2086 err
= zio_read (&ubbest
->ubp_uberblock
.ub_rootbp
,
2088 (void **) &osp
, &ospsize
, data
);
2091 grub_dprintf ("zfs", "couldn't zio_read\n");
2092 grub_errno
= GRUB_ERR_NONE
;
2096 if (ospsize
< sizeof (objset_phys_t
))
2098 grub_dprintf ("zfs", "osp too small\n");
2102 grub_dprintf ("zfs", "ubbest %p\n", ubbest
);
2104 data
->current_label
= label
;
2105 err
= check_pool_label (data
);
2108 grub_errno
= GRUB_ERR_NONE
;
2112 if (find_best_root
&&
2113 vdev_uberblock_compare (&ubbest
->ubp_uberblock
,
2114 &(current_uberblock
)) <= 0)
2117 /* Got the MOS. Save it at the memory addr MOS. */
2118 grub_memmove (&(data
->mos
.dn
), &osp
->os_meta_dnode
, DNODE_SIZE
);
2119 data
->mos
.endian
= (grub_zfs_to_cpu64 (ubbest
->ubp_uberblock
.ub_rootbp
.blk_prop
, ub_endian
) >> 63) & 1;
2120 grub_memmove (&(data
->current_uberblock
),
2121 &ubbest
->ubp_uberblock
, sizeof (uberblock_t
));
2123 grub_memmove (data
->current_bootpath
, tmp_bootpath
, MAXNAMELEN
);
2124 grub_memmove (data
->current_devid
, tmp_devid
,
2125 grub_strlen (tmp_devid
));
2127 grub_free (ub_array
);
2132 grub_error (GRUB_ERR_BAD_FS
, "couldn't find a valid label");
2134 grub_free (ub_array
);
2142 zfs_label (grub_device_t device
, char **label
)
2146 struct grub_zfs_data
*data
;
2148 data
= zfs_mount (device
->disk
);
2152 err
= grub_zfs_fetch_nvlist (data
, &nvlist
);
2159 *label
= nvlist_lookup_string (nvlist
, ZPOOL_CONFIG_POOL_NAME
);
2166 zfs_uuid (grub_device_t device
, char **uuid
)
2170 struct grub_zfs_data
*data
;
2173 data
= zfs_mount (device
->disk
);
2177 err
= grub_zfs_fetch_nvlist (data
, &nvlist
);
2184 err
= nvlist_lookup_uint64 (nvlist
, ZPOOL_CONFIG_POOL_NAME
, &guid
);
2188 *uuid
= grub_malloc (16 + sizeof ('\0'));
2191 grub_sprintf (*uuid
, "%016llx", (long long unsigned) guid
);
2193 return GRUB_ERR_NONE
;
2197 * zfs_open() locates a file in the rootpool by following the
2198 * MOS and places the dnode of the file in the memory address DNODE.
2201 grub_zfs_open (struct grub_file
*file
, const char *fsfilename
)
2203 struct grub_zfs_data
*data
;
2207 data
= zfs_mount (file
->device
->disk
);
2211 err
= dnode_get_fullpath (fsfilename
, &(data
->mdn
),
2212 &(data
->dnode
), &isfs
, data
);
2222 return grub_error (GRUB_ERR_FILE_NOT_FOUND
, "Missing @ or / separator");
2225 /* We found the dnode for this file. Verify if it is a plain file. */
2226 if (data
->dnode
.dn
.dn_type
!= DMU_OT_PLAIN_FILE_CONTENTS
)
2229 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a file");
2232 /* get the file size and set the file position to 0 */
2235 file
->size
= grub_zfs_to_cpu64 (((znode_phys_t
*) &(data
->dnode
.dn
.dn_bonus
))->zp_size
, data
->dnode
.endian
);
2238 grub_dl_ref (my_mod
);
2241 return GRUB_ERR_NONE
;
2245 grub_zfs_read (grub_file_t file
, char *buf
, grub_size_t len
)
2247 struct grub_zfs_data
*data
= (struct grub_zfs_data
*) file
->data
;
2248 int blksz
, movesize
;
2253 if (data
->file_buf
== NULL
)
2255 data
->file_buf
= grub_malloc (SPA_MAXBLOCKSIZE
);
2256 if (!data
->file_buf
)
2258 data
->file_start
= data
->file_end
= 0;
2262 * If offset is in memory, move it into the buffer provided and return.
2264 if (file
->offset
>= data
->file_start
2265 && file
->offset
+ len
<= data
->file_end
)
2267 grub_memmove (buf
, data
->file_buf
+ file
->offset
- data
->file_start
,
2272 blksz
= grub_zfs_to_cpu16 (data
->dnode
.dn
.dn_datablkszsec
,
2273 data
->dnode
.endian
) << SPA_MINBLOCKSHIFT
;
2276 * Entire Dnode is too big to fit into the space available. We
2277 * will need to read it in chunks. This could be optimized to
2278 * read in as large a chunk as there is space available, but for
2279 * now, this only reads in one data block at a time.
2286 * Find requested blkid and the offset within that block.
2288 grub_uint64_t blkid
= grub_divmod64 (file
->offset
+ read
, blksz
, 0);
2289 grub_free (data
->file_buf
);
2292 err
= dmu_read (&(data
->dnode
), blkid
, &(data
->file_buf
), 0, data
);
2296 data
->file_start
= blkid
* blksz
;
2297 data
->file_end
= data
->file_start
+ blksz
;
2299 movesize
= MIN (length
, data
->file_end
- (int) file
->offset
- read
);
2301 grub_memmove (buf
, data
->file_buf
+ file
->offset
+ read
2302 - data
->file_start
, movesize
);
2312 grub_zfs_close (grub_file_t file
)
2314 zfs_unmount ((struct grub_zfs_data
*) file
->data
);
2317 grub_dl_unref (my_mod
);
2320 return GRUB_ERR_NONE
;
2324 fill_fs_info (struct grub_dirhook_info
*info
,
2325 dnode_end_t mdn
, struct grub_zfs_data
*data
)
2329 grub_uint64_t objnum
;
2330 grub_uint64_t headobj
;
2332 grub_memset (info
, 0, sizeof (*info
));
2336 if (mdn
.dn
.dn_type
== DMU_OT_DSL_DIR
)
2338 headobj
= grub_zfs_to_cpu64 (((dsl_dir_phys_t
*) &(mdn
.dn
.dn_bonus
))->dd_head_dataset_obj
, mdn
.endian
);
2340 err
= dnode_get (&(data
->mos
), headobj
, DMU_OT_DSL_DATASET
, &mdn
, data
);
2343 grub_dprintf ("zfs", "failed here\n");
2347 make_mdn (&mdn
, data
);
2348 err
= dnode_get (&mdn
, MASTER_NODE_OBJ
, DMU_OT_MASTER_NODE
,
2352 grub_dprintf ("zfs", "failed here\n");
2356 err
= zap_lookup (&dn
, ZFS_ROOT_OBJ
, &objnum
, data
);
2359 grub_dprintf ("zfs", "failed here\n");
2363 err
= dnode_get (&mdn
, objnum
, 0, &dn
, data
);
2366 grub_dprintf ("zfs", "failed here\n");
2371 info
->mtime
= grub_zfs_to_cpu64 (((znode_phys_t
*) &dn
.dn
.dn_bonus
)->zp_mtime
[0], dn
.endian
);
2376 grub_zfs_dir (grub_device_t device
, const char *path
,
2377 int (*hook
) (const char *, const struct grub_dirhook_info
*))
2379 struct grub_zfs_data
*data
;
2382 auto int NESTED_FUNC_ATTR
iterate_zap (const char *name
, grub_uint64_t val
);
2383 auto int NESTED_FUNC_ATTR
iterate_zap_fs (const char *name
,
2385 auto int NESTED_FUNC_ATTR
iterate_zap_snap (const char *name
,
2388 int NESTED_FUNC_ATTR
iterate_zap (const char *name
, grub_uint64_t val
)
2390 struct grub_dirhook_info info
;
2392 grub_memset (&info
, 0, sizeof (info
));
2394 dnode_get (&(data
->mdn
), val
, 0, &dn
, data
);
2396 info
.mtime
= grub_zfs_to_cpu64 (((znode_phys_t
*) &dn
.dn
.dn_bonus
)->zp_mtime
[0], dn
.endian
);
2397 info
.dir
= (dn
.dn
.dn_type
== DMU_OT_DIRECTORY_CONTENTS
);
2398 grub_dprintf ("zfs", "type=%d, name=%s\n",
2399 (int)dn
.dn
.dn_type
, (char *)name
);
2400 return hook (name
, &info
);
2403 int NESTED_FUNC_ATTR
iterate_zap_fs (const char *name
, grub_uint64_t val
)
2405 struct grub_dirhook_info info
;
2407 err
= dnode_get (&(data
->mos
), val
, 0, &mdn
, data
);
2410 if (mdn
.dn
.dn_type
!= DMU_OT_DSL_DIR
)
2413 fill_fs_info (&info
, mdn
, data
);
2414 return hook (name
, &info
);
2416 int NESTED_FUNC_ATTR
iterate_zap_snap (const char *name
, grub_uint64_t val
)
2418 struct grub_dirhook_info info
;
2423 err
= dnode_get (&(data
->mos
), val
, 0, &mdn
, data
);
2427 if (mdn
.dn
.dn_type
!= DMU_OT_DSL_DATASET
)
2430 fill_fs_info (&info
, mdn
, data
);
2432 name2
= grub_malloc (grub_strlen (name
) + 2);
2434 grub_memcpy (name2
+ 1, name
, grub_strlen (name
) + 1);
2435 ret
= hook (name2
, &info
);
2440 data
= zfs_mount (device
->disk
);
2443 err
= dnode_get_fullpath (path
, &(data
->mdn
), &(data
->dnode
), &isfs
, data
);
2451 grub_uint64_t childobj
, headobj
;
2452 grub_uint64_t snapobj
;
2454 struct grub_dirhook_info info
;
2457 fill_fs_info (&info
, data
->dnode
, data
);
2460 childobj
= grub_zfs_to_cpu64 (((dsl_dir_phys_t
*) &(data
->dnode
.dn
.dn_bonus
))->dd_child_dir_zapobj
, data
->dnode
.endian
);
2461 headobj
= grub_zfs_to_cpu64 (((dsl_dir_phys_t
*) &(data
->dnode
.dn
.dn_bonus
))->dd_head_dataset_obj
, data
->dnode
.endian
);
2462 err
= dnode_get (&(data
->mos
), childobj
,
2463 DMU_OT_DSL_DIR_CHILD_MAP
, &dn
, data
);
2470 zap_iterate (&dn
, iterate_zap_fs
, data
);
2472 err
= dnode_get (&(data
->mos
), headobj
, DMU_OT_DSL_DATASET
, &dn
, data
);
2479 snapobj
= grub_zfs_to_cpu64 (((dsl_dataset_phys_t
*) &dn
.dn
.dn_bonus
)->ds_snapnames_zapobj
, dn
.endian
);
2481 err
= dnode_get (&(data
->mos
), snapobj
,
2482 DMU_OT_DSL_DS_SNAP_MAP
, &dn
, data
);
2489 zap_iterate (&dn
, iterate_zap_snap
, data
);
2493 if (data
->dnode
.dn
.dn_type
!= DMU_OT_DIRECTORY_CONTENTS
)
2496 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
2498 zap_iterate (&(data
->dnode
), iterate_zap
, data
);
2504 static struct grub_fs grub_zfs_fs
= {
2506 .dir
= grub_zfs_dir
,
2507 .open
= grub_zfs_open
,
2508 .read
= grub_zfs_read
,
2509 .close
= grub_zfs_close
,
2518 grub_fs_register (&grub_zfs_fs
);
2526 grub_fs_unregister (&grub_zfs_fs
);