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 <machine/_inttypes.h> | #include <machine/_inttypes.h> | |||||||||||
#include "zfsimpl.h" | #include "zfsimpl.h" | |||||||||||
#include "zfssubr.c" | #include "zfssubr.c" | |||||||||||
▲ Show 20 Lines • Show All 172 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 1,084 Lines • ▼ Show 20 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 233 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; | ||||||||||||
int rv; | ||||||||||||
if (nvl->nv_size > sizeof(be->vbe_nvlist)) | ||||||||||||
return (E2BIG); | ||||||||||||
be = calloc(1, sizeof(*be)); | ||||||||||||
if (be == NULL) | ||||||||||||
return (ENOMEM); | ||||||||||||
nv.nv_header = nvl->nv_header; | ||||||||||||
nv.nv_asize = nvl->nv_asize; | ||||||||||||
nv.nv_size = nvl->nv_size; | ||||||||||||
*(nvs_header_t *)be->vbe_nvlist = nv.nv_header; | ||||||||||||
nv.nv_data = be->vbe_nvlist + sizeof(nvs_header_t); | ||||||||||||
bcopy(nvl->nv_data, nv.nv_data, nv.nv_size); | ||||||||||||
rv = nvlist_export(&nv); | ||||||||||||
if (rv == 0) { | ||||||||||||
rv = vdev_write_bootenv_impl(vdev, be); | ||||||||||||
} | ||||||||||||
free(be); | ||||||||||||
return (rv); | ||||||||||||
} | ||||||||||||
/* | ||||||||||||
* 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; | ||||||||||||
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, | ||||||||||||
Done Inline Actions
We leak be here. glebius: We leak be here. | ||||||||||||
offsetof(vdev_label_t, vl_be), | ||||||||||||
sizeof (*be)); | ||||||||||||
if (rv == 0) | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
if (rv != 0) { | ||||||||||||
free(be); | ||||||||||||
return (NULL); | ||||||||||||
} | ||||||||||||
benv = nvlist_import(be->vbe_nvlist + 4, be->vbe_nvlist[0], | ||||||||||||
be->vbe_nvlist[1]); | ||||||||||||
if (benv == NULL) { | ||||||||||||
char *command = (char *)be; | ||||||||||||
bool 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 (ok) | ||||||||||||
nvlist_add_string(benv, "command", command); | ||||||||||||
} | ||||||||||||
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 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | vdev_label_read_config(vdev_t *vd, uint64_t txg) | |||||||||||
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 + 4, | |||||||||||
tmp = nvlist_import(nvlist + 4, nvlist[0], nvlist[1]); | label->vp_nvlist[0], label->vp_nvlist[1]); | |||||||||||
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,253 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); | |||||||||||
} | } | |||||||||||
▲ Show 20 Lines • Show All 351 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.