commit f6aefc6b55e1e85715d3239cae04d355932fa5b2 Author: Andriy Gapon Date: Thu May 3 18:14:17 2012 +0300 zfs boot: chose a "first" pool if none is explicitly requested diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c index 6c964b0..eb8833f 100644 --- a/sys/boot/zfs/zfs.c +++ b/sys/boot/zfs/zfs.c @@ -513,7 +513,10 @@ zfs_dev_open(struct open_file *f, ...) dev = va_arg(args, struct zfs_devdesc *); va_end(args); - spa = spa_find_by_guid(dev->pool_guid); + if (dev->pool_guid == 0) + spa = STAILQ_FIRST(&zfs_pools); + else + spa = spa_find_by_guid(dev->pool_guid); if (!spa) return (ENXIO); rv = zfs_spa_init(spa); @@ -627,7 +630,10 @@ zfs_fmtdev(void *vdev) if (dev->d_type != DEVT_ZFS) return (buf); - spa = spa_find_by_guid(dev->pool_guid); + 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 (buf); commit 3725d5ce986efdd5b1390e4098fc615b990953c0 Author: Andriy Gapon Date: Sat May 5 10:46:16 2012 +0300 [wip] export boot/primary pool and vdev guid all the way to kenv diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 91f4e32..80c8178 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -209,6 +209,7 @@ extract_currdev(void) { struct i386_devdesc new_currdev; #ifdef LOADER_ZFS_SUPPORT + char buf[20]; struct zfs_boot_args *zargs; #endif int biosdev = -1; @@ -239,10 +240,17 @@ extract_currdev(void) if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) zargs = (struct zfs_boot_args *)(kargs + 1); - if (zargs != NULL && zargs->size >= sizeof(*zargs)) { + if (zargs != NULL && + zargs->size >= offsetof(struct zfs_boot_args, primary_pool)) { /* sufficient data is provided */ new_currdev.d_kind.zfs.pool_guid = zargs->pool; new_currdev.d_kind.zfs.root_guid = zargs->root; + if (zargs->size >= sizeof(*zargs) && zargs->primary_vdev != 0) { + sprintf(buf, "%llu", zargs->primary_pool); + setenv("vfs.zfs.boot.primary_pool", buf, 1); + sprintf(buf, "%llu", zargs->primary_vdev); + setenv("vfs.zfs.boot.primary_vdev", buf, 1); + } } else { /* old style zfsboot block */ new_currdev.d_kind.zfs.pool_guid = kargs->zfspool; diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index c92b11e..9a6e45b 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -176,6 +176,8 @@ zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t * Current ZFS pool */ static spa_t *spa; +static spa_t *primary_spa; +static vdev_t *primary_vdev; /* * A wrapper for dskread that doesn't have to worry about whether the @@ -526,7 +528,7 @@ main(void) * first pool we found, if any. */ if (!spa) { - spa = STAILQ_FIRST(&zfs_pools); + spa = spa_get_primary(); if (!spa) { printf("%s: No ZFS pools located, can't boot\n", BOOTPROG); for (;;) @@ -534,6 +536,9 @@ main(void) } } + primary_spa = spa; + primary_vdev = spa_get_primary_vdev(spa); + if (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0) { printf("%s: failed to mount default pool %s\n", BOOTPROG, spa->spa_name); @@ -698,6 +703,11 @@ load(void) zfsargs.size = sizeof(zfsargs); zfsargs.pool = zfsmount.spa->spa_guid; zfsargs.root = zfsmount.rootobj; + zfsargs.primary_pool = primary_spa->spa_guid; + if (primary_vdev != NULL) + zfsargs.primary_vdev = primary_vdev->v_guid; + else + printf("failed to detect primary vdev\n"); __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), bootdev, KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h index 5c114db..7ad3a72 100644 --- a/sys/boot/zfs/libzfs.h +++ b/sys/boot/zfs/libzfs.h @@ -53,6 +53,8 @@ struct zfs_boot_args uint32_t reserved; uint64_t pool; uint64_t root; + uint64_t primary_pool; + uint64_t primary_vdev; }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c index a366c04..219d7af 100644 --- a/sys/boot/zfs/zfsimpl.c +++ b/sys/boot/zfs/zfsimpl.c @@ -704,6 +704,34 @@ spa_find_by_name(const char *name) return (0); } +#ifdef BOOT2 +static spa_t * +spa_get_primary(void) +{ + + return (STAILQ_FIRST(&zfs_pools)); +} + +static vdev_t * +spa_get_primary_vdev(const spa_t *spa) +{ + vdev_t *vdev; + vdev_t *kid; + + if (spa == NULL) + spa = spa_get_primary(); + if (spa == NULL) + return (NULL); + vdev = STAILQ_FIRST(&spa->spa_vdevs); + if (vdev == NULL) + return (NULL); + for (kid = STAILQ_FIRST(&vdev->v_children); kid != NULL; + kid = STAILQ_FIRST(&vdev->v_children)) + vdev = kid; + return (vdev); +} +#endif + static spa_t * spa_create(uint64_t guid) { commit c4a8bee53d7201df0cdb9b78084a35eacc20e300 Author: Andriy Gapon Date: Fri Sep 14 01:09:13 2012 +0300 zfsboot: simplify probe_drive() a little bit The first discovered pool, whether it covers the whole boot disk or not, is going to be first in zfs_pools list. So there is no need at all for spapp parameter. This commit also fixes a bug where NULL would be assigned to NULL pointer when probe_drive was called with the spapp parameter of NULL. diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index 4375a66..b0b852d 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -345,7 +345,7 @@ copy_dsk(struct dsk *dsk) } static void -probe_drive(struct dsk *dsk, spa_t **spap) +probe_drive(struct dsk *dsk) { #ifdef GPT struct gpt_hdr hdr; @@ -359,9 +359,10 @@ probe_drive(struct dsk *dsk, spa_t **spap) /* * If we find a vdev on the whole disk, stop here. Otherwise dig - * out the MBR and probe each slice in turn for a vdev. + * out the partition table and probe each slice/partition + * in turn for a vdev. */ - if (vdev_probe(vdev_read, dsk, spap) == 0) + if (vdev_probe(vdev_read, dsk, NULL) == 0) return; sec = dmadat->secbuf; @@ -399,13 +400,7 @@ probe_drive(struct dsk *dsk, spa_t **spap) if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, sizeof(uuid_t)) == 0) { dsk->start = ent->ent_lba_start; - if (vdev_probe(vdev_read, dsk, spap) == 0) { - /* - * We record the first pool we find (we will try - * to boot from that one). - */ - spap = NULL; - + if (vdev_probe(vdev_read, dsk, NULL) == 0) { /* * This slice had a vdev. We need a new dsk * structure now since the vdev now owns this one. @@ -428,13 +423,7 @@ trymbr: if (!dp[i].dp_typ) continue; dsk->start = dp[i].dp_start; - if (vdev_probe(vdev_read, dsk, spap) == 0) { - /* - * We record the first pool we find (we will try to boot - * from that one. - */ - spap = 0; - + if (vdev_probe(vdev_read, dsk, NULL) == 0) { /* * This slice had a vdev. We need a new dsk structure now * since the vdev now owns this one. @@ -493,7 +482,7 @@ main(void) * Probe the boot drive first - we will try to boot from whatever * pool we find on that drive. */ - probe_drive(dsk, &spa); + probe_drive(dsk); /* * Probe the rest of the drives that the bios knows about. This @@ -520,20 +509,17 @@ main(void) dsk->part = 0; dsk->start = 0; dsk->init = 0; - probe_drive(dsk, NULL); + probe_drive(dsk); } /* - * If we didn't find a pool on the boot drive, default to the - * first pool we found, if any. + * The first discovered pool, if any, is the pool. */ + spa = spa_get_primary(); if (!spa) { - spa = spa_get_primary(); - if (!spa) { - printf("%s: No ZFS pools located, can't boot\n", BOOTPROG); - for (;;) - ; - } + printf("%s: No ZFS pools located, can't boot\n", BOOTPROG); + for (;;) + ; } primary_spa = spa; commit 4e25bc66c0a1ff82d5ef9ba3e8cffd16aad0e9a5 Author: Andriy Gapon Date: Fri Sep 14 23:07:54 2012 +0300 i386 boot: expose drvwrite and drvsize under ZFS too This is currently a NOP. diff --git a/sys/boot/i386/common/drv.c b/sys/boot/i386/common/drv.c index 52933d5..c4e9245 100644 --- a/sys/boot/i386/common/drv.c +++ b/sys/boot/i386/common/drv.c @@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$"); #include "xreadorg.h" #endif -#ifdef GPT +#if defined(GPT) || defined(ZFS) static struct edd_params params; uint64_t @@ -50,7 +50,7 @@ drvsize(struct dsk *dskp) } return (params.sectors); } -#endif /* GPT */ +#endif /* GPT || ZFS */ #ifndef USE_XREAD static struct edd_packet packet; @@ -93,7 +93,7 @@ drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) return (0); } -#ifdef GPT +#if defined(GPT) || defined(ZFS) int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) { @@ -116,4 +116,4 @@ drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) } return (0); } -#endif /* GPT */ +#endif /* GPT || ZFS */ diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h index 1ecfbc3..1e58503 100644 --- a/sys/boot/i386/common/drv.h +++ b/sys/boot/i386/common/drv.h @@ -40,9 +40,9 @@ struct dsk { }; int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); -#ifdef GPT +#if defined(GPT) || defined(ZFS) int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); uint64_t drvsize(struct dsk *dskp); -#endif /* GPT */ +#endif /* GPT || ZFS */ #endif /* !_DRV_H_ */ commit 1c31cafce185d2df0b0865e5ef35c90f1a47138b Author: Andriy Gapon Date: Fri Sep 14 23:13:29 2012 +0300 zfsboot: add support for reading one time boot directives from zfs pool pad2 area The area was previously described as "Boot Block Header", but currently it is know as Pad2, marked as reserved and is zeroed out on pool creation. The new code interprets data in this area, if any, using the same format as boot.config. The area is immediately cleaned out. Failure to parse the directives results in a reboot right after the wipe out. Otherwise the boot sequence proceeds as usual. Also, detect, ignore and wipe out the old style "b007 b10c" header in the pad2 area. diff --git a/sys/boot/i386/gptzfsboot/Makefile b/sys/boot/i386/gptzfsboot/Makefile index a2b8fcc..20dca79 100644 --- a/sys/boot/i386/gptzfsboot/Makefile +++ b/sys/boot/i386/gptzfsboot/Makefile @@ -18,7 +18,7 @@ ORG2= 0x0 CFLAGS= -DBOOTPROG=\"gptzfsboot\" \ -O1 \ - -DGPT -DBOOT2 \ + -DGPT -DZFS -DBOOT2 \ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ -DSIOFMT=${B2SIOFMT} \ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ diff --git a/sys/boot/i386/zfsboot/Makefile b/sys/boot/i386/zfsboot/Makefile index b2db778..5a23a3b 100644 --- a/sys/boot/i386/zfsboot/Makefile +++ b/sys/boot/i386/zfsboot/Makefile @@ -16,7 +16,7 @@ ORG2= 0x2000 CFLAGS= -DBOOTPROG=\"zfsboot\" \ -O1 \ - -DBOOT2 \ + -DZFS -DBOOT2 \ -DSIOPRT=${BOOT_COMCONSOLE_PORT} \ -DSIOFMT=${B2SIOFMT} \ -DSIOSPD=${BOOT_COMCONSOLE_SPEED} \ diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index b0b852d..e304e01 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -122,6 +122,7 @@ struct dmadat { static struct dmadat *dmadat; void exit(int); +void reboot(void); static void load(void); static int parse(void); static void bios_getmem(void); @@ -163,7 +164,7 @@ zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t n = size; if (*offp + n > zp->zp_size) n = zp->zp_size - *offp; - + rc = dnode_read(spa, dnode, *offp, start, n); if (rc) return (-1); @@ -213,6 +214,35 @@ vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) } static int +vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) +{ + char *p; + daddr_t lba; + unsigned int nb; + struct dsk *dsk = (struct dsk *) priv; + + if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) + return -1; + + p = buf; + lba = off / DEV_BSIZE; + lba += dsk->start; + while (bytes > 0) { + nb = bytes / DEV_BSIZE; + if (nb > READ_BUF_SIZE / DEV_BSIZE) + nb = READ_BUF_SIZE / DEV_BSIZE; + memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE); + if (drvwrite(dsk, dmadat->rdbuf, lba, nb)) + return -1; + p += nb * DEV_BSIZE; + lba += nb; + bytes -= nb * DEV_BSIZE; + } + + return 0; +} + +static int xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte) { if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) { @@ -222,6 +252,52 @@ xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte) return 0; } +/* + * Read Pad2 (formerly "Boot Block Header") area of the first + * vdev label of the given vdev. + */ +static int +vdev_read_pad2(vdev_t *vdev, char *buf, size_t size) +{ + blkptr_t bp; + char *tmp = zap_scratch; + off_t off = offsetof(vdev_label_t, vl_pad2); + + if (size > VDEV_PAD_SIZE) + size = VDEV_PAD_SIZE; + + BP_ZERO(&bp); + BP_SET_LSIZE(&bp, VDEV_PAD_SIZE); + BP_SET_PSIZE(&bp, VDEV_PAD_SIZE); + BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); + BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); + DVA_SET_OFFSET(BP_IDENTITY(&bp), off); + if (vdev_read_phys(vdev, &bp, tmp, off, 0)) + return (EIO); + memcpy(buf, tmp, size); + return (0); +} + +static int +vdev_clear_pad2(vdev_t *vdev) +{ + char *zeroes = zap_scratch; + uint64_t *end; + off_t off = offsetof(vdev_label_t, vl_pad2); + + memset(zeroes, 0, VDEV_PAD_SIZE); + end = (uint64_t *)(zeroes + VDEV_PAD_SIZE); + /* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */ + end[-5] = 0x0210da7ab10c7a11; + end[-4] = 0x97f48f807f6e2a3f; + end[-3] = 0xaf909f1658aacefc; + end[-2] = 0xcbd1ea57ff6db48b; + end[-1] = 0x6ec692db0d465fab; + if (vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE)) + return (EIO); + return (0); +} + static void bios_getmem(void) { @@ -436,10 +512,12 @@ trymbr: int main(void) { - int autoboot, i; dnode_phys_t dn; off_t off; struct dsk *dsk; + int autoboot, i; + int nextboot; + int nextboot_failed; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); @@ -525,7 +603,41 @@ main(void) primary_spa = spa; primary_vdev = spa_get_primary_vdev(spa); - if (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0) { + nextboot = 0; + nextboot_failed = 0; + if (vdev_read_pad2(primary_vdev, cmd, sizeof(cmd)) == 0) { + if (*cmd) { + /* + * We could find an old-style ZFS Boot Block header here. + * Simply ignore it. + */ + if (*(uint64_t *)cmd != 0x2f5b007b10c) { + /* + * Note that parse() is destructive to cmd[] and we also want + * to honor RBX_QUIET option that could be present in cmd[]. + */ + nextboot = 1; + memcpy(cmddup, cmd, sizeof(cmd)); + if (parse()) { + printf("failed to parse pad2 area of primary vdev\n"); + nextboot_failed = 1; + } + if (!OPT_CHECK(RBX_QUIET)) + printf("zfs nextboot: %s\n", cmddup); + } + /* Do not process this command twice */ + *cmd = 0; + } + } else + printf("failed to read pad2 area of primary vdev\n"); + if (vdev_clear_pad2(primary_vdev)) + printf("failed to clear pad2 area of primary vdev\n"); + if (nextboot_failed) + reboot(); + + /* Mount ZFS only if it's not already mounted via nextboot parsing. */ + if (zfsmount.spa == NULL && + (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) { printf("%s: failed to mount default pool %s\n", BOOTPROG, spa->spa_name); autoboot = 0; @@ -549,6 +661,10 @@ main(void) *cmd = 0; } + /* Do not risk waiting at the prompt forever. */ + if (nextboot && !autoboot) + reboot(); + /* * Try to exec stage 3 boot loader. If interrupted by a keypress, * or in case of failure, try to load a kernel directly instead. @@ -598,6 +714,13 @@ main(void) void exit(int x) { + __exit(x); +} + +void +reboot(void) +{ + __exit(0); } static void commit 840815777ead08e682306106b7beaac724d5037f Author: Andriy Gapon Date: Fri Sep 14 23:15:43 2012 +0300 [wip] zfs: add a new ioctl that allows to place text data into pad2 area The data is placed into Pad2 area of the first vdev label of a given vdev in a given pool. diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h index 7e34889..38e92c2 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/vdev.h @@ -156,6 +156,8 @@ typedef enum { extern int vdev_label_init(vdev_t *vd, uint64_t txg, vdev_labeltype_t reason); +extern int vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size); + #ifdef __cplusplus } #endif diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c index e573feb..5e5c88d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_label.c @@ -855,6 +855,44 @@ retry: return (error); } +int +vdev_label_write_pad2(vdev_t *vd, const char *buf, size_t size) +{ + spa_t *spa = vd->vdev_spa; + zio_t *zio; + char *pad2; + int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL; + int error; + + if (size > VDEV_PAD_SIZE) + return (EINVAL); + + if (!vd->vdev_ops->vdev_op_leaf) + return (ENODEV); + if (vdev_is_dead(vd)) + return (ENXIO); + + ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL); + + pad2 = zio_buf_alloc(VDEV_PAD_SIZE); + bzero(pad2, VDEV_PAD_SIZE); + memcpy(pad2, buf, size); + +retry: + zio = zio_root(spa, NULL, NULL, flags); + vdev_label_write(zio, vd, 0, pad2, + offsetof(vdev_label_t, vl_pad2), + VDEV_PAD_SIZE, NULL, NULL, flags); + error = zio_wait(zio); + if (error != 0 && !(flags & ZIO_FLAG_TRYHARD)) { + flags |= ZIO_FLAG_TRYHARD; + goto retry; + } + + zio_buf_free(pad2, VDEV_PAD_SIZE); + return (error); +} + /* * ========================================================================== * uberblock load/sync diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c index b2f0f4b..aa0ba43 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -4061,6 +4061,37 @@ zfs_ioc_error_log(zfs_cmd_t *zc) } static int +zfs_ioc_nextboot(zfs_cmd_t *zc) +{ + spa_t *spa; + vdev_t *vd; + int error; + + mutex_enter(&spa_namespace_lock); + spa = spa_by_guid(zc->zc_obj, zc->zc_guid); + if (spa != NULL) + strcpy(zc->zc_name, spa_name(spa)); + mutex_exit(&spa_namespace_lock); + if (spa == NULL) + return (ENOENT); + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + spa_vdev_state_enter(spa, SCL_ALL); + vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE); + if (vd == NULL) { + (void) spa_vdev_state_exit(spa, NULL, ENXIO); + spa_close(spa, FTAG); + return (ENODEV); + } + error = vdev_label_write_pad2(vd, zc->zc_string, strlen(zc->zc_string)); + (void) spa_vdev_state_exit(spa, NULL, 0); + txg_wait_synced(spa->spa_dsl_pool, 0); + spa_close(spa, FTAG); + return (error); +} + +static int zfs_ioc_clear(zfs_cmd_t *zc) { spa_t *spa; @@ -5036,6 +5067,8 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { B_FALSE }, { zfs_ioc_pool_reopen, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_TRUE }, + { zfs_ioc_nextboot, zfs_secpolicy_config, NO_NAME, B_FALSE, + B_FALSE }, }; int diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h index 64fd2e6..1eceaef 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h @@ -817,6 +817,7 @@ typedef unsigned long zfs_ioc_t; #define ZFS_IOC_SPACE_SNAPS _IOWR('Z', 62, struct zfs_cmd) #define ZFS_IOC_SEND_PROGRESS _IOWR('Z', 63, struct zfs_cmd) #define ZFS_IOC_POOL_REOPEN _IOWR('Z', 64, struct zfs_cmd) +#define ZFS_IOC_NEXTBOOT _IOWR('Z', 65, struct zfs_cmd) /* * Internal SPA load state. Used by FMA diagnosis engine. commit 1779384ea9ebcd45e49e3f779cd5b535ebd14e3a Author: Andriy Gapon Date: Sun Sep 16 11:00:06 2012 +0300 [wip] znextboot: a simple tool to set zfs next boot options for zfsboot diff --git a/sbin/znextboot/Makefile b/sbin/znextboot/Makefile new file mode 100644 index 0000000..cc22809 --- /dev/null +++ b/sbin/znextboot/Makefile @@ -0,0 +1,19 @@ +# @(#)Makefile 8.4 (Berkeley) 6/22/95 +# +# $FreeBSD$ + +PROG= znextboot +WARNS?= 1 +MAN= + +CFLAGS+= -I${.CURDIR}/../../sys/cddl/compat/opensolaris +CFLAGS+= -I${.CURDIR}/../../cddl/compat/opensolaris/include +CFLAGS+= -I${.CURDIR}/../../cddl/compat/opensolaris/lib/libumem +CFLAGS+= -I${.CURDIR}/../../cddl/contrib/opensolaris/lib/libzpool/common +CFLAGS+= -I${.CURDIR}/../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs +CFLAGS+= -I${.CURDIR}/../../sys/cddl/contrib/opensolaris/uts/common +CFLAGS+= -I${.CURDIR}/../../sys/cddl/contrib/opensolaris/uts/common/sys +CFLAGS+= -I${.CURDIR}/../../cddl/contrib/opensolaris/head +CFLAGS+= -DNEED_SOLARIS_BOOLEAN + +.include diff --git a/sbin/znextboot/znextboot.c b/sbin/znextboot/znextboot.c new file mode 100644 index 0000000..0484be0 --- /dev/null +++ b/sbin/znextboot/znextboot.c @@ -0,0 +1,90 @@ +#include +#define _KERNEL +#include +#include +#undef _KERNEL +#include + +#undef lbolt +#undef lbolt64 +#undef gethrestime +#undef gethrestime_sec +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, const char * const *argv) +{ + zfs_cmd_t zc = { 0 }; + char buf[32]; + uint64_t pool_guid; + uint64_t vdev_guid; + int zfs_fd; + int len; + + if (argc != 2) { + fprintf(stderr, "usage: znextboot \n"); + return (1); + } + + len = strlen(argv[1]); + if (len == 0) { + fprintf(stderr, "empty options are not allowed\n"); + return (1); + } + if (len >= sizeof(zc.zc_string) ) { + fprintf(stderr, "options string is too long\n"); + return (1); + } + + if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) { + perror("failed to open ZFS device"); + return (1); + } + + if (kenv(KENV_GET, "vfs.zfs.boot.primary_pool", buf, sizeof(buf)) <= 0) { + perror("can't get vfs.zfs.boot.primary_pool"); + return (1); + } + pool_guid = strtoumax(buf, NULL, 10); + if (pool_guid == 0) { + perror("can't parse vfs.zfs.boot.primary_pool"); + return (1); + } + + if (kenv(KENV_GET, "vfs.zfs.boot.primary_vdev", buf, sizeof(buf)) <= 0) { + perror("can't get vfs.zfs.boot.primary_vdev"); + return (1); + } + vdev_guid = strtoumax(buf, NULL, 10); + if (vdev_guid == 0) { + perror("can't parse vfs.zfs.boot.primary_vdev"); + return (1); + } + + zc.zc_obj = pool_guid; + zc.zc_guid = vdev_guid; + memcpy(zc.zc_string, argv[1], len); + + if (ioctl(zfs_fd, ZFS_IOC_NEXTBOOT, &zc) != 0) { + perror("ZFS_IOC_NEXTBOOT failed"); + return (1); + } + + printf("zfs next boot options are successfully written\n"); + return (0); +} +