2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc.
2N/A * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * GRUB is free software; you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation; either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/err.h>
2N/A#include <grub/file.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/disk.h>
2N/A#include <grub/partition.h>
2N/A#include <grub/dl.h>
2N/A#include <grub/types.h>
2N/A#include <grub/zfs/zfs.h>
2N/A#include <grub/zfs/zio.h>
2N/A#include <grub/zfs/dnode.h>
2N/A#include <grub/zfs/uberblock_impl.h>
2N/A#include <grub/zfs/vdev_impl.h>
2N/A#include <grub/zfs/zio_checksum.h>
2N/A#include <grub/zfs/zap_impl.h>
2N/A#include <grub/zfs/zap_leaf.h>
2N/A#include <grub/zfs/zfs_znode.h>
2N/A#include <grub/zfs/dmu.h>
2N/A#include <grub/zfs/dmu_objset.h>
2N/A#include <grub/zfs/sa_impl.h>
2N/A#include <grub/zfs/dsl_dir.h>
2N/A#include <grub/zfs/dsl_dataset.h>
2N/A#include <grub/deflate.h>
2N/A#include <grub/crypto.h>
2N/A
2N/AGRUB_MOD_LICENSE ("GPLv3+");
2N/A
2N/A#define ZPOOL_PROP_BOOTFS "bootfs"
2N/A
2N/A#define BOOTFSNAME_SIZE 256
2N/A
2N/A/*
2N/A * For nvlist manipulation. (from nvpair.h)
2N/A */
2N/A#define NV_ENCODE_NATIVE 0
2N/A#define NV_ENCODE_XDR 1
2N/A#define NV_BIG_ENDIAN 0
2N/A#define NV_LITTLE_ENDIAN 1
2N/A#define DATA_TYPE_UINT64 8
2N/A#define DATA_TYPE_STRING 9
2N/A#define DATA_TYPE_NVLIST 19
2N/A#define DATA_TYPE_NVLIST_ARRAY 20
2N/A
2N/A#ifndef GRUB_UTIL
2N/Astatic grub_dl_t my_mod;
2N/A#endif
2N/A
2N/A#define P2PHASE(x, align) ((x) & ((align) - 1))
2N/A
2N/Astatic inline grub_disk_addr_t
2N/ADVA_OFFSET_TO_PHYS_SECTOR (grub_disk_addr_t offset)
2N/A{
2N/A return ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT);
2N/A}
2N/A
2N/A/*
2N/A * FAT ZAP data structures
2N/A */
2N/A#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */
2N/Astatic inline grub_uint64_t
2N/AZAP_HASH_IDX (grub_uint64_t hash, grub_uint64_t n)
2N/A{
2N/A return (((n) == 0) ? 0 : ((hash) >> (64 - (n))));
2N/A}
2N/A
2N/A#define CHAIN_END 0xffff /* end of the chunk chain */
2N/A
2N/A/*
2N/A * The amount of space within the chunk available for the array is:
2N/A * chunk size - space for type (1) - space for next pointer (2)
2N/A */
2N/A#define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3)
2N/A
2N/Astatic inline int
2N/AZAP_LEAF_HASH_SHIFT (int bs)
2N/A{
2N/A return bs - 5;
2N/A}
2N/A
2N/Astatic inline int
2N/AZAP_LEAF_HASH_NUMENTRIES (int bs)
2N/A{
2N/A return 1 << ZAP_LEAF_HASH_SHIFT(bs);
2N/A}
2N/A
2N/Astatic inline grub_size_t
2N/ALEAF_HASH (int bs, grub_uint64_t h, zap_leaf_phys_t *l)
2N/A{
2N/A return ((ZAP_LEAF_HASH_NUMENTRIES (bs)-1)
2N/A & ((h) >> (64 - ZAP_LEAF_HASH_SHIFT (bs) - l->l_hdr.lh_prefix_len)));
2N/A}
2N/A
2N/A/*
2N/A * The amount of space available for chunks is:
2N/A * block size shift - hash entry size (2) * number of hash
2N/A * entries - header space (2*chunksize)
2N/A */
2N/Astatic inline int
2N/AZAP_LEAF_NUMCHUNKS (int bs)
2N/A{
2N/A return (((1 << bs) - 2 * ZAP_LEAF_HASH_NUMENTRIES (bs)) /
2N/A ZAP_LEAF_CHUNKSIZE - 2);
2N/A}
2N/A
2N/A/*
2N/A * The chunks start immediately after the hash table. The end of the
2N/A * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a
2N/A * chunk_t.
2N/A */
2N/Astatic inline zap_leaf_chunk_t *
2N/AZAP_LEAF_CHUNK (zap_leaf_phys_t *l, int bs, int idx)
2N/A{
2N/A return &((zap_leaf_chunk_t *) (l->l_entries
2N/A + (ZAP_LEAF_HASH_NUMENTRIES(bs) * 2)
2N/A / sizeof (grub_properly_aligned_t)))[idx];
2N/A}
2N/A
2N/Astatic inline struct zap_leaf_entry *
2N/AZAP_LEAF_ENTRY(zap_leaf_phys_t *l, int bs, int idx)
2N/A{
2N/A return &ZAP_LEAF_CHUNK(l, bs, idx)->l_entry;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Decompression Entry - lzjb
2N/A */
2N/A
2N/Aextern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t);
2N/A
2N/Atypedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start,
2N/A grub_size_t s_len, grub_size_t d_len);
2N/Atypedef struct decomp_entry
2N/A{
2N/A const char *name;
2N/A zfs_decomp_func_t *decomp_func;
2N/A} decomp_entry_t;
2N/A
2N/A/*
2N/A * Signature for checksum functions.
2N/A */
2N/Atypedef void zio_checksum_t(const void *data, grub_uint64_t size,
2N/A grub_zfs_endian_t endian, zio_cksum_t *zcp);
2N/A
2N/A/*
2N/A * Information about each checksum function.
2N/A */
2N/Atypedef struct zio_checksum_info {
2N/A zio_checksum_t *ci_func; /* checksum function for each byteorder */
2N/A int ci_correctable; /* number of correctable bits */
2N/A int ci_eck; /* uses zio embedded checksum? */
2N/A const char *ci_name; /* descriptive name */
2N/A} zio_checksum_info_t;
2N/A
2N/Atypedef struct dnode_end
2N/A{
2N/A dnode_phys_t dn;
2N/A grub_zfs_endian_t endian;
2N/A} dnode_end_t;
2N/A
2N/Astruct grub_zfs_device_desc
2N/A{
2N/A enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type;
2N/A enum { DEVICE_OK, DEVICE_ERROR } dev_state;
2N/A grub_uint64_t id;
2N/A grub_uint64_t guid;
2N/A
2N/A /* Valid only for non-leafs. */
2N/A unsigned n_children;
2N/A struct grub_zfs_device_desc *children;
2N/A
2N/A /* Valid only for RAIDZ. */
2N/A unsigned nparity;
2N/A
2N/A /* Valid for all */
2N/A unsigned ashift;
2N/A
2N/A /* Valid only for leaf devices. */
2N/A grub_device_t dev;
2N/A grub_disk_addr_t vdev_phys_sector;
2N/A uberblock_t current_uberblock;
2N/A int original;
2N/A};
2N/A
2N/Astruct subvolume
2N/A{
2N/A dnode_end_t mdn;
2N/A grub_uint64_t obj;
2N/A grub_uint64_t case_insensitive;
2N/A grub_size_t nkeys;
2N/A struct
2N/A {
2N/A grub_crypto_cipher_handle_t cipher;
2N/A grub_uint64_t txg;
2N/A grub_uint64_t algo;
2N/A } *keyring;
2N/A};
2N/A
2N/Astruct grub_zfs_data
2N/A{
2N/A int zcached; /* the value should be zero if no cache available */
2N/A
2N/A /* cache for a file block of the currently zfs_open()-ed file */
2N/A char *file_buf;
2N/A grub_uint64_t file_start;
2N/A grub_uint64_t file_end;
2N/A
2N/A /* cache for a dnode block */
2N/A dnode_phys_t *dnode_buf;
2N/A dnode_phys_t *dnode_mdn;
2N/A grub_uint64_t dnode_start;
2N/A grub_uint64_t dnode_end;
2N/A grub_zfs_endian_t dnode_endian;
2N/A
2N/A dnode_end_t mos;
2N/A dnode_end_t dnode;
2N/A struct subvolume subvol;
2N/A
2N/A struct grub_zfs_device_desc *devices_attached;
2N/A unsigned n_devices_attached;
2N/A unsigned n_devices_allocated;
2N/A struct grub_zfs_device_desc *device_original;
2N/A
2N/A uberblock_t current_uberblock;
2N/A
2N/A int mounted;
2N/A int mount_count;
2N/A grub_uint64_t guid;
2N/A char *dev_name;
2N/A};
2N/A
2N/A/* cache list for ZFS mount */
2N/Astruct zfs_mount_cache
2N/A{
2N/A char *zcache_dev_name;
2N/A unsigned long zcache_dev_id;
2N/A struct grub_zfs_data *zcache_zfs_data;
2N/A struct zfs_mount_cache *next;
2N/A} *zfs_mount_cache_list;
2N/A
2N/A/* cache list for disk dev with ZFS uberblock being found */
2N/Astruct zfs_dev_ublock
2N/A{
2N/A char *dev_name;
2N/A unsigned long dev_id;
2N/A grub_uint64_t pool_id;
2N/A grub_disk_addr_t vdev_phys_sector;
2N/A uberblock_t current_uberblock;
2N/A struct zfs_dev_ublock *next;
2N/A} *zfs_dev_ublock_list;
2N/A
2N/A/* cache list for non-zfs disk dev */
2N/Astruct zfs_dev_notzfs
2N/A{
2N/A char *dev_name;
2N/A unsigned long dev_id;
2N/A struct zfs_dev_notzfs *next;
2N/A} *zfs_dev_notzfs_list;
2N/A
2N/Agrub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
2N/A grub_uint64_t algo,
2N/A void *nonce,
2N/A char *buf, grub_size_t size,
2N/A const grub_uint32_t *expected_mac,
2N/A grub_zfs_endian_t endian) = NULL;
2N/Agrub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key,
2N/A grub_size_t keysize,
2N/A grub_uint64_t salt,
2N/A grub_uint64_t algo) = NULL;
2N/A
2N/Astatic grub_err_t
2N/Azlib_decompress (void *s, void *d,
2N/A grub_size_t slen, grub_size_t dlen)
2N/A{
2N/A if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0)
2N/A return grub_errno;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Azle_decompress (void *s, void *d,
2N/A grub_size_t slen, grub_size_t dlen)
2N/A{
2N/A grub_uint8_t *iptr, *optr;
2N/A grub_size_t clen;
2N/A for (iptr = s, optr = d; iptr < (grub_uint8_t *) s + slen
2N/A && optr < (grub_uint8_t *) d + dlen;)
2N/A {
2N/A if (*iptr & 0x80)
2N/A clen = ((*iptr) & 0x7f) + 0x41;
2N/A else
2N/A clen = ((*iptr) & 0x3f) + 1;
2N/A if ((grub_ssize_t) clen > (grub_uint8_t *) d + dlen - optr)
2N/A clen = (grub_uint8_t *) d + dlen - optr;
2N/A if (*iptr & 0x40 || *iptr & 0x80)
2N/A {
2N/A grub_memset (optr, 0, clen);
2N/A iptr++;
2N/A optr += clen;
2N/A continue;
2N/A }
2N/A if ((grub_ssize_t) clen > (grub_uint8_t *) s + slen - iptr - 1)
2N/A clen = (grub_uint8_t *) s + slen - iptr - 1;
2N/A grub_memcpy (optr, iptr + 1, clen);
2N/A optr += clen;
2N/A iptr += clen + 1;
2N/A }
2N/A if (optr < (grub_uint8_t *) d + dlen)
2N/A grub_memset (optr, 0, (grub_uint8_t *) d + dlen - optr);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = {
2N/A {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */
2N/A {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */
2N/A {"off", NULL}, /* ZIO_COMPRESS_OFF */
2N/A {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */
2N/A {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */
2N/A {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */
2N/A {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */
2N/A {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */
2N/A {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */
2N/A {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */
2N/A {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */
2N/A {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */
2N/A {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */
2N/A {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */
2N/A {"zle", zle_decompress}, /* ZIO_COMPRESS_ZLE */
2N/A};
2N/A
2N/Astatic grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian,
2N/A void *buf, struct grub_zfs_data *data);
2N/Astatic grub_err_t zio_read_common (blkptr_t * bp, dva_t *dva,
2N/A grub_zfs_endian_t endian, void *buf,
2N/A struct grub_zfs_data *data);
2N/A
2N/A/*
2N/A * Our own version of log2(). Same thing as highbit()-1.
2N/A */
2N/Astatic int
2N/Azfs_log2 (grub_uint64_t num)
2N/A{
2N/A int i = 0;
2N/A
2N/A while (num > 1)
2N/A {
2N/A i++;
2N/A num = num >> 1;
2N/A }
2N/A
2N/A return (i);
2N/A}
2N/A
2N/A/* Checksum Functions */
2N/Astatic void
2N/Azio_checksum_off (const void *buf __attribute__ ((unused)),
2N/A grub_uint64_t size __attribute__ ((unused)),
2N/A grub_zfs_endian_t endian __attribute__ ((unused)),
2N/A zio_cksum_t * zcp)
2N/A{
2N/A ZIO_SET_CHECKSUM (zcp, 0, 0, 0, 0);
2N/A}
2N/A
2N/A/* Checksum Table and Values */
2N/Astatic zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = {
2N/A {NULL, 0, 0, "inherit"},
2N/A {NULL, 0, 0, "on"},
2N/A {zio_checksum_off, 0, 0, "off"},
2N/A {zio_checksum_SHA256, 1, 1, "label"},
2N/A {zio_checksum_SHA256, 1, 1, "gang_header"},
2N/A {NULL, 0, 0, "zilog"},
2N/A {fletcher_2, 0, 0, "fletcher2"},
2N/A {fletcher_4, 1, 0, "fletcher4"},
2N/A {zio_checksum_SHA256, 1, 0, "SHA256"},
2N/A {NULL, 0, 0, "zilog2"},
2N/A {zio_checksum_SHA256, 1, 0, "SHA256+MAC"},
2N/A};
2N/A
2N/A/*
2N/A * zio_checksum_verify: Provides support for checksum verification.
2N/A *
2N/A * Fletcher2, Fletcher4, and SHA256 are supported.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Azio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum,
2N/A grub_zfs_endian_t endian,
2N/A char *buf, grub_size_t size)
2N/A{
2N/A zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1;
2N/A zio_checksum_info_t *ci = &zio_checksum_table[checksum];
2N/A zio_cksum_t actual_cksum, expected_cksum;
2N/A
2N/A if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL)
2N/A {
2N/A grub_dprintf ("zfs", "unknown checksum function %d\n", checksum);
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "unknown checksum function %d", checksum);
2N/A }
2N/A
2N/A if (ci->ci_eck)
2N/A {
2N/A expected_cksum = zec->zec_cksum;
2N/A zec->zec_cksum = zc;
2N/A ci->ci_func (buf, size, endian, &actual_cksum);
2N/A zec->zec_cksum = expected_cksum;
2N/A zc = expected_cksum;
2N/A }
2N/A else
2N/A ci->ci_func (buf, size, endian, &actual_cksum);
2N/A
2N/A if (grub_memcmp (&actual_cksum, &zc,
2N/A checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0)
2N/A {
2N/A grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name);
2N/A grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n",
2N/A (unsigned long long) actual_cksum.zc_word[0],
2N/A (unsigned long long) actual_cksum.zc_word[1],
2N/A (unsigned long long) actual_cksum.zc_word[2],
2N/A (unsigned long long) actual_cksum.zc_word[3]);
2N/A grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n",
2N/A (unsigned long long) zc.zc_word[0],
2N/A (unsigned long long) zc.zc_word[1],
2N/A (unsigned long long) zc.zc_word[2],
2N/A (unsigned long long) zc.zc_word[3]);
2N/A return grub_error (GRUB_ERR_BAD_FS, "checksum verification failed");
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * vdev_uberblock_compare takes two uberblock structures and returns an integer
2N/A * indicating the more recent of the two.
2N/A * Return Value = 1 if ub2 is more recent
2N/A * Return Value = -1 if ub1 is more recent
2N/A * The most recent uberblock is determined using its transaction number and
2N/A * timestamp. The uberblock with the highest transaction number is
2N/A * considered "newer". If the transaction numbers of the two blocks match, the
2N/A * timestamps are compared to determine the "newer" of the two.
2N/A */
2N/Astatic int
2N/Avdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2)
2N/A{
2N/A grub_zfs_endian_t ub1_endian, ub2_endian;
2N/A if (grub_zfs_to_cpu64 (ub1->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
2N/A == UBERBLOCK_MAGIC)
2N/A ub1_endian = GRUB_ZFS_LITTLE_ENDIAN;
2N/A else
2N/A ub1_endian = GRUB_ZFS_BIG_ENDIAN;
2N/A if (grub_zfs_to_cpu64 (ub2->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
2N/A == UBERBLOCK_MAGIC)
2N/A ub2_endian = GRUB_ZFS_LITTLE_ENDIAN;
2N/A else
2N/A ub2_endian = GRUB_ZFS_BIG_ENDIAN;
2N/A
2N/A if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian)
2N/A < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
2N/A return (1);
2N/A if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian)
2N/A > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian))
2N/A return (-1);
2N/A
2N/A if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian)
2N/A < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
2N/A return (1);
2N/A if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian)
2N/A > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian))
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Three pieces of information are needed to verify an uberblock: the magic
2N/A * number, the version number, and the checksum.
2N/A *
2N/A * Currently Implemented: version number, magic number
2N/A * Need to Implement: checksum
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Auberblock_verify (struct grub_zfs_device_desc *dev_desc, uberblock_t * uber, grub_uint64_t offset)
2N/A{
2N/A grub_err_t err;
2N/A grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN;
2N/A zio_cksum_t zc;
2N/A
2N/A if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN)
2N/A == UBERBLOCK_MAGIC
2N/A && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN) > 0
2N/A && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN)
2N/A <= SPA_VERSION)
2N/A endian = GRUB_ZFS_LITTLE_ENDIAN;
2N/A else if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC
2N/A && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN) > 0
2N/A && grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN)
2N/A <= SPA_VERSION)
2N/A endian = GRUB_ZFS_BIG_ENDIAN;
2N/A
2N/A if (endian == GRUB_ZFS_UNKNOWN_ENDIAN)
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic");
2N/A
2N/A grub_memset (&zc, 0, sizeof (zc));
2N/A
2N/A zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian);
2N/A err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian,
2N/A (char *) uber, VDEV_UBERBLOCK_SIZE(dev_desc));
2N/A
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Find the best uberblock.
2N/A * Return:
2N/A * Success - Pointer to the best uberblock.
2N/A * Failure - NULL
2N/A */
2N/Astatic uberblock_t *
2N/Afind_bestub (struct grub_zfs_device_desc *dev_desc, grub_uint8_t *ub_array,
2N/A grub_disk_addr_t sector)
2N/A{
2N/A uberblock_t *ubbest = NULL;
2N/A grub_uint64_t i;
2N/A grub_disk_addr_t offset;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A
2N/A for (i = 0; i < (grub_uint64_t)VDEV_UBERBLOCK_RING; i += (1ULL << VDEV_UBERBLOCK_SHIFT(dev_desc)))
2N/A {
2N/A offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE + i;
2N/A
2N/A err = uberblock_verify (dev_desc, (uberblock_t *)&ub_array[i], offset);
2N/A if (err)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A if (ubbest == NULL
2N/A || vdev_uberblock_compare ((uberblock_t *)&ub_array[i], ubbest) < 0)
2N/A ubbest = (uberblock_t *)&ub_array[i];
2N/A }
2N/A if (!ubbest)
2N/A grub_errno = err;
2N/A
2N/A return (ubbest);
2N/A}
2N/A
2N/Astatic inline grub_size_t
2N/Aget_psize (blkptr_t * bp, grub_zfs_endian_t endian)
2N/A{
2N/A return ((((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) >> 16) & 0xffff) + 1)
2N/A << SPA_MINBLOCKSHIFT);
2N/A}
2N/A
2N/Astatic grub_uint64_t
2N/Adva_get_offset (const dva_t *dva, grub_zfs_endian_t endian)
2N/A{
2N/A grub_dprintf ("zfs", "dva=%llx, %llx\n",
2N/A (unsigned long long) dva->dva_word[0],
2N/A (unsigned long long) dva->dva_word[1]);
2N/A return grub_zfs_to_cpu64 ((dva)->dva_word[1],
2N/A endian) << SPA_MINBLOCKSHIFT;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Azfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist)
2N/A{
2N/A grub_err_t err;
2N/A
2N/A if (!diskdesc->dev)
2N/A return grub_error (GRUB_ERR_BAD_FS, "member drive unknown");
2N/A
2N/A *nvlist = grub_malloc (VDEV_PHYS_SIZE);
2N/A
2N/A /* Read in the vdev name-value pair list (112K). */
2N/A grub_dprintf("zfs", "read: sector %llx size %x\n", (unsigned long long)
2N/A diskdesc->vdev_phys_sector, VDEV_PHYS_SIZE);
2N/A err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0,
2N/A VDEV_PHYS_SIZE, *nvlist);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "grub_disk_read failed with err = %d\n", err);
2N/A grub_print_error();
2N/A grub_free (*nvlist);
2N/A *nvlist = 0;
2N/A return err;
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * Check if this vdev is online and is in a good state.
2N/A */
2N/Astatic int
2N/Avdev_validate (const char *nv)
2N/A{
2N/A grub_uint64_t ival = 0;
2N/A
2N/A if (grub_zfs_nvlist_lookup_uint64 (nv, ZPOOL_CONFIG_OFFLINE, &ival) && ival)
2N/A {
2N/A grub_dprintf ("zfs", "vdev_validate: ZPOOL_CONFIG_OFFLINE\n");
2N/A return (1);
2N/A }
2N/A if ((grub_zfs_nvlist_lookup_uint64 (nv, ZPOOL_CONFIG_FAULTED, &ival) && ival) &&
2N/A !(grub_zfs_nvlist_lookup_uint64 (nv, ZPOOL_CONFIG_DEGRADED, &ival) && ival))
2N/A {
2N/A grub_dprintf ("zfs", "vdev_validate: ZPOOL_CONFIG_FAULTED\n");
2N/A return (1);
2N/A }
2N/A if (grub_zfs_nvlist_lookup_uint64 (nv, ZPOOL_CONFIG_REMOVED, &ival) && ival)
2N/A {
2N/A grub_dprintf ("zfs", "vdev_validate: ZPOOL_CONFIG_REMOVED\n");
2N/A return (1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Afill_vdev_info_real (struct grub_zfs_data *data,
2N/A const char *nvlist,
2N/A struct grub_zfs_device_desc *fill,
2N/A struct grub_zfs_device_desc *insert,
2N/A int *inserted)
2N/A{
2N/A char *type;
2N/A
2N/A type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE);
2N/A grub_dprintf("zfs", "type = %s\n", type ? : "(null)");
2N/A
2N/A if (!type)
2N/A return grub_errno;
2N/A
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id)))
2N/A {
2N/A grub_free (type);
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
2N/A }
2N/A
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid)))
2N/A {
2N/A grub_free (type);
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
2N/A }
2N/A
2N/A if (grub_strcmp (type, VDEV_TYPE_DISK) == 0
2N/A || grub_strcmp (type, VDEV_TYPE_FILE) == 0)
2N/A {
2N/A grub_uint64_t ashift;
2N/A fill->type = DEVICE_LEAF;
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &ashift))
2N/A {
2N/A fill->ashift = UBERBLOCK_SHIFT;
2N/A }
2N/A else
2N/A fill->ashift = ashift;
2N/A
2N/A grub_dprintf("zfs", "fill->dev=%p fill->guid=%llx insert->guid=%llx insert->dev=%p ashift=%d\n",
2N/A fill->dev, (unsigned long long)fill->guid,
2N/A (unsigned long long)insert->guid, insert->dev, fill->ashift);
2N/A if (!fill->dev && fill->guid == insert->guid)
2N/A {
2N/A fill->dev = insert->dev;
2N/A fill->vdev_phys_sector = insert->vdev_phys_sector;
2N/A fill->current_uberblock = insert->current_uberblock;
2N/A fill->original = insert->original;
2N/A *inserted = 1;
2N/A }
2N/A if (fill->guid == insert->guid && !data->device_original)
2N/A data->device_original = fill;
2N/A
2N/A if (vdev_validate(nvlist))
2N/A fill->dev_state = DEVICE_ERROR;
2N/A else
2N/A fill->dev_state = DEVICE_OK;
2N/A
2N/A grub_free (type);
2N/A
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0
2N/A || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0)
2N/A {
2N/A int nelm, i;
2N/A
2N/A if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0)
2N/A fill->type = DEVICE_MIRROR;
2N/A else
2N/A {
2N/A grub_uint64_t par;
2N/A fill->type = DEVICE_RAIDZ;
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par))
2N/A {
2N/A grub_free (type);
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity");
2N/A }
2N/A fill->nparity = par;
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par))
2N/A {
2N/A grub_free (type);
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz ashift");
2N/A }
2N/A fill->ashift = par;
2N/A }
2N/A
2N/A nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist,
2N/A ZPOOL_CONFIG_CHILDREN);
2N/A
2N/A if (nelm <= 0)
2N/A {
2N/A grub_free (type);
2N/A return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV");
2N/A }
2N/A
2N/A if (!fill->children)
2N/A {
2N/A fill->n_children = nelm;
2N/A
2N/A fill->children = grub_zalloc (fill->n_children
2N/A * sizeof (fill->children[0]));
2N/A }
2N/A
2N/A for (i = 0; i < nelm; i++)
2N/A {
2N/A char *child;
2N/A grub_err_t err;
2N/A
2N/A child = grub_zfs_nvlist_lookup_nvlist_array
2N/A (nvlist, ZPOOL_CONFIG_CHILDREN, i);
2N/A if (! child)
2N/A {
2N/A grub_free(type);
2N/A return grub_errno;
2N/A }
2N/A
2N/A err = fill_vdev_info_real (data, child, &fill->children[i], insert,
2N/A inserted);
2N/A grub_dprintf("zfs", "Child %d: fill returned %d\n", i, err);
2N/A
2N/A grub_free (child);
2N/A
2N/A if (err)
2N/A {
2N/A grub_free (type);
2N/A return err;
2N/A }
2N/A }
2N/A grub_free (type);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported", type);
2N/A grub_free (type);
2N/A return grub_errno;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Afill_vdev_info (struct grub_zfs_data *data,
2N/A char *nvlist, struct grub_zfs_device_desc *diskdesc,
2N/A int *inserted)
2N/A{
2N/A grub_uint64_t id;
2N/A unsigned i;
2N/A
2N/A *inserted = 0;
2N/A if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id)) {
2N/A grub_dprintf("zfs", "nvlist lookup of id failed\n");
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id");
2N/A }
2N/A
2N/A grub_dprintf("zfs", "nvlist lookup of id => 0x%llx\n",
2N/A (unsigned long long)id);
2N/A
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A if (data->devices_attached[i].id == id)
2N/A return fill_vdev_info_real (data, nvlist, &data->devices_attached[i],
2N/A diskdesc, inserted);
2N/A
2N/A data->n_devices_attached++;
2N/A if (data->n_devices_attached > data->n_devices_allocated)
2N/A {
2N/A void *tmp = data->devices_attached;
2N/A unsigned num = 2 * data->n_devices_attached + 1;
2N/A
2N/A data->devices_attached
2N/A = grub_realloc (tmp, num * sizeof (data->devices_attached[0]));
2N/A if (data->devices_attached == tmp)
2N/A {
2N/A data->devices_attached = tmp;
2N/A return grub_errno;
2N/A }
2N/A data->n_devices_allocated = num;
2N/A }
2N/A
2N/A grub_memset (&data->devices_attached[data->n_devices_attached - 1],
2N/A 0, sizeof (data->devices_attached[data->n_devices_attached - 1]));
2N/A
2N/A return fill_vdev_info_real (data, nvlist,
2N/A &data->devices_attached[data->n_devices_attached - 1],
2N/A diskdesc, inserted);
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Azfs_pool_validate(struct grub_zfs_device_desc *diskdesc, grub_uint64_t *poolguid,
2N/A char **ret_nvlist)
2N/A{
2N/A grub_uint64_t pool_state, txg = 0;
2N/A char *nvlist;
2N/A grub_uint64_t version;
2N/A int found;
2N/A grub_err_t err;
2N/A
2N/A err = zfs_fetch_nvlist (diskdesc, &nvlist);
2N/A if (err) {
2N/A grub_dprintf("zfs", "zfs_fetch_nvlist failed\n");
2N/A return err;
2N/A }
2N/A
2N/A grub_dprintf ("zfs", "check 2 passed\n");
2N/A
2N/A found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE,
2N/A &pool_state);
2N/A if (! found)
2N/A {
2N/A grub_free (nvlist);
2N/A if (! grub_errno)
2N/A grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found");
2N/A return grub_errno;
2N/A }
2N/A grub_dprintf ("zfs", "check 3 passed\n");
2N/A
2N/A if (pool_state == POOL_STATE_DESTROYED)
2N/A {
2N/A grub_free (nvlist);
2N/A return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed");
2N/A }
2N/A grub_dprintf ("zfs", "check 4 passed\n");
2N/A
2N/A found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg);
2N/A if (!found)
2N/A {
2N/A grub_free (nvlist);
2N/A if (! grub_errno)
2N/A grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found");
2N/A return grub_errno;
2N/A }
2N/A grub_dprintf ("zfs", "check 6 passed\n");
2N/A
2N/A /* not an active device */
2N/A if (txg == 0)
2N/A {
2N/A grub_free (nvlist);
2N/A return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active");
2N/A }
2N/A grub_dprintf ("zfs", "check 7 passed\n");
2N/A
2N/A found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION,
2N/A &version);
2N/A if (! found)
2N/A {
2N/A grub_free (nvlist);
2N/A if (! grub_errno)
2N/A grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found");
2N/A return grub_errno;
2N/A }
2N/A grub_dprintf ("zfs", "check 8 passed\n");
2N/A
2N/A if (version > SPA_VERSION)
2N/A {
2N/A grub_dprintf("zfs", "SPA_VERSION is too old! (%llu vs. %llu on disk)\n",
2N/A SPA_VERSION, (unsigned long long)version);
2N/A grub_printf (
2N/A"ZFS pool version (%llu) is too new for the GRUB2 ZFS module (maximum version\n"
2N/A"supported is %llu)\n", (unsigned long long)version, SPA_VERSION);
2N/A grub_free (nvlist);
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "ZFS SPA version is later than GRUB2 ZFS "
2N/A "reader code (%llu > %llu)",
2N/A (unsigned long long) version,
2N/A (unsigned long long) SPA_VERSION);
2N/A }
2N/A grub_dprintf ("zfs", "check 9 passed\n");
2N/A
2N/A found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID,
2N/A &(diskdesc->guid));
2N/A if (! found)
2N/A {
2N/A grub_free (nvlist);
2N/A if (! grub_errno)
2N/A grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found");
2N/A return grub_errno;
2N/A }
2N/A
2N/A found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID,
2N/A poolguid);
2N/A if (! found)
2N/A {
2N/A grub_free (nvlist);
2N/A if (! grub_errno)
2N/A grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found");
2N/A return grub_errno;
2N/A }
2N/A
2N/A grub_dprintf ("zfs", "check 10 passed\n");
2N/A if (ret_nvlist == NULL)
2N/A grub_free (nvlist);
2N/A else
2N/A *ret_nvlist = nvlist;
2N/A return (GRUB_ERR_NONE);
2N/A}
2N/A
2N/A/*
2N/A * Check the disk label information and retrieve needed vdev name-value pairs.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Acheck_pool_label (struct grub_zfs_data *data,
2N/A struct grub_zfs_device_desc *diskdesc,
2N/A int *inserted)
2N/A{
2N/A grub_uint64_t poolguid;
2N/A grub_err_t err;
2N/A char *nvlist = NULL;
2N/A
2N/A *inserted = 0;
2N/A
2N/A err = zfs_pool_validate(diskdesc, &poolguid, &nvlist);
2N/A if (err)
2N/A return err;
2N/A
2N/A if (data->mounted && data->guid != poolguid)
2N/A return grub_error (GRUB_ERR_BAD_FS, "another zpool");
2N/A else
2N/A data->guid = poolguid;
2N/A
2N/A {
2N/A char *nv;
2N/A nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
2N/A
2N/A if (!nv)
2N/A {
2N/A grub_free (nvlist);
2N/A grub_dprintf("zfs", "Couldn't find vdev tree\n");
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree");
2N/A }
2N/A err = fill_vdev_info (data, nv, diskdesc, inserted);
2N/A if (err)
2N/A {
2N/A grub_free (nv);
2N/A grub_free (nvlist);
2N/A grub_dprintf("zfs", "fill_vdev_info() failed: %d\n", err);
2N/A return err;
2N/A }
2N/A grub_free (nv);
2N/A }
2N/A grub_dprintf ("zfs", "done filling in vdev info\n");
2N/A
2N/A grub_free (nvlist);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic int
2N/Avdev_is_pool_member(struct grub_zfs_data *data, grub_uint64_t *vdev_guid)
2N/A{
2N/A unsigned int i, j;
2N/A int ret = 0;
2N/A
2N/A for (i = 0; ret == 0 && i < data->n_devices_attached; i++)
2N/A {
2N/Agrub_dprintf("zfs", "vdev_is_pool_member: type = %s\n",
2N/A (data->devices_attached[i].type == DEVICE_LEAF) ? "leaf" :
2N/A ((data->devices_attached[i].type == DEVICE_MIRROR) ? "mirror" : "unknown"));
2N/A
2N/A if (data->devices_attached[i].type == DEVICE_LEAF)
2N/A {
2N/Agrub_dprintf("zfs", "vdev_guid = 0x%llx | device[%d].guid = 0x%llx\n",
2N/A(unsigned long long)*vdev_guid,
2N/Ai,
2N/A(unsigned long long)data->devices_attached[i].guid);
2N/A ret = (*vdev_guid == data->devices_attached[i].guid);
2N/A break;
2N/A }
2N/A else if (data->devices_attached[i].type == DEVICE_MIRROR)
2N/A {
2N/A for (j = 0; j < data->devices_attached[i].n_children; j++)
2N/A {
2N/Agrub_dprintf("zfs", "vdev_guid = 0x%llx | device[%d].c[%d]guid = 0x%llx\n",
2N/A(unsigned long long)*vdev_guid,
2N/Ai, j,
2N/A(unsigned long long)data->devices_attached[i].children[j].guid);
2N/A if (*vdev_guid == data->devices_attached[i].children[j].guid)
2N/A {
2N/A ret = 1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A grub_dprintf("zfs", "vdev_is_pool_member: returning %d\n", ret);
2N/A return (ret);
2N/A}
2N/A
2N/Astatic grub_uint64_t
2N/Azfs_get_poolprop_ashift(struct grub_zfs_device_desc *dev_desc)
2N/A{
2N/A grub_err_t err;
2N/A grub_uint64_t ashift = UBERBLOCK_SHIFT;
2N/A char *nvlist, *vdev_nv;
2N/A
2N/A err = zfs_fetch_nvlist (dev_desc, &nvlist);
2N/A if (!err) {
2N/A vdev_nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE);
2N/A if (vdev_nv) {
2N/A err = grub_zfs_nvlist_lookup_uint64 (vdev_nv, "ashift", &ashift);
2N/A grub_free(vdev_nv);
2N/A }
2N/A grub_free(nvlist);
2N/A grub_dprintf("zfs", "Using ashift = %u\n", (unsigned)ashift);
2N/A } else
2N/A grub_dprintf("zfs", "ashift nvlist lookup failed\n");
2N/A
2N/A return ashift;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Afind_better_vdev(struct grub_zfs_data *data, int *better_found)
2N/A{
2N/A uberblock_t *ub_array;
2N/A
2N/A *better_found = 0;
2N/A
2N/A ub_array = grub_malloc (VDEV_UBERBLOCK_RING);
2N/A if (!ub_array)
2N/A return grub_errno;
2N/A
2N/A auto int hook (const char *name);
2N/A int hook (const char *name)
2N/A {
2N/A int label = 0, vdevnum = VDEV_LABELS;
2N/A uberblock_t *ubbest = NULL;
2N/A grub_device_t dev;
2N/A grub_err_t err;
2N/A struct grub_zfs_device_desc desc;
2N/A struct zfs_dev_notzfs *dev_notzfs;
2N/A struct zfs_dev_ublock *dev_ub;
2N/A grub_uint64_t poolguid;
2N/A char *partname, *partname_new;
2N/A
2N/A if (grub_strncmp(name, "fd", 2) == 0)
2N/A return 0;
2N/A
2N/A /* search cached non-zfs disk */
2N/A for (dev_notzfs = zfs_dev_notzfs_list; dev_notzfs != NULL; dev_notzfs = dev_notzfs->next)
2N/A if (grub_strcmp (name, dev_notzfs->dev_name) == 0)
2N/A {
2N/A grub_dprintf("zfs", "find_better_vdev: found cached non-zfs disk dev: %s\n", dev_notzfs->dev_name);
2N/A return 0;
2N/A }
2N/A
2N/A dev = grub_device_open (name);
2N/A if (!dev)
2N/A return 0;
2N/A
2N/A /* the mirrored root pool should be on other disk(s) */
2N/A if (!dev->disk || dev->disk->id == data->device_original->dev->disk->id)
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A
2N/A /* the partition name for the mirrored root pool should be same */
2N/A partname = grub_partition_get_name(data->device_original->dev->disk->partition);
2N/A partname_new = grub_partition_get_name(dev->disk->partition);
2N/A if (!(((partname == NULL) && (partname_new == NULL)) ||
2N/A (partname && partname_new && grub_strcmp(partname_new, partname) == 0)))
2N/A {
2N/A grub_free (partname);
2N/A grub_free (partname_new);
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A grub_free (partname);
2N/A grub_free (partname_new);
2N/A
2N/A grub_dprintf("zfs", "find_better_vdev: Trying %s\n", name);
2N/A
2N/A desc.dev = dev;
2N/A
2N/A for (dev_ub = zfs_dev_ublock_list; dev_ub != NULL; dev_ub = dev_ub->next)
2N/A if (grub_strcmp (name, dev_ub->dev_name) == 0)
2N/A {
2N/A if (data->guid != dev_ub->pool_id)
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A else
2N/A {
2N/A grub_dprintf("zfs", "find_better_vdev: cached dev_ub for %s\n", dev_ub->dev_name);
2N/A grub_device_close (dev);
2N/A if (vdev_uberblock_compare(&data->current_uberblock,
2N/A &dev_ub->current_uberblock) > 0)
2N/A {
2N/A grub_dprintf("zfs", "Better uberblock found!!!\n");
2N/A *better_found = 1;
2N/A return 1; /* Terminate iteration */
2N/A }
2N/A else
2N/A return 0;
2N/A }
2N/A }
2N/A
2N/A /* Don't check back labels on CDROM. */
2N/A if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
2N/A vdevnum = VDEV_LABELS / 2;
2N/A
2N/A for (label = 0; ubbest == NULL && label < vdevnum; label++)
2N/A {
2N/A desc.vdev_phys_sector
2N/A = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)
2N/A + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT)
2N/A + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk)
2N/A - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT));
2N/A
2N/A /* Read in the uberblock ring (128K). */
2N/A err = grub_disk_read (dev->disk, desc.vdev_phys_sector
2N/A + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
2N/A 0, VDEV_UBERBLOCK_RING, (char *) ub_array);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "find_better_vdev: grub_disk_read() failed\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A grub_dprintf ("zfs", "label ok %d\n", label);
2N/A
2N/A /* Make sure that ashift is filled in desc before calling find_bestub */
2N/A desc.ashift = zfs_get_poolprop_ashift(&desc);
2N/A
2N/A ubbest = find_bestub (&desc, (grub_uint8_t *) ub_array, desc.vdev_phys_sector);
2N/A if (!ubbest)
2N/A {
2N/A grub_dprintf ("zfs", "find_better_vdev: No uberblock found\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A
2N/A grub_memmove (&(desc.current_uberblock), ubbest, sizeof (uberblock_t));
2N/A
2N/A err = zfs_pool_validate (&desc, &poolguid, NULL);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "zfs_pool_validate of dev=`%s' failed: %d\n",
2N/A desc.dev->disk->name, err);
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A
2N/A dev_ub = grub_malloc (sizeof (struct zfs_dev_ublock));
2N/A if (dev_ub)
2N/A {
2N/A dev_ub->dev_name = grub_strdup (name);
2N/A dev_ub->dev_id = dev->disk->id;
2N/A dev_ub->pool_id = poolguid;
2N/A dev_ub->vdev_phys_sector = desc.vdev_phys_sector;
2N/A grub_memmove (&(dev_ub->current_uberblock), ubbest, sizeof (uberblock_t));
2N/A if (zfs_dev_ublock_list)
2N/A dev_ub->next = zfs_dev_ublock_list;
2N/A else
2N/A dev_ub->next = NULL;
2N/A zfs_dev_ublock_list = dev_ub;
2N/A }
2N/A
2N/A if (data->guid != poolguid || !vdev_is_pool_member(data, &desc.guid))
2N/A {
2N/A grub_dprintf("zfs", "pool guid 0x%llx not matched for disk dev %s\n",
2N/A (unsigned long long) poolguid,
2N/A desc.dev->disk->name);
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A
2N/A /*
2N/A * zfs_pool_validate() has the side effect of filling in the VDEV
2N/A * guid for this VDEV, so ensure that this VDEV is a member of the
2N/A * pool.
2N/A */
2N/A if (vdev_uberblock_compare(&data->current_uberblock, ubbest) > 0)
2N/A {
2N/A grub_dprintf("zfs", "Better uberblock found!\n");
2N/A grub_device_close (dev);
2N/A *better_found = 1;
2N/A return 1; /* Terminate iteration */
2N/A }
2N/A else
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A }
2N/A
2N/A dev_notzfs = grub_malloc (sizeof (struct zfs_dev_notzfs));
2N/A if (dev_notzfs)
2N/A {
2N/A dev_notzfs->dev_name = grub_strdup (name);
2N/A dev_notzfs->dev_id = dev->disk->id;
2N/A if (zfs_dev_notzfs_list)
2N/A dev_notzfs->next = zfs_dev_notzfs_list;
2N/A else
2N/A dev_notzfs->next = NULL;
2N/A zfs_dev_notzfs_list = dev_notzfs;
2N/A }
2N/A
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A
2N/A grub_device_iterate (hook);
2N/A grub_free (ub_array);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Ascan_disk (grub_device_t dev, struct grub_zfs_data *data,
2N/A int original, int *inserted)
2N/A{
2N/A int label = 0;
2N/A int better_found = 0;
2N/A uberblock_t *ub_array, *ubbest = NULL;
2N/A grub_err_t err;
2N/A int vdevnum;
2N/A struct grub_zfs_device_desc desc;
2N/A struct zfs_dev_notzfs *dev_notzfs;
2N/A struct zfs_dev_ublock *dev_ub = NULL;
2N/A
2N/A /* search for cached non-zfs dev first */
2N/A for (dev_notzfs = zfs_dev_notzfs_list; dev_notzfs != NULL; dev_notzfs = dev_notzfs->next)
2N/A if (grub_strcmp (data->dev_name, dev_notzfs->dev_name) == 0)
2N/A {
2N/A grub_dprintf("zfs", "scan_disk: found cached non-zfs disk dev: %s\n", dev_notzfs->dev_name);
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid ZFS label");
2N/A }
2N/A ub_array = grub_malloc (VDEV_UBERBLOCK_RING);
2N/A if (!ub_array)
2N/A {
2N/A grub_dprintf("zfs", "ub_array allocation failed.\n");
2N/A return grub_errno;
2N/A }
2N/A
2N/A vdevnum = VDEV_LABELS;
2N/A
2N/A desc.dev = dev;
2N/A desc.original = original;
2N/A
2N/A if (original)
2N/A {
2N/A for (dev_ub = zfs_dev_ublock_list; dev_ub != NULL; dev_ub = dev_ub->next)
2N/A if (grub_strcmp (data->dev_name, dev_ub->dev_name) == 0)
2N/A {
2N/A grub_dprintf("zfs", "scan_disk: found cached dev_ub for %s\n", dev_ub->dev_name);
2N/A desc.vdev_phys_sector = dev_ub->vdev_phys_sector;
2N/A grub_memmove (&(desc.current_uberblock),
2N/A &dev_ub->current_uberblock, sizeof (uberblock_t));
2N/A grub_memmove (&(data->current_uberblock),
2N/A &dev_ub->current_uberblock, sizeof (uberblock_t));
2N/A goto check_label;
2N/A }
2N/A }
2N/A
2N/A /* Don't check back labels on CDROM. */
2N/A if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
2N/A vdevnum = VDEV_LABELS / 2;
2N/A
2N/A for (label = 0; ubbest == NULL && label < vdevnum; label++)
2N/A {
2N/A desc.vdev_phys_sector
2N/A = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)
2N/A + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT)
2N/A + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk)
2N/A - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT));
2N/A
2N/A /* Read in the uberblock ring (128K). */
2N/A err = grub_disk_read (dev->disk, desc.vdev_phys_sector
2N/A + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
2N/A 0, VDEV_UBERBLOCK_RING, (char *) ub_array);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "scan_disk: grub_disk_read() failed\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A grub_dprintf ("zfs", "label ok %d\n", label);
2N/A
2N/A /* Make sure that ashift is filled in desc before calling find_bestub */
2N/A desc.ashift = zfs_get_poolprop_ashift(&desc);
2N/A
2N/A ubbest = find_bestub (&desc, (grub_uint8_t *)ub_array, desc.vdev_phys_sector);
2N/A if (!ubbest)
2N/A {
2N/A grub_dprintf ("zfs", "No uberblock found\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A
2N/A grub_memmove (&(desc.current_uberblock), ubbest, sizeof (uberblock_t));
2N/A if (original)
2N/A grub_memmove (&(data->current_uberblock), ubbest, sizeof (uberblock_t));
2N/A
2N/Acheck_label:
2N/A err = check_pool_label (data, &desc, inserted);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "check_pool_label of dev=`%s' failed: %d\n",
2N/A desc.dev->disk->name, err);
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A
2N/A if (original && !dev_ub)
2N/A {
2N/A dev_ub = grub_malloc (sizeof (struct zfs_dev_ublock));
2N/A if (dev_ub)
2N/A {
2N/A dev_ub->dev_name = grub_strdup (data->dev_name);
2N/A dev_ub->dev_id = dev->disk->id;
2N/A dev_ub->pool_id = data->guid;
2N/A dev_ub->vdev_phys_sector = desc.vdev_phys_sector;
2N/A grub_memmove (&(dev_ub->current_uberblock), ubbest, sizeof (uberblock_t));
2N/A if (zfs_dev_ublock_list)
2N/A dev_ub->next = zfs_dev_ublock_list;
2N/A else
2N/A dev_ub->next = NULL;
2N/A zfs_dev_ublock_list = dev_ub;
2N/A }
2N/A }
2N/A
2N/A if (original && data->n_devices_attached > 0 &&
2N/A data->devices_attached[0].type == DEVICE_MIRROR &&
2N/A find_better_vdev (data, &better_found) == GRUB_ERR_NONE &&
2N/A better_found != 0)
2N/A {
2N/A grub_free(ub_array);
2N/A grub_printf("The %s device is too old and should not be trusted as part of this zpool\n",
2N/A data->device_original->dev->disk->name);
2N/A return grub_error(GRUB_ERR_BAD_FS, "Better VDEV found for this pool");
2N/A }
2N/A
2N/A grub_free (ub_array);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A if (original)
2N/A {
2N/A dev_notzfs = grub_malloc (sizeof (struct zfs_dev_notzfs));
2N/A if (dev_notzfs)
2N/A {
2N/A dev_notzfs->dev_name = grub_strdup (data->dev_name);
2N/A dev_notzfs->dev_id = dev->disk->id;
2N/A if (zfs_dev_notzfs_list)
2N/A dev_notzfs->next = zfs_dev_notzfs_list;
2N/A else
2N/A dev_notzfs->next = NULL;
2N/A zfs_dev_notzfs_list = dev_notzfs;
2N/A }
2N/A }
2N/A
2N/A grub_free (ub_array);
2N/A
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label");
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Ascan_devices (struct grub_zfs_data *data, grub_uint64_t id)
2N/A{
2N/A grub_device_t dev_found = NULL;
2N/A auto int hook (const char *name);
2N/A int hook (const char *name)
2N/A {
2N/A grub_device_t dev;
2N/A grub_err_t err;
2N/A int inserted;
2N/A
2N/A grub_dprintf("zfs", "scan_devices: %s\n", name);
2N/A
2N/A dev = grub_device_open (name);
2N/A if (!dev)
2N/A return 0;
2N/A if (!dev->disk)
2N/A {
2N/A grub_device_close (dev);
2N/A return 0;
2N/A }
2N/A err = scan_disk (dev, data, 0, &inserted);
2N/A if (err == GRUB_ERR_BAD_FS)
2N/A {
2N/A grub_device_close (dev);
2N/A grub_errno = GRUB_ERR_NONE;
2N/A return 0;
2N/A }
2N/A if (err)
2N/A {
2N/A grub_device_close (dev);
2N/A grub_print_error ();
2N/A return 0;
2N/A }
2N/A
2N/A if (!inserted)
2N/A grub_device_close (dev);
2N/A
2N/A dev_found = dev;
2N/A return 1;
2N/A }
2N/A grub_device_iterate (hook);
2N/A if (!dev_found)
2N/A return grub_error (GRUB_ERR_BAD_FS,
2N/A "couldn't find device 0x%" PRIxGRUB_UINT64_T, id);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/* x**y. */
2N/Astatic grub_uint8_t powx[255 * 2];
2N/A/* Such an s that x**s = y */
2N/Astatic int powx_inv[256];
2N/Astatic const grub_uint8_t poly = 0x1d;
2N/A
2N/A/* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */
2N/Astatic inline void
2N/Axor_out (grub_uint8_t *a, const grub_uint8_t *b, grub_size_t s,
2N/A int known_idx, int recovery_pow)
2N/A{
2N/A int add;
2N/A
2N/A /* Simple xor. */
2N/A if (known_idx == 0 || recovery_pow == 0)
2N/A {
2N/A grub_crypto_xor (a, a, b, s);
2N/A return;
2N/A }
2N/A add = (known_idx * recovery_pow) % 255;
2N/A for (;s--; b++, a++)
2N/A if (*b)
2N/A *a ^= powx[powx_inv[*b] + add];
2N/A}
2N/A
2N/Astatic inline grub_uint8_t
2N/Agf_mul (grub_uint8_t a, grub_uint8_t b)
2N/A{
2N/A if (a == 0 || b == 0)
2N/A return 0;
2N/A return powx[powx_inv[a] + powx_inv[b]];
2N/A}
2N/A
2N/Astatic inline grub_err_t
2N/Arecovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs,
2N/A const unsigned *powers,
2N/A const int *idx)
2N/A{
2N/A grub_dprintf ("zfs", "recovering %u buffers\n", nbufs);
2N/A /* Now we have */
2N/A /* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/
2N/A /* Let's invert the matrix in question. */
2N/A switch (nbufs)
2N/A {
2N/A /* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */
2N/A case 1:
2N/A {
2N/A int add;
2N/A grub_uint8_t *a;
2N/A if (powers[0] == 0 || idx[0] == 0)
2N/A return GRUB_ERR_NONE;
2N/A add = 255 - ((powers[0] * idx[0]) % 255);
2N/A for (a = bufs[0]; s--; a++)
2N/A if (*a)
2N/A *a = powx[powx_inv[*a] + add];
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A /* Case 2x2: Let's use the determinant formula. */
2N/A case 2:
2N/A {
2N/A grub_uint8_t det, det_inv;
2N/A grub_uint8_t matrixinv[2][2];
2N/A unsigned i;
2N/A /* The determinant is: */
2N/A det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255]
2N/A ^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]);
2N/A if (det == 0)
2N/A return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
2N/A det_inv = powx[255 - powx_inv[det]];
2N/A matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv);
2N/A matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv);
2N/A matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv);
2N/A matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv);
2N/A for (i = 0; i < s; i++)
2N/A {
2N/A grub_uint8_t b0, b1;
2N/A b0 = bufs[0][i];
2N/A b1 = bufs[1][i];
2N/A
2N/A bufs[0][i] = (gf_mul (b0, matrixinv[0][0])
2N/A ^ gf_mul (b1, matrixinv[0][1]));
2N/A bufs[1][i] = (gf_mul (b0, matrixinv[1][0])
2N/A ^ gf_mul (b1, matrixinv[1][1]));
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A /* Otherwise use Gauss. */
2N/A default:
2N/A {
2N/A grub_uint8_t matrix1[nbufs][nbufs], matrix2[nbufs][nbufs];
2N/A int i, j, k;
2N/A
2N/A for (i = 0; i < nbufs; i++)
2N/A for (j = 0; j < nbufs; j++)
2N/A matrix1[i][j] = powx[(powers[i] * idx[j]) % 255];
2N/A for (i = 0; i < nbufs; i++)
2N/A for (j = 0; j < nbufs; j++)
2N/A matrix2[i][j] = 0;
2N/A for (i = 0; i < nbufs; i++)
2N/A matrix2[i][i] = 1;
2N/A
2N/A for (i = 0; i < nbufs; i++)
2N/A {
2N/A grub_uint8_t mul;
2N/A for (j = i; j < nbufs; j++)
2N/A if (matrix1[i][j])
2N/A break;
2N/A if (j == nbufs)
2N/A return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix");
2N/A if (j != i)
2N/A {
2N/A int xchng;
2N/A xchng = j;
2N/A for (j = 0; j < nbufs; j++)
2N/A {
2N/A grub_uint8_t t;
2N/A t = matrix1[xchng][j];
2N/A matrix1[xchng][j] = matrix1[i][j];
2N/A matrix1[i][j] = t;
2N/A }
2N/A for (j = 0; j < nbufs; j++)
2N/A {
2N/A grub_uint8_t t;
2N/A t = matrix2[xchng][j];
2N/A matrix2[xchng][j] = matrix2[i][j];
2N/A matrix2[i][j] = t;
2N/A }
2N/A }
2N/A mul = powx[255 - powx_inv[matrix1[i][i]]];
2N/A for (j = 0; j < nbufs; j++)
2N/A matrix1[i][j] = gf_mul (matrix1[i][j], mul);
2N/A for (j = 0; j < nbufs; j++)
2N/A matrix2[i][j] = gf_mul (matrix2[i][j], mul);
2N/A for (j = i + 1; j < nbufs; j++)
2N/A {
2N/A mul = matrix1[j][i];
2N/A for (k = 0; k < nbufs; k++)
2N/A matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
2N/A for (k = 0; k < nbufs; k++)
2N/A matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
2N/A }
2N/A }
2N/A for (i = nbufs - 1; i >= 0; i--)
2N/A {
2N/A for (j = 0; j < i; j++)
2N/A {
2N/A grub_uint8_t mul;
2N/A mul = matrix1[j][i];
2N/A for (k = 0; k < nbufs; k++)
2N/A matrix1[j][k] ^= gf_mul (matrix1[i][k], mul);
2N/A for (k = 0; k < nbufs; k++)
2N/A matrix2[j][k] ^= gf_mul (matrix2[i][k], mul);
2N/A }
2N/A }
2N/A
2N/A for (i = 0; i < (int) s; i++)
2N/A {
2N/A grub_uint8_t b[nbufs];
2N/A for (j = 0; j < nbufs; j++)
2N/A b[j] = bufs[j][i];
2N/A for (j = 0; j < nbufs; j++)
2N/A {
2N/A bufs[j][i] = 0;
2N/A for (k = 0; k < nbufs; k++)
2N/A bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]);
2N/A }
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Aread_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc,
2N/A grub_size_t len, void *buf)
2N/A{
2N/A switch (desc->type)
2N/A {
2N/A case DEVICE_LEAF:
2N/A {
2N/A grub_uint64_t sector;
2N/A sector = DVA_OFFSET_TO_PHYS_SECTOR (offset);
2N/A if (!desc->dev)
2N/A {
2N/A return grub_error (GRUB_ERR_BAD_FS, "member drive unknown");
2N/A }
2N/A if (desc->dev_state == DEVICE_ERROR)
2N/A {
2N/A return grub_error (GRUB_ERR_BAD_DEVICE, "dev state is NOT OK.\n");
2N/A }
2N/A /* read in a data block */
2N/A return grub_disk_read (desc->dev->disk, sector, 0, len, buf);
2N/A }
2N/A case DEVICE_MIRROR:
2N/A {
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A unsigned i;
2N/A if (desc->n_children <= 0)
2N/A return grub_error (GRUB_ERR_BAD_FS,
2N/A "non-positive number of mirror children");
2N/A for (i = 0; i < desc->n_children; i++)
2N/A {
2N/A err = read_device (offset, &desc->children[i],
2N/A len, buf);
2N/A if (!err)
2N/A break;
2N/A grub_errno = GRUB_ERR_NONE;
2N/A }
2N/A return (grub_errno = err);
2N/A }
2N/A case DEVICE_RAIDZ:
2N/A {
2N/A unsigned c = 0;
2N/A grub_uint64_t high;
2N/A grub_uint64_t devn;
2N/A grub_uint64_t m;
2N/A grub_uint32_t s, orig_s;
2N/A void *orig_buf = buf;
2N/A grub_size_t orig_len = len;
2N/A grub_uint8_t *recovery_buf[4];
2N/A grub_size_t recovery_len[4];
2N/A int recovery_idx[4];
2N/A unsigned failed_devices = 0;
2N/A int idx, orig_idx;
2N/A
2N/A if (desc->nparity < 1 || desc->nparity > 3)
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "raidz%d is not supported", desc->nparity);
2N/A
2N/A orig_s = (((len + (1 << desc->ashift) - 1) >> desc->ashift)
2N/A + (desc->n_children - desc->nparity) - 1);
2N/A s = orig_s;
2N/A
2N/A high = grub_divmod64 ((offset >> desc->ashift),
2N/A desc->n_children, &m);
2N/A if (desc->nparity == 2)
2N/A c = 2;
2N/A if (desc->nparity == 3)
2N/A c = 3;
2N/A if (((len + (1 << desc->ashift) - 1) >> desc->ashift)
2N/A >= (desc->n_children - desc->nparity))
2N/A idx = (desc->n_children - desc->nparity - 1);
2N/A else
2N/A idx = ((len + (1 << desc->ashift) - 1) >> desc->ashift) - 1;
2N/A orig_idx = idx;
2N/A while (len > 0)
2N/A {
2N/A grub_size_t csize;
2N/A grub_uint32_t bsize;
2N/A grub_err_t err;
2N/A bsize = s / (desc->n_children - desc->nparity);
2N/A
2N/A if (desc->nparity == 1
2N/A && ((offset >> (desc->ashift + 11)) & 1) == c)
2N/A c++;
2N/A
2N/A high = grub_divmod64 ((offset >> desc->ashift) + c,
2N/A desc->n_children, &devn);
2N/A csize = bsize << desc->ashift;
2N/A if (csize > len)
2N/A csize = len;
2N/A
2N/A grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T
2N/A "+%u (%" PRIxGRUB_SIZE ", %" PRIxGRUB_UINT32_T
2N/A ") -> (0x%" PRIxGRUB_UINT64_T ", 0x%"
2N/A PRIxGRUB_UINT64_T ")\n",
2N/A offset >> desc->ashift, c, len, bsize, high,
2N/A devn);
2N/A err = read_device ((high << desc->ashift)
2N/A | (offset & ((1 << desc->ashift) - 1)),
2N/A &desc->children[devn],
2N/A csize, buf);
2N/A if (err && failed_devices < desc->nparity)
2N/A {
2N/A recovery_buf[failed_devices] = buf;
2N/A recovery_len[failed_devices] = csize;
2N/A recovery_idx[failed_devices] = idx;
2N/A failed_devices++;
2N/A grub_errno = err = 0;
2N/A }
2N/A if (err)
2N/A return err;
2N/A
2N/A c++;
2N/A idx--;
2N/A s--;
2N/A buf = (char *) buf + csize;
2N/A len -= csize;
2N/A }
2N/A if (failed_devices)
2N/A {
2N/A unsigned redundancy_pow[4];
2N/A unsigned cur_redundancy_pow = 0;
2N/A unsigned n_redundancy = 0;
2N/A unsigned i, j;
2N/A grub_err_t err;
2N/A
2N/A /* Compute mul. x**s has a period of 255. */
2N/A if (powx[0] == 0)
2N/A {
2N/A grub_uint8_t cur = 1;
2N/A for (i = 0; i < 255; i++)
2N/A {
2N/A powx[i] = cur;
2N/A powx[i + 255] = cur;
2N/A powx_inv[cur] = i;
2N/A if (cur & 0x80)
2N/A cur = (cur << 1) ^ poly;
2N/A else
2N/A cur <<= 1;
2N/A }
2N/A }
2N/A
2N/A /* Read redundancy data. */
2N/A for (n_redundancy = 0, cur_redundancy_pow = 0;
2N/A n_redundancy < failed_devices;
2N/A cur_redundancy_pow++)
2N/A {
2N/A high = grub_divmod64 ((offset >> desc->ashift)
2N/A + cur_redundancy_pow
2N/A + ((desc->nparity == 1)
2N/A && ((offset >> (desc->ashift + 11))
2N/A & 1)),
2N/A desc->n_children, &devn);
2N/A err = read_device ((high << desc->ashift)
2N/A | (offset & ((1 << desc->ashift) - 1)),
2N/A &desc->children[devn],
2N/A recovery_len[n_redundancy],
2N/A recovery_buf[n_redundancy]);
2N/A /* Ignore error if we may still have enough devices. */
2N/A if (err && n_redundancy + desc->nparity - cur_redundancy_pow - 1
2N/A >= failed_devices)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A if (err)
2N/A return err;
2N/A redundancy_pow[n_redundancy] = cur_redundancy_pow;
2N/A n_redundancy++;
2N/A }
2N/A /* Now xor-our the parts we already know. */
2N/A buf = orig_buf;
2N/A len = orig_len;
2N/A s = orig_s;
2N/A idx = orig_idx;
2N/A
2N/A while (len > 0)
2N/A {
2N/A grub_size_t csize;
2N/A csize = ((s / (desc->n_children - desc->nparity))
2N/A << desc->ashift);
2N/A if (csize > len)
2N/A csize = len;
2N/A
2N/A for (j = 0; j < failed_devices; j++)
2N/A if (buf == recovery_buf[j])
2N/A break;
2N/A
2N/A if (j == failed_devices)
2N/A for (j = 0; j < failed_devices; j++)
2N/A xor_out (recovery_buf[j], buf,
2N/A csize < recovery_len[j] ? csize : recovery_len[j],
2N/A idx, redundancy_pow[j]);
2N/A
2N/A s--;
2N/A buf = (char *) buf + csize;
2N/A len -= csize;
2N/A idx--;
2N/A }
2N/A for (i = 0; i < failed_devices
2N/A && recovery_len[i] == recovery_len[0];
2N/A i++);
2N/A /* Since the chunks have variable length handle the last block
2N/A separately. */
2N/A if (i != failed_devices)
2N/A {
2N/A grub_uint8_t *tmp_recovery_buf[4];
2N/A for (j = 0; j < i; j++)
2N/A tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[failed_devices - 1];
2N/A err = recovery (tmp_recovery_buf, recovery_len[0] - recovery_len[failed_devices - 1], i, redundancy_pow,
2N/A recovery_idx);
2N/A if (err)
2N/A return err;
2N/A }
2N/A err = recovery (recovery_buf, recovery_len[failed_devices - 1],
2N/A failed_devices, redundancy_pow, recovery_idx);
2N/A if (err)
2N/A return err;
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A return grub_error (GRUB_ERR_BAD_FS, "unsupported device type");
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Aread_dva (const dva_t *dva,
2N/A grub_zfs_endian_t endian, struct grub_zfs_data *data,
2N/A void *buf, grub_size_t len)
2N/A{
2N/A grub_uint64_t offset;
2N/A unsigned i;
2N/A grub_err_t err = 0;
2N/A int try;
2N/A offset = dva_get_offset (dva, endian);
2N/A
2N/A for (try = 0; try < 2; try++)
2N/A {
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A if (data->devices_attached[i].id == DVA_GET_VDEV (dva))
2N/A return read_device (offset, &data->devices_attached[i],
2N/A len, buf);
2N/A if (try == 1)
2N/A break;
2N/A err = scan_devices (data, DVA_GET_VDEV (dva));
2N/A if (err)
2N/A return err;
2N/A }
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Read a block of data based on the gang block address dva,
2N/A * and put its data in buf.
2N/A */
2N/Astatic grub_err_t
2N/Azio_read_gang_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A int i;
2N/A
2N/A /* pick a good dva from the block pointer */
2N/A for (i = 0; i < BP_GET_NDVAS(bp); i++)
2N/A {
2N/A if (zio_read_common(bp, &bp->blk_dva[i], endian, buf, data) == GRUB_ERR_NONE)
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA");
2N/A}
2N/A
2N/A/*
2N/A * Read gang block header, verify its checksum, loop through all gang blocks
2N/A * to collect its data based on the gang block address dva and put it in buf.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Azio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A zio_gbh_phys_t *zio_gb;
2N/A unsigned i;
2N/A grub_err_t err;
2N/A zio_cksum_t zc;
2N/A
2N/A grub_memset (&zc, 0, sizeof (zc));
2N/A
2N/A zio_gb = grub_malloc (SPA_GANGBLOCKSIZE);
2N/A if (!zio_gb)
2N/A return grub_errno;
2N/A grub_dprintf ("zfs", endian == GRUB_ZFS_LITTLE_ENDIAN ? "little-endian gang\n"
2N/A :"big-endian gang\n");
2N/A
2N/A err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE);
2N/A if (err)
2N/A {
2N/A grub_free (zio_gb);
2N/A return err;
2N/A }
2N/A
2N/A /* XXX */
2N/A /* self checksuming the gang block header */
2N/A ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva),
2N/A dva_get_offset (dva, endian), bp->blk_birth, 0);
2N/A err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, endian,
2N/A (char *) zio_gb, SPA_GANGBLOCKSIZE);
2N/A if (err)
2N/A {
2N/A grub_free (zio_gb);
2N/A return err;
2N/A }
2N/A
2N/A endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
2N/A
2N/A for (i = 0; i < SPA_GBH_NBLKPTRS; i++)
2N/A {
2N/A if (zio_gb->zg_blkptr[i].blk_birth == 0)
2N/A continue;
2N/A
2N/A err = zio_read_gang_data (&zio_gb->zg_blkptr[i], endian, buf, data);
2N/A if (err)
2N/A {
2N/A grub_free (zio_gb);
2N/A return err;
2N/A }
2N/A buf = (char *) buf + get_psize (&zio_gb->zg_blkptr[i], endian);
2N/A }
2N/A grub_free (zio_gb);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * Read in a block of raw data to buf.
2N/A */
2N/Astatic grub_err_t
2N/Azio_read_common (blkptr_t * bp, dva_t *dva, grub_zfs_endian_t endian,
2N/A void *buf, struct grub_zfs_data *data)
2N/A{
2N/A int psize;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A
2N/A psize = get_psize (bp, endian);
2N/A
2N/A if (dva->dva_word[0] == 0 && dva->dva_word[1] == 0)
2N/A return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA");
2N/A
2N/A if ((grub_zfs_to_cpu64 (dva->dva_word[1], endian)>>63) & 1)
2N/A err = zio_read_gang (bp, endian, dva, buf, data);
2N/A else
2N/A err = read_dva (dva, endian, data, buf, psize);
2N/A
2N/A return err;
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Loop through DVAs to read in a block of raw data to buf and verify
2N/A * the checksum.
2N/A */
2N/Astatic grub_err_t
2N/Azio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A int i, psize;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A zio_cksum_t zc = bp->blk_cksum;
2N/A grub_uint32_t checksum;
2N/A
2N/A psize = get_psize (bp, endian);
2N/A checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
2N/A
2N/A /* pick a good dva from the block pointer */
2N/A for (i = 0; i < BP_GET_NDVAS(bp); i++)
2N/A {
2N/A if (zio_read_common(bp, &bp->blk_dva[i], endian, buf, data) != GRUB_ERR_NONE)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A err = zio_checksum_verify (zc, checksum, endian, buf, psize);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "incorrect checksum\n");
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A /* if no errors, return from here */
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A err = grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA");
2N/A grub_errno = err;
2N/A
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Read in a block of data, decompress if needed,
2N/A * and put the uncompressed data in buf.
2N/A */
2N/Astatic grub_err_t
2N/Azio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf,
2N/A grub_size_t *size, struct grub_zfs_data *data)
2N/A{
2N/A grub_size_t lsize, psize;
2N/A unsigned int comp, encrypted;
2N/A char *compbuf = NULL;
2N/A grub_err_t err;
2N/A zio_cksum_t zc = bp->blk_cksum;
2N/A grub_uint32_t checksum;
2N/A
2N/A *buf = NULL;
2N/A
2N/A checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff;
2N/A comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff;
2N/A encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3);
2N/A lsize = (BP_IS_HOLE(bp) ? 0 :
2N/A (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1)
2N/A << SPA_MINBLOCKSHIFT));
2N/A psize = get_psize (bp, endian);
2N/A
2N/A if (size)
2N/A *size = lsize;
2N/A
2N/A if (comp >= ZIO_COMPRESS_FUNCTIONS)
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "compression algorithm %u not supported\n", (unsigned int) comp);
2N/A
2N/A if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL)
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "compression algorithm %s not supported\n", decomp_table[comp].name);
2N/A
2N/A if (comp != ZIO_COMPRESS_OFF)
2N/A {
2N/A /* It's not really necessary to align to 16, just for safety. */
2N/A compbuf = grub_malloc (ALIGN_UP (psize, 16));
2N/A if (! compbuf)
2N/A return grub_errno;
2N/A }
2N/A else
2N/A {
2N/A compbuf = *buf = grub_malloc (lsize);
2N/A if (! compbuf)
2N/A return grub_errno;
2N/A }
2N/A
2N/A grub_dprintf ("zfs", "endian = %d\n", endian);
2N/A err = zio_read_data (bp, endian, compbuf, data);
2N/A if (err)
2N/A {
2N/A grub_free (compbuf);
2N/A *buf = NULL;
2N/A return err;
2N/A }
2N/A grub_memset (compbuf, 0, ALIGN_UP (psize, 16) - psize);
2N/A
2N/A if (encrypted)
2N/A {
2N/A if (!grub_zfs_decrypt)
2N/A err = grub_error (GRUB_ERR_BAD_FS, "zfscrypt module not loaded");
2N/A else
2N/A {
2N/A unsigned i, besti = 0;
2N/A grub_uint64_t bestval = 0;
2N/A for (i = 0; i < data->subvol.nkeys; i++)
2N/A if (data->subvol.keyring[i].txg <= grub_zfs_to_cpu64 (bp->blk_birth,
2N/A endian)
2N/A && data->subvol.keyring[i].txg > bestval)
2N/A {
2N/A besti = i;
2N/A bestval = data->subvol.keyring[i].txg;
2N/A }
2N/A if (bestval == 0)
2N/A {
2N/A grub_free (compbuf);
2N/A *buf = NULL;
2N/A grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n",
2N/A grub_zfs_to_cpu64 (bp->blk_birth,
2N/A endian));
2N/A return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain");
2N/A }
2N/A grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T
2N/A ", %p) for txg %" PRIxGRUB_UINT64_T "\n",
2N/A besti, data->subvol.keyring[besti].txg,
2N/A data->subvol.keyring[besti].cipher,
2N/A grub_zfs_to_cpu64 (bp->blk_birth,
2N/A endian));
2N/A err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher,
2N/A data->subvol.keyring[besti].algo,
2N/A &(bp)->blk_dva[encrypted],
2N/A compbuf, psize, ((grub_uint32_t *) &zc + 5),
2N/A endian);
2N/A }
2N/A if (err)
2N/A {
2N/A grub_free (compbuf);
2N/A *buf = NULL;
2N/A return err;
2N/A }
2N/A }
2N/A
2N/A if (comp != ZIO_COMPRESS_OFF)
2N/A {
2N/A *buf = grub_malloc (lsize);
2N/A if (!*buf)
2N/A {
2N/A grub_free (compbuf);
2N/A return grub_errno;
2N/A }
2N/A
2N/A err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize);
2N/A grub_free (compbuf);
2N/A if (err)
2N/A {
2N/A grub_free (*buf);
2N/A *buf = NULL;
2N/A return err;
2N/A }
2N/A }
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * Get the block from a block id.
2N/A * push the block onto the stack.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Admu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf,
2N/A grub_zfs_endian_t *endian_out, struct grub_zfs_data *data)
2N/A{
2N/A int level;
2N/A grub_off_t idx;
2N/A blkptr_t *bp_array = dn->dn.dn_blkptr;
2N/A int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT;
2N/A blkptr_t *bp;
2N/A void *tmpbuf = 0;
2N/A grub_zfs_endian_t endian;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A
2N/A endian = dn->endian;
2N/A for (level = dn->dn.dn_nlevels - 1; level >= 0; level--)
2N/A {
2N/A grub_dprintf ("zfs", "endian = %d\n", endian);
2N/A idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1);
2N/A bp = &bp_array[idx];
2N/A if (BP_IS_HOLE (bp))
2N/A {
2N/A grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec,
2N/A dn->endian)
2N/A << SPA_MINBLOCKSHIFT;
2N/A *buf = grub_malloc (size);
2N/A if (*buf == NULL)
2N/A {
2N/A err = grub_errno;
2N/A break;
2N/A }
2N/A grub_memset (*buf, 0, size);
2N/A endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
2N/A break;
2N/A }
2N/A if (level == 0)
2N/A {
2N/A grub_dprintf ("zfs", "endian = %d\n", endian);
2N/A err = zio_read (bp, endian, buf, 0, data);
2N/A endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
2N/A break;
2N/A }
2N/A grub_dprintf ("zfs", "endian = %d\n", endian);
2N/A err = zio_read (bp, endian, &tmpbuf, 0, data);
2N/A endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1;
2N/A if (err)
2N/A break;
2N/A
2N/A if (bp_array != dn->dn.dn_blkptr)
2N/A grub_free (bp_array);
2N/A bp_array = tmpbuf;
2N/A }
2N/A if (bp_array != dn->dn.dn_blkptr)
2N/A grub_free (bp_array);
2N/A if (endian_out)
2N/A *endian_out = endian;
2N/A
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * mzap_lookup: Looks up property described by "name" and returns the value
2N/A * in "value".
2N/A */
2N/Astatic grub_err_t
2N/Amzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
2N/A int objsize, const char *name, grub_uint64_t * value,
2N/A int case_insensitive)
2N/A{
2N/A int i, chunks;
2N/A mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
2N/A
2N/A chunks = objsize / MZAP_ENT_LEN - 1;
2N/A for (i = 0; i < chunks; i++)
2N/A {
2N/A if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0)
2N/A : (grub_strcmp (mzap_ent[i].mze_name, name) == 0))
2N/A {
2N/A *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_FILE_NOT_FOUND, "mzap_lookup: couldn't find %s", name);
2N/A}
2N/A
2N/A/*
2N/A * mzap_value_search: Looks up value and returns property name.
2N/A */
2N/Astatic grub_err_t
2N/Amzap_value_search (mzap_phys_t *zapobj, grub_zfs_endian_t endian,
2N/A int objsize, char *name, grub_uint64_t *value)
2N/A{
2N/A int i, chunks;
2N/A mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
2N/A grub_uint64_t mze_val;
2N/A
2N/A chunks = objsize / MZAP_ENT_LEN - 1;
2N/A for (i = 0; i < chunks; i++)
2N/A {
2N/A mze_val = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian);
2N/A if (mze_val == *value)
2N/A {
2N/A grub_memcpy (name, mzap_ent[i].mze_name, grub_strlen
2N/A (mzap_ent[i].mze_name) + 1);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_FILE_NOT_FOUND,
2N/A "mzap_value_search: couldn't find %s", value);
2N/A}
2N/A
2N/Astatic int
2N/Amzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize,
2N/A int NESTED_FUNC_ATTR (*hook) (const char *name,
2N/A grub_uint64_t val))
2N/A{
2N/A int i, chunks;
2N/A mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
2N/A
2N/A chunks = objsize / MZAP_ENT_LEN - 1;
2N/A for (i = 0; i < chunks; i++)
2N/A {
2N/A grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n",
2N/A mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value,
2N/A (int)mzap_ent[i].mze_cd);
2N/A if (hook (mzap_ent[i].mze_name,
2N/A grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian)))
2N/A return 1;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astatic grub_uint64_t
2N/Azap_hash (grub_uint64_t salt, const char *name,
2N/A int case_insensitive)
2N/A{
2N/A static grub_uint64_t table[256];
2N/A const grub_uint8_t *cp;
2N/A grub_uint8_t c;
2N/A grub_uint64_t crc = salt;
2N/A
2N/A if (table[128] == 0)
2N/A {
2N/A grub_uint64_t *ct;
2N/A int i, j;
2N/A for (i = 0; i < 256; i++)
2N/A {
2N/A for (ct = table + i, *ct = i, j = 8; j > 0; j--)
2N/A *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY);
2N/A }
2N/A }
2N/A
2N/A if (case_insensitive)
2N/A for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++)
2N/A crc = (crc >> 8) ^ table[(crc ^ grub_toupper (c)) & 0xFF];
2N/A else
2N/A for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++)
2N/A crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF];
2N/A
2N/A /*
2N/A * Only use 28 bits, since we need 4 bits in the cookie for the
2N/A * collision differentiator. We MUST use the high bits, since
2N/A * those are the onces that we first pay attention to when
2N/A * chosing the bucket.
2N/A */
2N/A crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1);
2N/A
2N/A return (crc);
2N/A}
2N/A
2N/A/*
2N/A * Only to be used on 8-bit arrays.
2N/A * array_len is actual len in bytes (not encoded le_value_length).
2N/A * buf is null-terminated.
2N/A */
2N/A
2N/Astatic inline int
2N/Aname_cmp (const char *s1, const char *s2, grub_size_t n,
2N/A int case_insensitive)
2N/A{
2N/A const char *t1 = (const char *) s1;
2N/A const char *t2 = (const char *) s2;
2N/A
2N/A if (!case_insensitive)
2N/A return grub_memcmp (t1, t2, n);
2N/A
2N/A while (n--)
2N/A {
2N/A if (grub_toupper (*t1) != grub_toupper (*t2))
2N/A return (int) grub_toupper (*t1) - (int) grub_toupper (*t2);
2N/A
2N/A t1++;
2N/A t2++;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/* XXX */
2N/Astatic int
2N/Azap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
2N/A int blksft, int chunk, grub_size_t array_len,
2N/A const char *buf, int case_insensitive)
2N/A{
2N/A grub_size_t bseen = 0;
2N/A
2N/A while (bseen < array_len)
2N/A {
2N/A struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array;
2N/A grub_size_t toread = array_len - bseen;
2N/A
2N/A if (toread > ZAP_LEAF_ARRAY_BYTES)
2N/A toread = ZAP_LEAF_ARRAY_BYTES;
2N/A
2N/A if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
2N/A return 0;
2N/A
2N/A if (name_cmp ((char *) la->la_array, buf + bseen, toread,
2N/A case_insensitive) != 0)
2N/A break;
2N/A chunk = grub_zfs_to_cpu16 (la->la_next, endian);
2N/A bseen += toread;
2N/A }
2N/A return (bseen == array_len);
2N/A}
2N/A
2N/A/* XXX */
2N/Astatic grub_err_t
2N/Azap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft,
2N/A int chunk, grub_size_t array_len, char *buf)
2N/A{
2N/A grub_size_t bseen = 0;
2N/A
2N/A while (bseen < array_len)
2N/A {
2N/A struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array;
2N/A grub_size_t toread = array_len - bseen;
2N/A
2N/A if (toread > ZAP_LEAF_ARRAY_BYTES)
2N/A toread = ZAP_LEAF_ARRAY_BYTES;
2N/A
2N/A if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
2N/A /* Don't use grub_error because this error is to be ignored. */
2N/A return GRUB_ERR_BAD_FS;
2N/A
2N/A grub_memcpy (buf + bseen,la->la_array, toread);
2N/A chunk = grub_zfs_to_cpu16 (la->la_next, endian);
2N/A bseen += toread;
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the
2N/A * value for the property "name".
2N/A *
2N/A */
2N/A/* XXX */
2N/Astatic grub_err_t
2N/Azap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian,
2N/A int blksft, grub_uint64_t h,
2N/A const char *name, grub_uint64_t * value,
2N/A int case_insensitive)
2N/A{
2N/A grub_uint16_t chunk;
2N/A struct zap_leaf_entry *le;
2N/A
2N/A /* Verify if this is a valid leaf block */
2N/A if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid leaf type");
2N/A if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic");
2N/A
2N/A for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h, l)], endian);
2N/A chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian))
2N/A {
2N/A
2N/A if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft))
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid chunk number");
2N/A
2N/A le = ZAP_LEAF_ENTRY (l, blksft, chunk);
2N/A
2N/A /* Verify the chunk entry */
2N/A if (le->le_type != ZAP_CHUNK_ENTRY)
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid chunk entry");
2N/A
2N/A if (grub_zfs_to_cpu64 (le->le_hash,endian) != h)
2N/A continue;
2N/A
2N/A grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length);
2N/A
2N/A if (zap_leaf_array_equal (l, endian, blksft,
2N/A grub_zfs_to_cpu16 (le->le_name_chunk,endian),
2N/A grub_zfs_to_cpu16 (le->le_name_length, endian),
2N/A name, case_insensitive))
2N/A {
2N/A struct zap_leaf_array *la;
2N/A
2N/A if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length,
2N/A endian) != 1)
2N/A return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry");
2N/A
2N/A /* get the uint64_t property value */
2N/A la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk)->l_array;
2N/A
2N/A *value = grub_be_to_cpu64 (la->la_array64);
2N/A
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_FILE_NOT_FOUND, "couldn't find %s", name);
2N/A}
2N/A
2N/A
2N/A/* Verify if this is a fat zap header block */
2N/Astatic grub_err_t
2N/Azap_verify (zap_phys_t *zap, grub_zfs_endian_t endian)
2N/A{
2N/A if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC)
2N/A return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic");
2N/A
2N/A if (zap->zap_salt == 0)
2N/A return grub_error (GRUB_ERR_BAD_FS, "bad ZAP salt");
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * Fat ZAP lookup
2N/A *
2N/A */
2N/A/* XXX */
2N/Astatic grub_err_t
2N/Afzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
2N/A const char *name, grub_uint64_t * value,
2N/A struct grub_zfs_data *data, int case_insensitive)
2N/A{
2N/A void *l;
2N/A grub_uint64_t hash, idx, blkid;
2N/A int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
2N/A zap_dnode->endian) << DNODE_SHIFT);
2N/A grub_err_t err;
2N/A grub_zfs_endian_t leafendian;
2N/A
2N/A err = zap_verify (zap, zap_dnode->endian);
2N/A if (err)
2N/A return err;
2N/A
2N/A hash = zap_hash (zap->zap_salt, name, case_insensitive);
2N/A
2N/A /* get block id from index */
2N/A if (zap->zap_ptrtbl.zt_numblks != 0)
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "external pointer tables not supported");
2N/A idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift);
2N/A blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian);
2N/A
2N/A /* Get the leaf block */
2N/A if ((1U << blksft) < sizeof (zap_leaf_phys_t))
2N/A return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
2N/A err = dmu_read (zap_dnode, blkid, &l, &leafendian, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value,
2N/A case_insensitive);
2N/A grub_free (l);
2N/A return err;
2N/A}
2N/A
2N/A/* XXX */
2N/Astatic int
2N/Afzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
2N/A grub_size_t name_elem_length,
2N/A int NESTED_FUNC_ATTR (*hook) (const void *name,
2N/A grub_size_t name_length,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize),
2N/A struct grub_zfs_data *data)
2N/A{
2N/A zap_leaf_phys_t *l;
2N/A void *l_in;
2N/A grub_uint64_t idx, idx2, blkid;
2N/A grub_uint16_t chunk;
2N/A int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
2N/A zap_dnode->endian) << DNODE_SHIFT);
2N/A grub_err_t err;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A if (zap_verify (zap, zap_dnode->endian))
2N/A return 0;
2N/A
2N/A /* get block id from index */
2N/A if (zap->zap_ptrtbl.zt_numblks != 0)
2N/A {
2N/A grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "external pointer tables not supported");
2N/A return 0;
2N/A }
2N/A /* Get the leaf block */
2N/A if ((1U << blksft) < sizeof (zap_leaf_phys_t))
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small");
2N/A return 0;
2N/A }
2N/A for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++)
2N/A {
2N/A blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))],
2N/A zap_dnode->endian);
2N/A
2N/A for (idx2 = 0; idx2 < idx; idx2++)
2N/A if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))],
2N/A zap_dnode->endian))
2N/A break;
2N/A if (idx2 != idx)
2N/A continue;
2N/A
2N/A err = dmu_read (zap_dnode, blkid, &l_in, &endian, data);
2N/A l = l_in;
2N/A if (err)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A continue;
2N/A }
2N/A
2N/A /* Verify if this is a valid leaf block */
2N/A if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF)
2N/A {
2N/A grub_free (l);
2N/A continue;
2N/A }
2N/A if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC)
2N/A {
2N/A grub_free (l);
2N/A continue;
2N/A }
2N/A
2N/A for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS (blksft); chunk++)
2N/A {
2N/A char *buf;
2N/A struct zap_leaf_entry *le;
2N/A char *val;
2N/A grub_size_t val_length;
2N/A le = ZAP_LEAF_ENTRY (l, blksft, chunk);
2N/A
2N/A /* Verify the chunk entry */
2N/A if (le->le_type != ZAP_CHUNK_ENTRY)
2N/A continue;
2N/A
2N/A buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian)
2N/A * name_elem_length + 1);
2N/A if (zap_leaf_array_get (l, endian, blksft,
2N/A grub_zfs_to_cpu16 (le->le_name_chunk,
2N/A endian),
2N/A grub_zfs_to_cpu16 (le->le_name_length,
2N/A endian)
2N/A * name_elem_length, buf))
2N/A {
2N/A grub_free (buf);
2N/A continue;
2N/A }
2N/A buf[le->le_name_length * name_elem_length] = 0;
2N/A
2N/A val_length = ((int) le->le_value_length
2N/A * (int) le->le_int_size);
2N/A val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian));
2N/A if (zap_leaf_array_get (l, endian, blksft,
2N/A grub_zfs_to_cpu16 (le->le_value_chunk,
2N/A endian),
2N/A val_length, val))
2N/A {
2N/A grub_free (buf);
2N/A grub_free (val);
2N/A continue;
2N/A }
2N/A
2N/A if (hook (buf, le->le_name_length,
2N/A val, le->le_value_length, le->le_int_size))
2N/A {
2N/A grub_free (l);
2N/A return 1;
2N/A }
2N/A grub_free (buf);
2N/A grub_free (val);
2N/A }
2N/A grub_free (l);
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Read in the data of a zap object and find the value for a matching
2N/A * property name.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Azap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val,
2N/A struct grub_zfs_data *data, int case_insensitive)
2N/A{
2N/A grub_uint64_t block_type;
2N/A int size;
2N/A void *zapbuf;
2N/A grub_err_t err;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A grub_dprintf ("zfs", "looking for '%s'\n", name);
2N/A
2N/A /* Read in the first block of the zap object data. */
2N/A size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
2N/A zap_dnode->endian) << SPA_MINBLOCKSHIFT;
2N/A err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2N/A if (err)
2N/A return err;
2N/A block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2N/A
2N/A grub_dprintf ("zfs", "zap read\n");
2N/A
2N/A if (block_type == ZBT_MICRO)
2N/A {
2N/A grub_dprintf ("zfs", "micro zap\n");
2N/A err = mzap_lookup (zapbuf, endian, size, name, val,
2N/A case_insensitive);
2N/A grub_dprintf ("zfs", "returned %d\n", err);
2N/A grub_free (zapbuf);
2N/A return err;
2N/A }
2N/A else if (block_type == ZBT_HEADER)
2N/A {
2N/A grub_dprintf ("zfs", "fat zap\n");
2N/A /* this is a fat zap */
2N/A err = fzap_lookup (zap_dnode, zapbuf, name, val, data,
2N/A case_insensitive);
2N/A grub_dprintf ("zfs", "returned %d\n", err);
2N/A grub_free (zapbuf);
2N/A return err;
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2N/A}
2N/A
2N/A/*
2N/A * Read in the data of a zap object and find the property name for a
2N/A * matching value.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Azap_value_search (dnode_end_t *zap_dnode, char *name, grub_uint64_t *val,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t block_type;
2N/A int size;
2N/A void *zapbuf;
2N/A grub_err_t err;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A grub_dprintf ("zfs", "zap_value_search: looking for '%lld'\n", (unsigned long long)*val);
2N/A
2N/A /* Read in the first block of the zap object data. */
2N/A size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec,
2N/A zap_dnode->endian) << SPA_MINBLOCKSHIFT;
2N/A err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2N/A if (err)
2N/A return err;
2N/A block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2N/A
2N/A grub_dprintf ("zfs", "zap_value_search: zap read\n");
2N/A
2N/A if (block_type == ZBT_MICRO)
2N/A {
2N/A grub_dprintf ("zfs", "zap_value_search: micro zap value search\n");
2N/A err = (mzap_value_search (zapbuf, endian, size, name, val));
2N/A grub_dprintf ("zfs", "zap_value_search: returned %d\n", err);
2N/A grub_free (zapbuf);
2N/A return err;
2N/A }
2N/A else if (block_type == ZBT_HEADER)
2N/A {
2N/A /* this is a fat zap */
2N/A grub_dprintf ("zfs", "fat zap value search not supported\n");
2N/A grub_free (zapbuf);
2N/A return grub_error (GRUB_ERR_BAD_FS, "fat zap value search not supported");
2N/A }
2N/A
2N/A return grub_error (GRUB_ERR_BAD_FS, "zap_value_search: unknown ZAP type");
2N/A}
2N/A
2N/Astatic int
2N/Azap_iterate_u64 (dnode_end_t * zap_dnode,
2N/A int NESTED_FUNC_ATTR (*hook) (const char *name,
2N/A grub_uint64_t val),
2N/A struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t block_type;
2N/A int size;
2N/A void *zapbuf;
2N/A grub_err_t err;
2N/A int ret;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A auto int NESTED_FUNC_ATTR transform (const void *name,
2N/A grub_size_t namelen,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize);
2N/A
2N/A int NESTED_FUNC_ATTR transform (const void *name,
2N/A grub_size_t namelen __attribute__ ((unused)),
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize)
2N/A {
2N/A if (elemsize != sizeof (grub_uint64_t) || nelem != 1)
2N/A return 0;
2N/A return hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in));
2N/A }
2N/A
2N/A /* Read in the first block of the zap object data. */
2N/A size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
2N/A err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2N/A if (err)
2N/A return 0;
2N/A block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2N/A
2N/A grub_dprintf ("zfs", "zap iterate\n");
2N/A
2N/A if (block_type == ZBT_MICRO)
2N/A {
2N/A grub_dprintf ("zfs", "micro zap\n");
2N/A ret = mzap_iterate (zapbuf, endian, size, hook);
2N/A grub_free (zapbuf);
2N/A return ret;
2N/A }
2N/A else if (block_type == ZBT_HEADER)
2N/A {
2N/A grub_dprintf ("zfs", "fat zap\n");
2N/A /* this is a fat zap */
2N/A ret = fzap_iterate (zap_dnode, zapbuf, 1, transform, data);
2N/A grub_free (zapbuf);
2N/A return ret;
2N/A }
2N/A grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2N/A return 0;
2N/A}
2N/A
2N/Astatic int
2N/Azap_iterate (dnode_end_t * zap_dnode,
2N/A grub_size_t nameelemlen,
2N/A int NESTED_FUNC_ATTR (*hook) (const void *name,
2N/A grub_size_t namelen,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize),
2N/A struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t block_type;
2N/A void *zapbuf;
2N/A grub_err_t err;
2N/A int ret;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A /* Read in the first block of the zap object data. */
2N/A err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
2N/A if (err)
2N/A return 0;
2N/A block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian);
2N/A
2N/A grub_dprintf ("zfs", "zap iterate\n");
2N/A
2N/A if (block_type == ZBT_MICRO)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected");
2N/A return 0;
2N/A }
2N/A if (block_type == ZBT_HEADER)
2N/A {
2N/A grub_dprintf ("zfs", "fat zap\n");
2N/A /* this is a fat zap */
2N/A ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, data);
2N/A grub_free (zapbuf);
2N/A return ret;
2N/A }
2N/A grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Get the dnode of an object number from the metadnode of an object set.
2N/A *
2N/A * Input
2N/A * mdn - metadnode to get the object dnode
2N/A * objnum - object number for the object dnode
2N/A * buf - data buffer that holds the returning dnode
2N/A */
2N/Astatic grub_err_t
2N/Adnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type,
2N/A dnode_end_t * buf, struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t blkid, blksz; /* the block id this object dnode is in */
2N/A int epbs; /* shift of number of dnodes in a block */
2N/A int idx; /* index within a block */
2N/A void *dnbuf;
2N/A grub_err_t err;
2N/A grub_zfs_endian_t endian;
2N/A
2N/A blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec,
2N/A mdn->endian) << SPA_MINBLOCKSHIFT;
2N/A epbs = zfs_log2 (blksz) - DNODE_SHIFT;
2N/A blkid = objnum >> epbs;
2N/A idx = objnum & ((1 << epbs) - 1);
2N/A
2N/A if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, mdn,
2N/A sizeof (*mdn)) == 0
2N/A && objnum >= data->dnode_start && objnum < data->dnode_end)
2N/A {
2N/A grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE);
2N/A buf->endian = data->dnode_endian;
2N/A if (type && buf->dn.dn_type != type)
2N/A return grub_error(GRUB_ERR_BAD_FS, "[1] incorrect dnode type: %d != %d\n", buf->dn.dn_type, type);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A
2N/A grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian,
2N/A (unsigned long long) blkid);
2N/A err = dmu_read (mdn, blkid, &dnbuf, &endian, data);
2N/A if (err)
2N/A return err;
2N/A grub_dprintf ("zfs", "alive\n");
2N/A
2N/A grub_free (data->dnode_buf);
2N/A data->dnode_buf = NULL;
2N/A grub_free (data->dnode_mdn);
2N/A data->dnode_mdn = grub_malloc (sizeof (*mdn));
2N/A if (! data->dnode_mdn)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A data->dnode_buf = 0;
2N/A }
2N/A else
2N/A {
2N/A grub_memcpy (data->dnode_mdn, mdn, sizeof (*mdn));
2N/A data->dnode_buf = dnbuf;
2N/A data->dnode_start = blkid << epbs;
2N/A data->dnode_end = (blkid + 1) << epbs;
2N/A data->dnode_endian = endian;
2N/A }
2N/A
2N/A grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE);
2N/A buf->endian = endian;
2N/A if (type && buf->dn.dn_type != type)
2N/A return grub_error(GRUB_ERR_BAD_FS, "[2] incorrect dnode type: %d != %d\n", buf->dn.dn_type, type);
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * Get the file dnode for a given file name where mdn is the meta dnode
2N/A * for this ZFS object set. When found, place the file dnode in dn.
2N/A * The 'path' argument will be mangled.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Adnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t objnum, version;
2N/A char *cname, ch;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A char *path, *path_buf;
2N/A struct dnode_chain
2N/A {
2N/A struct dnode_chain *next;
2N/A dnode_end_t dn;
2N/A };
2N/A struct dnode_chain *dnode_path = 0, *dn_new, *root;
2N/A
2N/A dn_new = grub_malloc (sizeof (*dn_new));
2N/A if (! dn_new)
2N/A return grub_errno;
2N/A dn_new->next = 0;
2N/A dnode_path = root = dn_new;
2N/A
2N/A err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
2N/A &(dnode_path->dn), data);
2N/A if (err)
2N/A {
2N/A grub_free (dn_new);
2N/A return err;
2N/A }
2N/A
2N/A err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version,
2N/A data, 0);
2N/A if (err)
2N/A {
2N/A grub_free (dn_new);
2N/A return err;
2N/A }
2N/A
2N/A if (version > ZPL_VERSION)
2N/A {
2N/A grub_free (dn_new);
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version");
2N/A }
2N/A
2N/A err = zap_lookup (&(dnode_path->dn), "casesensitivity",
2N/A &subvol->case_insensitive,
2N/A data, 0);
2N/A if (err == GRUB_ERR_FILE_NOT_FOUND)
2N/A {
2N/A grub_errno = GRUB_ERR_NONE;
2N/A subvol->case_insensitive = 0;
2N/A }
2N/A
2N/A err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0);
2N/A if (err)
2N/A {
2N/A grub_free (dn_new);
2N/A return err;
2N/A }
2N/A
2N/A err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
2N/A if (err)
2N/A {
2N/A grub_free (dn_new);
2N/A return err;
2N/A }
2N/A
2N/A path = path_buf = grub_strdup (path_in);
2N/A if (!path_buf)
2N/A {
2N/A grub_free (dn_new);
2N/A return grub_errno;
2N/A }
2N/A
2N/A while (1)
2N/A {
2N/A /* skip leading slashes */
2N/A while (*path == '/')
2N/A path++;
2N/A if (!*path)
2N/A break;
2N/A /* get the next component name */
2N/A cname = path;
2N/A while (*path && *path != '/')
2N/A path++;
2N/A /* Skip dot. */
2N/A if (cname + 1 == path && cname[0] == '.')
2N/A continue;
2N/A /* Handle double dot. */
2N/A if (cname + 2 == path && cname[0] == '.' && cname[1] == '.')
2N/A {
2N/A if (dn_new->next)
2N/A {
2N/A dn_new = dnode_path;
2N/A dnode_path = dn_new->next;
2N/A grub_free (dn_new);
2N/A }
2N/A else
2N/A {
2N/A err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
2N/A "can't resolve ..");
2N/A break;
2N/A }
2N/A continue;
2N/A }
2N/A
2N/A ch = *path;
2N/A *path = 0; /* ensure null termination */
2N/A
2N/A if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
2N/A {
2N/A grub_free (path_buf);
2N/A return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
2N/A }
2N/A err = zap_lookup (&(dnode_path->dn), cname, &objnum,
2N/A data, subvol->case_insensitive);
2N/A if (err)
2N/A break;
2N/A
2N/A dn_new = grub_malloc (sizeof (*dn_new));
2N/A if (! dn_new)
2N/A {
2N/A err = grub_errno;
2N/A break;
2N/A }
2N/A dn_new->next = dnode_path;
2N/A dnode_path = dn_new;
2N/A
2N/A objnum = ZFS_DIRENT_OBJ (objnum);
2N/A err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data);
2N/A if (err)
2N/A break;
2N/A
2N/A *path = ch;
2N/A if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE
2N/A && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
2N/A {
2N/A char *sym_value;
2N/A grub_size_t sym_sz;
2N/A int free_symval = 0;
2N/A char *oldpath = path, *oldpathbuf = path_buf;
2N/A sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys));
2N/A
2N/A sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian);
2N/A
2N/A if (dnode_path->dn.dn.dn_flags & 1)
2N/A {
2N/A grub_size_t block;
2N/A grub_size_t blksz;
2N/A blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec,
2N/A dnode_path->dn.endian)
2N/A << SPA_MINBLOCKSHIFT);
2N/A
2N/A sym_value = grub_malloc (sym_sz);
2N/A if (!sym_value)
2N/A return grub_errno;
2N/A for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++)
2N/A {
2N/A void *t;
2N/A grub_size_t movesize;
2N/A
2N/A err = dmu_read (&(dnode_path->dn), block, &t, 0, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A movesize = sym_sz - block * blksz;
2N/A if (movesize > blksz)
2N/A movesize = blksz;
2N/A
2N/A grub_memcpy (sym_value + block * blksz, t, movesize);
2N/A grub_free (t);
2N/A }
2N/A free_symval = 1;
2N/A }
2N/A path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1);
2N/A if (!path_buf)
2N/A {
2N/A grub_free (oldpathbuf);
2N/A return grub_errno;
2N/A }
2N/A grub_memcpy (path, sym_value, sym_sz);
2N/A if (free_symval)
2N/A grub_free (sym_value);
2N/A path [sym_sz] = 0;
2N/A grub_memcpy (path + grub_strlen (path), oldpath,
2N/A grub_strlen (oldpath) + 1);
2N/A
2N/A grub_free (oldpathbuf);
2N/A if (path[0] != '/')
2N/A {
2N/A dn_new = dnode_path;
2N/A dnode_path = dn_new->next;
2N/A grub_free (dn_new);
2N/A }
2N/A else while (dnode_path != root)
2N/A {
2N/A dn_new = dnode_path;
2N/A dnode_path = dn_new->next;
2N/A grub_free (dn_new);
2N/A }
2N/A }
2N/A if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA)
2N/A {
2N/A void *sahdrp;
2N/A int hdrsize;
2N/A
2N/A if (dnode_path->dn.dn.dn_bonuslen != 0)
2N/A {
2N/A sahdrp = DN_BONUS (&dnode_path->dn.dn);
2N/A }
2N/A else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
2N/A {
2N/A blkptr_t *bp = &dnode_path->dn.dn.dn_spill;
2N/A
2N/A err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data);
2N/A if (err)
2N/A return err;
2N/A }
2N/A else
2N/A {
2N/A return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
2N/A }
2N/A
2N/A hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
2N/A
2N/A if (((grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
2N/A + hdrsize
2N/A + SA_TYPE_OFFSET),
2N/A dnode_path->dn.endian) >> 12) & 0xf) == 0xa)
2N/A {
2N/A char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET;
2N/A grub_size_t sym_sz =
2N/A grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp
2N/A + hdrsize
2N/A + SA_SIZE_OFFSET),
2N/A dnode_path->dn.endian);
2N/A char *oldpath = path, *oldpathbuf = path_buf;
2N/A path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1);
2N/A if (!path_buf)
2N/A {
2N/A grub_free (oldpathbuf);
2N/A return grub_errno;
2N/A }
2N/A grub_memcpy (path, sym_value, sym_sz);
2N/A path [sym_sz] = 0;
2N/A grub_memcpy (path + grub_strlen (path), oldpath,
2N/A grub_strlen (oldpath) + 1);
2N/A
2N/A grub_free (oldpathbuf);
2N/A if (path[0] != '/')
2N/A {
2N/A dn_new = dnode_path;
2N/A dnode_path = dn_new->next;
2N/A grub_free (dn_new);
2N/A }
2N/A else while (dnode_path != root)
2N/A {
2N/A dn_new = dnode_path;
2N/A dnode_path = dn_new->next;
2N/A grub_free (dn_new);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (!err)
2N/A grub_memcpy (dn, &(dnode_path->dn), sizeof (*dn));
2N/A
2N/A while (dnode_path)
2N/A {
2N/A dn_new = dnode_path->next;
2N/A grub_free (dnode_path);
2N/A dnode_path = dn_new;
2N/A }
2N/A grub_free (path_buf);
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Get the default 'bootfs' dataset name using rootfs object number
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Aget_default_bootfsname (dnode_end_t * mosmdn, grub_uint64_t bootfsobj,
2N/A struct grub_zfs_data *data, char **bootfsname)
2N/A{
2N/A dnode_end_t dn;
2N/A dnode_end_t mdn;
2N/A grub_uint64_t dirobj;
2N/A grub_uint64_t parentobj;
2N/A grub_uint64_t childobj;
2N/A grub_uint64_t rootobj;
2N/A grub_size_t buf_size;
2N/A grub_err_t err = GRUB_ERR_NONE;
2N/A char *bootfs;
2N/A
2N/A *bootfsname = 0;
2N/A if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2N/A DMU_OT_OBJECT_DIRECTORY, &mdn, data)))
2N/A return (grub_errno);
2N/A
2N/A err = zap_lookup (&mdn, DMU_POOL_ROOT_DATASET, &rootobj, data, 0);
2N/A if (err)
2N/A return err;
2N/A
2N/A if ((grub_errno = dnode_get (mosmdn, bootfsobj, DMU_OT_DSL_DATASET, &dn, data)))
2N/A return (grub_errno);
2N/A
2N/A dirobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *)
2N/A DN_BONUS (&dn.dn))->ds_dir_obj, dn.endian);
2N/A
2N/A buf_size = BOOTFSNAME_SIZE;
2N/A bootfs = grub_zalloc(buf_size);
2N/A do
2N/A {
2N/A if ((grub_errno = dnode_get (mosmdn, dirobj, DMU_OT_DSL_DIR, &dn, data)))
2N/A {
2N/A grub_free (bootfs);
2N/A return (grub_errno);
2N/A }
2N/A
2N/A parentobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *)
2N/A DN_BONUS (&dn.dn)))->dd_parent_obj, dn.endian);
2N/A
2N/A if ((grub_errno = dnode_get (mosmdn, parentobj, DMU_OT_DSL_DIR, &dn, data)))
2N/A {
2N/A grub_free (bootfs);
2N/A return (grub_errno);
2N/A }
2N/A
2N/A childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *)
2N/A DN_BONUS (&dn.dn)))->dd_child_dir_zapobj, dn.endian);
2N/A
2N/A if ((grub_errno = dnode_get (mosmdn, childobj,
2N/A DMU_OT_DSL_DIR_CHILD_MAP, &dn, data)))
2N/A {
2N/A grub_free (bootfs);
2N/A return (grub_errno);
2N/A }
2N/A
2N/A char cname[64];
2N/A grub_memset (cname, 0, sizeof(cname));
2N/A
2N/A if (zap_value_search (&dn, cname, &dirobj, data))
2N/A {
2N/A grub_free (bootfs);
2N/A return (GRUB_ERR_BAD_FS);
2N/A }
2N/A
2N/A grub_size_t cname_len = grub_strlen(cname);
2N/A cname[cname_len++] = '/';
2N/A
2N/A grub_size_t bootfs_len= grub_strlen(bootfs);
2N/A grub_size_t expected_len = bootfs_len + cname_len;
2N/A if (expected_len >= buf_size)
2N/A {
2N/A while((buf_size = buf_size * 2) < expected_len);
2N/A char *tmp = grub_realloc (bootfs, buf_size);
2N/A if (! tmp)
2N/A {
2N/A grub_free (bootfs);
2N/A return (grub_errno);
2N/A }
2N/A bootfs = tmp;
2N/A grub_memset((bootfs + bootfs_len), 0, (buf_size - bootfs_len));
2N/A }
2N/A
2N/A /* create space for parent dataset name */
2N/A grub_memmove((bootfs + cname_len), bootfs, bootfs_len);
2N/A grub_memmove(bootfs, cname, cname_len);
2N/A
2N/A } while ((dirobj = parentobj) != rootobj);
2N/A
2N/A /* remove trailing slash */
2N/A *(bootfs + grub_strlen(bootfs) - 1) = '\0';
2N/A *bootfsname = bootfs;
2N/A
2N/A grub_dprintf ("zfs", "get_default_bootfsname: %s\n", *bootfsname);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get the default 'bootfs' property value from the rootpool.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Aget_default_bootfsobj (dnode_end_t * mosmdn, grub_uint64_t * obj,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t objnum = 0;
2N/A dnode_end_t dn;
2N/A
2N/A grub_dprintf ("zfs", "get_default_bootfsobj called\n");
2N/A if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2N/A DMU_OT_OBJECT_DIRECTORY, &dn, data)))
2N/A return (grub_errno);
2N/A
2N/A /*
2N/A * find the object number for 'pool_props', and get the dnode
2N/A * of the 'pool_props'.
2N/A */
2N/A if (zap_lookup (&dn, DMU_POOL_PROPS, &objnum, data, 0))
2N/A return (GRUB_ERR_BAD_FS);
2N/A
2N/A if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, &dn, data)))
2N/A return (grub_errno);
2N/A
2N/A if (zap_lookup (&dn, ZPOOL_PROP_BOOTFS, &objnum, data, 0))
2N/A return (GRUB_ERR_BAD_FS);
2N/A
2N/A if (!objnum)
2N/A return (GRUB_ERR_BAD_FS);
2N/A
2N/A *obj = objnum;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname),
2N/A * e.g. pool/rootfs, or a given object number (obj), e.g. the object number
2N/A * of pool/rootfs.
2N/A *
2N/A * If no fsname and no obj are given, return the DSL_DIR metadnode.
2N/A * If fsname is given, return its metadnode and its matching object number.
2N/A * If only obj is given, return the metadnode for this object number.
2N/A *
2N/A */
2N/Astatic grub_err_t
2N/Aget_filesystem_dnode (dnode_end_t * mosmdn, char *fsname,
2N/A dnode_end_t * mdn, struct grub_zfs_data *data)
2N/A{
2N/A grub_uint64_t objnum;
2N/A grub_err_t err;
2N/A
2N/A grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian);
2N/A
2N/A err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT,
2N/A DMU_OT_OBJECT_DIRECTORY, mdn, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A grub_dprintf ("zfs", "alive\n");
2N/A
2N/A err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data, 0);
2N/A if (err)
2N/A return err;
2N/A
2N/A grub_dprintf ("zfs", "alive\n");
2N/A
2N/A err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A grub_dprintf ("zfs", "alive\n");
2N/A
2N/A while (*fsname)
2N/A {
2N/A grub_uint64_t childobj;
2N/A char *cname, ch;
2N/A
2N/A while (*fsname == '/')
2N/A fsname++;
2N/A
2N/A if (! *fsname || *fsname == '@')
2N/A break;
2N/A
2N/A cname = fsname;
2N/A while (*fsname && !grub_isspace (*fsname) && *fsname != '/')
2N/A fsname++;
2N/A ch = *fsname;
2N/A *fsname = 0;
2N/A
2N/A childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian);
2N/A if (childobj == 0)
2N/A return grub_error(GRUB_ERR_BAD_FS, "file system not found");
2N/A
2N/A err = dnode_get (mosmdn, childobj,
2N/A DMU_OT_DSL_DIR_CHILD_MAP, mdn, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A err = zap_lookup (mdn, cname, &objnum, data, 0);
2N/A if (err)
2N/A return err;
2N/A
2N/A err = dnode_get (mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data);
2N/A if (err)
2N/A return err;
2N/A
2N/A *fsname = ch;
2N/A }
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Amake_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
2N/A{
2N/A void *osp;
2N/A blkptr_t *bp;
2N/A grub_size_t ospsize;
2N/A grub_err_t err;
2N/A
2N/A grub_dprintf ("zfs", "endian = %d\n", mdn->endian);
2N/A
2N/A bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp);
2N/A err = zio_read (bp, mdn->endian, &osp, &ospsize, data);
2N/A if (err)
2N/A return err;
2N/A if (ospsize < OBJSET_PHYS_SIZE_V14)
2N/A {
2N/A grub_free (osp);
2N/A return grub_error (GRUB_ERR_BAD_FS, "too small osp");
2N/A }
2N/A
2N/A mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1;
2N/A grub_memmove ((char *) &(mdn->dn),
2N/A (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE);
2N/A grub_free (osp);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Adnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
2N/A dnode_end_t * dn, int *isfs,
2N/A struct grub_zfs_data *data)
2N/A{
2N/A char *fsname, *snapname;
2N/A const char *ptr_at, *filename;
2N/A grub_uint64_t headobj;
2N/A grub_uint64_t keychainobj;
2N/A grub_uint64_t salt;
2N/A grub_err_t err;
2N/A int keyn = 0;
2N/A
2N/A auto int NESTED_FUNC_ATTR count_zap_keys (const void *name,
2N/A grub_size_t namelen,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize);
2N/A int NESTED_FUNC_ATTR count_zap_keys (const void *name __attribute__ ((unused)),
2N/A grub_size_t namelen __attribute__ ((unused)),
2N/A const void *val_in __attribute__ ((unused)),
2N/A grub_size_t nelem __attribute__ ((unused)),
2N/A grub_size_t elemsize __attribute__ ((unused)))
2N/A {
2N/A subvol->nkeys++;
2N/A return 0;
2N/A }
2N/A
2N/A auto int NESTED_FUNC_ATTR load_zap_key (const void *name,
2N/A grub_size_t namelen,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize);
2N/A int NESTED_FUNC_ATTR load_zap_key (const void *name,
2N/A grub_size_t namelen,
2N/A const void *val_in,
2N/A grub_size_t nelem,
2N/A grub_size_t elemsize)
2N/A {
2N/A if (namelen != 1)
2N/A {
2N/A grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n",
2N/A namelen);
2N/A return 0;
2N/A }
2N/A
2N/A if (elemsize != 1)
2N/A {
2N/A grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n",
2N/A elemsize);
2N/A return 0;
2N/A }
2N/A
2N/A subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name);
2N/A subvol->keyring[keyn].algo = grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
2N/A subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt,
2N/A subvol->keyring[keyn].algo);
2N/A keyn++;
2N/A return 0;
2N/A }
2N/A
2N/A ptr_at = grub_strchr (fullpath, '@');
2N/A if (! ptr_at)
2N/A {
2N/A *isfs = 1;
2N/A filename = 0;
2N/A snapname = 0;
2N/A fsname = grub_strdup (fullpath);
2N/A }
2N/A else
2N/A {
2N/A const char *ptr_slash = grub_strchr (ptr_at, '/');
2N/A
2N/A *isfs = 0;
2N/A fsname = grub_malloc (ptr_at - fullpath + 1);
2N/A if (!fsname)
2N/A return grub_errno;
2N/A grub_memcpy (fsname, fullpath, ptr_at - fullpath);
2N/A fsname[ptr_at - fullpath] = 0;
2N/A if (ptr_at[1] && ptr_at[1] != '/')
2N/A {
2N/A snapname = grub_malloc (ptr_slash - ptr_at);
2N/A if (!snapname)
2N/A {
2N/A grub_free (fsname);
2N/A return grub_errno;
2N/A }
2N/A grub_memcpy (snapname, ptr_at + 1, ptr_slash - ptr_at - 1);
2N/A snapname[ptr_slash - ptr_at - 1] = 0;
2N/A }
2N/A else
2N/A snapname = 0;
2N/A if (ptr_slash)
2N/A filename = ptr_slash;
2N/A else
2N/A filename = "/";
2N/A grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n",
2N/A fsname, snapname, filename);
2N/A }
2N/A grub_dprintf ("zfs", "alive\n");
2N/A err = get_filesystem_dnode (&(data->mos), fsname, dn, data);
2N/A if (err)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A
2N/A grub_dprintf ("zfs", "alive\n");
2N/A
2N/A headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian);
2N/A
2N/A grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
2N/A
2N/A err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &subvol->mdn,
2N/A data);
2N/A if (err)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
2N/A
2N/A keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian);
2N/A if (grub_zfs_load_key && keychainobj)
2N/A {
2N/A dnode_end_t keychain_dn, props_dn;
2N/A grub_uint64_t propsobj;
2N/A propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian);
2N/A
2N/A err = dnode_get (&(data->mos), propsobj, DMU_OT_DSL_PROPS,
2N/A &props_dn, data);
2N/A if (err)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A
2N/A err = zap_lookup (&props_dn, "salt", &salt, data, 0);
2N/A if (err == GRUB_ERR_FILE_NOT_FOUND)
2N/A {
2N/A err = 0;
2N/A grub_errno = 0;
2N/A salt = 0;
2N/A }
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "failed here\n");
2N/A return err;
2N/A }
2N/A
2N/A err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN,
2N/A &keychain_dn, data);
2N/A if (err)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A subvol->nkeys = 0;
2N/A zap_iterate (&keychain_dn, 8, count_zap_keys, data);
2N/A subvol->keyring = grub_zalloc (subvol->nkeys * sizeof (subvol->keyring[0]));
2N/A if (!subvol->keyring)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A zap_iterate (&keychain_dn, 8, load_zap_key, data);
2N/A }
2N/A
2N/A if (snapname)
2N/A {
2N/A grub_uint64_t snapobj;
2N/A
2N/A snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian);
2N/A
2N/A err = dnode_get (&(data->mos), snapobj,
2N/A DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data);
2N/A if (!err)
2N/A err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0);
2N/A if (!err)
2N/A err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET,
2N/A &subvol->mdn, data);
2N/A if (err)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A }
2N/A }
2N/A
2N/A subvol->obj = headobj;
2N/A
2N/A make_mdn (&subvol->mdn, data);
2N/A
2N/A grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian);
2N/A
2N/A if (*isfs)
2N/A {
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return GRUB_ERR_NONE;
2N/A }
2N/A err = dnode_get_path (subvol, filename, dn, data);
2N/A grub_free (fsname);
2N/A grub_free (snapname);
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * For a given XDR packed nvlist, verify the first 4 bytes and move on.
2N/A *
2N/A * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) :
2N/A *
2N/A * encoding method/host endian (4 bytes)
2N/A * nvl_version (4 bytes)
2N/A * nvl_nvflag (4 bytes)
2N/A * encoded nvpairs:
2N/A * encoded size of the nvpair (4 bytes)
2N/A * decoded size of the nvpair (4 bytes)
2N/A * name string size (4 bytes)
2N/A * name string data (sizeof(NV_ALIGN4(string))
2N/A * data type (4 bytes)
2N/A * # of elements in the nvpair (4 bytes)
2N/A * data
2N/A * 2 zero's for the last nvpair
2N/A * (end of the entire list) (8 bytes)
2N/A *
2N/A */
2N/A
2N/Astatic int
2N/Anvlist_find_value (const char *nvlist_in, const char *name,
2N/A int valtype, char **val,
2N/A grub_size_t *size_out, grub_size_t *nelm_out)
2N/A{
2N/A int name_len, type, encode_size;
2N/A const char *nvpair, *nvp_name, *nvlist = nvlist_in;
2N/A
2N/A /* Verify if the 1st and 2nd byte in the nvlist are valid. */
2N/A /* NOTE: independently of what endianness header announces all
2N/A subsequent values are big-endian. */
2N/A if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN
2N/A && nvlist[1] != NV_BIG_ENDIAN))
2N/A {
2N/A grub_dprintf ("zfs", "incorrect nvlist header\n");
2N/A grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist");
2N/A return 0;
2N/A }
2N/A
2N/A /* skip the header, nvl_version, and nvl_nvflag */
2N/A nvlist = nvlist + 4 * 3;
2N/A /*
2N/A * Loop thru the nvpair list
2N/A * The XDR representation of an integer is in big-endian byte order.
2N/A */
2N/A while ((encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (nvlist))))
2N/A {
2N/A int nelm;
2N/A
2N/A if (nvlist + 4 * 4 >= nvlist_in + VDEV_PHYS_SIZE)
2N/A {
2N/A grub_dprintf("zfs", "nvlist overflow\n");
2N/A grub_error(GRUB_ERR_BAD_FS, "incorrect nvlist");
2N/A return 0;
2N/A }
2N/A
2N/A nvpair = nvlist + 4 * 2; /* skip the encode/decode size */
2N/A
2N/A name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
2N/A nvpair += 4;
2N/A
2N/A nvp_name = nvpair;
2N/A nvpair = nvpair + ((name_len + 3) & ~3); /* align */
2N/A
2N/A if (nvpair + 8 >= nvlist_in + VDEV_PHYS_SIZE
2N/A || encode_size < 0
2N/A || nvpair + 8 + encode_size > nvlist_in + VDEV_PHYS_SIZE)
2N/A {
2N/A grub_dprintf("zfs", "nvlist overflow\n");
2N/A grub_error(GRUB_ERR_BAD_FS, "incorrect nvlist");
2N/A return 0;
2N/A }
2N/A
2N/A type = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
2N/A nvpair += 4;
2N/A
2N/A nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
2N/A if (nelm < 1)
2N/A return grub_error (GRUB_ERR_BAD_FS, "empty nvpair");
2N/A
2N/A nvpair += 4;
2N/A
2N/A if ((grub_strncmp (nvp_name, name, name_len) == 0) && type == valtype)
2N/A {
2N/A *val = (char *) nvpair;
2N/A *size_out = encode_size;
2N/A if (nelm_out)
2N/A *nelm_out = nelm;
2N/A return 1;
2N/A }
2N/A
2N/A nvlist += encode_size; /* goto the next nvpair */
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Aint
2N/Agrub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name,
2N/A grub_uint64_t * out)
2N/A{
2N/A char *nvpair;
2N/A grub_size_t size;
2N/A int found;
2N/A
2N/A found = nvlist_find_value (nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0);
2N/A if (!found)
2N/A return 0;
2N/A if (size < sizeof (grub_uint64_t))
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "invalid uint64");
2N/A return 0;
2N/A }
2N/A
2N/A *out = grub_be_to_cpu64 (grub_get_unaligned64 (nvpair));
2N/A return 1;
2N/A}
2N/A
2N/Achar *
2N/Agrub_zfs_nvlist_lookup_string (const char *nvlist, const char *name)
2N/A{
2N/A char *nvpair;
2N/A char *ret;
2N/A grub_size_t slen;
2N/A grub_size_t size;
2N/A int found;
2N/A
2N/A found = nvlist_find_value (nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0);
2N/A if (!found)
2N/A return 0;
2N/A if (size < 4)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "invalid string");
2N/A return 0;
2N/A }
2N/A slen = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair));
2N/A if (slen > size - 4)
2N/A slen = size - 4;
2N/A ret = grub_malloc (slen + 1);
2N/A if (!ret)
2N/A return 0;
2N/A grub_memcpy (ret, nvpair + 4, slen);
2N/A ret[slen] = 0;
2N/A return ret;
2N/A}
2N/A
2N/Achar *
2N/Agrub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name)
2N/A{
2N/A char *nvpair;
2N/A char *ret;
2N/A grub_size_t size;
2N/A int found;
2N/A
2N/A found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair,
2N/A &size, 0);
2N/A if (!found)
2N/A return 0;
2N/A ret = grub_zalloc (size + 3 * sizeof (grub_uint32_t));
2N/A if (!ret)
2N/A return 0;
2N/A grub_memcpy (ret, nvlist, sizeof (grub_uint32_t));
2N/A
2N/A grub_memcpy (ret + sizeof (grub_uint32_t), nvpair, size);
2N/A return ret;
2N/A}
2N/A
2N/Aint
2N/Agrub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist,
2N/A const char *name)
2N/A{
2N/A char *nvpair;
2N/A grub_size_t nelm, size;
2N/A int found;
2N/A
2N/A found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair,
2N/A &size, &nelm);
2N/A if (! found)
2N/A return -1;
2N/A return nelm;
2N/A}
2N/A
2N/Astatic int
2N/Aget_nvlist_size (const char *beg, const char *limit)
2N/A{
2N/A const char *ptr;
2N/A grub_uint32_t encode_size;
2N/A
2N/A ptr = beg + 8;
2N/A
2N/A while (ptr < limit
2N/A && (encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (ptr))))
2N/A ptr += encode_size; /* goto the next nvpair */
2N/A ptr += 8;
2N/A return (ptr > limit) ? -1 : (ptr - beg);
2N/A}
2N/A
2N/Achar *
2N/Agrub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name,
2N/A grub_size_t index)
2N/A{
2N/A char *nvpair, *nvpairptr;
2N/A int found;
2N/A char *ret;
2N/A grub_size_t size;
2N/A unsigned i;
2N/A grub_size_t nelm;
2N/A int elemsize = 0;
2N/A
2N/A found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair,
2N/A &size, &nelm);
2N/A if (!found)
2N/A return 0;
2N/A if (index >= nelm)
2N/A {
2N/A grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array");
2N/A return 0;
2N/A }
2N/A
2N/A nvpairptr = nvpair;
2N/A
2N/A for (i = 0; i < index; i++)
2N/A {
2N/A int r;
2N/A r = get_nvlist_size (nvpairptr, nvpair + size);
2N/A
2N/A if (r < 0)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array");
2N/A return NULL;
2N/A }
2N/A nvpairptr += r;
2N/A }
2N/A
2N/A elemsize = get_nvlist_size (nvpairptr, nvpair + size);
2N/A
2N/A if (elemsize < 0)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array");
2N/A return 0;
2N/A }
2N/A
2N/A ret = grub_zalloc (elemsize + sizeof (grub_uint32_t));
2N/A if (!ret)
2N/A return 0;
2N/A grub_memcpy (ret, nvlist, sizeof (grub_uint32_t));
2N/A
2N/A grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, elemsize);
2N/A return ret;
2N/A}
2N/A
2N/Astatic void
2N/Aunmount_device (struct grub_zfs_device_desc *desc)
2N/A{
2N/A unsigned i;
2N/A switch (desc->type)
2N/A {
2N/A case DEVICE_LEAF:
2N/A if (!desc->original && desc->dev)
2N/A grub_device_close (desc->dev);
2N/A return;
2N/A case DEVICE_RAIDZ:
2N/A case DEVICE_MIRROR:
2N/A for (i = 0; i < desc->n_children; i++)
2N/A unmount_device (&desc->children[i]);
2N/A return;
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Azfs_unmount (struct grub_zfs_data *data)
2N/A{
2N/A unsigned i;
2N/A
2N/A grub_free (data->dnode_buf);
2N/A data->dnode_buf = NULL;
2N/A grub_free (data->dnode_mdn);
2N/A data->dnode_mdn = NULL;
2N/A grub_free (data->file_buf);
2N/A data->file_buf = NULL;
2N/A for (i = 0; i < data->subvol.nkeys; i++)
2N/A grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
2N/A grub_free (data->subvol.keyring);
2N/A data->subvol.nkeys = 0;
2N/A data->subvol.keyring = NULL;
2N/A
2N/A data->mount_count--;
2N/A if (! data->zcached)
2N/A {
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A {
2N/A unmount_device (&data->devices_attached[i]);
2N/A grub_free (data->devices_attached[i].children);
2N/A }
2N/A grub_free (data->devices_attached);
2N/A grub_free (data);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Free all the caches
2N/A */
2N/Astatic void
2N/Azfs_free_caches (void)
2N/A{
2N/A unsigned int i;
2N/A struct grub_zfs_data *data;
2N/A struct zfs_mount_cache *zcache, *zcache_next;
2N/A struct zfs_dev_notzfs *dev_notzfs, *dev_notzfs_next;
2N/A struct zfs_dev_ublock *dev_ub, *dev_ub_next;
2N/A
2N/A /* free the zfs mount cache list */
2N/A zcache = zfs_mount_cache_list;
2N/A while (zcache != NULL)
2N/A {
2N/A data = zcache->zcache_zfs_data;
2N/A if (data)
2N/A {
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A {
2N/A unmount_device (&data->devices_attached[i]);
2N/A grub_free (data->devices_attached[i].children);
2N/A }
2N/A grub_free (data->devices_attached);
2N/A
2N/A for (i = 0; i < data->subvol.nkeys; i++)
2N/A grub_crypto_cipher_close (data->subvol.keyring[i].cipher);
2N/A grub_free (data->subvol.keyring);
2N/A
2N/A grub_free (data);
2N/A zcache->zcache_zfs_data = NULL;
2N/A }
2N/A
2N/A grub_free (zcache->zcache_dev_name);
2N/A zcache->zcache_dev_name = NULL;
2N/A zcache->zcache_dev_id = 0;
2N/A
2N/A zcache_next = zcache->next;
2N/A grub_free (zcache);
2N/A zcache = zcache_next;
2N/A }
2N/A zfs_mount_cache_list = NULL;
2N/A
2N/A /* free the cache list for disk dev with ZFS uberblock */
2N/A dev_ub = zfs_dev_ublock_list;
2N/A while (dev_ub != NULL)
2N/A {
2N/A grub_free (dev_ub->dev_name);
2N/A dev_ub_next = dev_ub->next;
2N/A grub_free (dev_ub);
2N/A dev_ub = dev_ub_next;
2N/A }
2N/A zfs_dev_ublock_list = NULL;
2N/A
2N/A /* free the cache list for non-zfs disk dev */
2N/A dev_notzfs = zfs_dev_notzfs_list;
2N/A while (dev_notzfs != NULL)
2N/A {
2N/A grub_free (dev_notzfs->dev_name);
2N/A dev_notzfs_next = dev_notzfs->next;
2N/A grub_free (dev_notzfs);
2N/A dev_notzfs = dev_notzfs_next;
2N/A }
2N/A zfs_dev_notzfs_list = NULL;
2N/A}
2N/A
2N/A/*
2N/A * zfs_mount() locates a valid uberblock of the root pool and
2N/A * read in its MOS to the device grub_zfs_data structure.
2N/A *
2N/A * zfs_mount() is called frequently by GRUB2 zfs interfaces, so
2N/A * a cache mechanism is implemented to save the successful zfs
2N/A * mount data for devices. This is good for zfs mount performance.
2N/A *
2N/A */
2N/Astatic struct grub_zfs_data *
2N/Azfs_mount (grub_device_t dev)
2N/A{
2N/A struct grub_zfs_data *data = 0;
2N/A struct zfs_mount_cache *zcache = 0;
2N/A grub_err_t err;
2N/A objset_phys_t *osp = 0;
2N/A grub_size_t ospsize;
2N/A grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
2N/A uberblock_t *ub;
2N/A int inserted;
2N/A unsigned int i, j;
2N/A char *diskname, *partname, *fullname;
2N/A
2N/A if (! dev->disk)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_DEVICE, "not a disk");
2N/A return 0;
2N/A }
2N/A
2N/A diskname = (char *)dev->disk->name;
2N/A partname = grub_partition_get_name(dev->disk->partition);
2N/A if (partname)
2N/A {
2N/A fullname = grub_xasprintf("%s,%s", diskname, partname);
2N/A grub_free (partname);
2N/A }
2N/A else
2N/A fullname = grub_strdup (diskname);
2N/A
2N/A /* check the zfs mount cache list first */
2N/A for (zcache = zfs_mount_cache_list; zcache != NULL; zcache = zcache->next)
2N/A {
2N/A if (grub_strcmp (fullname, zcache->zcache_dev_name) != 0)
2N/A continue;
2N/A
2N/A data = zcache->zcache_zfs_data;
2N/A /* use the previous mount data */
2N/A if (data->mount_count == 0)
2N/A {
2N/A /* below items will be re-assigned out of zfs_mount() */
2N/A data->file_buf = 0;
2N/A data->file_start = 0;
2N/A data->file_end = 0;
2N/A data->dnode_buf = 0;
2N/A data->dnode_mdn = 0;
2N/A data->dnode_start = 0;
2N/A data->dnode_end = 0;
2N/A data->dnode_endian = 0;
2N/A
2N/A /* assign to the new grub device instance */
2N/A if (data->devices_attached)
2N/A {
2N/A struct grub_zfs_device_desc *desc;
2N/A
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A {
2N/A desc = &data->devices_attached[i];
2N/A if (desc->original && desc->dev)
2N/A desc->dev = dev;
2N/A if (desc->children)
2N/A {
2N/A for (j = 0; j < desc->n_children; j++)
2N/A if (desc->children[j].original && desc->children[j].dev)
2N/A desc->children[j].dev = dev;
2N/A }
2N/A }
2N/A }
2N/A if (data->device_original && data->device_original->dev)
2N/A data->device_original->dev = dev;
2N/A
2N/A data->mount_count++;
2N/A grub_free (fullname);
2N/A return (data);
2N/A }
2N/A }
2N/A
2N/A /* allocate the zfs mount data structure */
2N/A data = grub_zalloc (sizeof (*data));
2N/A if (! data)
2N/A {
2N/A grub_error (GRUB_ERR_OUT_OF_MEMORY, "zfs data allocation failed");
2N/A grub_free (fullname);
2N/A return 0;
2N/A }
2N/A
2N/A /* need to do a new mount */
2N/A data->dev_name = fullname;
2N/A data->mount_count++;
2N/A data->n_devices_allocated = 16;
2N/A data->devices_attached = grub_malloc (sizeof (data->devices_attached[0])
2N/A * data->n_devices_allocated);
2N/A data->n_devices_attached = 0;
2N/A err = scan_disk (dev, data, 1, &inserted);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "scan_disk failed\n");
2N/A grub_free (fullname);
2N/A zfs_unmount (data);
2N/A return NULL;
2N/A }
2N/A grub_dprintf("zfs", "zfs_mount->scan_disk-> #devs = %d\n",
2N/A data->n_devices_attached);
2N/A for (i = 0; i < data->n_devices_attached; i++)
2N/A grub_dprintf("zfs", "devices[%u].dev = %s\n", i,
2N/A data->devices_attached[i].dev ?
2N/A data->devices_attached[i].dev->disk->name : "(null)" );
2N/A
2N/A ub = &(data->current_uberblock);
2N/A ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
2N/A GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
2N/A ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
2N/A
2N/A err = zio_read (&ub->ub_rootbp, ub_endian,
2N/A (void **) &osp, &ospsize, data);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "zio_read failed: %s\n", grub_errmsg );
2N/A grub_free (fullname);
2N/A zfs_unmount (data);
2N/A return NULL;
2N/A }
2N/A
2N/A if (ospsize < OBJSET_PHYS_SIZE_V14)
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "OSP too small");
2N/A grub_free (fullname);
2N/A grub_free (osp);
2N/A zfs_unmount (data);
2N/A return NULL;
2N/A }
2N/A
2N/A /* Got the MOS. Save it to data->mos. */
2N/A grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode,
2N/A DNODE_SIZE);
2N/A data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop,
2N/A ub_endian) >> 63) & 1;
2N/A grub_free (osp);
2N/A
2N/A data->mounted = 1; /* mount succeeded */
2N/A if (zcache == NULL) /* allocate the zfs mount cache structure */
2N/A zcache = grub_zalloc (sizeof (*zcache));
2N/A else
2N/A zcache = NULL; /* intend for a non-cached mount */
2N/A
2N/A if (zcache != NULL) /* cache this OK zfs mount */
2N/A {
2N/A zcache->zcache_zfs_data = data;
2N/A zcache->zcache_dev_id = dev->disk->id;
2N/A zcache->zcache_dev_name = fullname;
2N/A zcache->next = zfs_mount_cache_list;
2N/A zfs_mount_cache_list = zcache;
2N/A data->zcached = 1;
2N/A }
2N/A else
2N/A {
2N/A grub_free (fullname);
2N/A data->dev_name = NULL;
2N/A }
2N/A
2N/A return data;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist)
2N/A{
2N/A struct grub_zfs_data *zfs;
2N/A grub_err_t err;
2N/A
2N/A zfs = zfs_mount (dev);
2N/A if (!zfs)
2N/A return grub_errno;
2N/A err = zfs_fetch_nvlist (zfs->device_original, nvlist);
2N/A if (err)
2N/A grub_dprintf("zfs", "zfs_fetch_nvlist failed: #devices=%d type=%d\n",
2N/A zfs->n_devices_attached, zfs->devices_attached[0].type);
2N/A zfs_unmount (zfs);
2N/A return err;
2N/A}
2N/A
2N/A/*
2N/A * Returns the pool name.
2N/A */
2N/Astatic grub_err_t
2N/Azfs_label (grub_device_t device, char **label)
2N/A{
2N/A char *nvlist;
2N/A grub_err_t err;
2N/A struct grub_zfs_data *data;
2N/A
2N/A *label = NULL;
2N/A
2N/A data = zfs_mount (device);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A err = zfs_fetch_nvlist (data->device_original, &nvlist);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "zfs_fetch_nvlist failed\n");
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A
2N/A *label = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME);
2N/A grub_free (nvlist);
2N/A zfs_unmount (data);
2N/A return grub_errno;
2N/A}
2N/A
2N/A/*
2N/A * Returns the pool GUID.
2N/A */
2N/Astatic grub_err_t
2N/Azfs_uuid (grub_device_t device, char **uuid)
2N/A{
2N/A struct grub_zfs_data *data;
2N/A
2N/A *uuid = 0;
2N/A
2N/A data = zfs_mount (device);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A *uuid = grub_xasprintf ("%016llx", (long long unsigned) data->guid);
2N/A zfs_unmount (data);
2N/A if (! *uuid)
2N/A return grub_errno;
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Azfs_mtime (grub_device_t device, grub_int32_t *mt)
2N/A{
2N/A struct grub_zfs_data *data;
2N/A grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
2N/A uberblock_t *ub;
2N/A
2N/A *mt = 0;
2N/A
2N/A data = zfs_mount (device);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A ub = &(data->current_uberblock);
2N/A ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic,
2N/A GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC
2N/A ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN);
2N/A
2N/A *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian);
2N/A zfs_unmount (data);
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/A/*
2N/A * zfs_open() locates a file in the rootpool by following the
2N/A * MOS and places the dnode of the file in the device
2N/A * grub_zfs_data structure.
2N/A */
2N/Astatic grub_err_t
2N/Agrub_zfs_open (struct grub_file *file, const char *fsfilename)
2N/A{
2N/A struct grub_zfs_data *data;
2N/A grub_err_t err;
2N/A int isfs;
2N/A
2N/A data = zfs_mount (file->device);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A err = dnode_get_fullpath (fsfilename, &(data->subvol),
2N/A &(data->dnode), &isfs, data);
2N/A if (err)
2N/A {
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A
2N/A if (isfs)
2N/A {
2N/A zfs_unmount (data);
2N/A return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Missing @ or / separator");
2N/A }
2N/A
2N/A /* We found the dnode for this file. Verify if it is a plain file. */
2N/A if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS)
2N/A {
2N/A zfs_unmount (data);
2N/A return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
2N/A }
2N/A
2N/A /* get the file size and set the file position to 0 */
2N/A
2N/A /*
2N/A * For DMU_OT_SA we will need to locate the SIZE attribute
2N/A * attribute, which could be either in the bonus buffer
2N/A * or the "spill" block.
2N/A */
2N/A if (data->dnode.dn.dn_bonustype == DMU_OT_SA)
2N/A {
2N/A void *sahdrp;
2N/A int hdrsize;
2N/A
2N/A if (data->dnode.dn.dn_bonuslen != 0)
2N/A {
2N/A sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
2N/A }
2N/A else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
2N/A {
2N/A blkptr_t *bp = &data->dnode.dn.dn_spill;
2N/A
2N/A err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data);
2N/A if (err)
2N/A return err;
2N/A }
2N/A else
2N/A {
2N/A return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
2N/A }
2N/A
2N/A hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
2N/A file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian);
2N/A }
2N/A else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE)
2N/A {
2N/A file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian);
2N/A }
2N/A else
2N/A return grub_error (GRUB_ERR_BAD_FS, "bad bonus type");
2N/A
2N/A file->data = data;
2N/A file->offset = 0;
2N/A
2N/A#ifndef GRUB_UTIL
2N/A grub_dl_ref (my_mod);
2N/A#endif
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Astatic grub_ssize_t
2N/Agrub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
2N/A{
2N/A struct grub_zfs_data *data = (struct grub_zfs_data *) file->data;
2N/A grub_size_t blksz, movesize;
2N/A grub_size_t length;
2N/A grub_size_t read;
2N/A grub_err_t err;
2N/A
2N/A /*
2N/A * If offset is in memory, move it into the buffer provided and return.
2N/A */
2N/A if (file->offset >= data->file_start
2N/A && file->offset + len <= data->file_end)
2N/A {
2N/A grub_memmove (buf, data->file_buf + file->offset - data->file_start,
2N/A len);
2N/A return len;
2N/A }
2N/A
2N/A blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec,
2N/A data->dnode.endian) << SPA_MINBLOCKSHIFT;
2N/A
2N/A /*
2N/A * Entire Dnode is too big to fit into the space available. We
2N/A * will need to read it in chunks. This could be optimized to
2N/A * read in as large a chunk as there is space available, but for
2N/A * now, this only reads in one data block at a time.
2N/A */
2N/A length = len;
2N/A read = 0;
2N/A while (length)
2N/A {
2N/A void *t;
2N/A /*
2N/A * Find requested blkid and the offset within that block.
2N/A */
2N/A grub_uint64_t blkid = grub_divmod64 (file->offset + read, blksz, 0);
2N/A grub_free (data->file_buf);
2N/A data->file_buf = 0;
2N/A data->file_start = data->file_end = 0;
2N/A
2N/A err = dmu_read (&(data->dnode), blkid, &t,
2N/A 0, data);
2N/A data->file_buf = t;
2N/A if (err)
2N/A {
2N/A data->file_buf = NULL;
2N/A data->file_start = data->file_end = 0;
2N/A return -1;
2N/A }
2N/A
2N/A data->file_start = blkid * blksz;
2N/A data->file_end = data->file_start + blksz;
2N/A
2N/A movesize = data->file_end - file->offset - read;
2N/A if (movesize > length)
2N/A movesize = length;
2N/A
2N/A grub_memmove (buf, data->file_buf + file->offset + read
2N/A - data->file_start, movesize);
2N/A buf += movesize;
2N/A length -= movesize;
2N/A read += movesize;
2N/A }
2N/A
2N/A return len;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_zfs_close (grub_file_t file)
2N/A{
2N/A zfs_unmount ((struct grub_zfs_data *) file->data);
2N/A
2N/A#ifndef GRUB_UTIL
2N/A grub_dl_unref (my_mod);
2N/A#endif
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename,
2N/A grub_uint64_t *mdnobj)
2N/A{
2N/A struct grub_zfs_data *data;
2N/A grub_err_t err;
2N/A int isfs;
2N/A
2N/A data = zfs_mount (dev);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A err = dnode_get_fullpath (fsfilename, &(data->subvol),
2N/A &(data->dnode), &isfs, data);
2N/A *mdnobj = data->subvol.obj;
2N/A zfs_unmount (data);
2N/A return err;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_zfs_defaultbootfsobj (grub_device_t dev, grub_uint64_t *mdnobj)
2N/A{
2N/A struct grub_zfs_data *data;
2N/A grub_err_t err;
2N/A
2N/A data = zfs_mount (dev);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A err = get_default_bootfsobj (&(data->mos), mdnobj, data);
2N/A zfs_unmount (data);
2N/A
2N/A return err;
2N/A}
2N/A
2N/Agrub_err_t
2N/Agrub_zfs_defaultbootfsname (grub_device_t dev, char **bootfsname)
2N/A{
2N/A grub_uint64_t mdnobj;
2N/A struct grub_zfs_data *data;
2N/A grub_err_t err;
2N/A
2N/A data = zfs_mount (dev);
2N/A if (! data)
2N/A return grub_errno;
2N/A
2N/A err = get_default_bootfsobj (&(data->mos), &mdnobj, data);
2N/A if (! err)
2N/A err = get_default_bootfsname(&(data->mos), mdnobj, data, &(*bootfsname));
2N/A
2N/A zfs_unmount (data);
2N/A
2N/A return err;
2N/A}
2N/A
2N/Astatic void
2N/Afill_fs_info (struct grub_dirhook_info *info,
2N/A dnode_end_t mdn, struct grub_zfs_data *data)
2N/A{
2N/A grub_err_t err;
2N/A dnode_end_t dn;
2N/A grub_uint64_t objnum;
2N/A grub_uint64_t headobj;
2N/A
2N/A grub_memset (info, 0, sizeof (*info));
2N/A
2N/A info->dir = 1;
2N/A
2N/A if (mdn.dn.dn_type == DMU_OT_DSL_DIR)
2N/A {
2N/A headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian);
2N/A
2N/A err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "failed here\n");
2N/A return;
2N/A }
2N/A }
2N/A make_mdn (&mdn, data);
2N/A err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE,
2N/A &dn, data);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "failed here\n");
2N/A return;
2N/A }
2N/A
2N/A err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "failed here\n");
2N/A return;
2N/A }
2N/A
2N/A err = dnode_get (&mdn, objnum, 0, &dn, data);
2N/A if (err)
2N/A {
2N/A grub_dprintf ("zfs", "failed here\n");
2N/A return;
2N/A }
2N/A
2N/A if (dn.dn.dn_bonustype == DMU_OT_SA)
2N/A {
2N/A void *sahdrp;
2N/A int hdrsize;
2N/A
2N/A if (dn.dn.dn_bonuslen != 0)
2N/A {
2N/A sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn);
2N/A }
2N/A else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
2N/A {
2N/A blkptr_t *bp = &dn.dn.dn_spill;
2N/A
2N/A err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
2N/A if (err)
2N/A return;
2N/A }
2N/A else
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
2N/A return;
2N/A }
2N/A
2N/A hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
2N/A info->mtimeset = 1;
2N/A info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
2N/A }
2N/A
2N/A if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
2N/A {
2N/A info->mtimeset = 1;
2N/A info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian);
2N/A }
2N/A return;
2N/A}
2N/A
2N/Astatic grub_err_t
2N/Agrub_zfs_dir (grub_device_t device, const char *path,
2N/A int (*hook) (const char *, const struct grub_dirhook_info *))
2N/A{
2N/A struct grub_zfs_data *data;
2N/A grub_err_t err;
2N/A int isfs;
2N/A auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val);
2N/A auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name,
2N/A grub_uint64_t val);
2N/A auto int NESTED_FUNC_ATTR iterate_zap_snap (const char *name,
2N/A grub_uint64_t val);
2N/A
2N/A int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val)
2N/A {
2N/A struct grub_dirhook_info info;
2N/A dnode_end_t dn;
2N/A grub_memset (&info, 0, sizeof (info));
2N/A
2N/A dnode_get (&(data->subvol.mdn), val, 0, &dn, data);
2N/A
2N/A if (dn.dn.dn_bonustype == DMU_OT_SA)
2N/A {
2N/A void *sahdrp;
2N/A int hdrsize;
2N/A
2N/A if (dn.dn.dn_bonuslen != 0)
2N/A {
2N/A sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
2N/A }
2N/A else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
2N/A {
2N/A blkptr_t *bp = &dn.dn.dn_spill;
2N/A
2N/A err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
2N/A if (err)
2N/A {
2N/A grub_print_error ();
2N/A return 0;
2N/A }
2N/A }
2N/A else
2N/A {
2N/A grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
2N/A grub_print_error ();
2N/A return 0;
2N/A }
2N/A
2N/A hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
2N/A info.mtimeset = 1;
2N/A info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
2N/A info.case_insensitive = data->subvol.case_insensitive;
2N/A }
2N/A
2N/A if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
2N/A {
2N/A info.mtimeset = 1;
2N/A info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0],
2N/A dn.endian);
2N/A }
2N/A info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
2N/A grub_dprintf ("zfs", "type=%d, name=%s\n",
2N/A (int)dn.dn.dn_type, (char *)name);
2N/A return hook (name, &info);
2N/A }
2N/A
2N/A int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, grub_uint64_t val)
2N/A {
2N/A struct grub_dirhook_info info;
2N/A dnode_end_t mdn;
2N/A err = dnode_get (&(data->mos), val, 0, &mdn, data);
2N/A if (err)
2N/A return 0;
2N/A if (mdn.dn.dn_type != DMU_OT_DSL_DIR)
2N/A return 0;
2N/A
2N/A fill_fs_info (&info, mdn, data);
2N/A return hook (name, &info);
2N/A }
2N/A int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, grub_uint64_t val)
2N/A {
2N/A struct grub_dirhook_info info;
2N/A char *name2;
2N/A int ret;
2N/A dnode_end_t mdn;
2N/A
2N/A err = dnode_get (&(data->mos), val, 0, &mdn, data);
2N/A if (err)
2N/A return 0;
2N/A
2N/A if (mdn.dn.dn_type != DMU_OT_DSL_DATASET)
2N/A return 0;
2N/A
2N/A fill_fs_info (&info, mdn, data);
2N/A
2N/A name2 = grub_malloc (grub_strlen (name) + 2);
2N/A name2[0] = '@';
2N/A grub_memcpy (name2 + 1, name, grub_strlen (name) + 1);
2N/A ret = hook (name2, &info);
2N/A grub_free (name2);
2N/A return ret;
2N/A }
2N/A
2N/A data = zfs_mount (device);
2N/A if (! data)
2N/A return grub_errno;
2N/A err = dnode_get_fullpath (path, &(data->subvol), &(data->dnode), &isfs, data);
2N/A if (err)
2N/A {
2N/A grub_dprintf("zfs", "dnode_get_fullpath failed\n");
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A if (isfs)
2N/A {
2N/A grub_uint64_t childobj, headobj;
2N/A grub_uint64_t snapobj;
2N/A dnode_end_t dn;
2N/A struct grub_dirhook_info info;
2N/A
2N/A fill_fs_info (&info, data->dnode, data);
2N/A hook ("@", &info);
2N/A
2N/A childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
2N/A headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
2N/A err = dnode_get (&(data->mos), childobj,
2N/A DMU_OT_DSL_DIR_CHILD_MAP, &dn, data);
2N/A if (err)
2N/A {
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A
2N/A zap_iterate_u64 (&dn, iterate_zap_fs, data);
2N/A
2N/A err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data);
2N/A if (err)
2N/A {
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A
2N/A snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian);
2N/A
2N/A err = dnode_get (&(data->mos), snapobj,
2N/A DMU_OT_DSL_DS_SNAP_MAP, &dn, data);
2N/A if (err)
2N/A {
2N/A zfs_unmount (data);
2N/A return err;
2N/A }
2N/A
2N/A zap_iterate_u64 (&dn, iterate_zap_snap, data);
2N/A }
2N/A else
2N/A {
2N/A if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS)
2N/A {
2N/A zfs_unmount (data);
2N/A return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
2N/A }
2N/A zap_iterate_u64 (&(data->dnode), iterate_zap, data);
2N/A }
2N/A zfs_unmount (data);
2N/A return grub_errno;
2N/A}
2N/A
2N/A#ifdef GRUB_UTIL
2N/Astatic grub_err_t
2N/Agrub_zfs_embed (grub_device_t device __attribute__ ((unused)),
2N/A unsigned int *nsectors,
2N/A grub_embed_type_t embed_type,
2N/A grub_disk_addr_t **sectors)
2N/A{
2N/A unsigned i;
2N/A
2N/A if (embed_type != GRUB_EMBED_PCBIOS)
2N/A return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
2N/A "ZFS currently supports only PC-BIOS embedding");
2N/A
2N/A if ((VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS) < *nsectors)
2N/A return grub_error (GRUB_ERR_OUT_OF_RANGE,
2N/A "Your core.img is unusually large. "
2N/A "It won't fit in the embedding area.");
2N/A
2N/A *nsectors = (VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS);
2N/A *sectors = grub_malloc (*nsectors * sizeof (**sectors));
2N/A if (!*sectors)
2N/A return grub_errno;
2N/A for (i = 0; i < *nsectors; i++)
2N/A (*sectors)[i] = i + (VDEV_BOOT_OFFSET >> GRUB_DISK_SECTOR_BITS);
2N/A
2N/A return GRUB_ERR_NONE;
2N/A}
2N/A#endif
2N/A
2N/A/* GRUB2 zfs FS module interface structure */
2N/Astatic struct grub_fs grub_zfs_fs = {
2N/A .name = "zfs",
2N/A .dir = grub_zfs_dir,
2N/A .open = grub_zfs_open,
2N/A .read = grub_zfs_read,
2N/A .close = grub_zfs_close,
2N/A .label = zfs_label,
2N/A .uuid = zfs_uuid,
2N/A .mtime = zfs_mtime,
2N/A#ifdef GRUB_UTIL
2N/A .embed = grub_zfs_embed,
2N/A .reserved_first_sector = 1,
2N/A#endif
2N/A .next = 0
2N/A};
2N/A
2N/AGRUB_MOD_INIT (zfs)
2N/A{
2N/A COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE);
2N/A grub_fs_register (&grub_zfs_fs);
2N/A#ifndef GRUB_UTIL
2N/A my_mod = mod;
2N/A#endif
2N/A}
2N/A
2N/AGRUB_MOD_FINI (zfs)
2N/A{
2N/A zfs_free_caches ();
2N/A grub_fs_unregister (&grub_zfs_fs);
2N/A}