Index: stand/libsa/zfs/zfsimpl.c =================================================================== --- stand/libsa/zfs/zfsimpl.c +++ stand/libsa/zfs/zfsimpl.c @@ -78,6 +78,7 @@ static char *zfs_temp_buf, *zfs_temp_end, *zfs_temp_ptr; #define TEMP_SIZE (1024 * 1024) +#define ALL_LABELS_INVALID ((int)0xFFFFFFFF) static int zio_read(const spa_t *spa, const blkptr_t *bp, void *buf); static int zfs_get_root(const spa_t *spa, uint64_t *objid); @@ -513,6 +514,20 @@ return (vdev); } +static void +vdev_delete_by_guid(uint64_t guid) +{ + vdev_t *dev, *kid; + + if(dev = vdev_find(guid)) { + STAILQ_FOREACH(kid, &dev->v_children, v_childlink) + vdev_delete_by_guid(kid->v_guid); + + STAILQ_REMOVE(&zfs_vdevs, dev, vdev, v_alllink); + free(dev); + } +} + static int vdev_init_from_nvlist(const unsigned char *nvlist, vdev_t *pvdev, vdev_t **vdevp, int is_newer) @@ -766,6 +781,17 @@ return (spa); } +static void +spa_delete_by_guid(uint64_t guid) +{ + spa_t *spa; + + if (spa = spa_find_by_guid(guid)) { + STAILQ_REMOVE(&zfs_pools, spa, spa, spa_link); + free(spa); + } +} + static const char * state_name(vdev_state_t state) { @@ -929,7 +955,7 @@ } static int -vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) +vdev_probe_try(vdev_phys_read_t *_read, void *read_priv, spa_t **spap, int *tried_labels) { vdev_t vtmp; vdev_phys_t *vdev_label = (vdev_phys_t *) zap_scratch; @@ -950,6 +976,7 @@ int i, l, rc, is_newer; char *upbuf; const struct uberblock *up; + int best_label = 0; /* * Load the vdev label and figure out which @@ -962,8 +989,10 @@ (uint64_t)sizeof (vdev_label_t)); /* Test for minimum pool size. */ - if (psize < SPA_MINDEVSIZE) + if (psize < SPA_MINDEVSIZE) { + *tried_labels = ALL_LABELS_INVALID; return (EIO); + } tmp_label = zfs_alloc(sizeof(vdev_phys_t)); @@ -990,16 +1019,21 @@ DATA_TYPE_UINT64, NULL, &pool_txg) != 0) continue; - if (best_txg <= pool_txg) { + if (best_txg <= pool_txg && (*tried_labels & (1 << l)) == 0) { + printf("ZFS: found label %d was written in newer txg\n", l); best_txg = pool_txg; + best_label = l; memcpy(vdev_label, tmp_label, sizeof (vdev_phys_t)); } } + *tried_labels |= 1 << best_label; zfs_free(tmp_label, sizeof (vdev_phys_t)); - if (best_txg == 0) + if (best_txg == 0) { + *tried_labels = ALL_LABELS_INVALID; return (EIO); + } if (vdev_label->vp_nvlist[0] != NV_ENCODE_XDR) return (EIO); @@ -1077,20 +1111,25 @@ */ if (nvlist_find(nvlist, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, NULL, &guid) != 0) { - return (EIO); + rc = EIO; + goto err_delete_spa; } + vdev = vdev_find(guid); - if (vdev && vdev->v_phys_read) /* Has this vdev already been inited? */ - return (EIO); + if (vdev && vdev->v_phys_read) { /* Has this vdev already been inited? */ + rc = EIO; + goto err_delete_spa; + } if (nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, NULL, &vdevs)) { - return (EIO); + rc = EIO; + goto err_delete_spa; } rc = vdev_init_from_nvlist(vdevs, NULL, &top_vdev, is_newer); if (rc != 0) - return (rc); + goto err_delete_vdev; /* * Add the toplevel vdev to the pool if its not already there. @@ -1114,7 +1153,8 @@ vdev->v_state = VDEV_STATE_HEALTHY; } else { printf("ZFS: inconsistent nvlist contents\n"); - return (EIO); + rc = EIO; + goto err_delete_vdev; } /* @@ -1158,10 +1198,38 @@ } zfs_free(upbuf, VDEV_UBERBLOCK_SIZE(vdev)); - vdev->spa = spa; - if (spap != NULL) - *spap = spa; - return (0); + /* check uberblock and return as soon as a valid dva is found */ + for (i = 0; i < SPA_DVAS_PER_BP; i++) { + const dva_t *const dva = &spa->spa_uberblock.ub_rootbp.blk_dva[i]; + + if (dva->dva_word[0] || dva->dva_word[1]) { + vdev->spa = spa; + if (spap != NULL) + *spap = spa; + return (0); + } + } + + /* no uberblock with valid dva found */ + rc = EIO; +err_delete_vdev: + vdev_delete_by_guid(guid); +err_delete_spa: + spa_delete_by_guid(pool_guid); + return (rc); +} + +static int +vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) +{ + int tried_labels = 0; // this bitmask encodes tried labels + int err; + + do { + err = vdev_probe_try(_read, read_priv, spap, &tried_labels); + } while (err && (tried_labels != ALL_LABELS_INVALID)); + + return (err); } static int