From: Vladimir 'phcoder' Serbinenko Date: Wed, 26 Oct 2011 17:26:24 +0000 (+0200) Subject: merge mainline into zfs X-Git-Tag: 2.00~1054^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=11e50e923a51eb9da5798cb9c296e991ca8c4333;p=thirdparty%2Fgrub.git merge mainline into zfs --- 11e50e923a51eb9da5798cb9c296e991ca8c4333 diff --cc grub-core/fs/zfs/zfs.c index 327e99a90,6721cddc1..e248a1114 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@@ -432,519 -409,16 +434,520 @@@ get_psize (blkptr_t * bp, grub_zfs_endi << SPA_MINBLOCKSHIFT); } -static grub_uint64_t -dva_get_offset (dva_t * dva, grub_zfs_endian_t endian) +static grub_uint64_t +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], + (unsigned long long) dva->dva_word[1]); + return grub_zfs_to_cpu64 ((dva)->dva_word[1], + 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; + } + + 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 grub_err_t +read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, + grub_uint32_t asize, 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; + 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], asize, + len, buf); + if (!err) + break; + grub_errno = GRUB_ERR_NONE; + } + return (grub_errno = err); + } + case DEVICE_RAIDZ: + { + grub_uint64_t sector; + grub_uint32_t bsize; + unsigned c = 0; + + bsize = (asize + desc->nparity) / desc->n_children; + sector = offset >> 9; + while (len > 0) + { + grub_size_t csize; + grub_uint64_t high; - grub_uint32_t devn; ++ grub_uint64_t devn; + grub_err_t err; + high = grub_divmod64 (sector + (asize > 2) + c, desc->n_children, + &devn); + csize = bsize << 9; + if (csize > len) + csize = len; + grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T - "+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%x)\n", ++ "+%d+%u -> (0x%" PRIxGRUB_UINT64_T ", 0x%" ++ PRIxGRUB_UINT64_T ")\n", + sector,(asize > 2), c, high, devn); + err = read_device (high << 9, &desc->children[devn], + bsize, csize, buf); + if (err) + return err; + c++; + buf = (char *) buf + csize; + len -= csize; + } + return GRUB_ERR_NONE; + } + 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_dprintf ("zfs", "dva=%llx, %llx\n", - (unsigned long long) dva->dva_word[0], - (unsigned long long) dva->dva_word[1]); - return grub_zfs_to_cpu64 ((dva)->dva_word[1], - endian) << SPA_MINBLOCKSHIFT; -} + 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], + dva->dva_word[0] & 0xffffff, 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, @@@ -1369,10 -858,8 +1374,9 @@@ zap_leaf_lookup (zap_leaf_phys_t * l, g name)) { struct zap_leaf_array *la; - grub_uint8_t *ip; - 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 */ @@@ -1477,13 -964,18 +1481,19 @@@ fzap_iterate (dnode_end_t * zap_dnode, grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); return 0; } - for (idx = 0; idx < grub_zfs_to_cpu64 (zap->zap_num_leafs, - zap_dnode->endian); 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); - err = dmu_read (zap_dnode, blkid, (void **) &l, &endian, data); + for (idx2 = 0; idx2 < idx; idx2++) + if (blkid == ((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))]) + break; + if (idx2 != idx) + continue; + + err = dmu_read (zap_dnode, blkid, &l_in, &endian, data); + l = l_in; if (err) { grub_errno = GRUB_ERR_NONE; diff --cc util/grub-fstest.c index 122465d7a,ad64701a2..b6ed2ef0c --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@@ -54,14 -54,17 +54,17 @@@ execute_command (char *name, int n, cha return (cmd->func) (cmd, n, args); } - #define CMD_LS 1 - #define CMD_CP 2 - #define CMD_CMP 3 - #define CMD_HEX 4 - #define CMD_CRC 6 - #define CMD_BLOCKLIST 7 - #define CMD_ZFSINFO 8 - + enum { + CMD_LS = 1, + CMD_CP, + CMD_CAT, + CMD_CMP, + CMD_HEX, + CMD_CRC, + CMD_BLOCKLIST, - CMD_TESTLOAD ++ CMD_TESTLOAD, ++ CMD_ZFSINFO + }; - #define BUF_SIZE 32256 static grub_disk_addr_t skip, leng;