Changeset View
Changeset View
Standalone View
Standalone View
head/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; | |||||
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); | |||||
} | |||||
/* | |||||
* 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 |