From a109bb6d0eb936ac4e2a9f0ee46a269a58ec48ce Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Nov 2012 11:52:02 +0000 Subject: fish --- master/debian/zfs_update.patch | 2415 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2415 insertions(+) create mode 100644 master/debian/zfs_update.patch (limited to 'master/debian/zfs_update.patch') diff --git a/master/debian/zfs_update.patch b/master/debian/zfs_update.patch new file mode 100644 index 0000000..12e4599 --- /dev/null +++ b/master/debian/zfs_update.patch @@ -0,0 +1,2415 @@ + +Revisions: + +3340: ZFS zlib support +3474: Fix 2G limit on ZFS +3486: ZFS fixes +3488: ZFS multi-device and version 33 support +3494: Fix memory leak +3518: Rewrite RAIDZ part based on reverse engineering +3519: Fix RAIDZ(2) for >= 5 devices +3520: Add ability to sustain a single drive failure on both raidz and raidz2 +3521: Support raidz3 +3533: Support second redundancy strip on raidz(2,3) +3534: Support case-insensitive ZFS subvolumes +3535: Support third redundancy strip on raidz3 + +--- a/grub-core/fs/zfs/zfs.c ++++ b/grub-core/fs/zfs/zfs.c +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + + GRUB_MOD_LICENSE ("GPLv3+"); + +@@ -139,6 +140,27 @@ + grub_zfs_endian_t endian; + } dnode_end_t; + ++struct grub_zfs_device_desc ++{ ++ enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type; ++ grub_uint64_t id; ++ grub_uint64_t guid; ++ ++ /* Valid only for non-leafs. */ ++ unsigned n_children; ++ struct grub_zfs_device_desc *children; ++ ++ /* Valid only for RAIDZ. */ ++ unsigned nparity; ++ unsigned ashift; ++ ++ /* Valid only for leaf devices. */ ++ grub_device_t dev; ++ grub_disk_addr_t vdev_phys_sector; ++ uberblock_t current_uberblock; ++ int original; ++}; ++ + struct grub_zfs_data + { + /* cache for a file block of the currently zfs_open()-ed file */ +@@ -153,23 +175,45 @@ + grub_uint64_t dnode_end; + grub_zfs_endian_t dnode_endian; + +- uberblock_t current_uberblock; +- grub_disk_t disk; +- + dnode_end_t mos; + dnode_end_t mdn; + dnode_end_t dnode; + +- grub_disk_addr_t vdev_phys_sector; ++ struct grub_zfs_device_desc *devices_attached; ++ unsigned n_devices_attached; ++ unsigned n_devices_allocated; ++ struct grub_zfs_device_desc *device_original; ++ ++ uberblock_t current_uberblock; ++ ++ int mounted; ++ grub_uint64_t guid; + }; + ++static grub_err_t ++zlib_decompress (void *s, void *d, ++ grub_size_t slen, grub_size_t dlen) ++{ ++ if (grub_zlib_decompress (s, slen, 0, d, dlen) < 0) ++ return grub_errno; ++ return GRUB_ERR_NONE; ++} ++ + static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { + {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ + {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ + {"off", NULL}, /* ZIO_COMPRESS_OFF */ + {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ + {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ +- {"gzip", NULL}, /* ZIO_COMPRESS_GZIP */ ++ {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ ++ {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ ++ {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ ++ {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ ++ {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ ++ {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ ++ {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ ++ {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ ++ {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ + }; + + static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, +@@ -224,7 +268,7 @@ + */ + static grub_err_t + zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, +- grub_zfs_endian_t endian, char *buf, int size) ++ grub_zfs_endian_t endian, char *buf, grub_size_t size) + { + zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; +@@ -253,13 +297,13 @@ + || (actual_cksum.zc_word[2] != zc.zc_word[2]) + || (actual_cksum.zc_word[3] != zc.zc_word[3])) + { +- grub_dprintf ("zfs", "checksum %d verification failed\n", checksum); +- grub_dprintf ("zfs", "actual checksum %16llx %16llx %16llx %16llx\n", ++ grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name); ++ grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n", + (unsigned long long) actual_cksum.zc_word[0], + (unsigned long long) actual_cksum.zc_word[1], + (unsigned long long) actual_cksum.zc_word[2], + (unsigned long long) actual_cksum.zc_word[3]); +- grub_dprintf ("zfs", "expected checksum %16llx %16llx %16llx %16llx\n", ++ grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n", + (unsigned long long) zc.zc_word[0], + (unsigned long long) zc.zc_word[1], + (unsigned long long) zc.zc_word[2], +@@ -319,7 +363,7 @@ + * + */ + static grub_err_t +-uberblock_verify (uberblock_phys_t * ub, int offset) ++uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset) + { + uberblock_t *uber = &ub->ubp_uberblock; + grub_err_t err; +@@ -392,7 +436,7 @@ + } + + static grub_uint64_t +-dva_get_offset (dva_t * dva, grub_zfs_endian_t endian) ++dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian) + { + grub_dprintf ("zfs", "dva=%llx, %llx\n", + (unsigned long long) dva->dva_word[0], +@@ -401,6 +445,842 @@ + endian) << SPA_MINBLOCKSHIFT; + } + ++static grub_err_t ++zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist) ++{ ++ grub_err_t err; ++ ++ *nvlist = grub_malloc (VDEV_PHYS_SIZE); ++ if (!diskdesc->dev) ++ return grub_error (GRUB_ERR_BAD_FS, "member drive unknown"); ++ ++ /* Read in the vdev name-value pair list (112K). */ ++ err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0, ++ VDEV_PHYS_SIZE, *nvlist); ++ if (err) ++ { ++ grub_free (*nvlist); ++ *nvlist = 0; ++ return err; ++ } ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++fill_vdev_info_real (struct grub_zfs_data *data, ++ const char *nvlist, ++ struct grub_zfs_device_desc *fill, ++ struct grub_zfs_device_desc *insert) ++{ ++ char *type; ++ ++ type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); ++ ++ if (!type) ++ return grub_errno; ++ ++ if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id))) ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); ++ ++ if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid))) ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); ++ ++ if (grub_strcmp (type, VDEV_TYPE_DISK) == 0 ++ || grub_strcmp (type, VDEV_TYPE_FILE) == 0) ++ { ++ fill->type = DEVICE_LEAF; ++ ++ if (!fill->dev && fill->guid == insert->guid) ++ { ++ fill->dev = insert->dev; ++ fill->vdev_phys_sector = insert->vdev_phys_sector; ++ fill->current_uberblock = insert->current_uberblock; ++ fill->original = insert->original; ++ if (!data->device_original) ++ data->device_original = fill; ++ } ++ ++ return GRUB_ERR_NONE; ++ } ++ ++ if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0 ++ || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0) ++ { ++ int nelm, i; ++ ++ if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0) ++ fill->type = DEVICE_MIRROR; ++ else ++ { ++ grub_uint64_t par; ++ fill->type = DEVICE_RAIDZ; ++ if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par)) ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity"); ++ fill->nparity = par; ++ if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par)) ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz ashift"); ++ fill->ashift = par; ++ } ++ ++ nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist, ZPOOL_CONFIG_CHILDREN); ++ ++ if (nelm <= 0) ++ return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV"); ++ ++ if (!fill->children) ++ { ++ fill->n_children = nelm; ++ ++ fill->children = grub_zalloc (fill->n_children ++ * sizeof (fill->children[0])); ++ } ++ ++ for (i = 0; i < nelm; i++) ++ { ++ char *child; ++ grub_err_t err; ++ ++ child = grub_zfs_nvlist_lookup_nvlist_array ++ (nvlist, ZPOOL_CONFIG_CHILDREN, i); ++ ++ err = fill_vdev_info_real (data, child, &fill->children[i], insert); ++ ++ grub_free (child); ++ ++ if (err) ++ return err; ++ } ++ return GRUB_ERR_NONE; ++ } ++ ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported", ++ type); ++} ++ ++static grub_err_t ++fill_vdev_info (struct grub_zfs_data *data, ++ char *nvlist, struct grub_zfs_device_desc *diskdesc) ++{ ++ grub_uint64_t id; ++ unsigned i; ++ ++ if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id)) ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); ++ ++ for (i = 0; i < data->n_devices_attached; i++) ++ if (data->devices_attached[i].id == id) ++ return fill_vdev_info_real (data, nvlist, &data->devices_attached[i], ++ diskdesc); ++ ++ data->n_devices_attached++; ++ if (data->n_devices_attached > data->n_devices_allocated) ++ { ++ void *tmp; ++ data->n_devices_allocated = 2 * data->n_devices_attached + 1; ++ data->devices_attached ++ = grub_realloc (tmp = data->devices_attached, ++ data->n_devices_allocated ++ * sizeof (data->devices_attached[0])); ++ if (!data->devices_attached) ++ { ++ data->devices_attached = tmp; ++ return grub_errno; ++ } ++ } ++ ++ grub_memset (&data->devices_attached[data->n_devices_attached - 1], ++ 0, sizeof (data->devices_attached[data->n_devices_attached - 1])); ++ ++ return fill_vdev_info_real (data, nvlist, ++ &data->devices_attached[data->n_devices_attached - 1], ++ diskdesc); ++} ++ ++/* ++ * Check the disk label information and retrieve needed vdev name-value pairs. ++ * ++ */ ++static grub_err_t ++check_pool_label (struct grub_zfs_data *data, ++ struct grub_zfs_device_desc *diskdesc) ++{ ++ grub_uint64_t pool_state, txg = 0; ++ char *nvlist; ++#if 0 ++ char *nv; ++#endif ++ grub_uint64_t poolguid; ++ grub_uint64_t version; ++ int found; ++ grub_err_t err; ++ ++ err = zfs_fetch_nvlist (diskdesc, &nvlist); ++ if (err) ++ return err; ++ ++ grub_dprintf ("zfs", "check 2 passed\n"); ++ ++ found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, ++ &pool_state); ++ if (! found) ++ { ++ grub_free (nvlist); ++ if (! grub_errno) ++ grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found"); ++ return grub_errno; ++ } ++ grub_dprintf ("zfs", "check 3 passed\n"); ++ ++ if (pool_state == POOL_STATE_DESTROYED) ++ { ++ grub_free (nvlist); ++ return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed"); ++ } ++ grub_dprintf ("zfs", "check 4 passed\n"); ++ ++ found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg); ++ if (!found) ++ { ++ grub_free (nvlist); ++ if (! grub_errno) ++ grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found"); ++ return grub_errno; ++ } ++ grub_dprintf ("zfs", "check 6 passed\n"); ++ ++ /* not an active device */ ++ if (txg == 0) ++ { ++ grub_free (nvlist); ++ return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active"); ++ } ++ grub_dprintf ("zfs", "check 7 passed\n"); ++ ++ found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION, ++ &version); ++ if (! found) ++ { ++ grub_free (nvlist); ++ if (! grub_errno) ++ grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found"); ++ return grub_errno; ++ } ++ grub_dprintf ("zfs", "check 8 passed\n"); ++ ++ if (version > SPA_VERSION) ++ { ++ grub_free (nvlist); ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "too new version %llu > %llu", ++ (unsigned long long) version, ++ (unsigned long long) SPA_VERSION); ++ } ++ grub_dprintf ("zfs", "check 9 passed\n"); ++ ++ found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID, ++ &(diskdesc->guid)); ++ if (! found) ++ { ++ grub_free (nvlist); ++ if (! grub_errno) ++ grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found"); ++ return grub_errno; ++ } ++ ++ found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, ++ &poolguid); ++ if (! found) ++ { ++ grub_free (nvlist); ++ if (! grub_errno) ++ grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found"); ++ return grub_errno; ++ } ++ ++ grub_dprintf ("zfs", "check 11 passed\n"); ++ ++ if (data->mounted && data->guid != poolguid) ++ return grub_error (GRUB_ERR_BAD_FS, "another zpool"); ++ else ++ data->guid = poolguid; ++ ++ { ++ char *nv; ++ nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); ++ ++ if (!nv) ++ { ++ grub_free (nvlist); ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree"); ++ } ++ err = fill_vdev_info (data, nv, diskdesc); ++ if (err) ++ { ++ grub_free (nvlist); ++ return err; ++ } ++ } ++ grub_dprintf ("zfs", "check 10 passed\n"); ++ ++ grub_free (nvlist); ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++scan_disk (grub_device_t dev, struct grub_zfs_data *data, ++ int original) ++{ ++ int label = 0; ++ uberblock_phys_t *ub_array, *ubbest = NULL; ++ vdev_boot_header_t *bh; ++ grub_err_t err; ++ int vdevnum; ++ struct grub_zfs_device_desc desc; ++ ++ ub_array = grub_malloc (VDEV_UBERBLOCK_RING); ++ if (!ub_array) ++ return grub_errno; ++ ++ bh = grub_malloc (VDEV_BOOT_HEADER_SIZE); ++ if (!bh) ++ { ++ grub_free (ub_array); ++ return grub_errno; ++ } ++ ++ vdevnum = VDEV_LABELS; ++ ++ desc.dev = dev; ++ desc.original = original; ++ ++ /* Don't check back labels on CDROM. */ ++ if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN) ++ vdevnum = VDEV_LABELS / 2; ++ ++ for (label = 0; ubbest == NULL && label < vdevnum; label++) ++ { ++ desc.vdev_phys_sector ++ = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT) ++ + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT) ++ + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk) ++ - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)); ++ ++ /* Read in the uberblock ring (128K). */ ++ err = grub_disk_read (dev->disk, desc.vdev_phys_sector ++ + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT), ++ 0, VDEV_UBERBLOCK_RING, (char *) ub_array); ++ if (err) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ continue; ++ } ++ grub_dprintf ("zfs", "label ok %d\n", label); ++ ++ ubbest = find_bestub (ub_array, desc.vdev_phys_sector); ++ if (!ubbest) ++ { ++ grub_dprintf ("zfs", "No uberblock found\n"); ++ grub_errno = GRUB_ERR_NONE; ++ continue; ++ } ++ ++ grub_memmove (&(desc.current_uberblock), ++ &ubbest->ubp_uberblock, sizeof (uberblock_t)); ++ if (original) ++ grub_memmove (&(data->current_uberblock), ++ &ubbest->ubp_uberblock, sizeof (uberblock_t)); ++ ++ err = check_pool_label (data, &desc); ++ if (err) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ continue; ++ } ++#if 0 ++ if (find_best_root && ++ vdev_uberblock_compare (&ubbest->ubp_uberblock, ++ &(current_uberblock)) <= 0) ++ continue; ++#endif ++ grub_free (ub_array); ++ grub_free (bh); ++ return GRUB_ERR_NONE; ++ } ++ ++ grub_free (ub_array); ++ grub_free (bh); ++ ++ return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label"); ++} ++ ++static grub_err_t ++scan_devices (struct grub_zfs_data *data) ++{ ++ auto int hook (const char *name); ++ int hook (const char *name) ++ { ++ grub_device_t dev; ++ grub_err_t err; ++ dev = grub_device_open (name); ++ if (!dev) ++ return 0; ++ if (!dev->disk) ++ { ++ grub_device_close (dev); ++ return 0; ++ } ++ err = scan_disk (dev, data, 0); ++ if (err == GRUB_ERR_BAD_FS) ++ { ++ grub_device_close (dev); ++ grub_errno = GRUB_ERR_NONE; ++ return 0; ++ } ++ if (err) ++ { ++ grub_device_close (dev); ++ grub_print_error (); ++ return 0; ++ } ++ ++ return 0; ++ } ++ grub_device_iterate (hook); ++ return GRUB_ERR_NONE; ++} ++ ++static inline void ++xor (grub_uint64_t *a, const grub_uint64_t *b, grub_size_t s) ++{ ++ s /= sizeof (grub_uint64_t); ++ while (s--) ++ *a++ ^= *b++; ++} ++ ++/* x**y. */ ++static grub_uint8_t powx[255 * 2]; ++/* Such an s that x**s = y */ ++static int powx_inv[256]; ++static const grub_uint8_t poly = 0x1d; ++ ++/* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */ ++static inline void ++xor_out (void *a_in, const void *b_in, grub_size_t s, ++ int known_idx, int recovery_pow) ++{ ++ int add; ++ grub_uint8_t *a = a_in; ++ const grub_uint8_t *b = b_in; ++ ++ /* Simple xor. */ ++ if (known_idx == 0 || recovery_pow == 0) ++ { ++ xor (a_in, b_in, s); ++ return; ++ } ++ add = (known_idx * recovery_pow) % 255; ++ for (;s--; b++, a++) ++ if (*b) ++ *a ^= powx[powx_inv[*b] + add]; ++} ++ ++static inline grub_uint8_t ++gf_mul (grub_uint8_t a, grub_uint8_t b) ++{ ++ if (a == 0 || b == 0) ++ return 0; ++ return powx[powx_inv[a] + powx_inv[b]]; ++} ++ ++static inline grub_err_t ++recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs, ++ const unsigned *powers, ++ const int *idx) ++{ ++ grub_dprintf ("zfs", "recovering %u bufers\n", nbufs); ++ /* Now we have */ ++ /* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/ ++ /* Let's invert the matrix in question. */ ++ switch (nbufs) ++ { ++ /* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */ ++ case 1: ++ { ++ int add; ++ grub_uint8_t *a; ++ if (powers[0] == 0 || idx[0] == 0) ++ return GRUB_ERR_NONE; ++ add = 255 - ((powers[0] * idx[0]) % 255); ++ for (a = bufs[0]; s--; a++) ++ if (*a) ++ *a = powx[powx_inv[*a] + add]; ++ return GRUB_ERR_NONE; ++ } ++ /* Case 2x2: Let's use the determinant formula. */ ++ case 2: ++ { ++ grub_uint8_t det, det_inv; ++ grub_uint8_t matrixinv[2][2]; ++ unsigned i; ++ /* The determinant is: */ ++ det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255] ++ ^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]); ++ if (det == 0) ++ return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); ++ det_inv = powx[255 - powx_inv[det]]; ++ matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv); ++ matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv); ++ matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv); ++ matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv); ++ for (i = 0; i < s; i++) ++ { ++ grub_uint8_t b0, b1; ++ b0 = bufs[0][i]; ++ b1 = bufs[1][i]; ++ ++ bufs[0][i] = (gf_mul (b0, matrixinv[0][0]) ++ ^ gf_mul (b1, matrixinv[0][1])); ++ bufs[1][i] = (gf_mul (b0, matrixinv[1][0]) ++ ^ gf_mul (b1, matrixinv[1][1])); ++ } ++ return GRUB_ERR_NONE; ++ } ++ /* Otherwise use Gauss. */ ++ default: ++ { ++ grub_uint8_t matrix1[nbufs][nbufs], matrix2[nbufs][nbufs]; ++ int i, j, k; ++ ++ for (i = 0; i < nbufs; i++) ++ for (j = 0; j < nbufs; j++) ++ matrix1[i][j] = powx[(powers[i] * idx[j]) % 255]; ++ for (i = 0; i < nbufs; i++) ++ for (j = 0; j < nbufs; j++) ++ matrix2[i][j] = 0; ++ for (i = 0; i < nbufs; i++) ++ matrix2[i][i] = 1; ++ ++ for (i = 0; i < nbufs; i++) ++ { ++ grub_uint8_t mul; ++ for (j = i; j < nbufs; j++) ++ if (matrix1[i][j]) ++ break; ++ if (j == nbufs) ++ return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); ++ if (j != i) ++ { ++ int xchng; ++ xchng = j; ++ for (j = 0; j < nbufs; j++) ++ { ++ grub_uint8_t t; ++ t = matrix1[xchng][j]; ++ matrix1[xchng][j] = matrix1[i][j]; ++ matrix1[i][j] = t; ++ } ++ for (j = 0; j < nbufs; j++) ++ { ++ grub_uint8_t t; ++ t = matrix2[xchng][j]; ++ matrix2[xchng][j] = matrix2[i][j]; ++ matrix2[i][j] = t; ++ } ++ } ++ mul = powx[255 - powx_inv[matrix1[i][i]]]; ++ for (j = 0; j < nbufs; j++) ++ matrix1[i][j] = gf_mul (matrix1[i][j], mul); ++ for (j = 0; j < nbufs; j++) ++ matrix2[i][j] = gf_mul (matrix2[i][j], mul); ++ for (j = i + 1; j < nbufs; j++) ++ { ++ mul = matrix1[j][i]; ++ for (k = 0; k < nbufs; k++) ++ matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); ++ for (k = 0; k < nbufs; k++) ++ matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); ++ } ++ } ++ for (i = nbufs - 1; i >= 0; i--) ++ { ++ for (j = 0; j < i; j++) ++ { ++ grub_uint8_t mul; ++ mul = matrix1[j][i]; ++ for (k = 0; k < nbufs; k++) ++ matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); ++ for (k = 0; k < nbufs; k++) ++ matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); ++ } ++ } ++ ++ for (i = 0; i < (int) s; i++) ++ { ++ grub_uint8_t b[nbufs]; ++ for (j = 0; j < nbufs; j++) ++ b[j] = bufs[j][i]; ++ for (j = 0; j < nbufs; j++) ++ { ++ bufs[j][i] = 0; ++ for (k = 0; k < nbufs; k++) ++ bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]); ++ } ++ } ++ return GRUB_ERR_NONE; ++ } ++ } ++} ++ ++static grub_err_t ++read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, ++ grub_size_t len, void *buf) ++{ ++ switch (desc->type) ++ { ++ case DEVICE_LEAF: ++ { ++ grub_uint64_t sector; ++ sector = DVA_OFFSET_TO_PHYS_SECTOR (offset); ++ if (!desc->dev) ++ { ++ return grub_error (GRUB_ERR_BAD_FS, "member drive unknown"); ++ } ++ /* read in a data block */ ++ return grub_disk_read (desc->dev->disk, sector, 0, len, buf); ++ } ++ case DEVICE_MIRROR: ++ { ++ grub_err_t err = GRUB_ERR_NONE; ++ unsigned i; ++ if (desc->n_children <= 0) ++ return grub_error (GRUB_ERR_BAD_FS, ++ "non-positive number of mirror children"); ++ for (i = 0; i < desc->n_children; i++) ++ { ++ err = read_device (offset, &desc->children[i], ++ len, buf); ++ if (!err) ++ break; ++ grub_errno = GRUB_ERR_NONE; ++ } ++ return (grub_errno = err); ++ } ++ case DEVICE_RAIDZ: ++ { ++ unsigned c = 0; ++ grub_uint64_t high; ++ grub_uint64_t devn; ++ grub_uint64_t m; ++ grub_uint32_t s, orig_s; ++ void *orig_buf = buf; ++ grub_size_t orig_len = len; ++ grub_uint8_t *recovery_buf[4]; ++ grub_size_t recovery_len[4]; ++ int recovery_idx[4]; ++ unsigned failed_devices = 0; ++ int idx, orig_idx; ++ ++ if (desc->nparity < 1 || desc->nparity > 3) ++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, ++ "raidz%d is not supported", desc->nparity); ++ ++ orig_s = (((len + (1 << desc->ashift) - 1) >> desc->ashift) ++ + (desc->n_children - desc->nparity) - 1); ++ s = orig_s; ++ ++ high = grub_divmod64_full ((offset >> desc->ashift), ++ desc->n_children, &m); ++ if (desc->nparity == 2) ++ c = 2; ++ if (desc->nparity == 3) ++ c = 3; ++ if (((len + (1 << desc->ashift) - 1) >> desc->ashift) ++ >= (desc->n_children - desc->nparity)) ++ idx = (desc->n_children - desc->nparity - 1); ++ else ++ idx = ((len + (1 << desc->ashift) - 1) >> desc->ashift) - 1; ++ orig_idx = idx; ++ while (len > 0) ++ { ++ grub_size_t csize; ++ grub_uint32_t bsize; ++ grub_err_t err; ++ bsize = s / (desc->n_children - desc->nparity); ++ ++ if (desc->nparity == 1 ++ && ((offset >> (desc->ashift + 11)) & 1) == c) ++ c++; ++ ++ high = grub_divmod64_full ((offset >> desc->ashift) + c, ++ desc->n_children, &devn); ++ csize = bsize << desc->ashift; ++ if (csize > len) ++ csize = len; ++ ++ grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T ++ "+%u (%" PRIxGRUB_SIZE ", %" PRIxGRUB_UINT32_T ++ ") -> (0x%" PRIxGRUB_UINT64_T ", 0x%" ++ PRIxGRUB_UINT64_T ")\n", ++ offset >> desc->ashift, c, len, bsize, high, ++ devn); ++ err = read_device ((high << desc->ashift) ++ | (offset & ((1 << desc->ashift) - 1)), ++ &desc->children[devn], ++ csize, buf); ++ if (err && failed_devices < desc->nparity) ++ { ++ recovery_buf[failed_devices] = buf; ++ recovery_len[failed_devices] = csize; ++ recovery_idx[failed_devices] = idx; ++ failed_devices++; ++ grub_errno = err = 0; ++ } ++ if (err) ++ return err; ++ ++ c++; ++ idx--; ++ s--; ++ buf = (char *) buf + csize; ++ len -= csize; ++ } ++ if (failed_devices) ++ { ++ unsigned redundancy_pow[4]; ++ unsigned cur_redundancy_pow = 0; ++ unsigned n_redundancy = 0; ++ unsigned i, j; ++ grub_err_t err; ++ ++ /* Compute mul. x**s has a period of 255. */ ++ if (powx[0] == 0) ++ { ++ grub_uint8_t cur = 1; ++ for (i = 0; i < 255; i++) ++ { ++ powx[i] = cur; ++ powx[i + 255] = cur; ++ powx_inv[cur] = i; ++ if (cur & 0x80) ++ cur = (cur << 1) ^ poly; ++ else ++ cur <<= 1; ++ } ++ } ++ ++ /* Read redundancy data. */ ++ for (n_redundancy = 0, cur_redundancy_pow = 0; ++ n_redundancy < failed_devices; ++ cur_redundancy_pow++) ++ { ++ high = grub_divmod64_full ((offset >> desc->ashift) ++ + cur_redundancy_pow ++ + ((desc->nparity == 1) ++ && ((offset >> (desc->ashift + 11)) ++ & 1)), ++ desc->n_children, &devn); ++ err = read_device ((high << desc->ashift) ++ | (offset & ((1 << desc->ashift) - 1)), ++ &desc->children[devn], ++ recovery_len[n_redundancy], ++ recovery_buf[n_redundancy]); ++ /* Ignore error if we may still have enough devices. */ ++ if (err && n_redundancy + desc->nparity - cur_redundancy_pow - 1 ++ >= failed_devices) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ continue; ++ } ++ if (err) ++ return err; ++ redundancy_pow[n_redundancy] = cur_redundancy_pow; ++ n_redundancy++; ++ } ++ /* Now xor-our the parts we already know. */ ++ buf = orig_buf; ++ len = orig_len; ++ s = orig_s; ++ idx = orig_idx; ++ ++ while (len > 0) ++ { ++ grub_size_t csize; ++ csize = ((s / (desc->n_children - desc->nparity)) ++ << desc->ashift); ++ if (csize > len) ++ csize = len; ++ ++ for (j = 0; j < failed_devices; j++) ++ if (buf == recovery_buf[j]) ++ break; ++ ++ if (j == failed_devices) ++ for (j = 0; j < failed_devices; j++) ++ xor_out (recovery_buf[j], buf, ++ csize < recovery_len[j] ? csize : recovery_len[j], ++ idx, redundancy_pow[j]); ++ ++ s--; ++ buf = (char *) buf + csize; ++ len -= csize; ++ idx--; ++ } ++ for (i = 0; i < failed_devices ++ && recovery_len[i] == recovery_len[0]; ++ i++); ++ /* Since the chunks have variable length handle the last block ++ separately. */ ++ if (i != failed_devices) ++ { ++ grub_uint8_t *tmp_recovery_buf[4]; ++ for (j = 0; j < i; j++) ++ tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[j] - 1; ++ err = recovery (tmp_recovery_buf, 1, i, redundancy_pow, ++ recovery_idx); ++ if (err) ++ return err; ++ } ++ err = recovery (recovery_buf, recovery_len[failed_devices - 1], ++ failed_devices, redundancy_pow, recovery_idx); ++ if (err) ++ return err; ++ } ++ return GRUB_ERR_NONE; ++ } ++ } ++ return grub_error (GRUB_ERR_BAD_FS, "unsupported device type"); ++} ++ ++static grub_err_t ++read_dva (const dva_t *dva, ++ grub_zfs_endian_t endian, struct grub_zfs_data *data, ++ void *buf, grub_size_t len) ++{ ++ grub_uint64_t offset; ++ unsigned i; ++ grub_err_t err; ++ int try = 0; ++ offset = dva_get_offset (dva, endian); ++ ++ for (try = 0; try < 2; try++) ++ { ++ for (i = 0; i < data->n_devices_attached; i++) ++ if (data->devices_attached[i].id == DVA_GET_VDEV (dva)) ++ { ++ err = read_device (offset, &data->devices_attached[i], len, buf); ++ if (!err) ++ return GRUB_ERR_NONE; ++ break; ++ } ++ if (try == 1) ++ break; ++ err = scan_devices (data); ++ if (err) ++ return err; ++ } ++ return err; ++} + + /* + * Read a block of data based on the gang block address dva, +@@ -412,7 +1292,6 @@ + struct grub_zfs_data *data) + { + zio_gbh_phys_t *zio_gb; +- grub_uint64_t offset, sector; + unsigned i; + grub_err_t err; + zio_cksum_t zc; +@@ -424,13 +1303,8 @@ + return grub_errno; + grub_dprintf ("zfs", endian == LITTLE_ENDIAN ? "little-endian gang\n" + :"big-endian gang\n"); +- offset = dva_get_offset (dva, endian); +- sector = DVA_OFFSET_TO_PHYS_SECTOR (offset); +- grub_dprintf ("zfs", "offset=%llx\n", (unsigned long long) offset); + +- /* read in the gang block header */ +- err = grub_disk_read (data->disk, sector, 0, SPA_GANGBLOCKSIZE, +- (char *) zio_gb); ++ err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE); + if (err) + { + grub_free (zio_gb); +@@ -483,20 +1357,13 @@ + /* pick a good dva from the block pointer */ + for (i = 0; i < SPA_DVAS_PER_BP; i++) + { +- grub_uint64_t offset, sector; +- + if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) + continue; + + if ((grub_zfs_to_cpu64 (bp->blk_dva[i].dva_word[1], endian)>>63) & 1) + err = zio_read_gang (bp, endian, &bp->blk_dva[i], buf, data); + else +- { +- /* read in a data block */ +- offset = dva_get_offset (&bp->blk_dva[i], endian); +- sector = DVA_OFFSET_TO_PHYS_SECTOR (offset); +- err = grub_disk_read (data->disk, sector, 0, psize, buf); +- } ++ err = read_dva (&bp->blk_dva[i], endian, data, buf, psize); + if (!err) + return GRUB_ERR_NONE; + grub_errno = GRUB_ERR_NONE; +@@ -527,7 +1394,7 @@ + *buf = NULL; + + checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; +- comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0x7; ++ comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff; + lsize = (BP_IS_HOLE(bp) ? 0 : + (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1) + << SPA_MINBLOCKSHIFT)); +@@ -602,7 +1469,8 @@ + dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, + grub_zfs_endian_t *endian_out, struct grub_zfs_data *data) + { +- int idx, level; ++ int level; ++ grub_off_t idx; + blkptr_t *bp_array = dn->dn.dn_blkptr; + int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT; + blkptr_t *bp; +@@ -670,7 +1538,8 @@ + */ + static grub_err_t + mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian, +- int objsize, char *name, grub_uint64_t * value) ++ int objsize, char *name, grub_uint64_t * value, ++ int case_insensitive) + { + int i, chunks; + mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; +@@ -678,7 +1547,8 @@ + chunks = objsize / MZAP_ENT_LEN - 1; + for (i = 0; i < chunks; i++) + { +- if (grub_strcmp (mzap_ent[i].mze_name, name) == 0) ++ if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0) ++ : (grub_strcmp (mzap_ent[i].mze_name, name) == 0)) + { + *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian); + return GRUB_ERR_NONE; +@@ -711,7 +1581,8 @@ + } + + static grub_uint64_t +-zap_hash (grub_uint64_t salt, const char *name) ++zap_hash (grub_uint64_t salt, const char *name, ++ int case_insensitive) + { + static grub_uint64_t table[256]; + const grub_uint8_t *cp; +@@ -729,8 +1600,12 @@ + } + } + +- for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) +- crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; ++ if (case_insensitive) ++ for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) ++ crc = (crc >> 8) ^ table[(crc ^ grub_toupper (c)) & 0xFF]; ++ else ++ for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) ++ crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; + + /* + * Only use 28 bits, since we need 4 bits in the cookie for the +@@ -748,10 +1623,34 @@ + * array_len is actual len in bytes (not encoded le_value_length). + * buf is null-terminated. + */ ++ ++static inline int ++name_cmp (const char *s1, const char *s2, grub_size_t n, ++ int case_insensitive) ++{ ++ const char *t1 = (const char *) s1; ++ const char *t2 = (const char *) s2; ++ ++ if (!case_insensitive) ++ return grub_memcmp (t1, t2, n); ++ ++ while (n--) ++ { ++ if (grub_toupper (*t1) != grub_toupper (*t2)) ++ return (int) grub_toupper (*t1) - (int) grub_toupper (*t2); ++ ++ t1++; ++ t2++; ++ } ++ ++ return 0; ++} ++ + /* XXX */ + static int + zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian, +- int blksft, int chunk, int array_len, const char *buf) ++ int blksft, int chunk, int array_len, const char *buf, ++ int case_insensitive) + { + int bseen = 0; + +@@ -763,7 +1662,8 @@ + if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) + return (0); + +- if (grub_memcmp (la->la_array, buf + bseen, toread) != 0) ++ if (name_cmp ((char *) la->la_array, buf + bseen, toread, ++ case_insensitive) != 0) + break; + chunk = grub_zfs_to_cpu16 (la->la_next, endian); + bseen += toread; +@@ -804,7 +1704,8 @@ + static grub_err_t + zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian, + int blksft, grub_uint64_t h, +- const char *name, grub_uint64_t * value) ++ const char *name, grub_uint64_t * value, ++ int case_insensitive) + { + grub_uint16_t chunk; + struct zap_leaf_entry *le; +@@ -816,7 +1717,7 @@ + return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic"); + + for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h)], endian); +- chunk != CHAIN_END; chunk = le->le_next) ++ chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian)) + { + + if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) +@@ -836,11 +1737,12 @@ + if (zap_leaf_array_equal (l, endian, blksft, + grub_zfs_to_cpu16 (le->le_name_chunk,endian), + grub_zfs_to_cpu16 (le->le_name_length, endian), +- name)) ++ name, case_insensitive)) + { + struct zap_leaf_array *la; + +- if (le->le_int_size != 8 || le->le_value_length != 1) ++ if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length, ++ endian) != 1) + return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry"); + + /* get the uint64_t property value */ +@@ -858,9 +1760,9 @@ + + /* Verify if this is a fat zap header block */ + static grub_err_t +-zap_verify (zap_phys_t *zap) ++zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian) + { +- if (zap->zap_magic != (grub_uint64_t) ZAP_MAGIC) ++ if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic"); + + if (zap->zap_flags != 0) +@@ -879,7 +1781,8 @@ + /* XXX */ + static grub_err_t + fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap, +- char *name, grub_uint64_t * value, struct grub_zfs_data *data) ++ char *name, grub_uint64_t * value, struct grub_zfs_data *data, ++ int case_insensitive) + { + void *l; + grub_uint64_t hash, idx, blkid; +@@ -888,18 +1791,18 @@ + grub_err_t err; + grub_zfs_endian_t leafendian; + +- err = zap_verify (zap); ++ err = zap_verify (zap, zap_dnode->endian); + if (err) + return err; + +- hash = zap_hash (zap->zap_salt, name); ++ hash = zap_hash (zap->zap_salt, name, case_insensitive); + + /* get block id from index */ + if (zap->zap_ptrtbl.zt_numblks != 0) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "external pointer tables not supported"); + idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift); +- blkid = ((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; ++ blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian); + + /* Get the leaf block */ + if ((1U << blksft) < sizeof (zap_leaf_phys_t)) +@@ -908,7 +1811,8 @@ + if (err) + return err; + +- err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value); ++ err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value, ++ case_insensitive); + grub_free (l); + return err; + } +@@ -922,14 +1826,14 @@ + { + zap_leaf_phys_t *l; + void *l_in; +- grub_uint64_t idx, blkid; ++ grub_uint64_t idx, idx2, blkid; + grub_uint16_t chunk; + int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + zap_dnode->endian) << DNODE_SHIFT); + grub_err_t err; + grub_zfs_endian_t endian; + +- if (zap_verify (zap)) ++ if (zap_verify (zap, zap_dnode->endian)) + return 0; + + /* get block id from index */ +@@ -945,9 +1849,17 @@ + grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); + return 0; + } +- for (idx = 0; idx < zap->zap_ptrtbl.zt_numblks; idx++) ++ for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++) + { +- blkid = ((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; ++ blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], ++ zap_dnode->endian); ++ ++ for (idx2 = 0; idx2 < idx; idx2++) ++ if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))], ++ zap_dnode->endian)) ++ break; ++ if (idx2 != idx) ++ continue; + + err = dmu_read (zap_dnode, blkid, &l_in, &endian, data); + l = l_in; +@@ -983,8 +1895,11 @@ + + buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) + + 1); +- if (zap_leaf_array_get (l, endian, blksft, le->le_name_chunk, +- le->le_name_length, buf)) ++ if (zap_leaf_array_get (l, endian, blksft, ++ grub_zfs_to_cpu16 (le->le_name_chunk, ++ endian), ++ grub_zfs_to_cpu16 (le->le_name_length, ++ endian), buf)) + { + grub_free (buf); + continue; +@@ -996,7 +1911,9 @@ + continue; + + /* get the uint64_t property value */ +- la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk).l_array; ++ la = &ZAP_LEAF_CHUNK (l, blksft, ++ grub_zfs_to_cpu16 (le->le_value_chunk, ++ endian)).l_array; + val = grub_be_to_cpu64 (la->la_array64); + if (hook (buf, val)) + return 1; +@@ -1014,7 +1931,7 @@ + */ + static grub_err_t + zap_lookup (dnode_end_t * zap_dnode, char *name, grub_uint64_t * val, +- struct grub_zfs_data *data) ++ struct grub_zfs_data *data, int case_insensitive) + { + grub_uint64_t block_type; + int size; +@@ -1037,7 +1954,8 @@ + if (block_type == ZBT_MICRO) + { + grub_dprintf ("zfs", "micro zap\n"); +- err = (mzap_lookup (zapbuf, endian, size, name, val)); ++ err = mzap_lookup (zapbuf, endian, size, name, val, ++ case_insensitive); + grub_dprintf ("zfs", "returned %d\n", err); + grub_free (zapbuf); + return err; +@@ -1046,7 +1964,8 @@ + { + grub_dprintf ("zfs", "fat zap\n"); + /* this is a fat zap */ +- err = (fzap_lookup (zap_dnode, zapbuf, name, val, data)); ++ err = fzap_lookup (zap_dnode, zapbuf, name, val, data, ++ case_insensitive); + grub_dprintf ("zfs", "returned %d\n", err); + grub_free (zapbuf); + return err; +@@ -1074,7 +1993,7 @@ + return 0; + block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); + +- grub_dprintf ("zfs", "zap read\n"); ++ grub_dprintf ("zfs", "zap iterate\n"); + + if (block_type == ZBT_MICRO) + { +@@ -1172,9 +2091,9 @@ + */ + static grub_err_t + dnode_get_path (dnode_end_t * mdn, const char *path_in, dnode_end_t * dn, +- struct grub_zfs_data *data) ++ struct grub_zfs_data *data, int *case_insensitive) + { +- grub_uint64_t objnum, version; ++ grub_uint64_t objnum, version, insensitivity; + char *cname, ch; + grub_err_t err = GRUB_ERR_NONE; + char *path, *path_buf; +@@ -1199,19 +2118,31 @@ + return err; + } + +- err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version, data); ++ err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version, ++ data, 0); + if (err) + { + grub_free (dn_new); + return err; + } ++ + if (version > ZPL_VERSION) + { + grub_free (dn_new); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version"); + } +- +- err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data); ++ ++ err = zap_lookup (&(dnode_path->dn), "casesensitivity", &insensitivity, ++ data, 0); ++ if (err == GRUB_ERR_FILE_NOT_FOUND) ++ { ++ grub_errno = GRUB_ERR_NONE; ++ insensitivity = 0; ++ } ++ if (case_insensitive) ++ *case_insensitive = insensitivity; ++ ++ err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0); + if (err) + { + grub_free (dn_new); +@@ -1272,7 +2203,7 @@ + grub_free (path_buf); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + } +- err = zap_lookup (&(dnode_path->dn), cname, &objnum, data); ++ err = zap_lookup (&(dnode_path->dn), cname, &objnum, data, insensitivity); + if (err) + break; + +@@ -1291,22 +2222,54 @@ + break; + + *path = ch; +-#if 0 +- if (((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa && ch) ++ if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE ++ && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) + { ++ char *sym_value; ++ grub_size_t sym_sz; ++ int free_symval = 0; + char *oldpath = path, *oldpathbuf = path_buf; +- path = path_buf +- = grub_malloc (sizeof (dnode_path->dn.dn.dn_bonus) +- - sizeof (znode_phys_t) + grub_strlen (oldpath) + 1); ++ sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys)); ++ ++ sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian); ++ ++ if (dnode_path->dn.dn.dn_flags & 1) ++ { ++ grub_size_t block; ++ grub_size_t blksz; ++ blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, ++ dnode_path->dn.endian) ++ << SPA_MINBLOCKSHIFT); ++ ++ sym_value = grub_malloc (sym_sz); ++ if (!sym_value) ++ return grub_errno; ++ for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++) ++ { ++ void *t; ++ grub_size_t movesize; ++ ++ err = dmu_read (&(dnode_path->dn), block, &t, 0, data); ++ if (err) ++ return err; ++ ++ movesize = MIN (sym_sz - block * blksz, blksz); ++ ++ grub_memcpy (sym_value + block * blksz, t, movesize); ++ grub_free (t); ++ } ++ free_symval = 1; ++ } ++ path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); + if (!path_buf) + { + grub_free (oldpathbuf); + return grub_errno; + } +- grub_memcpy (path, +- (char *) DN_BONUS(&dnode_path->dn.dn) + sizeof (znode_phys_t), +- sizeof (dnode_path->dn.dn.dn_bonus) - sizeof (znode_phys_t)); +- path [sizeof (dnode_path->dn.dn.dn_bonus) - sizeof (znode_phys_t)] = 0; ++ grub_memcpy (path, sym_value, sym_sz); ++ if (free_symval) ++ grub_free (sym_value); ++ path [sym_sz] = 0; + grub_memcpy (path + grub_strlen (path), oldpath, + grub_strlen (oldpath) + 1); + +@@ -1324,7 +2287,62 @@ + grub_free (dn_new); + } + } +-#endif ++ if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA) ++ { ++ void *sahdrp; ++ int hdrsize; ++ ++ if (dnode_path->dn.dn.dn_bonuslen != 0) ++ { ++ sahdrp = DN_BONUS (&dnode_path->dn.dn); ++ } ++ else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) ++ { ++ blkptr_t *bp = &dnode_path->dn.dn.dn_spill; ++ ++ err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data); ++ if (err) ++ return err; ++ } ++ else ++ { ++ return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); ++ } ++ ++ hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); ++ ++ if (((grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_TYPE_OFFSET), dnode_path->dn.endian) >> 12) & 0xf) == 0xa) ++ { ++ char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET; ++ grub_size_t sym_sz = ++ grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), dnode_path->dn.endian); ++ char *oldpath = path, *oldpathbuf = path_buf; ++ path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); ++ if (!path_buf) ++ { ++ grub_free (oldpathbuf); ++ return grub_errno; ++ } ++ grub_memcpy (path, sym_value, sym_sz); ++ path [sym_sz] = 0; ++ grub_memcpy (path + grub_strlen (path), oldpath, ++ grub_strlen (oldpath) + 1); ++ ++ grub_free (oldpathbuf); ++ if (path[0] != '/') ++ { ++ dn_new = dnode_path; ++ dnode_path = dn_new->next; ++ grub_free (dn_new); ++ } ++ else while (dnode_path != root) ++ { ++ dn_new = dnode_path; ++ dnode_path = dn_new->next; ++ grub_free (dn_new); ++ } ++ } ++ } + } + + if (!err) +@@ -1417,7 +2435,7 @@ + + grub_dprintf ("zfs", "alive\n"); + +- err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data); ++ err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data, 0); + if (err) + return err; + +@@ -1452,7 +2470,7 @@ + if (err) + return err; + +- err = zap_lookup (mdn, cname, &objnum, data); ++ err = zap_lookup (mdn, cname, &objnum, data, 0); + if (err) + return err; + +@@ -1495,7 +2513,7 @@ + static grub_err_t + dnode_get_fullpath (const char *fullpath, dnode_end_t * mdn, + grub_uint64_t *mdnobj, dnode_end_t * dn, int *isfs, +- struct grub_zfs_data *data) ++ struct grub_zfs_data *data, int *case_insensitive) + { + char *fsname, *snapname; + const char *ptr_at, *filename; +@@ -1573,7 +2591,7 @@ + err = dnode_get (&(data->mos), snapobj, + DMU_OT_DSL_DS_SNAP_MAP, mdn, data); + if (!err) +- err = zap_lookup (mdn, snapname, &headobj, data); ++ err = zap_lookup (mdn, snapname, &headobj, data, 0); + if (!err) + err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); + if (err) +@@ -1597,7 +2615,7 @@ + grub_free (snapname); + return GRUB_ERR_NONE; + } +- err = dnode_get_path (mdn, filename, dn, data); ++ err = dnode_get_path (mdn, filename, dn, data, case_insensitive); + grub_free (fsname); + grub_free (snapname); + return err; +@@ -1625,11 +2643,12 @@ + */ + + static int +-nvlist_find_value (char *nvlist, char *name, int valtype, char **val, ++nvlist_find_value (const char *nvlist, const char *name, ++ int valtype, char **val, + grub_size_t *size_out, grub_size_t *nelm_out) + { + int name_len, type, encode_size; +- char *nvpair, *nvp_name; ++ const char *nvpair, *nvp_name; + + /* Verify if the 1st and 2nd byte in the nvlist are valid. */ + /* NOTE: independently of what endianness header announces all +@@ -1671,7 +2690,7 @@ + + if ((grub_strncmp (nvp_name, name, name_len) == 0) && type == valtype) + { +- *val = nvpair; ++ *val = (char *) nvpair; + *size_out = encode_size; + if (nelm_out) + *nelm_out = nelm; +@@ -1684,7 +2703,8 @@ + } + + int +-grub_zfs_nvlist_lookup_uint64 (char *nvlist, char *name, grub_uint64_t * out) ++grub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name, ++ grub_uint64_t * out) + { + char *nvpair; + grub_size_t size; +@@ -1704,7 +2724,7 @@ + } + + char * +-grub_zfs_nvlist_lookup_string (char *nvlist, char *name) ++grub_zfs_nvlist_lookup_string (const char *nvlist, const char *name) + { + char *nvpair; + char *ret; +@@ -1732,7 +2752,7 @@ + } + + char * +-grub_zfs_nvlist_lookup_nvlist (char *nvlist, char *name) ++grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name) + { + char *nvpair; + char *ret; +@@ -1753,199 +2773,114 @@ + } + + int +-grub_zfs_nvlist_lookup_nvlist_array_get_nelm (char *nvlist, char *name) +-{ +- char *nvpair; +- grub_size_t nelm, size; +- int found; +- +- found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair, +- &size, &nelm); +- if (! found) +- return -1; +- return nelm; +-} +- +-char * +-grub_zfs_nvlist_lookup_nvlist_array (char *nvlist, char *name, +- grub_size_t index) +-{ +- char *nvpair, *nvpairptr; +- int found; +- char *ret; +- grub_size_t size; +- unsigned i; +- grub_size_t nelm; +- +- found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair, +- &size, &nelm); +- if (!found) +- return 0; +- if (index >= nelm) +- { +- grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array"); +- return 0; +- } +- +- nvpairptr = nvpair; +- +- for (i = 0; i < index; i++) +- { +- grub_uint32_t encode_size; +- +- /* skip the header, nvl_version, and nvl_nvflag */ +- nvpairptr = nvpairptr + 4 * 2; +- +- while (nvpairptr < nvpair + size +- && (encode_size = grub_be_to_cpu32 (*(grub_uint32_t *) nvpairptr))) +- nvlist += encode_size; /* goto the next nvpair */ +- +- nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ +- } +- +- if (nvpairptr >= nvpair + size +- || nvpairptr + grub_be_to_cpu32 (*(grub_uint32_t *) (nvpairptr + 4 * 2)) +- >= nvpair + size) +- { +- grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); +- return 0; +- } +- +- ret = grub_zalloc (grub_be_to_cpu32 (*(grub_uint32_t *) (nvpairptr + 4 * 2)) +- + 3 * sizeof (grub_uint32_t)); +- if (!ret) +- return 0; +- grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); +- +- grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, size); +- return ret; +-} +- +-static grub_err_t +-zfs_fetch_nvlist (struct grub_zfs_data * data, char **nvlist) +-{ +- grub_err_t err; +- +- *nvlist = grub_malloc (VDEV_PHYS_SIZE); +- /* Read in the vdev name-value pair list (112K). */ +- err = grub_disk_read (data->disk, data->vdev_phys_sector, 0, +- VDEV_PHYS_SIZE, *nvlist); +- if (err) +- { +- grub_free (*nvlist); +- *nvlist = 0; +- return err; +- } +- return GRUB_ERR_NONE; +-} +- +-/* +- * Check the disk label information and retrieve needed vdev name-value pairs. +- * +- */ +-static grub_err_t +-check_pool_label (struct grub_zfs_data *data) ++grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, ++ const char *name) + { +- grub_uint64_t pool_state, txg = 0; +- char *nvlist; +-#if 0 +- char *nv; +-#endif +- grub_uint64_t diskguid; +- grub_uint64_t version; ++ char *nvpair; ++ grub_size_t nelm, size; + int found; +- grub_err_t err; + +- err = zfs_fetch_nvlist (data, &nvlist); +- if (err) +- return err; ++ found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, ++ &size, &nelm); ++ if (! found) ++ return -1; ++ return nelm; ++} + +- grub_dprintf ("zfs", "check 2 passed\n"); ++static int ++get_nvlist_size (const char *beg, const char *limit) ++{ ++ const char *ptr; ++ grub_uint32_t encode_size; ++ ++ ptr = beg + 8; + +- found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, +- &pool_state); +- if (! found) +- { +- grub_free (nvlist); +- if (! grub_errno) +- grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found"); +- return grub_errno; +- } +- grub_dprintf ("zfs", "check 3 passed\n"); ++ while (ptr < limit ++ && (encode_size = grub_be_to_cpu32 (*(grub_uint32_t *) ptr))) ++ ptr += encode_size; /* goto the next nvpair */ ++ ptr += 8; ++ return (ptr > limit) ? -1 : (ptr - beg); ++} + +- if (pool_state == POOL_STATE_DESTROYED) +- { +- grub_free (nvlist); +- return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed"); +- } +- grub_dprintf ("zfs", "check 4 passed\n"); ++char * ++grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, ++ grub_size_t index) ++{ ++ char *nvpair, *nvpairptr; ++ int found; ++ char *ret; ++ grub_size_t size; ++ unsigned i; ++ grub_size_t nelm; ++ int elemsize = 0; + +- found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg); ++ found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, ++ &size, &nelm); + if (!found) ++ return 0; ++ if (index >= nelm) + { +- grub_free (nvlist); +- if (! grub_errno) +- grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found"); +- return grub_errno; ++ grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array"); ++ return 0; + } +- grub_dprintf ("zfs", "check 6 passed\n"); + +- /* not an active device */ +- if (txg == 0) +- { +- grub_free (nvlist); +- return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active"); +- } +- grub_dprintf ("zfs", "check 7 passed\n"); ++ nvpairptr = nvpair; + +- found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION, +- &version); +- if (! found) ++ for (i = 0; i < index; i++) + { +- grub_free (nvlist); +- if (! grub_errno) +- grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found"); +- return grub_errno; +- } +- grub_dprintf ("zfs", "check 8 passed\n"); ++ int r; ++ r = get_nvlist_size (nvpairptr, nvpair + size); + +- if (version > SPA_VERSION) +- { +- grub_free (nvlist); +- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, +- "too new version %llu > %llu", +- (unsigned long long) version, +- (unsigned long long) SPA_VERSION); +- } +- grub_dprintf ("zfs", "check 9 passed\n"); +-#if 0 +- if (nvlist_lookup_value (nvlist, ZPOOL_CONFIG_VDEV_TREE, &nv, +- DATA_TYPE_NVLIST, NULL)) +- { +- grub_free (vdev); +- return (GRUB_ERR_BAD_FS); ++ if (r < 0) ++ { ++ grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); ++ return NULL; ++ } ++ nvpairptr += r; + } +- grub_dprintf ("zfs", "check 10 passed\n"); +-#endif + +- found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID, &diskguid); +- if (! found) ++ elemsize = get_nvlist_size (nvpairptr, nvpair + size); ++ ++ if (elemsize < 0) + { +- grub_free (nvlist); +- if (! grub_errno) +- grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found"); +- return grub_errno; ++ grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); ++ return 0; + } +- grub_dprintf ("zfs", "check 11 passed\n"); + +- grub_free (nvlist); ++ ret = grub_zalloc (elemsize + sizeof (grub_uint32_t)); ++ if (!ret) ++ return 0; ++ grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); + +- return GRUB_ERR_NONE; ++ grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, elemsize); ++ return ret; ++} ++ ++static void ++unmount_device (struct grub_zfs_device_desc *desc) ++{ ++ unsigned i; ++ switch (desc->type) ++ { ++ case DEVICE_LEAF: ++ if (!desc->original && desc->dev) ++ grub_device_close (desc->dev); ++ return; ++ case DEVICE_RAIDZ: ++ case DEVICE_MIRROR: ++ for (i = 0; i < desc->n_children; i++) ++ unmount_device (&desc->children[i]); ++ return; ++ } + } + + static void + zfs_unmount (struct grub_zfs_data *data) + { ++ unsigned i; ++ for (i = 0; i < data->n_devices_attached; i++) ++ unmount_device (&data->devices_attached[i]); ++ grub_free (data->devices_attached); + grub_free (data->dnode_buf); + grub_free (data->dnode_mdn); + grub_free (data->file_buf); +@@ -1961,13 +2896,11 @@ + zfs_mount (grub_device_t dev) + { + struct grub_zfs_data *data = 0; +- int label = 0; +- uberblock_phys_t *ub_array, *ubbest = NULL; +- vdev_boot_header_t *bh; +- void *osp = 0; +- grub_size_t ospsize; + grub_err_t err; +- int vdevnum; ++ objset_phys_t *osp = 0; ++ grub_size_t ospsize; ++ grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN; ++ uberblock_t *ub; + + if (! dev->disk) + { +@@ -1975,119 +2908,56 @@ + return 0; + } + +- data = grub_malloc (sizeof (*data)); ++ data = grub_zalloc (sizeof (*data)); + if (!data) + return 0; +- grub_memset (data, 0, sizeof (*data)); + #if 0 + /* if it's our first time here, zero the best uberblock out */ + if (data->best_drive == 0 && data->best_part == 0 && find_best_root) + grub_memset (¤t_uberblock, 0, sizeof (uberblock_t)); + #endif + +- data->disk = dev->disk; +- +- ub_array = grub_malloc (VDEV_UBERBLOCK_RING); +- if (!ub_array) ++ data->n_devices_allocated = 16; ++ data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) ++ * data->n_devices_allocated); ++ data->n_devices_attached = 0; ++ err = scan_disk (dev, data, 1); ++ if (err) + { + zfs_unmount (data); +- return 0; ++ return NULL; + } + +- bh = grub_malloc (VDEV_BOOT_HEADER_SIZE); +- if (!bh) ++ ub = &(data->current_uberblock); ++ ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, ++ LITTLE_ENDIAN) == UBERBLOCK_MAGIC ++ ? LITTLE_ENDIAN : BIG_ENDIAN); ++ ++ err = zio_read (&ub->ub_rootbp, ub_endian, ++ (void **) &osp, &ospsize, data); ++ if (err) + { + zfs_unmount (data); +- grub_free (ub_array); +- return 0; ++ return NULL; + } + +- vdevnum = VDEV_LABELS; +- +- /* Don't check back labels on CDROM. */ +- if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN) +- vdevnum = VDEV_LABELS / 2; +- +- for (label = 0; ubbest == NULL && label < vdevnum; label++) ++ if (ospsize < OBJSET_PHYS_SIZE_V14) + { +- grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN; +- grub_dprintf ("zfs", "label %d\n", label); +- +- data->vdev_phys_sector +- = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT) +- + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT) +- + (label < VDEV_LABELS / 2 ? 0 : grub_disk_get_size (dev->disk) +- - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)); +- +- /* Read in the uberblock ring (128K). */ +- err = grub_disk_read (data->disk, data->vdev_phys_sector +- + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT), +- 0, VDEV_UBERBLOCK_RING, (char *) ub_array); +- if (err) +- { +- grub_errno = GRUB_ERR_NONE; +- continue; +- } +- grub_dprintf ("zfs", "label ok %d\n", label); +- +- ubbest = find_bestub (ub_array, data->vdev_phys_sector); +- if (!ubbest) +- { +- grub_dprintf ("zfs", "No uberblock found\n"); +- grub_errno = GRUB_ERR_NONE; +- continue; +- } +- ub_endian = (grub_zfs_to_cpu64 (ubbest->ubp_uberblock.ub_magic, +- LITTLE_ENDIAN) == UBERBLOCK_MAGIC +- ? LITTLE_ENDIAN : BIG_ENDIAN); +- err = zio_read (&ubbest->ubp_uberblock.ub_rootbp, +- ub_endian, +- &osp, &ospsize, data); +- if (err) +- { +- grub_dprintf ("zfs", "couldn't zio_read\n"); +- grub_errno = GRUB_ERR_NONE; +- continue; +- } +- +- if (ospsize < OBJSET_PHYS_SIZE_V14) +- { +- grub_dprintf ("zfs", "osp too small\n"); +- grub_free (osp); +- continue; +- } +- grub_dprintf ("zfs", "ubbest %p\n", ubbest); +- +- err = check_pool_label (data); +- if (err) +- { +- grub_errno = GRUB_ERR_NONE; +- continue; +- } +-#if 0 +- if (find_best_root && +- vdev_uberblock_compare (&ubbest->ubp_uberblock, +- &(current_uberblock)) <= 0) +- continue; +-#endif +- /* Got the MOS. Save it at the memory addr MOS. */ +- grub_memmove (&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, +- DNODE_SIZE); +- data->mos.endian = (grub_zfs_to_cpu64 (ubbest->ubp_uberblock.ub_rootbp.blk_prop, ub_endian) >> 63) & 1; +- grub_memmove (&(data->current_uberblock), +- &ubbest->ubp_uberblock, sizeof (uberblock_t)); +- grub_free (ub_array); +- grub_free (bh); ++ grub_error (GRUB_ERR_BAD_FS, "OSP too small"); + grub_free (osp); +- return data; ++ zfs_unmount (data); ++ return NULL; + } +- grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label"); +- zfs_unmount (data); +- grub_free (ub_array); +- grub_free (bh); ++ ++ /* Got the MOS. Save it at the memory addr MOS. */ ++ grub_memmove (&(data->mos.dn), &osp->os_meta_dnode, DNODE_SIZE); ++ data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop, ++ ub_endian) >> 63) & 1; + grub_free (osp); + +- return 0; ++ data->mounted = 1; ++ ++ return data; + } + + grub_err_t +@@ -2099,7 +2969,7 @@ + zfs = zfs_mount (dev); + if (!zfs) + return grub_errno; +- err = zfs_fetch_nvlist (zfs, nvlist); ++ err = zfs_fetch_nvlist (zfs->device_original, nvlist); + zfs_unmount (zfs); + return err; + } +@@ -2115,7 +2985,7 @@ + if (! data) + return grub_errno; + +- err = zfs_fetch_nvlist (data, &nvlist); ++ err = zfs_fetch_nvlist (data->device_original, &nvlist); + if (err) + { + zfs_unmount (data); +@@ -2131,11 +3001,7 @@ + static grub_err_t + zfs_uuid (grub_device_t device, char **uuid) + { +- char *nvlist; +- int found; + struct grub_zfs_data *data; +- grub_uint64_t guid; +- grub_err_t err; + + *uuid = 0; + +@@ -2143,24 +3009,36 @@ + if (! data) + return grub_errno; + +- err = zfs_fetch_nvlist (data, &nvlist); +- if (err) +- { +- zfs_unmount (data); +- return err; +- } +- +- found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid); +- if (! found) +- return grub_errno; +- grub_free (nvlist); +- *uuid = grub_xasprintf ("%016llx", (long long unsigned) guid); ++ *uuid = grub_xasprintf ("%016llx", (long long unsigned) data->guid); + zfs_unmount (data); + if (! *uuid) + return grub_errno; + return GRUB_ERR_NONE; + } + ++static grub_err_t ++zfs_mtime (grub_device_t device, grub_int32_t *mt) ++{ ++ struct grub_zfs_data *data; ++ grub_zfs_endian_t ub_endian = UNKNOWN_ENDIAN; ++ uberblock_t *ub; ++ ++ *mt = 0; ++ ++ data = zfs_mount (device); ++ if (! data) ++ return grub_errno; ++ ++ ub = &(data->current_uberblock); ++ ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, ++ LITTLE_ENDIAN) == UBERBLOCK_MAGIC ++ ? LITTLE_ENDIAN : BIG_ENDIAN); ++ ++ *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian); ++ zfs_unmount (data); ++ return GRUB_ERR_NONE; ++} ++ + /* + * zfs_open() locates a file in the rootpool by following the + * MOS and places the dnode of the file in the memory address DNODE. +@@ -2177,7 +3055,7 @@ + return grub_errno; + + err = dnode_get_fullpath (fsfilename, &(data->mdn), 0, +- &(data->dnode), &isfs, data); ++ &(data->dnode), &isfs, data, NULL); + if (err) + { + zfs_unmount (data); +@@ -2227,12 +3105,14 @@ + } + + hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); +- file->size = *(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET); ++ file->size = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian); + } +- else ++ else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE) + { + file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian); + } ++ else ++ return grub_error (GRUB_ERR_BAD_FS, "bad bonus type"); + + file->data = data; + file->offset = 0; +@@ -2248,7 +3128,7 @@ + grub_zfs_read (grub_file_t file, char *buf, grub_size_t len) + { + struct grub_zfs_data *data = (struct grub_zfs_data *) file->data; +- int blksz, movesize; ++ grub_size_t blksz, movesize; + grub_size_t length; + grub_size_t read; + grub_err_t err; +@@ -2302,7 +3182,7 @@ + data->file_start = blkid * blksz; + data->file_end = data->file_start + blksz; + +- movesize = MIN (length, data->file_end - (int) file->offset - read); ++ movesize = MIN (length, data->file_end - file->offset - read); + + grub_memmove (buf, data->file_buf + file->offset + read + - data->file_start, movesize); +@@ -2339,7 +3219,7 @@ + return grub_errno; + + err = dnode_get_fullpath (fsfilename, &(data->mdn), mdnobj, +- &(data->dnode), &isfs, data); ++ &(data->dnode), &isfs, data, NULL); + zfs_unmount (data); + return err; + } +@@ -2377,7 +3257,7 @@ + return; + } + +- err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data); ++ err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0); + if (err) + { + grub_dprintf ("zfs", "failed here\n"); +@@ -2391,8 +3271,39 @@ + return; + } + +- info->mtimeset = 1; +- info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); ++ if (dn.dn.dn_bonustype == DMU_OT_SA) ++ { ++ void *sahdrp; ++ int hdrsize; ++ ++ if (dn.dn.dn_bonuslen != 0) ++ { ++ sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn); ++ } ++ else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) ++ { ++ blkptr_t *bp = &dn.dn.dn_spill; ++ ++ err = zio_read (bp, dn.endian, &sahdrp, NULL, data); ++ if (err) ++ return; ++ } ++ else ++ { ++ grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); ++ return; ++ } ++ ++ hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); ++ info->mtimeset = 1; ++ info->mtime = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); ++ } ++ ++ if (dn.dn.dn_bonustype == DMU_OT_ZNODE) ++ { ++ info->mtimeset = 1; ++ info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); ++ } + return; + } + +@@ -2403,6 +3314,7 @@ + struct grub_zfs_data *data; + grub_err_t err; + int isfs; ++ int case_insensitive = 0; + auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val); + auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, + grub_uint64_t val); +@@ -2416,10 +3328,48 @@ + grub_memset (&info, 0, sizeof (info)); + + dnode_get (&(data->mdn), val, 0, &dn, data); +- info.mtimeset = 1; +- info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); +- info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); +- grub_dprintf ("zfs", "type=%d, name=%s\n", ++ ++ if (dn.dn.dn_bonustype == DMU_OT_SA) ++ { ++ void *sahdrp; ++ int hdrsize; ++ ++ if (dn.dn.dn_bonuslen != 0) ++ { ++ sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn); ++ } ++ else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) ++ { ++ blkptr_t *bp = &dn.dn.dn_spill; ++ ++ err = zio_read (bp, dn.endian, &sahdrp, NULL, data); ++ if (err) ++ { ++ grub_print_error (); ++ return 0; ++ } ++ } ++ else ++ { ++ grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); ++ grub_print_error (); ++ return 0; ++ } ++ ++ hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); ++ info.mtimeset = 1; ++ info.mtime = grub_zfs_to_cpu64 (*(grub_uint64_t *) ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); ++ info.case_insensitive = case_insensitive; ++ } ++ ++ if (dn.dn.dn_bonustype == DMU_OT_ZNODE) ++ { ++ info.mtimeset = 1; ++ info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], ++ dn.endian); ++ } ++ info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); ++ grub_dprintf ("zfs", "type=%d, name=%s\n", + (int)dn.dn.dn_type, (char *)name); + return hook (name, &info); + } +@@ -2464,7 +3414,8 @@ + data = zfs_mount (device); + if (! data) + return grub_errno; +- err = dnode_get_fullpath (path, &(data->mdn), 0, &(data->dnode), &isfs, data); ++ err = dnode_get_fullpath (path, &(data->mdn), 0, &(data->dnode), &isfs, data, ++ &case_insensitive); + if (err) + { + zfs_unmount (data); +@@ -2532,12 +3483,13 @@ + .close = grub_zfs_close, + .label = zfs_label, + .uuid = zfs_uuid, +- .mtime = 0, ++ .mtime = zfs_mtime, + .next = 0 + }; + + GRUB_MOD_INIT (zfs) + { ++ COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE); + grub_fs_register (&grub_zfs_fs); + #ifndef GRUB_UTIL + my_mod = mod; +--- a/include/grub/zfs/zio.h ++++ b/include/grub/zfs/zio.h +@@ -77,7 +77,15 @@ + ZIO_COMPRESS_OFF, + ZIO_COMPRESS_LZJB, + ZIO_COMPRESS_EMPTY, +- ZIO_COMPRESS_GZIP, ++ ZIO_COMPRESS_GZIP1, ++ ZIO_COMPRESS_GZIP2, ++ ZIO_COMPRESS_GZIP3, ++ ZIO_COMPRESS_GZIP4, ++ ZIO_COMPRESS_GZIP5, ++ ZIO_COMPRESS_GZIP6, ++ ZIO_COMPRESS_GZIP7, ++ ZIO_COMPRESS_GZIP8, ++ ZIO_COMPRESS_GZIP9, + ZIO_COMPRESS_FUNCTIONS + }; + +--- a/grub-core/fs/zfs/zfsinfo.c ++++ b/grub-core/fs/zfs/zfsinfo.c +@@ -141,7 +141,6 @@ + } + grub_printf ("Mirror VDEV with %d children\n", nelm); + print_state (nvlist, tab); +- + for (i = 0; i < nelm; i++) + { + char *child; +@@ -161,6 +160,7 @@ + + grub_free (child); + } ++ return GRUB_ERR_NONE; + } + + print_tabs (tab); +--- a/include/grub/zfs/sa_impl.h ++++ b/include/grub/zfs/sa_impl.h +@@ -29,6 +29,9 @@ + } sa_hdr_phys_t; + + #define SA_HDR_SIZE(hdr) BF32_GET_SB(hdr->sa_layout_info, 10, 16, 3, 0) ++#define SA_TYPE_OFFSET 0x0 + #define SA_SIZE_OFFSET 0x8 ++#define SA_MTIME_OFFSET 0x38 ++#define SA_SYMLINK_OFFSET 0xa0 + + #endif /* _SYS_SA_IMPL_H */ +--- a/include/grub/zfs/zfs.h ++++ b/include/grub/zfs/zfs.h +@@ -28,7 +28,7 @@ + /* + * On-disk version number. + */ +-#define SPA_VERSION 28ULL ++#define SPA_VERSION 33ULL + + /* + * The following are configuration names used in the nvlist describing a pool's +@@ -112,12 +112,14 @@ + grub_err_t grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename, + grub_uint64_t *mdnobj); + +-char *grub_zfs_nvlist_lookup_string (char *nvlist, char *name); +-char *grub_zfs_nvlist_lookup_nvlist (char *nvlist, char *name); +-int grub_zfs_nvlist_lookup_uint64 (char *nvlist, char *name, ++char *grub_zfs_nvlist_lookup_string (const char *nvlist, const char *name); ++char *grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name); ++int grub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name, + grub_uint64_t *out); +-char *grub_zfs_nvlist_lookup_nvlist_array (char *nvlist, char *name, ++char *grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, ++ const char *name, + grub_size_t index); +-int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (char *nvlist, char *name); ++int grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, ++ const char *name); + + #endif /* ! GRUB_ZFS_HEADER */ -- cgit v1.2.3