Changeset View
Changeset View
Standalone View
Standalone View
stand/libsa/zfs/zfs.c
Show First 20 Lines • Show All 480 Lines • ▼ Show 20 Lines | vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t bytes) | ||||
ret = 0; | ret = 0; | ||||
error: | error: | ||||
free(bouncebuf); | free(bouncebuf); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | static int | ||||
vdev_write(vdev_t *vdev __unused, void *priv, off_t offset, void *buf, | vdev_write(vdev_t *vdev, off_t offset, void *buf, size_t bytes) | ||||
size_t bytes) | |||||
{ | { | ||||
int fd, ret; | int fd, ret; | ||||
size_t head, tail, total_size, full_sec_size; | size_t head, tail, total_size, full_sec_size; | ||||
unsigned secsz, do_tail_write; | unsigned secsz, do_tail_write; | ||||
off_t start_sec; | off_t start_sec; | ||||
ssize_t res; | ssize_t res; | ||||
char *outbuf, *bouncebuf; | char *outbuf, *bouncebuf; | ||||
fd = (uintptr_t)priv; | fd = (uintptr_t)vdev->v_priv; | ||||
outbuf = (char *) buf; | outbuf = (char *)buf; | ||||
bouncebuf = NULL; | bouncebuf = NULL; | ||||
ret = ioctl(fd, DIOCGSECTORSIZE, &secsz); | ret = ioctl(fd, DIOCGSECTORSIZE, &secsz); | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
start_sec = offset / secsz; | start_sec = offset / secsz; | ||||
head = offset % secsz; | head = offset % secsz; | ||||
Show All 18 Lines | vdev_write(vdev_t *vdev, off_t offset, void *buf, size_t bytes) | ||||
if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) { | if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) { | ||||
ret = errno; | ret = errno; | ||||
goto error; | goto error; | ||||
} | } | ||||
/* Partial data for first sector */ | /* Partial data for first sector */ | ||||
if (head > 0) { | if (head > 0) { | ||||
res = read(fd, bouncebuf, secsz); | res = read(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes)); | memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes)); | ||||
(void) lseek(fd, -secsz, SEEK_CUR); | (void) lseek(fd, -secsz, SEEK_CUR); | ||||
res = write(fd, bouncebuf, secsz); | res = write(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
outbuf += min(secsz - head, bytes); | outbuf += min(secsz - head, bytes); | ||||
} | } | ||||
/* | /* | ||||
* Full data write to sectors. | * Full data write to sectors. | ||||
* Note, there is still corner case where we write | * Note, there is still corner case where we write | ||||
* to sector boundary, but less than sector size, e.g. write 512B | * to sector boundary, but less than sector size, e.g. write 512B | ||||
* to 4k sector. | * to 4k sector. | ||||
*/ | */ | ||||
if (full_sec_size > 0) { | if (full_sec_size > 0) { | ||||
if (bytes < full_sec_size) { | if (bytes < full_sec_size) { | ||||
res = read(fd, bouncebuf, secsz); | res = read(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
memcpy(bouncebuf, outbuf, bytes); | memcpy(bouncebuf, outbuf, bytes); | ||||
(void) lseek(fd, -secsz, SEEK_CUR); | (void) lseek(fd, -secsz, SEEK_CUR); | ||||
res = write(fd, bouncebuf, secsz); | res = write(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
} else { | } else { | ||||
res = write(fd, outbuf, full_sec_size); | res = write(fd, outbuf, full_sec_size); | ||||
if (res != full_sec_size) { | if ((unsigned)res != full_sec_size) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
outbuf += full_sec_size; | outbuf += full_sec_size; | ||||
} | } | ||||
} | } | ||||
/* Partial data write to last sector */ | /* Partial data write to last sector */ | ||||
if (do_tail_write) { | if (do_tail_write) { | ||||
res = read(fd, bouncebuf, secsz); | res = read(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
memcpy(bouncebuf, outbuf, secsz - tail); | memcpy(bouncebuf, outbuf, secsz - tail); | ||||
(void) lseek(fd, -secsz, SEEK_CUR); | (void) lseek(fd, -secsz, SEEK_CUR); | ||||
res = write(fd, bouncebuf, secsz); | res = write(fd, bouncebuf, secsz); | ||||
if (res != secsz) { | if ((unsigned)res != secsz) { | ||||
ret = EIO; | ret = EIO; | ||||
goto error; | goto error; | ||||
} | } | ||||
} | } | ||||
ret = 0; | ret = 0; | ||||
error: | error: | ||||
free(bouncebuf); | free(bouncebuf); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static void | |||||
vdev_clear_pad2(vdev_t *vdev) | |||||
{ | |||||
vdev_t *kid; | |||||
vdev_boot_envblock_t *be; | |||||
off_t off = offsetof(vdev_label_t, vl_be); | |||||
zio_checksum_info_t *ci; | |||||
zio_cksum_t cksum; | |||||
STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { | |||||
if (kid->v_state != VDEV_STATE_HEALTHY) | |||||
continue; | |||||
vdev_clear_pad2(kid); | |||||
} | |||||
if (!STAILQ_EMPTY(&vdev->v_children)) | |||||
return; | |||||
be = calloc(1, sizeof (*be)); | |||||
if (be == NULL) { | |||||
printf("failed to clear be area: out of memory\n"); | |||||
return; | |||||
} | |||||
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, sizeof (*be), NULL, &cksum); | |||||
be->vbe_zbt.zec_cksum = cksum; | |||||
if (vdev_write(vdev, vdev->v_read_priv, off, be, VDEV_PAD_SIZE)) { | |||||
printf("failed to clear be area of primary vdev: %d\n", | |||||
errno); | |||||
} | |||||
free(be); | |||||
} | |||||
/* | |||||
* Read the next boot command from pad2. | |||||
* If any instance of pad2 is set to empty string, or the returned string | |||||
* values are not the same, we consider next boot not to be set. | |||||
*/ | |||||
static char * | |||||
vdev_read_pad2(vdev_t *vdev) | |||||
{ | |||||
vdev_t *kid; | |||||
char *tmp, *result = NULL; | |||||
vdev_boot_envblock_t *be; | |||||
off_t off = offsetof(vdev_label_t, vl_be); | |||||
STAILQ_FOREACH(kid, &vdev->v_children, v_childlink) { | |||||
if (kid->v_state != VDEV_STATE_HEALTHY) | |||||
continue; | |||||
tmp = vdev_read_pad2(kid); | |||||
if (tmp == NULL) | |||||
continue; | |||||
/* The next boot is not set, we are done. */ | |||||
if (*tmp == '\0') { | |||||
free(result); | |||||
return (tmp); | |||||
} | |||||
if (result == NULL) { | |||||
result = tmp; | |||||
continue; | |||||
} | |||||
/* Are the next boot strings different? */ | |||||
if (strcmp(result, tmp) != 0) { | |||||
free(tmp); | |||||
*result = '\0'; | |||||
break; | |||||
} | |||||
free(tmp); | |||||
} | |||||
if (result != NULL) | |||||
return (result); | |||||
be = malloc(sizeof (*be)); | |||||
if (be == NULL) | |||||
return (NULL); | |||||
if (vdev_read(vdev, vdev->v_read_priv, off, be, sizeof (*be))) { | |||||
return (NULL); | |||||
} | |||||
switch (be->vbe_version) { | |||||
case VB_RAW: | |||||
case VB_NVLIST: | |||||
result = strdup(be->vbe_bootenv); | |||||
default: | |||||
/* Backward compatibility with initial nextboot feaure. */ | |||||
result = strdup((char *)be); | |||||
} | |||||
return (result); | |||||
} | |||||
static int | static int | ||||
zfs_dev_init(void) | zfs_dev_init(void) | ||||
{ | { | ||||
spa_t *spa; | spa_t *spa; | ||||
spa_t *next; | spa_t *next; | ||||
spa_t *prev; | spa_t *prev; | ||||
zfs_init(); | zfs_init(); | ||||
Show All 36 Lines | |||||
static int | static int | ||||
zfs_probe(int fd, uint64_t *pool_guid) | zfs_probe(int fd, uint64_t *pool_guid) | ||||
{ | { | ||||
spa_t *spa; | spa_t *spa; | ||||
int ret; | int ret; | ||||
spa = NULL; | spa = NULL; | ||||
ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); | ret = vdev_probe(vdev_read, vdev_write, (void *)(uintptr_t)fd, &spa); | ||||
if (ret == 0 && pool_guid != NULL) | if (ret == 0 && pool_guid != NULL) | ||||
*pool_guid = spa->spa_guid; | *pool_guid = spa->spa_guid; | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | static int | ||||
zfs_probe_partition(void *arg, const char *partname, | zfs_probe_partition(void *arg, const char *partname, | ||||
const struct ptable_entry *part) | const struct ptable_entry *part) | ||||
{ | { | ||||
struct zfs_probe_args *ppa, pa; | struct zfs_probe_args *ppa, pa; | ||||
struct ptable *table; | struct ptable *table; | ||||
char devname[32]; | char devname[32]; | ||||
int ret; | int ret; | ||||
/* Probe only freebsd-zfs and freebsd partitions */ | /* Probe only freebsd-zfs and freebsd partitions */ | ||||
if (part->type != PART_FREEBSD && | if (part->type != PART_FREEBSD && | ||||
part->type != PART_FREEBSD_ZFS) | part->type != PART_FREEBSD_ZFS) | ||||
return (0); | return (0); | ||||
ppa = (struct zfs_probe_args *)arg; | ppa = (struct zfs_probe_args *)arg; | ||||
strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); | strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); | ||||
devname[strlen(ppa->devname) - 1] = '\0'; | devname[strlen(ppa->devname) - 1] = '\0'; | ||||
sprintf(devname, "%s%s:", devname, partname); | snprintf(devname, sizeof(devname), "%s%s:", devname, partname); | ||||
pa.fd = open(devname, O_RDWR); | pa.fd = open(devname, O_RDWR); | ||||
if (pa.fd == -1) | if (pa.fd == -1) | ||||
return (0); | return (0); | ||||
ret = zfs_probe(pa.fd, ppa->pool_guid); | ret = zfs_probe(pa.fd, ppa->pool_guid); | ||||
if (ret == 0) | if (ret == 0) | ||||
return (0); | return (0); | ||||
/* Do we have BSD label here? */ | /* Do we have BSD label here? */ | ||||
if (part->type == PART_FREEBSD) { | if (part->type == PART_FREEBSD) { | ||||
pa.devname = devname; | pa.devname = devname; | ||||
pa.pool_guid = ppa->pool_guid; | pa.pool_guid = ppa->pool_guid; | ||||
pa.secsz = ppa->secsz; | pa.secsz = ppa->secsz; | ||||
table = ptable_open(&pa, part->end - part->start + 1, | table = ptable_open(&pa, part->end - part->start + 1, | ||||
ppa->secsz, zfs_diskread); | ppa->secsz, zfs_diskread); | ||||
if (table != NULL) { | if (table != NULL) { | ||||
ptable_iterate(table, &pa, zfs_probe_partition); | ptable_iterate(table, &pa, zfs_probe_partition); | ||||
ptable_close(table); | ptable_close(table); | ||||
} | } | ||||
} | } | ||||
close(pa.fd); | close(pa.fd); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | |||||
* Return bootenv nvlist from pool label. | |||||
*/ | |||||
int | int | ||||
zfs_nextboot(void *vdev, char *buf, size_t size) | zfs_get_bootenv(void *vdev, nvlist_t **benvp) | ||||
{ | { | ||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | ||||
nvlist_t *benv = NULL; | |||||
vdev_t *vd; | |||||
spa_t *spa; | spa_t *spa; | ||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
if (spa->spa_bootenv == NULL) { | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, | |||||
v_childlink) { | |||||
benv = vdev_read_bootenv(vd); | |||||
if (benv != NULL) | |||||
break; | |||||
} | |||||
spa->spa_bootenv = benv; | |||||
} else { | |||||
benv = spa->spa_bootenv; | |||||
} | |||||
if (benv == NULL) | |||||
return (ENOENT); | |||||
*benvp = benv; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Store nvlist to pool label bootenv area. Also updates cached pointer in spa. | |||||
*/ | |||||
int | |||||
zfs_set_bootenv(void *vdev, nvlist_t *benv) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
vdev_t *vd; | vdev_t *vd; | ||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | |||||
vdev_write_bootenv(vd, benv); | |||||
} | |||||
spa->spa_bootenv = benv; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Get bootonce value by key. The bootonce <key, value> pair is removed | |||||
* from the bootenv nvlist and the remaining nvlist is committed back to disk. | |||||
*/ | |||||
int | |||||
zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size) | |||||
{ | |||||
nvlist_t *benv; | |||||
char *result = NULL; | char *result = NULL; | ||||
int result_size, rv; | |||||
if ((rv = zfs_get_bootenv(vdev, &benv)) != 0) | |||||
return (rv); | |||||
if ((rv = nvlist_find(benv, key, DATA_TYPE_STRING, NULL, | |||||
&result, &result_size)) == 0) { | |||||
if (result_size == 0) { | |||||
/* ignore empty string */ | |||||
rv = ENOENT; | |||||
} else { | |||||
size = MIN((size_t)result_size + 1, size); | |||||
strlcpy(buf, result, size); | |||||
} | |||||
(void) nvlist_remove(benv, key, DATA_TYPE_STRING); | |||||
(void) zfs_set_bootenv(vdev, benv); | |||||
} | |||||
return (rv); | |||||
} | |||||
/* | |||||
* nvstore backend. | |||||
*/ | |||||
static int zfs_nvstore_setter(void *, int, const char *, | |||||
const void *, size_t); | |||||
static int zfs_nvstore_setter_str(void *, const char *, const char *, | |||||
const char *); | |||||
static int zfs_nvstore_unset_impl(void *, const char *, bool); | |||||
static int zfs_nvstore_setenv(void *, void *); | |||||
/* | |||||
* nvstore is only present for current rootfs pool. | |||||
*/ | |||||
static int | |||||
zfs_nvstore_sethook(struct env_var *ev, int flags __unused, const void *value) | |||||
{ | |||||
struct zfs_devdesc *dev; | |||||
int rv; | |||||
archsw.arch_getdev((void **)&dev, NULL, NULL); | |||||
if (dev == NULL) | |||||
return (ENXIO); | |||||
rv = zfs_nvstore_setter_str(dev, NULL, ev->ev_name, value); | |||||
free(dev); | |||||
return (rv); | |||||
} | |||||
/* | |||||
* nvstore is only present for current rootfs pool. | |||||
*/ | |||||
static int | |||||
zfs_nvstore_unsethook(struct env_var *ev) | |||||
{ | |||||
struct zfs_devdesc *dev; | |||||
int rv; | |||||
archsw.arch_getdev((void **)&dev, NULL, NULL); | |||||
if (dev == NULL) | |||||
return (ENXIO); | |||||
rv = zfs_nvstore_unset_impl(dev, ev->ev_name, false); | |||||
free(dev); | |||||
return (rv); | |||||
} | |||||
static int | |||||
zfs_nvstore_getter(void *vdev, const char *name, void **data) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
nvlist_t *nv; | |||||
char *str, **ptr; | |||||
int size; | |||||
int rv; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | if (dev->dd.d_dev->dv_type != DEVT_ZFS) | ||||
return (1); | return (ENOTSUP); | ||||
if (dev->pool_guid == 0) | if ((spa = spa_find_by_dev(dev)) == NULL) | ||||
spa = STAILQ_FIRST(&zfs_pools); | return (ENXIO); | ||||
else | |||||
spa = spa_find_by_guid(dev->pool_guid); | |||||
if (spa == NULL) { | if (spa->spa_bootenv == NULL) | ||||
printf("ZFS: can't find pool by guid\n"); | return (ENXIO); | ||||
return (1); | |||||
if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST, | |||||
NULL, &nv, NULL) != 0) | |||||
return (ENOENT); | |||||
rv = nvlist_find(nv, name, DATA_TYPE_STRING, NULL, &str, &size); | |||||
if (rv == 0) { | |||||
ptr = (char **)data; | |||||
asprintf(ptr, "%.*s", size, str); | |||||
if (*data == NULL) | |||||
rv = ENOMEM; | |||||
} | } | ||||
nvlist_destroy(nv); | |||||
return (rv); | |||||
} | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | static int | ||||
char *tmp = vdev_read_pad2(vd); | zfs_nvstore_setter(void *vdev, int type, const char *name, | ||||
const void *data, size_t size) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
nvlist_t *nv; | |||||
int rv; | |||||
bool env_set = true; | |||||
/* Continue on error. */ | if (dev->dd.d_dev->dv_type != DEVT_ZFS) | ||||
if (tmp == NULL) | return (ENOTSUP); | ||||
continue; | |||||
/* Nextboot is not set. */ | if ((spa = spa_find_by_dev(dev)) == NULL) | ||||
if (*tmp == '\0') { | return (ENXIO); | ||||
free(result); | |||||
free(tmp); | if (spa->spa_bootenv == NULL) | ||||
return (1); | return (ENXIO); | ||||
if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST, | |||||
NULL, &nv, NULL) != 0) { | |||||
nv = nvlist_create(NV_UNIQUE_NAME); | |||||
if (nv == NULL) | |||||
return (ENOMEM); | |||||
} | } | ||||
if (result == NULL) { | |||||
result = tmp; | rv = 0; | ||||
continue; | switch (type) { | ||||
case DATA_TYPE_INT8: | |||||
if (size != sizeof (int8_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | } | ||||
free(tmp); | rv = nvlist_add_int8(nv, name, *(int8_t *)data); | ||||
break; | |||||
case DATA_TYPE_INT16: | |||||
if (size != sizeof (int16_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | } | ||||
if (result == NULL) | rv = nvlist_add_int16(nv, name, *(int16_t *)data); | ||||
return (1); | break; | ||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | case DATA_TYPE_INT32: | ||||
vdev_clear_pad2(vd); | if (size != sizeof (int32_t)) { | ||||
rv = EINVAL; | |||||
break; | |||||
} | } | ||||
rv = nvlist_add_int32(nv, name, *(int32_t *)data); | |||||
break; | |||||
strlcpy(buf, result, size); | case DATA_TYPE_INT64: | ||||
free(result); | if (size != sizeof (int64_t)) { | ||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_int64(nv, name, *(int64_t *)data); | |||||
break; | |||||
case DATA_TYPE_BYTE: | |||||
if (size != sizeof (uint8_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_byte(nv, name, *(int8_t *)data); | |||||
break; | |||||
case DATA_TYPE_UINT8: | |||||
if (size != sizeof (uint8_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_uint8(nv, name, *(int8_t *)data); | |||||
break; | |||||
case DATA_TYPE_UINT16: | |||||
if (size != sizeof (uint16_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_uint16(nv, name, *(uint16_t *)data); | |||||
break; | |||||
case DATA_TYPE_UINT32: | |||||
if (size != sizeof (uint32_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_uint32(nv, name, *(uint32_t *)data); | |||||
break; | |||||
case DATA_TYPE_UINT64: | |||||
if (size != sizeof (uint64_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_uint64(nv, name, *(uint64_t *)data); | |||||
break; | |||||
case DATA_TYPE_STRING: | |||||
rv = nvlist_add_string(nv, name, data); | |||||
break; | |||||
case DATA_TYPE_BOOLEAN_VALUE: | |||||
if (size != sizeof (boolean_t)) { | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
rv = nvlist_add_boolean_value(nv, name, *(boolean_t *)data); | |||||
break; | |||||
default: | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
if (rv == 0) { | |||||
rv = nvlist_add_nvlist(spa->spa_bootenv, OS_NVSTORE, nv); | |||||
if (rv == 0) { | |||||
rv = zfs_set_bootenv(vdev, spa->spa_bootenv); | |||||
} | |||||
if (rv == 0) { | |||||
if (env_set) { | |||||
rv = zfs_nvstore_setenv(vdev, | |||||
nvpair_find(nv, name)); | |||||
} else { | |||||
env_discard(env_getenv(name)); | |||||
rv = 0; | |||||
} | |||||
} | |||||
} | |||||
nvlist_destroy(nv); | |||||
return (rv); | |||||
} | |||||
static int | |||||
get_int64(const char *data, int64_t *ip) | |||||
{ | |||||
char *end; | |||||
int64_t val; | |||||
errno = 0; | |||||
val = strtoll(data, &end, 0); | |||||
if (errno != 0 || *data == '\0' || *end != '\0') | |||||
return (EINVAL); | |||||
*ip = val; | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
get_uint64(const char *data, uint64_t *ip) | |||||
{ | |||||
char *end; | |||||
uint64_t val; | |||||
errno = 0; | |||||
val = strtoull(data, &end, 0); | |||||
if (errno != 0 || *data == '\0' || *end != '\0') | |||||
return (EINVAL); | |||||
*ip = val; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Translate textual data to data type. If type is not set, and we are | |||||
* creating new pair, use DATA_TYPE_STRING. | |||||
*/ | |||||
static int | |||||
zfs_nvstore_setter_str(void *vdev, const char *type, const char *name, | |||||
const char *data) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
nvlist_t *nv; | |||||
int rv; | |||||
data_type_t dt; | |||||
int64_t val; | |||||
uint64_t uval; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
if (spa->spa_bootenv == NULL) | |||||
return (ENXIO); | |||||
if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST, | |||||
NULL, &nv, NULL) != 0) { | |||||
nv = NULL; | |||||
} | |||||
if (type == NULL) { | |||||
nvp_header_t *nvh; | |||||
/* | |||||
* if there is no existing pair, default to string. | |||||
* Otherwise, use type from existing pair. | |||||
*/ | |||||
nvh = nvpair_find(nv, name); | |||||
if (nvh == NULL) { | |||||
dt = DATA_TYPE_STRING; | |||||
} else { | |||||
nv_string_t *nvp_name; | |||||
nv_pair_data_t *nvp_data; | |||||
nvp_name = (nv_string_t *)(nvh + 1); | |||||
nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] + | |||||
NV_ALIGN4(nvp_name->nv_size)); | |||||
dt = nvp_data->nv_type; | |||||
} | |||||
} else { | |||||
dt = nvpair_type_from_name(type); | |||||
} | |||||
nvlist_destroy(nv); | |||||
rv = 0; | |||||
switch (dt) { | |||||
case DATA_TYPE_INT8: | |||||
rv = get_int64(data, &val); | |||||
if (rv == 0) { | |||||
int8_t v = val; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_INT16: | |||||
rv = get_int64(data, &val); | |||||
if (rv == 0) { | |||||
int16_t v = val; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_INT32: | |||||
rv = get_int64(data, &val); | |||||
if (rv == 0) { | |||||
int32_t v = val; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_INT64: | |||||
rv = get_int64(data, &val); | |||||
if (rv == 0) { | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &val, | |||||
sizeof (val)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_BYTE: | |||||
rv = get_uint64(data, &uval); | |||||
if (rv == 0) { | |||||
uint8_t v = uval; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_UINT8: | |||||
rv = get_uint64(data, &uval); | |||||
if (rv == 0) { | |||||
uint8_t v = uval; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_UINT16: | |||||
rv = get_uint64(data, &uval); | |||||
if (rv == 0) { | |||||
uint16_t v = uval; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_UINT32: | |||||
rv = get_uint64(data, &uval); | |||||
if (rv == 0) { | |||||
uint32_t v = uval; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_UINT64: | |||||
rv = get_uint64(data, &uval); | |||||
if (rv == 0) { | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &uval, | |||||
sizeof (uval)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_STRING: | |||||
rv = zfs_nvstore_setter(vdev, dt, name, data, strlen(data) + 1); | |||||
break; | |||||
case DATA_TYPE_BOOLEAN: | |||||
rv = get_int64(data, &val); | |||||
if (rv != 0) { | |||||
if (strcasecmp(data, "yes") == 0) { | |||||
rv = 0; | |||||
val = true; | |||||
} | |||||
if (strcasecmp(data, "no") == 0) { | |||||
rv = 0; | |||||
val = false; | |||||
} | |||||
if (strcasecmp(data, "on") == 0) { | |||||
rv = 0; | |||||
val = true; | |||||
} | |||||
if (strcasecmp(data, "off") == 0) { | |||||
rv = 0; | |||||
val = false; | |||||
} | |||||
} | |||||
if (rv == 0) { | |||||
int v = val; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
break; | |||||
case DATA_TYPE_BOOLEAN_VALUE: | |||||
rv = get_int64(data, &val); | |||||
if (rv == 0) { | |||||
boolean_t v = val; | |||||
rv = zfs_nvstore_setter(vdev, dt, name, &v, sizeof (v)); | |||||
} | |||||
default: | |||||
rv = EINVAL; | |||||
} | |||||
return (rv); | |||||
} | |||||
static int | |||||
zfs_nvstore_unset_impl(void *vdev, const char *name, bool unset_env) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
nvlist_t *nv; | |||||
int rv; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
if (spa->spa_bootenv == NULL) | |||||
return (ENXIO); | |||||
if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST, | |||||
NULL, &nv, NULL) != 0) | |||||
return (ENOENT); | |||||
rv = nvlist_remove(nv, name, DATA_TYPE_UNKNOWN); | |||||
if (rv == 0) { | |||||
if (nvlist_next_nvpair(nv, NULL) == NULL) { | |||||
rv = nvlist_remove(spa->spa_bootenv, OS_NVSTORE, | |||||
DATA_TYPE_NVLIST); | |||||
} else { | |||||
rv = nvlist_add_nvlist(spa->spa_bootenv, | |||||
OS_NVSTORE, nv); | |||||
} | |||||
if (rv == 0) | |||||
rv = zfs_set_bootenv(vdev, spa->spa_bootenv); | |||||
} | |||||
if (unset_env) | |||||
env_discard(env_getenv(name)); | |||||
return (rv); | |||||
} | |||||
static int | |||||
zfs_nvstore_unset(void *vdev, const char *name) | |||||
{ | |||||
return (zfs_nvstore_unset_impl(vdev, name, true)); | |||||
} | |||||
static int | |||||
zfs_nvstore_print(void *vdev __unused, void *ptr) | |||||
{ | |||||
nvpair_print(ptr, 0); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Create environment variable from nvpair. | |||||
* set hook will update nvstore with new value, unset hook will remove | |||||
* variable from nvstore. | |||||
*/ | |||||
static int | |||||
zfs_nvstore_setenv(void *vdev __unused, void *ptr) | |||||
{ | |||||
nvp_header_t *nvh = ptr; | |||||
nv_string_t *nvp_name, *nvp_value; | |||||
nv_pair_data_t *nvp_data; | |||||
char *name, *value; | |||||
int rv = 0; | |||||
if (nvh == NULL) | |||||
return (ENOENT); | |||||
nvp_name = (nv_string_t *)(nvh + 1); | |||||
nvp_data = (nv_pair_data_t *)(&nvp_name->nv_data[0] + | |||||
NV_ALIGN4(nvp_name->nv_size)); | |||||
if ((name = nvstring_get(nvp_name)) == NULL) | |||||
return (ENOMEM); | |||||
value = NULL; | |||||
switch (nvp_data->nv_type) { | |||||
case DATA_TYPE_BOOLEAN: | |||||
value = strdup("YES"); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_BYTE: | |||||
case DATA_TYPE_UINT8: | |||||
(void) asprintf(&value, "%uc", | |||||
*(unsigned *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_INT8: | |||||
(void) asprintf(&value, "%c", *(int *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_INT16: | |||||
(void) asprintf(&value, "%hd", *(short *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_UINT16: | |||||
(void) asprintf(&value, "%hu", | |||||
*(unsigned short *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_BOOLEAN_VALUE: | |||||
case DATA_TYPE_INT32: | |||||
(void) asprintf(&value, "%d", *(int *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_UINT32: | |||||
(void) asprintf(&value, "%u", | |||||
*(unsigned *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_INT64: | |||||
(void) asprintf(&value, "%jd", | |||||
(intmax_t)*(int64_t *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_UINT64: | |||||
(void) asprintf(&value, "%ju", | |||||
(uintmax_t)*(uint64_t *)&nvp_data->nv_data[0]); | |||||
if (value == NULL) | |||||
rv = ENOMEM; | |||||
break; | |||||
case DATA_TYPE_STRING: | |||||
nvp_value = (nv_string_t *)&nvp_data->nv_data[0]; | |||||
if ((value = nvstring_get(nvp_value)) == NULL) { | |||||
rv = ENOMEM; | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
rv = EINVAL; | |||||
break; | |||||
} | |||||
if (value != NULL) { | |||||
rv = env_setenv(name, EV_VOLATILE | EV_NOHOOK, value, | |||||
zfs_nvstore_sethook, zfs_nvstore_unsethook); | |||||
free(value); | |||||
} | |||||
free(name); | |||||
return (rv); | |||||
} | |||||
static int | |||||
zfs_nvstore_iterate(void *vdev, int (*cb)(void *, void *)) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
nvlist_t *nv; | |||||
nvp_header_t *nvh; | |||||
int rv; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
if (spa->spa_bootenv == NULL) | |||||
return (ENXIO); | |||||
if (nvlist_find(spa->spa_bootenv, OS_NVSTORE, DATA_TYPE_NVLIST, | |||||
NULL, &nv, NULL) != 0) | |||||
return (ENOENT); | |||||
rv = 0; | |||||
nvh = NULL; | |||||
while ((nvh = nvlist_next_nvpair(nv, nvh)) != NULL) { | |||||
rv = cb(vdev, nvh); | |||||
if (rv != 0) | |||||
break; | |||||
} | |||||
return (rv); | |||||
} | |||||
nvs_callbacks_t nvstore_zfs_cb = { | |||||
.nvs_getter = zfs_nvstore_getter, | |||||
.nvs_setter = zfs_nvstore_setter, | |||||
.nvs_setter_str = zfs_nvstore_setter_str, | |||||
.nvs_unset = zfs_nvstore_unset, | |||||
.nvs_print = zfs_nvstore_print, | |||||
.nvs_iterate = zfs_nvstore_iterate | |||||
}; | |||||
int | int | ||||
zfs_attach_nvstore(void *vdev) | |||||
{ | |||||
struct zfs_devdesc *dev = vdev; | |||||
spa_t *spa; | |||||
uint64_t version; | |||||
int rv; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (ENOTSUP); | |||||
if ((spa = spa_find_by_dev(dev)) == NULL) | |||||
return (ENXIO); | |||||
rv = nvlist_find(spa->spa_bootenv, BOOTENV_VERSION, DATA_TYPE_UINT64, | |||||
NULL, &version, NULL); | |||||
if (rv != 0 || version != VB_NVLIST) { | |||||
return (ENXIO); | |||||
} | |||||
dev = malloc(sizeof (*dev)); | |||||
if (dev == NULL) | |||||
return (ENOMEM); | |||||
memcpy(dev, vdev, sizeof (*dev)); | |||||
rv = nvstore_init(spa->spa_name, &nvstore_zfs_cb, dev); | |||||
if (rv != 0) | |||||
free(dev); | |||||
else | |||||
rv = zfs_nvstore_iterate(dev, zfs_nvstore_setenv); | |||||
return (rv); | |||||
} | |||||
int | |||||
zfs_probe_dev(const char *devname, uint64_t *pool_guid) | zfs_probe_dev(const char *devname, uint64_t *pool_guid) | ||||
{ | { | ||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
struct ptable *table; | struct ptable *table; | ||||
struct zfs_probe_args pa; | struct zfs_probe_args pa; | ||||
uint64_t mediasz; | uint64_t mediasz; | ||||
int ret; | int ret; | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | zfs_dev_open(struct open_file *f, ...) | ||||
struct zfsmount *mount; | struct zfsmount *mount; | ||||
spa_t *spa; | spa_t *spa; | ||||
int rv; | int rv; | ||||
va_start(args, f); | va_start(args, f); | ||||
dev = va_arg(args, struct zfs_devdesc *); | dev = va_arg(args, struct zfs_devdesc *); | ||||
va_end(args); | va_end(args); | ||||
if (dev->pool_guid == 0) | if ((spa = spa_find_by_dev(dev)) == NULL) | ||||
spa = STAILQ_FIRST(&zfs_pools); | |||||
else | |||||
spa = spa_find_by_guid(dev->pool_guid); | |||||
if (!spa) | |||||
return (ENXIO); | return (ENXIO); | ||||
mount = malloc(sizeof(*mount)); | mount = malloc(sizeof(*mount)); | ||||
if (mount == NULL) | if (mount == NULL) | ||||
rv = ENOMEM; | rv = ENOMEM; | ||||
else | else | ||||
rv = zfs_mount(spa, dev->root_guid, mount); | rv = zfs_mount(spa, dev->root_guid, mount); | ||||
if (rv != 0) { | if (rv != 0) { | ||||
free(mount); | free(mount); | ||||
return (rv); | return (rv); | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { | ||||
return (buf); | return (buf); | ||||
} | } | ||||
if (zfs_rlookup(spa, dev->root_guid, rootname)) { | if (zfs_rlookup(spa, dev->root_guid, rootname)) { | ||||
printf("ZFS: can't find filesystem by guid\n"); | printf("ZFS: can't find filesystem by guid\n"); | ||||
return (buf); | return (buf); | ||||
} | } | ||||
if (rootname[0] == '\0') | if (rootname[0] == '\0') | ||||
sprintf(buf, "%s:%s:", dev->dd.d_dev->dv_name, spa->spa_name); | snprintf(buf, sizeof(buf), "%s:%s:", dev->dd.d_dev->dv_name, | ||||
spa->spa_name); | |||||
else | else | ||||
sprintf(buf, "%s:%s/%s:", dev->dd.d_dev->dv_name, spa->spa_name, | snprintf(buf, sizeof(buf), "%s:%s/%s:", dev->dd.d_dev->dv_name, | ||||
rootname); | spa->spa_name, rootname); | ||||
return (buf); | return (buf); | ||||
} | } | ||||
static int | static int | ||||
split_devname(const char *name, char *poolname, size_t size, | split_devname(const char *name, char *poolname, size_t size, | ||||
const char **dsnamep) | const char **dsnamep) | ||||
{ | { | ||||
const char *dsname; | const char *dsname; | ||||
▲ Show 20 Lines • Show All 311 Lines • Show Last 20 Lines |