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, | |||||
size_t bytes) | |||||
{ | |||||
int fd, ret; | |||||
size_t head, tail, total_size, full_sec_size; | |||||
unsigned secsz, do_tail_write; | |||||
off_t start_sec; | |||||
ssize_t res; | |||||
char *outbuf, *bouncebuf; | |||||
fd = (uintptr_t)priv; | |||||
outbuf = (char *) buf; | |||||
bouncebuf = NULL; | |||||
ret = ioctl(fd, DIOCGSECTORSIZE, &secsz); | |||||
if (ret != 0) | |||||
return (ret); | |||||
start_sec = offset / secsz; | |||||
head = offset % secsz; | |||||
total_size = roundup2(head + bytes, secsz); | |||||
tail = total_size - (head + bytes); | |||||
do_tail_write = ((tail > 0) && (head + bytes > secsz)); | |||||
full_sec_size = total_size; | |||||
if (head > 0) | |||||
full_sec_size -= secsz; | |||||
if (do_tail_write) | |||||
full_sec_size -= secsz; | |||||
/* Partial sector write requires a bounce buffer. */ | |||||
if ((head > 0) || do_tail_write || bytes < secsz) { | |||||
bouncebuf = malloc(secsz); | |||||
if (bouncebuf == NULL) { | |||||
printf("vdev_write: out of memory\n"); | |||||
return (ENOMEM); | |||||
} | |||||
} | |||||
if (lseek(fd, start_sec * secsz, SEEK_SET) == -1) { | |||||
ret = errno; | |||||
goto error; | |||||
} | |||||
/* Partial data for first sector */ | |||||
if (head > 0) { | |||||
res = read(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
memcpy(bouncebuf + head, outbuf, min(secsz - head, bytes)); | |||||
(void) lseek(fd, -secsz, SEEK_CUR); | |||||
res = write(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
outbuf += min(secsz - head, bytes); | |||||
} | |||||
/* | |||||
* Full data write to sectors. | |||||
* Note, there is still corner case where we write | |||||
* to sector boundary, but less than sector size, e.g. write 512B | |||||
* to 4k sector. | |||||
*/ | |||||
if (full_sec_size > 0) { | |||||
if (bytes < full_sec_size) { | |||||
res = read(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
memcpy(bouncebuf, outbuf, bytes); | |||||
(void) lseek(fd, -secsz, SEEK_CUR); | |||||
res = write(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
} else { | |||||
res = write(fd, outbuf, full_sec_size); | |||||
if (res != full_sec_size) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
outbuf += full_sec_size; | |||||
} | |||||
} | |||||
/* Partial data write to last sector */ | |||||
if (do_tail_write) { | |||||
res = read(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
memcpy(bouncebuf, outbuf, secsz - tail); | |||||
(void) lseek(fd, -secsz, SEEK_CUR); | |||||
res = write(fd, bouncebuf, secsz); | |||||
if (res != secsz) { | |||||
ret = EIO; | |||||
goto error; | |||||
} | |||||
} | |||||
ret = 0; | |||||
error: | |||||
free(bouncebuf); | |||||
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 | |||||
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(); | ||||
if (archsw.arch_zfs_probe == NULL) | if (archsw.arch_zfs_probe == NULL) | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | zfs_probe_partition(void *arg, const char *partname, | ||||
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); | sprintf(devname, "%s%s:", devname, partname); | ||||
pa.fd = open(devname, O_RDONLY); | 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); | ||||
} | } | ||||
int | int | ||||
zfs_nextboot(void *vdev, char *buf, size_t size) | |||||
{ | |||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; | |||||
spa_t *spa; | |||||
vdev_t *vd; | |||||
char *result = NULL; | |||||
if (dev->dd.d_dev->dv_type != DEVT_ZFS) | |||||
return (1); | |||||
if (dev->pool_guid == 0) | |||||
spa = STAILQ_FIRST(&zfs_pools); | |||||
else | |||||
spa = spa_find_by_guid(dev->pool_guid); | |||||
if (spa == NULL) { | |||||
printf("ZFS: can't find pool by guid\n"); | |||||
return (1); | |||||
} | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | |||||
char *tmp = vdev_read_pad2(vd); | |||||
/* Continue on error. */ | |||||
if (tmp == NULL) | |||||
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); | |||||
STAILQ_FOREACH(vd, &spa->spa_root_vdev->v_children, v_childlink) { | |||||
vdev_clear_pad2(vd); | |||||
} | |||||
strlcpy(buf, result, size); | |||||
free(result); | |||||
return (0); | |||||
} | |||||
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; | ||||
if (pool_guid) | if (pool_guid) | ||||
*pool_guid = 0; | *pool_guid = 0; | ||||
pa.fd = open(devname, O_RDONLY); | pa.fd = open(devname, O_RDWR); | ||||
if (pa.fd == -1) | if (pa.fd == -1) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* | /* | ||||
* We will not probe the whole disk, we can not boot from such | * We will not probe the whole disk, we can not boot from such | ||||
* disks and some systems will misreport the disk sizes and will | * disks and some systems will misreport the disk sizes and will | ||||
* hang while accessing the disk. | * hang while accessing the disk. | ||||
*/ | */ | ||||
if (archsw.arch_getdev((void **)&dev, devname, NULL) == 0) { | if (archsw.arch_getdev((void **)&dev, devname, NULL) == 0) { | ||||
▲ Show 20 Lines • Show All 494 Lines • Show Last 20 Lines |