Changeset View
Changeset View
Standalone View
Standalone View
stand/libsa/zfs/zfs.c
Show First 20 Lines • Show All 477 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; | ||||
spa_t *spa; | nvlist_t *benv = NULL; | ||||
vdev_t *vd; | vdev_t *vd; | ||||
char *result = NULL; | spa_t *spa; | ||||
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"); | STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, | ||||
return (1); | v_childlink) { | ||||
benv = vdev_read_bootenv(vd); | |||||
if (benv != NULL) | |||||
break; | |||||
} | } | ||||
spa->spa_bootenv = benv; | |||||
} else { | |||||
benv = spa->spa_bootenv; | |||||
} | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | if (benv == NULL) | ||||
char *tmp = vdev_read_pad2(vd); | return (ENOENT); | ||||
/* Continue on error. */ | *benvp = benv; | ||||
if (tmp == NULL) | return (0); | ||||
continue; | |||||
/* Nextboot is not set. */ | |||||
if (*tmp == '\0') { | |||||
free(result); | |||||
free(tmp); | |||||
return (1); | |||||
} | } | ||||
if (result == NULL) { | |||||
result = tmp; | |||||
continue; | |||||
} | |||||
free(tmp); | |||||
} | |||||
if (result == NULL) | |||||
return (1); | |||||
/* | |||||
* 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; | |||||
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) { | STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | ||||
vdev_clear_pad2(vd); | vdev_write_bootenv(vd, benv); | ||||
} | } | ||||
strlcpy(buf, result, size); | spa->spa_bootenv = benv; | ||||
free(result); | |||||
return (0); | 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 | int | ||||
zfs_get_bootonce(void *vdev, const char *key, char *buf, size_t size) | |||||
{ | |||||
nvlist_t *benv; | |||||
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); | |||||
} | |||||
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); | ||||
} | } | ||||
int | int | ||||
zfs_list(const char *name) | zfs_list(const char *name) | ||||
{ | { | ||||
static char poolname[ZFS_MAXNAMELEN]; | static char poolname[ZFS_MAXNAMELEN]; | ||||
uint64_t objid; | uint64_t objid; | ||||
▲ Show 20 Lines • Show All 272 Lines • Show Last 20 Lines |