Changeset View
Changeset View
Standalone View
Standalone View
stand/libsa/zfs/zfsimpl.c
Show All 25 Lines | ||||||||||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | |||||||||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | |||||||||||
/* | /* | |||||||||||
* Stand-alone ZFS file reader. | * Stand-alone ZFS file reader. | |||||||||||
*/ | */ | |||||||||||
#include <stdbool.h> | ||||||||||||
#include <sys/endian.h> | #include <sys/endian.h> | |||||||||||
#include <sys/stat.h> | #include <sys/stat.h> | |||||||||||
#include <sys/stdint.h> | #include <sys/stdint.h> | |||||||||||
#include <sys/list.h> | #include <sys/list.h> | |||||||||||
#include <sys/zfs_bootenv.h> | ||||||||||||
#include <machine/_inttypes.h> | #include <machine/_inttypes.h> | |||||||||||
#include "zfsimpl.h" | #include "zfsimpl.h" | |||||||||||
#include "zfssubr.c" | #include "zfssubr.c" | |||||||||||
struct zfsmount { | struct zfsmount { | |||||||||||
const spa_t *spa; | const spa_t *spa; | |||||||||||
▲ Show 20 Lines • Show All 169 Lines • ▼ Show 20 Lines | ||||||||||||
static int | static int | |||||||||||
vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, | vdev_read_phys(vdev_t *vdev, const blkptr_t *bp, void *buf, | |||||||||||
off_t offset, size_t size) | off_t offset, size_t size) | |||||||||||
{ | { | |||||||||||
size_t psize; | size_t psize; | |||||||||||
int rc; | int rc; | |||||||||||
if (!vdev->v_phys_read) | if (vdev->v_phys_read == NULL) | |||||||||||
return (EIO); | return (ENOTSUP); | |||||||||||
if (bp) { | if (bp) { | |||||||||||
psize = BP_GET_PSIZE(bp); | psize = BP_GET_PSIZE(bp); | |||||||||||
} else { | } else { | |||||||||||
psize = size; | psize = size; | |||||||||||
} | } | |||||||||||
rc = vdev->v_phys_read(vdev, vdev->v_read_priv, offset, buf, psize); | rc = vdev->v_phys_read(vdev, vdev->v_priv, offset, buf, psize); | |||||||||||
if (rc == 0) { | if (rc == 0) { | |||||||||||
if (bp != NULL) | if (bp != NULL) | |||||||||||
rc = zio_checksum_verify(vdev->v_spa, bp, buf); | rc = zio_checksum_verify(vdev->v_spa, bp, buf); | |||||||||||
} | } | |||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
static int | ||||||||||||
vdev_write_phys(vdev_t *vdev, void *buf, off_t offset, size_t size) | ||||||||||||
{ | ||||||||||||
if (vdev->v_phys_write == NULL) | ||||||||||||
return (ENOTSUP); | ||||||||||||
return (vdev->v_phys_write(vdev, offset, buf, size)); | ||||||||||||
} | ||||||||||||
typedef struct remap_segment { | typedef struct remap_segment { | |||||||||||
vdev_t *rs_vd; | vdev_t *rs_vd; | |||||||||||
uint64_t rs_offset; | uint64_t rs_offset; | |||||||||||
uint64_t rs_asize; | uint64_t rs_asize; | |||||||||||
uint64_t rs_split_offset; | uint64_t rs_split_offset; | |||||||||||
list_node_t rs_node; | list_node_t rs_node; | |||||||||||
} remap_segment_t; | } remap_segment_t; | |||||||||||
▲ Show 20 Lines • Show All 830 Lines • ▼ Show 20 Lines | vdev_insert(vdev_t *top_vdev, vdev_t *vdev) | |||||||||||
if (top_vdev->v_nchildren < count) | if (top_vdev->v_nchildren < count) | |||||||||||
top_vdev->v_nchildren = count; | top_vdev->v_nchildren = count; | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist) | vdev_from_nvlist(spa_t *spa, uint64_t top_guid, const nvlist_t *nvlist) | |||||||||||
{ | { | |||||||||||
vdev_t *top_vdev, *vdev; | vdev_t *top_vdev, *vdev; | |||||||||||
nvlist_t *kids = NULL; | nvlist_t **kids = NULL; | |||||||||||
int rc, nkids; | int rc, nkids; | |||||||||||
/* Get top vdev. */ | /* Get top vdev. */ | |||||||||||
top_vdev = vdev_find(top_guid); | top_vdev = vdev_find(top_guid); | |||||||||||
if (top_vdev == NULL) { | if (top_vdev == NULL) { | |||||||||||
rc = vdev_init(top_guid, nvlist, &top_vdev); | rc = vdev_init(top_guid, nvlist, &top_vdev); | |||||||||||
if (rc != 0) | if (rc != 0) | |||||||||||
return (rc); | return (rc); | |||||||||||
top_vdev->v_spa = spa; | top_vdev->v_spa = spa; | |||||||||||
top_vdev->v_top = top_vdev; | top_vdev->v_top = top_vdev; | |||||||||||
vdev_insert(spa->spa_root_vdev, top_vdev); | vdev_insert(spa->spa_root_vdev, top_vdev); | |||||||||||
} | } | |||||||||||
/* Add children if there are any. */ | /* Add children if there are any. */ | |||||||||||
rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, | rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, | |||||||||||
&nkids, &kids, NULL); | &nkids, &kids, NULL); | |||||||||||
if (rc == 0) { | if (rc == 0) { | |||||||||||
for (int i = 0; i < nkids; i++) { | for (int i = 0; i < nkids; i++) { | |||||||||||
uint64_t guid; | uint64_t guid; | |||||||||||
rc = nvlist_find(kids, ZPOOL_CONFIG_GUID, | rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID, | |||||||||||
DATA_TYPE_UINT64, NULL, &guid, NULL); | DATA_TYPE_UINT64, NULL, &guid, NULL); | |||||||||||
if (rc != 0) { | if (rc != 0) | |||||||||||
nvlist_destroy(kids); | goto done; | |||||||||||
return (rc); | ||||||||||||
} | ||||||||||||
rc = vdev_init(guid, kids, &vdev); | ||||||||||||
if (rc != 0) { | ||||||||||||
nvlist_destroy(kids); | ||||||||||||
return (rc); | ||||||||||||
} | ||||||||||||
rc = vdev_init(guid, kids[i], &vdev); | ||||||||||||
if (rc != 0) | ||||||||||||
goto done; | ||||||||||||
vdev->v_spa = spa; | vdev->v_spa = spa; | |||||||||||
vdev->v_top = top_vdev; | vdev->v_top = top_vdev; | |||||||||||
vdev_insert(top_vdev, vdev); | vdev_insert(top_vdev, vdev); | |||||||||||
rc = nvlist_next(kids); | ||||||||||||
if (rc != 0) { | ||||||||||||
nvlist_destroy(kids); | ||||||||||||
return (rc); | ||||||||||||
} | } | |||||||||||
} | ||||||||||||
} else { | } else { | |||||||||||
/* | /* | |||||||||||
* When there are no children, nvlist_find() does return | * When there are no children, nvlist_find() does return | |||||||||||
* error, reset it because leaf devices have no children. | * error, reset it because leaf devices have no children. | |||||||||||
*/ | */ | |||||||||||
rc = 0; | rc = 0; | |||||||||||
} | } | |||||||||||
nvlist_destroy(kids); | done: | |||||||||||
if (kids != NULL) { | ||||||||||||
for (int i = 0; i < nkids; i++) | ||||||||||||
nvlist_destroy(kids[i]); | ||||||||||||
free(kids); | ||||||||||||
} | ||||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vdev_init_from_label(spa_t *spa, const nvlist_t *nvlist) | vdev_init_from_label(spa_t *spa, const nvlist_t *nvlist) | |||||||||||
{ | { | |||||||||||
uint64_t pool_guid, top_guid; | uint64_t pool_guid, top_guid; | |||||||||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | if (STAILQ_FIRST(&vdev->v_children)) { | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist) | vdev_update_from_nvlist(uint64_t top_guid, const nvlist_t *nvlist) | |||||||||||
{ | { | |||||||||||
vdev_t *vdev; | vdev_t *vdev; | |||||||||||
nvlist_t *kids = NULL; | nvlist_t **kids = NULL; | |||||||||||
int rc, nkids; | int rc, nkids; | |||||||||||
/* Update top vdev. */ | /* Update top vdev. */ | |||||||||||
vdev = vdev_find(top_guid); | vdev = vdev_find(top_guid); | |||||||||||
if (vdev != NULL) | if (vdev != NULL) | |||||||||||
vdev_set_initial_state(vdev, nvlist); | vdev_set_initial_state(vdev, nvlist); | |||||||||||
/* Update children if there are any. */ | /* Update children if there are any. */ | |||||||||||
rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, | rc = nvlist_find(nvlist, ZPOOL_CONFIG_CHILDREN, DATA_TYPE_NVLIST_ARRAY, | |||||||||||
&nkids, &kids, NULL); | &nkids, &kids, NULL); | |||||||||||
if (rc == 0) { | if (rc == 0) { | |||||||||||
for (int i = 0; i < nkids; i++) { | for (int i = 0; i < nkids; i++) { | |||||||||||
uint64_t guid; | uint64_t guid; | |||||||||||
rc = nvlist_find(kids, ZPOOL_CONFIG_GUID, | rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID, | |||||||||||
DATA_TYPE_UINT64, NULL, &guid, NULL); | DATA_TYPE_UINT64, NULL, &guid, NULL); | |||||||||||
if (rc != 0) | if (rc != 0) | |||||||||||
break; | break; | |||||||||||
vdev = vdev_find(guid); | vdev = vdev_find(guid); | |||||||||||
if (vdev != NULL) | if (vdev != NULL) | |||||||||||
vdev_set_initial_state(vdev, kids); | vdev_set_initial_state(vdev, kids[i]); | |||||||||||
rc = nvlist_next(kids); | ||||||||||||
if (rc != 0) | ||||||||||||
break; | ||||||||||||
} | } | |||||||||||
} else { | } else { | |||||||||||
rc = 0; | rc = 0; | |||||||||||
} | } | |||||||||||
nvlist_destroy(kids); | if (kids != NULL) { | |||||||||||
for (int i = 0; i < nkids; i++) | ||||||||||||
nvlist_destroy(kids[i]); | ||||||||||||
free(kids); | ||||||||||||
} | ||||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist) | vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist) | |||||||||||
{ | { | |||||||||||
uint64_t pool_guid, vdev_children; | uint64_t pool_guid, vdev_children; | |||||||||||
nvlist_t *vdevs = NULL, *kids = NULL; | nvlist_t *vdevs = NULL, **kids = NULL; | |||||||||||
int rc, nkids; | int rc, nkids; | |||||||||||
if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, | if (nvlist_find(nvlist, ZPOOL_CONFIG_POOL_GUID, DATA_TYPE_UINT64, | |||||||||||
NULL, &pool_guid, NULL) || | NULL, &pool_guid, NULL) || | |||||||||||
nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN, DATA_TYPE_UINT64, | nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_CHILDREN, DATA_TYPE_UINT64, | |||||||||||
NULL, &vdev_children, NULL) || | NULL, &vdev_children, NULL) || | |||||||||||
nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, | nvlist_find(nvlist, ZPOOL_CONFIG_VDEV_TREE, DATA_TYPE_NVLIST, | |||||||||||
NULL, &vdevs, NULL)) { | NULL, &vdevs, NULL)) { | |||||||||||
Show All 18 Lines | vdev_init_from_nvlist(spa_t *spa, const nvlist_t *nvlist) | |||||||||||
*/ | */ | |||||||||||
if (rc != 0) | if (rc != 0) | |||||||||||
return (rc); | return (rc); | |||||||||||
for (int i = 0; i < nkids; i++) { | for (int i = 0; i < nkids; i++) { | |||||||||||
uint64_t guid; | uint64_t guid; | |||||||||||
vdev_t *vdev; | vdev_t *vdev; | |||||||||||
rc = nvlist_find(kids, ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, | rc = nvlist_find(kids[i], ZPOOL_CONFIG_GUID, DATA_TYPE_UINT64, | |||||||||||
NULL, &guid, NULL); | NULL, &guid, NULL); | |||||||||||
if (rc != 0) | if (rc != 0) | |||||||||||
break; | break; | |||||||||||
vdev = vdev_find(guid); | vdev = vdev_find(guid); | |||||||||||
/* | /* | |||||||||||
* Top level vdev is missing, create it. | * Top level vdev is missing, create it. | |||||||||||
*/ | */ | |||||||||||
if (vdev == NULL) | if (vdev == NULL) | |||||||||||
rc = vdev_from_nvlist(spa, guid, kids); | rc = vdev_from_nvlist(spa, guid, kids[i]); | |||||||||||
else | else | |||||||||||
rc = vdev_update_from_nvlist(guid, kids); | rc = vdev_update_from_nvlist(guid, kids[i]); | |||||||||||
if (rc != 0) | if (rc != 0) | |||||||||||
break; | break; | |||||||||||
rc = nvlist_next(kids); | ||||||||||||
if (rc != 0) | ||||||||||||
break; | ||||||||||||
} | } | |||||||||||
nvlist_destroy(kids); | if (kids != NULL) { | |||||||||||
for (int i = 0; i < nkids; i++) | ||||||||||||
nvlist_destroy(kids[i]); | ||||||||||||
free(kids); | ||||||||||||
} | ||||||||||||
/* | /* | |||||||||||
* Re-evaluate top-level vdev state. | * Re-evaluate top-level vdev state. | |||||||||||
*/ | */ | |||||||||||
vdev_set_state(spa->spa_root_vdev); | vdev_set_state(spa->spa_root_vdev); | |||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
Show All 18 Lines | spa_find_by_name(const char *name) | |||||||||||
STAILQ_FOREACH(spa, &zfs_pools, spa_link) | STAILQ_FOREACH(spa, &zfs_pools, spa_link) | |||||||||||
if (strcmp(spa->spa_name, name) == 0) | if (strcmp(spa->spa_name, name) == 0) | |||||||||||
return (spa); | return (spa); | |||||||||||
return (NULL); | return (NULL); | |||||||||||
} | } | |||||||||||
static spa_t * | static spa_t * | |||||||||||
spa_find_by_dev(struct zfs_devdesc *dev) | ||||||||||||
{ | ||||||||||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | ||||||||||||
return (NULL); | ||||||||||||
if (dev->pool_guid == 0) | ||||||||||||
return (STAILQ_FIRST(&zfs_pools)); | ||||||||||||
return (spa_find_by_guid(dev->pool_guid)); | ||||||||||||
} | ||||||||||||
static spa_t * | ||||||||||||
spa_create(uint64_t guid, const char *name) | spa_create(uint64_t guid, const char *name) | |||||||||||
{ | { | |||||||||||
spa_t *spa; | spa_t *spa; | |||||||||||
if ((spa = calloc(1, sizeof(spa_t))) == NULL) | if ((spa = calloc(1, sizeof(spa_t))) == NULL) | |||||||||||
return (NULL); | return (NULL); | |||||||||||
if ((spa->spa_name = strdup(name)) == NULL) { | if ((spa->spa_name = strdup(name)) == NULL) { | |||||||||||
free(spa); | free(spa); | |||||||||||
▲ Show 20 Lines • Show All 235 Lines • ▼ Show 20 Lines | vdev_label_read(vdev_t *vd, int l, void *buf, uint64_t offset, | |||||||||||
BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); | BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); | |||||||||||
BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); | BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); | |||||||||||
DVA_SET_OFFSET(BP_IDENTITY(&bp), off); | DVA_SET_OFFSET(BP_IDENTITY(&bp), off); | |||||||||||
ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); | ZIO_SET_CHECKSUM(&bp.blk_cksum, off, 0, 0, 0); | |||||||||||
return (vdev_read_phys(vd, &bp, buf, off, size)); | return (vdev_read_phys(vd, &bp, buf, off, size)); | |||||||||||
} | } | |||||||||||
/* | ||||||||||||
* We do need to be sure we write to correct location. | ||||||||||||
* Our vdev label does consist of 4 fields: | ||||||||||||
* pad1 (8k), reserved. | ||||||||||||
* bootenv (8k), checksummed, previously reserved, may contian garbage. | ||||||||||||
* vdev_phys (112k), checksummed | ||||||||||||
* uberblock ring (128k), checksummed. | ||||||||||||
* | ||||||||||||
* Since bootenv area may contain garbage, we can not reliably read it, as | ||||||||||||
* we can get checksum errors. | ||||||||||||
* Next best thing is vdev_phys - it is just after bootenv. It still may | ||||||||||||
* be corrupted, but in such case we will miss this one write. | ||||||||||||
*/ | ||||||||||||
static int | ||||||||||||
vdev_label_write_validate(vdev_t *vd, int l, uint64_t offset) | ||||||||||||
{ | ||||||||||||
uint64_t off, o_phys; | ||||||||||||
void *buf; | ||||||||||||
size_t size = VDEV_PHYS_SIZE; | ||||||||||||
int rc; | ||||||||||||
o_phys = offsetof(vdev_label_t, vl_vdev_phys); | ||||||||||||
off = vdev_label_offset(vd->v_psize, l, o_phys); | ||||||||||||
/* off should be 8K from bootenv */ | ||||||||||||
if (vdev_label_offset(vd->v_psize, l, offset) + VDEV_PAD_SIZE != off) | ||||||||||||
return (EINVAL); | ||||||||||||
buf = malloc(size); | ||||||||||||
if (buf == NULL) | ||||||||||||
return (ENOMEM); | ||||||||||||
/* Read vdev_phys */ | ||||||||||||
rc = vdev_label_read(vd, l, buf, o_phys, size); | ||||||||||||
free(buf); | ||||||||||||
return (rc); | ||||||||||||
} | ||||||||||||
static int | ||||||||||||
vdev_label_write(vdev_t *vd, int l, vdev_boot_envblock_t *be, uint64_t offset) | ||||||||||||
{ | ||||||||||||
zio_checksum_info_t *ci; | ||||||||||||
zio_cksum_t cksum; | ||||||||||||
off_t off; | ||||||||||||
size_t size = VDEV_PAD_SIZE; | ||||||||||||
int rc; | ||||||||||||
if (vd->v_phys_write == NULL) | ||||||||||||
return (ENOTSUP); | ||||||||||||
off = vdev_label_offset(vd->v_psize, l, offset); | ||||||||||||
rc = vdev_label_write_validate(vd, l, offset); | ||||||||||||
if (rc != 0) { | ||||||||||||
return (rc); | ||||||||||||
} | ||||||||||||
ci = &zio_checksum_table[ZIO_CHECKSUM_LABEL]; | ||||||||||||
be->vbe_zbt.zec_magic = ZEC_MAGIC; | ||||||||||||
zio_checksum_label_verifier(&be->vbe_zbt.zec_cksum, off); | ||||||||||||
ci->ci_func[0](be, size, NULL, &cksum); | ||||||||||||
be->vbe_zbt.zec_cksum = cksum; | ||||||||||||
glebius: If I read code correct this message will print once again the failure message from… | ||||||||||||
return (vdev_write_phys(vd, be, off, size)); | ||||||||||||
} | ||||||||||||
static int | ||||||||||||
vdev_write_bootenv_impl(vdev_t *vdev, vdev_boot_envblock_t *be) | ||||||||||||
{ | ||||||||||||
vdev_t *kid; | ||||||||||||
int rv = 0, rc; | ||||||||||||
STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { | ||||||||||||
if (kid->v_state != VDEV_STATE_HEALTHY) | ||||||||||||
continue; | ||||||||||||
rc = vdev_write_bootenv_impl(kid, be); | ||||||||||||
if (rv == 0) | ||||||||||||
rv = rc; | ||||||||||||
} | ||||||||||||
/* | ||||||||||||
* Non-leaf vdevs do not have v_phys_write. | ||||||||||||
*/ | ||||||||||||
if (vdev->v_phys_write == NULL) | ||||||||||||
return (rv); | ||||||||||||
for (int l = 0; l < VDEV_LABELS; l++) { | ||||||||||||
rc = vdev_label_write(vdev, l, be, | ||||||||||||
offsetof(vdev_label_t, vl_be)); | ||||||||||||
if (rc != 0) { | ||||||||||||
printf("failed to write bootenv to %s label %d: %d\n", | ||||||||||||
vdev->v_name ? vdev->v_name : "unknown", l, rc); | ||||||||||||
rv = rc; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
return (rv); | ||||||||||||
} | ||||||||||||
int | ||||||||||||
vdev_write_bootenv(vdev_t *vdev, nvlist_t *nvl) | ||||||||||||
{ | ||||||||||||
vdev_boot_envblock_t *be; | ||||||||||||
nvlist_t nv, *nvp; | ||||||||||||
uint64_t version; | ||||||||||||
int rv; | ||||||||||||
if (nvl->nv_size > sizeof(be->vbe_bootenv)) | ||||||||||||
return (E2BIG); | ||||||||||||
version = VB_RAW; | ||||||||||||
nvp = vdev_read_bootenv(vdev); | ||||||||||||
if (nvp != NULL) { | ||||||||||||
nvlist_find(nvp, BOOTENV_VERSION, DATA_TYPE_UINT64, NULL, | ||||||||||||
&version, NULL); | ||||||||||||
nvlist_destroy(nvp); | ||||||||||||
} | ||||||||||||
be = calloc(1, sizeof(*be)); | ||||||||||||
if (be == NULL) | ||||||||||||
return (ENOMEM); | ||||||||||||
be->vbe_version = version; | ||||||||||||
switch (version) { | ||||||||||||
case VB_RAW: | ||||||||||||
/* | ||||||||||||
* If there is no envmap, we will just wipe bootenv. | ||||||||||||
*/ | ||||||||||||
nvlist_find(nvl, GRUB_ENVMAP, DATA_TYPE_STRING, NULL, | ||||||||||||
be->vbe_bootenv, NULL); | ||||||||||||
rv = 0; | ||||||||||||
break; | ||||||||||||
case VB_NVLIST: | ||||||||||||
nv.nv_header = nvl->nv_header; | ||||||||||||
nv.nv_asize = nvl->nv_asize; | ||||||||||||
nv.nv_size = nvl->nv_size; | ||||||||||||
bcopy(&nv.nv_header, be->vbe_bootenv, sizeof(nv.nv_header)); | ||||||||||||
nv.nv_data = be->vbe_bootenv + sizeof(nvs_header_t); | ||||||||||||
bcopy(nvl->nv_data, nv.nv_data, nv.nv_size); | ||||||||||||
rv = nvlist_export(&nv); | ||||||||||||
break; | ||||||||||||
default: | ||||||||||||
rv = EINVAL; | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
if (rv == 0) { | ||||||||||||
be->vbe_version = htobe64(be->vbe_version); | ||||||||||||
rv = vdev_write_bootenv_impl(vdev, be); | ||||||||||||
} | ||||||||||||
free(be); | ||||||||||||
return (rv); | ||||||||||||
} | ||||||||||||
Done Inline Actions
We leak be here. glebius: We leak be here. | ||||||||||||
/* | ||||||||||||
* Read the bootenv area from pool label, return the nvlist from it. | ||||||||||||
* We return from first successful read. | ||||||||||||
*/ | ||||||||||||
nvlist_t * | ||||||||||||
vdev_read_bootenv(vdev_t *vdev) | ||||||||||||
{ | ||||||||||||
vdev_t *kid; | ||||||||||||
nvlist_t *benv; | ||||||||||||
vdev_boot_envblock_t *be; | ||||||||||||
char *command; | ||||||||||||
bool ok; | ||||||||||||
int rv; | ||||||||||||
STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { | ||||||||||||
if (kid->v_state != VDEV_STATE_HEALTHY) | ||||||||||||
continue; | ||||||||||||
benv = vdev_read_bootenv(kid); | ||||||||||||
if (benv != NULL) | ||||||||||||
return (benv); | ||||||||||||
} | ||||||||||||
be = malloc(sizeof (*be)); | ||||||||||||
if (be == NULL) | ||||||||||||
return (NULL); | ||||||||||||
rv = 0; | ||||||||||||
for (int l = 0; l < VDEV_LABELS; l++) { | ||||||||||||
rv = vdev_label_read(vdev, l, be, | ||||||||||||
offsetof(vdev_label_t, vl_be), | ||||||||||||
sizeof (*be)); | ||||||||||||
if (rv == 0) | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
if (rv != 0) { | ||||||||||||
free(be); | ||||||||||||
return (NULL); | ||||||||||||
} | ||||||||||||
be->vbe_version = be64toh(be->vbe_version); | ||||||||||||
switch (be->vbe_version) { | ||||||||||||
case VB_RAW: | ||||||||||||
/* | ||||||||||||
* we have textual data in vbe_bootenv, create nvlist | ||||||||||||
* with key "envmap". | ||||||||||||
*/ | ||||||||||||
benv = nvlist_create(NV_UNIQUE_NAME); | ||||||||||||
if (benv != NULL) { | ||||||||||||
if (*be->vbe_bootenv == '\0') { | ||||||||||||
nvlist_add_uint64(benv, BOOTENV_VERSION, | ||||||||||||
VB_NVLIST); | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
nvlist_add_uint64(benv, BOOTENV_VERSION, VB_RAW); | ||||||||||||
be->vbe_bootenv[sizeof (be->vbe_bootenv) - 1] = '\0'; | ||||||||||||
nvlist_add_string(benv, GRUB_ENVMAP, be->vbe_bootenv); | ||||||||||||
} | ||||||||||||
break; | ||||||||||||
case VB_NVLIST: | ||||||||||||
benv = nvlist_import(be->vbe_bootenv, sizeof(be->vbe_bootenv)); | ||||||||||||
break; | ||||||||||||
default: | ||||||||||||
command = (char *)be; | ||||||||||||
ok = false; | ||||||||||||
/* Check for legacy zfsbootcfg command string */ | ||||||||||||
for (int i = 0; command[i] != '\0'; i++) { | ||||||||||||
if (iscntrl(command[i])) { | ||||||||||||
ok = false; | ||||||||||||
break; | ||||||||||||
} else { | ||||||||||||
ok = true; | ||||||||||||
} | ||||||||||||
} | ||||||||||||
benv = nvlist_create(NV_UNIQUE_NAME); | ||||||||||||
if (benv != NULL) { | ||||||||||||
if (ok) | ||||||||||||
nvlist_add_string(benv, FREEBSD_BOOTONCE, | ||||||||||||
command); | ||||||||||||
else | ||||||||||||
nvlist_add_uint64(benv, BOOTENV_VERSION, | ||||||||||||
VB_NVLIST); | ||||||||||||
} | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
free(be); | ||||||||||||
return (benv); | ||||||||||||
} | ||||||||||||
static uint64_t | static uint64_t | |||||||||||
vdev_get_label_asize(nvlist_t *nvl) | vdev_get_label_asize(nvlist_t *nvl) | |||||||||||
{ | { | |||||||||||
nvlist_t *vdevs; | nvlist_t *vdevs; | |||||||||||
uint64_t asize; | uint64_t asize; | |||||||||||
const char *type; | const char *type; | |||||||||||
int len; | int len; | |||||||||||
Show All 16 Lines | if (memcmp(type, VDEV_TYPE_MIRROR, len) != 0 && | |||||||||||
memcmp(type, VDEV_TYPE_RAIDZ, len) != 0) | memcmp(type, VDEV_TYPE_RAIDZ, len) != 0) | |||||||||||
goto done; | goto done; | |||||||||||
if (nvlist_find(vdevs, ZPOOL_CONFIG_ASIZE, DATA_TYPE_UINT64, | if (nvlist_find(vdevs, ZPOOL_CONFIG_ASIZE, DATA_TYPE_UINT64, | |||||||||||
NULL, &asize, NULL) != 0) | NULL, &asize, NULL) != 0) | |||||||||||
goto done; | goto done; | |||||||||||
if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) { | if (memcmp(type, VDEV_TYPE_RAIDZ, len) == 0) { | |||||||||||
nvlist_t *kids; | nvlist_t **kids; | |||||||||||
int nkids; | int nkids; | |||||||||||
if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN, | if (nvlist_find(vdevs, ZPOOL_CONFIG_CHILDREN, | |||||||||||
DATA_TYPE_NVLIST_ARRAY, &nkids, &kids, NULL) != 0) { | DATA_TYPE_NVLIST_ARRAY, &nkids, &kids, NULL) != 0) { | |||||||||||
asize = 0; | asize = 0; | |||||||||||
goto done; | goto done; | |||||||||||
} | } | |||||||||||
asize /= nkids; | asize /= nkids; | |||||||||||
nvlist_destroy(kids); | for (int i = 0; i < nkids; i++) | |||||||||||
nvlist_destroy(kids[i]); | ||||||||||||
free(kids); | ||||||||||||
} | } | |||||||||||
asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE; | asize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE; | |||||||||||
done: | done: | |||||||||||
nvlist_destroy(vdevs); | nvlist_destroy(vdevs); | |||||||||||
return (asize); | return (asize); | |||||||||||
} | } | |||||||||||
static nvlist_t * | static nvlist_t * | |||||||||||
vdev_label_read_config(vdev_t *vd, uint64_t txg) | vdev_label_read_config(vdev_t *vd, uint64_t txg) | |||||||||||
{ | { | |||||||||||
vdev_phys_t *label; | vdev_phys_t *label; | |||||||||||
uint64_t best_txg = 0; | uint64_t best_txg = 0; | |||||||||||
uint64_t label_txg = 0; | uint64_t label_txg = 0; | |||||||||||
uint64_t asize; | uint64_t asize; | |||||||||||
nvlist_t *nvl = NULL, *tmp; | nvlist_t *nvl = NULL, *tmp; | |||||||||||
int error; | int error; | |||||||||||
label = malloc(sizeof (vdev_phys_t)); | label = malloc(sizeof (vdev_phys_t)); | |||||||||||
if (label == NULL) | if (label == NULL) | |||||||||||
return (NULL); | return (NULL); | |||||||||||
for (int l = 0; l < VDEV_LABELS; l++) { | for (int l = 0; l < VDEV_LABELS; l++) { | |||||||||||
const unsigned char *nvlist; | ||||||||||||
if (vdev_label_read(vd, l, label, | if (vdev_label_read(vd, l, label, | |||||||||||
offsetof(vdev_label_t, vl_vdev_phys), | offsetof(vdev_label_t, vl_vdev_phys), | |||||||||||
sizeof (vdev_phys_t))) | sizeof (vdev_phys_t))) | |||||||||||
continue; | continue; | |||||||||||
nvlist = (const unsigned char *) label->vp_nvlist; | tmp = nvlist_import(label->vp_nvlist, | |||||||||||
tmp = nvlist_import(nvlist + 4, nvlist[0], nvlist[1]); | sizeof(label->vp_nvlist)); | |||||||||||
if (tmp == NULL) | if (tmp == NULL) | |||||||||||
continue; | continue; | |||||||||||
error = nvlist_find(tmp, ZPOOL_CONFIG_POOL_TXG, | error = nvlist_find(tmp, ZPOOL_CONFIG_POOL_TXG, | |||||||||||
DATA_TYPE_UINT64, NULL, &label_txg, NULL); | DATA_TYPE_UINT64, NULL, &label_txg, NULL); | |||||||||||
if (error != 0 || label_txg == 0) { | if (error != 0 || label_txg == 0) { | |||||||||||
nvlist_destroy(nvl); | nvlist_destroy(nvl); | |||||||||||
nvl = tmp; | nvl = tmp; | |||||||||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | for (int n = 0; n < VDEV_UBERBLOCK_COUNT(vd); n++) { | |||||||||||
if (vdev_uberblock_compare(buf, ub) > 0) | if (vdev_uberblock_compare(buf, ub) > 0) | |||||||||||
*ub = *buf; | *ub = *buf; | |||||||||||
} | } | |||||||||||
} | } | |||||||||||
free(buf); | free(buf); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap) | vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, | |||||||||||
spa_t **spap) | ||||||||||||
{ | { | |||||||||||
vdev_t vtmp; | vdev_t vtmp; | |||||||||||
spa_t *spa; | spa_t *spa; | |||||||||||
vdev_t *vdev; | vdev_t *vdev; | |||||||||||
nvlist_t *nvl; | nvlist_t *nvl; | |||||||||||
uint64_t val; | uint64_t val; | |||||||||||
uint64_t guid, vdev_children; | uint64_t guid, vdev_children; | |||||||||||
uint64_t pool_txg, pool_guid; | uint64_t pool_txg, pool_guid; | |||||||||||
const char *pool_name; | const char *pool_name; | |||||||||||
int rc, namelen; | int rc, namelen; | |||||||||||
/* | /* | |||||||||||
* Load the vdev label and figure out which | * Load the vdev label and figure out which | |||||||||||
* uberblock is most current. | * uberblock is most current. | |||||||||||
*/ | */ | |||||||||||
memset(&vtmp, 0, sizeof(vtmp)); | memset(&vtmp, 0, sizeof(vtmp)); | |||||||||||
vtmp.v_phys_read = _read; | vtmp.v_phys_read = _read; | |||||||||||
vtmp.v_read_priv = read_priv; | vtmp.v_phys_write = _write; | |||||||||||
vtmp.v_psize = P2ALIGN(ldi_get_size(read_priv), | vtmp.v_priv = priv; | |||||||||||
vtmp.v_psize = P2ALIGN(ldi_get_size(priv), | ||||||||||||
(uint64_t)sizeof (vdev_label_t)); | (uint64_t)sizeof (vdev_label_t)); | |||||||||||
/* Test for minimum device size. */ | /* Test for minimum device size. */ | |||||||||||
if (vtmp.v_psize < SPA_MINDEVSIZE) | if (vtmp.v_psize < SPA_MINDEVSIZE) | |||||||||||
return (EIO); | return (EIO); | |||||||||||
nvl = vdev_label_read_config(&vtmp, UINT64_MAX); | nvl = vdev_label_read_config(&vtmp, UINT64_MAX); | |||||||||||
if (nvl == NULL) | if (nvl == NULL) | |||||||||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | vdev_probe(vdev_phys_read_t *_read, vdev_phys_write_t *_write, void *priv, | |||||||||||
/* | /* | |||||||||||
* We should already have created an incomplete vdev for this | * We should already have created an incomplete vdev for this | |||||||||||
* vdev. Find it and initialise it with our read proc. | * vdev. Find it and initialise it with our read proc. | |||||||||||
*/ | */ | |||||||||||
vdev = vdev_find(guid); | vdev = vdev_find(guid); | |||||||||||
if (vdev != NULL) { | if (vdev != NULL) { | |||||||||||
vdev->v_phys_read = _read; | vdev->v_phys_read = _read; | |||||||||||
vdev->v_read_priv = read_priv; | vdev->v_phys_write = _write; | |||||||||||
vdev->v_priv = priv; | ||||||||||||
vdev->v_psize = vtmp.v_psize; | vdev->v_psize = vtmp.v_psize; | |||||||||||
/* | /* | |||||||||||
* If no other state is set, mark vdev healthy. | * If no other state is set, mark vdev healthy. | |||||||||||
*/ | */ | |||||||||||
if (vdev->v_state == VDEV_STATE_UNKNOWN) | if (vdev->v_state == VDEV_STATE_UNKNOWN) | |||||||||||
vdev->v_state = VDEV_STATE_HEALTHY; | vdev->v_state = VDEV_STATE_HEALTHY; | |||||||||||
} else { | } else { | |||||||||||
printf("ZFS: inconsistent nvlist contents\n"); | printf("ZFS: inconsistent nvlist contents\n"); | |||||||||||
▲ Show 20 Lines • Show All 1,254 Lines • ▼ Show 20 Lines | ||||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value) | load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value) | |||||||||||
{ | { | |||||||||||
dnode_phys_t dir; | dnode_phys_t dir; | |||||||||||
size_t size; | size_t size; | |||||||||||
int rc; | int rc; | |||||||||||
unsigned char *nv; | char *nv; | |||||||||||
*value = NULL; | *value = NULL; | |||||||||||
if ((rc = objset_get_dnode(spa, spa->spa_mos, obj, &dir)) != 0) | if ((rc = objset_get_dnode(spa, spa->spa_mos, obj, &dir)) != 0) | |||||||||||
return (rc); | return (rc); | |||||||||||
if (dir.dn_type != DMU_OT_PACKED_NVLIST && | if (dir.dn_type != DMU_OT_PACKED_NVLIST && | |||||||||||
dir.dn_bonustype != DMU_OT_PACKED_NVLIST_SIZE) { | dir.dn_bonustype != DMU_OT_PACKED_NVLIST_SIZE) { | |||||||||||
return (EIO); | return (EIO); | |||||||||||
} | } | |||||||||||
if (dir.dn_bonuslen != sizeof (uint64_t)) | if (dir.dn_bonuslen != sizeof (uint64_t)) | |||||||||||
return (EIO); | return (EIO); | |||||||||||
size = *(uint64_t *)DN_BONUS(&dir); | size = *(uint64_t *)DN_BONUS(&dir); | |||||||||||
nv = malloc(size); | nv = malloc(size); | |||||||||||
if (nv == NULL) | if (nv == NULL) | |||||||||||
return (ENOMEM); | return (ENOMEM); | |||||||||||
rc = dnode_read(spa, &dir, 0, nv, size); | rc = dnode_read(spa, &dir, 0, nv, size); | |||||||||||
if (rc != 0) { | if (rc != 0) { | |||||||||||
free(nv); | free(nv); | |||||||||||
nv = NULL; | nv = NULL; | |||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
*value = nvlist_import(nv + 4, nv[0], nv[1]); | *value = nvlist_import(nv, size); | |||||||||||
free(nv); | free(nv); | |||||||||||
return (rc); | return (rc); | |||||||||||
} | } | |||||||||||
static int | static int | |||||||||||
zfs_spa_init(spa_t *spa) | zfs_spa_init(spa_t *spa) | |||||||||||
{ | { | |||||||||||
struct uberblock checkpoint; | struct uberblock checkpoint; | |||||||||||
▲ Show 20 Lines • Show All 342 Lines • Show Last 20 Lines |
If I read code correct this message will print once again the failure message from vdev_label_write(), except it doesn't check for NULL v_name.