commit a7ae94fa337e3ddc2c97d3336a5312a957a28604 Author: Andriy Gapon Date: Wed Sep 21 12:35:41 2011 +0300 btx: increase size of args passed to client via exec ... by 8 bytes. See how non-obvious the code is. diff --git a/sys/boot/i386/btx/btxldr/btxldr.S b/sys/boot/i386/btx/btxldr/btxldr.S index d4176eb..115ff65 100644 --- a/sys/boot/i386/btx/btxldr/btxldr.S +++ b/sys/boot/i386/btx/btxldr/btxldr.S @@ -112,7 +112,7 @@ start.1: movl (%ebx),%eax # Get argument and call hexout # relocation call putstr # message #endif -start_null_bi: movl $0x18,%ecx # Allocate space +start_null_bi: movl $0x20,%ecx # Allocate space subl %ecx,%ebp # for arguments leal 0x4(%esp,1),%esi # Source movl %ebp,%edi # Destination diff --git a/sys/boot/i386/btx/lib/btxcsu.s b/sys/boot/i386/btx/lib/btxcsu.s index 6a00659..8221ace 100644 --- a/sys/boot/i386/btx/lib/btxcsu.s +++ b/sys/boot/i386/btx/lib/btxcsu.s @@ -26,7 +26,7 @@ # # Constants. # - .set ARGADJ,0xfa0 # Argument adjustment + .set ARGADJ,0xf98 # Argument adjustment # # Client entry point. # commit cc7f75e3cae33010a581a34c404bf830b7a51cbc Author: Andriy Gapon Date: Wed Apr 28 23:45:58 2010 +0300 zfsboot/zfsloader: support accessing filesystems within a pool In zfs loader zfs device name format now is "zfs:pool/fs", fully qualified file path is "zfs:pool/fs:/path/to/file" loader allows accessing files from various pools and filesystems as well as changing currdev to a different pool/filesystem. zfsboot accepts kernel/loader name in a format pool:fs:path/to/file or, as before, pool:path/to/file; in the latter case a default filesystem is used (pool root or bootfs). zfsboot passes guids of the selected pool and dataset to zfsloader to be used as its defaults. TODO: - clear up pool root filesystem vs pool bootfs filesystem distinction - set vfs.root.mountfrom based on currdev (for zfs) Mid-future TODO: - UI for selecting alternative boot environment Distant future TODO: - support accessing snapshots, using a snapshot as readonly root diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index 94a20c8..8e23771 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -41,6 +41,11 @@ CFLAGS+= -DSMBIOS_LITTLE_ENDIAN_UUID CFLAGS+= -DLOADER_GPT_SUPPORT .endif +.if !defined(LOADER_NO_ZFS_SUPPORT) +CFLAGS+= -DLOADER_ZFS_SUPPORT +SRCS+= ../../zfs/devicename.c +.endif + # Include simple terminal emulation (cons25-compatible) CFLAGS+= -DTERM_EMU diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c index d6f3ac1..7db5675 100644 --- a/sys/boot/i386/libi386/devicename.c +++ b/sys/boot/i386/libi386/devicename.c @@ -32,6 +32,9 @@ __FBSDID("$FreeBSD$"); #include #include "bootstrap.h" #include "libi386.h" +#ifdef LOADER_ZFS_SUPPORT +#include "../zfs/libzfs.h" +#endif static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path); @@ -88,6 +91,8 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) int i, unit, slice, partition, err; char *cp; const char *np; + const char *sep; + const char *end; /* minimum length check */ if (strlen(devspec) < 2) @@ -171,7 +176,6 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) case DEVT_CD: case DEVT_NET: - case DEVT_ZFS: unit = 0; if (*np && (*np != ':')) { @@ -192,7 +196,11 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) if (path != NULL) *path = (*cp == 0) ? cp : cp + 1; break; - +#ifdef LOADER_ZFS_SUPPORT + case DEVT_ZFS: + err = zfs_parsedev((struct zfs_devdesc *)idev, devspec, path); + break; +#endif default: err = EINVAL; goto fail; @@ -216,7 +224,7 @@ char * i386_fmtdev(void *vdev) { struct i386_devdesc *dev = (struct i386_devdesc *)vdev; - static char buf[128]; /* XXX device length constant? */ + static char buf[256]; /* XXX device length constant? */ char *cp; switch(dev->d_type) { @@ -247,9 +255,12 @@ i386_fmtdev(void *vdev) break; case DEVT_NET: - case DEVT_ZFS: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; +#ifdef LOADER_ZFS_SUPPORT + case DEVT_ZFS: + return(zfs_fmtdev(vdev)); +#endif } return(buf); } diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h index b8842ba..9705a84 100644 --- a/sys/boot/i386/libi386/libi386.h +++ b/sys/boot/i386/libi386/libi386.h @@ -30,7 +30,8 @@ /* * i386 fully-qualified device descriptor. * Note, this must match the 'struct devdesc' declaration - * in bootstrap.h. + * in bootstrap.h and also with struct zfs_devdesc for zfs + * support. */ struct i386_devdesc { @@ -49,6 +50,14 @@ struct i386_devdesc { void *data; } bioscd; +#ifdef LOADER_ZFS_SUPPORT + struct + { + void *data; + char poolname[256]; + char rootname[256]; + } zfs; +#endif } d_kind; }; diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c index bd14f47..4fca409 100644 --- a/sys/boot/i386/loader/conf.c +++ b/sys/boot/i386/loader/conf.c @@ -30,6 +30,9 @@ __FBSDID("$FreeBSD$"); #include #include #include "libi386/libi386.h" +#if defined(LOADER_ZFS_SUPPORT) +#include "../zfs/libzfs.h" +#endif /* * We could use linker sets for some or all of these, but @@ -50,10 +53,6 @@ __FBSDID("$FreeBSD$"); extern struct devsw fwohci; #endif -#if defined(LOADER_ZFS_SUPPORT) -extern struct devsw zfs_dev; -#endif - /* Exported for libstand */ struct devsw *devsw[] = { &bioscd, diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 75d5dbc..d270c2d 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -42,6 +42,10 @@ __FBSDID("$FreeBSD$"); #include "libi386/libi386.h" #include "btxv86.h" +#ifdef LOADER_ZFS_SUPPORT +#include "../zfs/libzfs.h" +#endif + #define KARGS_FLAGS_CD 0x1 #define KARGS_FLAGS_PXE 0x2 #define KARGS_FLAGS_ZFS 0x4 @@ -52,14 +56,21 @@ static struct u_int32_t howto; u_int32_t bootdev; u_int32_t bootflags; +#ifdef LOADER_ZFS_SUPPORT union { +#endif struct { u_int32_t pxeinfo; u_int32_t res2; }; +#ifdef LOADER_ZFS_SUPPORT uint64_t zfspool; }; +#endif u_int32_t bootinfo; +#ifdef LOADER_ZFS_SUPPORT + uint64_t zfsroot; +#endif } *kargs; static u_int32_t initial_howto; @@ -259,33 +270,17 @@ extract_currdev(void) "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev); new_currdev.d_unit = 0; } + +#ifdef LOADER_ZFS_SUPPORT + if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) + zfs_extract_currdev(kargs->zfspool, kargs->zfsroot, + (struct zfs_devdesc *)&new_currdev); +#endif + env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), i386_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, env_nounset); - -#ifdef LOADER_ZFS_SUPPORT - /* - * If we were started from a ZFS-aware boot2, we can work out - * which ZFS pool we are booting from. - */ - if (kargs->bootflags & KARGS_FLAGS_ZFS) { - /* - * Dig out the pool guid and convert it to a 'unit number' - */ - uint64_t guid; - int unit; - char devname[32]; - extern int zfs_guid_to_unit(uint64_t); - - guid = kargs->zfspool; - unit = zfs_guid_to_unit(guid); - if (unit >= 0) { - sprintf(devname, "zfs%d", unit); - setenv("currdev", devname, 1); - } - } -#endif } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index afefc4e..768a373 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -92,8 +92,10 @@ uint32_t opts; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; +struct zfsmount zfsmount; static char cmd[512]; static char kname[1024]; +static char rootname[256]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; static uint32_t bootdev; @@ -532,10 +534,12 @@ main(void) } } - zfs_mount_pool(spa); - - if (zfs_lookup(spa, PATH_CONFIG, &dn) == 0 || - zfs_lookup(spa, PATH_DOTCONFIG, &dn) == 0) { + 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); + autoboot = 0; + } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 || + zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) { off = 0; zfs_read(spa, &dn, &off, cmd, sizeof(cmd)); } @@ -565,11 +569,17 @@ main(void) /* Present the user with the boot2 prompt. */ for (;;) { - if (!autoboot || !OPT_CHECK(RBX_QUIET)) - printf("\nFreeBSD/x86 boot\n" - "Default: %s:%s\n" - "boot: ", - spa->spa_name, kname); + if (!autoboot || !OPT_CHECK(RBX_QUIET)) { + printf("\nFreeBSD/x86 boot\n"); + if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0) + printf("Default: %s:<0x%llx>:%s\n" + "boot: ", + spa->spa_name, zfsmount.rootobj, kname); + else + printf("Default: %s:%s:%s\n" + "boot: ", + spa->spa_name, rootname, kname); + } if (ioctrl & IO_SERIAL) sio_flush(); if (!autoboot || keyhit(5)) @@ -605,7 +615,8 @@ load(void) uint32_t addr, x; int fmt, i, j; - if (zfs_lookup(spa, kname, &dn)) { + if (zfs_lookup(&zfsmount, kname, &dn)) { + printf("\nCan't find %s\n", kname); return; } off = 0; @@ -684,7 +695,9 @@ load(void) KARGS_FLAGS_ZFS, (uint32_t) spa->spa_guid, (uint32_t) (spa->spa_guid >> 32), - VTOP(&bootinfo)); + VTOP(&bootinfo), + (uint32_t) zfsmount.rootobj, + (uint32_t) (zfsmount.rootobj >> 32)); } static int @@ -736,7 +749,7 @@ parse(void) } if (c == '?') { dnode_phys_t dn; - if (zfs_lookup(spa, arg, &dn) == 0) { + if (zfs_lookup(&zfsmount, arg, &dn) == 0) { zap_list(spa, &dn); } return -1; @@ -758,17 +771,32 @@ parse(void) q = (char *) strchr(arg, ':'); if (q) { spa_t *newspa; + uint64_t newroot; *q++ = 0; newspa = spa_find_by_name(arg); if (newspa) { + arg = q; spa = newspa; - zfs_mount_pool(spa); + newroot = 0; + q = (char *) strchr(arg, ':'); + if (q) { + *q++ = 0; + if (zfs_lookup_dataset(spa, arg, &newroot)) { + printf("\nCan't find dataset %s in ZFS pool %s\n", + arg, spa->spa_name); + return -1; + } + arg = q; + } + if (zfs_mount(spa, newroot, &zfsmount)) { + printf("\nCan't mount ZFS dataset\n"); + return -1; + } } else { printf("\nCan't find ZFS pool %s\n", arg); return -1; } - arg = q; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) diff --git a/sys/boot/zfs/devicename.c b/sys/boot/zfs/devicename.c new file mode 100644 index 0000000..7219df6 --- /dev/null +++ b/sys/boot/zfs/devicename.c @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2012 Andriy Gapon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "bootstrap.h" +#include "libzfs.h" + +int +zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) +{ + char *cp; + const char *np; + const char *sep; + const char *end; + int err; + + /* sanity check */ + if (strncmp(devspec, zfs_dev.dv_name, strlen(zfs_dev.dv_name)) != 0) + return (EINVAL); + + err = 0; + np = (devspec + strlen(zfs_dev.dv_name)); + + if (*np != ':') + return (EINVAL); + np++; + end = strchr(np, ':'); + if (end == NULL) + return (EINVAL); + sep = strchr(np, '/'); + if (sep == NULL || sep >= end) + sep = end; + memcpy(dev->poolname, np, sep - np); + dev->poolname[sep - np] = '\0'; + if (sep < end) { + sep++; + memcpy(dev->rootname, sep, end - sep); + dev->rootname[end - sep] = '\0'; + } + else + dev->rootname[0] = '\0'; + + if (path != NULL) + *path = (*end == '\0') ? end : end + 1; + + dev->d_dev = &zfs_dev; + dev->d_type = zfs_dev.dv_type; + return(0); +} + + +char * +zfs_fmtdev(void *vdev) +{ + struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; + static char buf[2 * ZFS_MAXNAMELEN + 8]; + char *cp; + + switch(dev->d_type) { + case DEVT_ZFS: + if (dev->rootname[0] == '\0') + sprintf(buf, "%s:%s:", dev->d_dev->dv_name, dev->poolname); + else + sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, dev->poolname, + dev->rootname); + break; + } + return(buf); +} + diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h new file mode 100644 index 0000000..fa9af06 --- /dev/null +++ b/sys/boot/zfs/libzfs.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2012 Andriy Gapon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +/* + * ZFS fully-qualified device descriptor. + * Note, this must match the 'struct devdesc' declaration in bootstrap.h. + * Arch-specific device descriptors should be binary compatible with this + * structure if they are to support ZFS. + */ +struct zfs_devdesc +{ + struct devsw *d_dev; + int d_type; + int d_unit; + void *d_opendata; +#define ZFS_MAXNAMELEN 256 + char poolname[ZFS_MAXNAMELEN]; + char rootname[ZFS_MAXNAMELEN]; +}; + +int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, + const char **path); +char *zfs_fmtdev(void *vdev); +int zfs_extract_currdev(uint64_t guid, uint64_t rootobj, + struct zfs_devdesc *dev); + +extern struct devsw zfs_dev; + diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c index e313fde..cf7cc2d 100644 --- a/sys/boot/zfs/zfs.c +++ b/sys/boot/zfs/zfs.c @@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include "libzfs.h" + #include "zfsimpl.c" #define MAXBDDEV 31 @@ -85,35 +87,20 @@ struct file { static int zfs_open(const char *upath, struct open_file *f) { - spa_t *spa = (spa_t *) f->f_devdata; + struct zfsmount *mount = (struct zfsmount *)f->f_devdata; struct file *fp; int rc; if (f->f_dev != &zfs_dev) return (EINVAL); - rc = zfs_mount_pool(spa); - if (rc) - return (rc); - /* allocate file system specific data structure */ fp = malloc(sizeof(struct file)); bzero(fp, sizeof(struct file)); f->f_fsdata = (void *)fp; - if (spa->spa_root_objset.os_type != DMU_OST_ZFS) { - printf("Unexpected object set type %llu\n", - spa->spa_root_objset.os_type); - rc = EIO; - goto out; - } - - rc = zfs_lookup(spa, upath, &fp->f_dnode); - if (rc) - goto out; - + rc = zfs_lookup(mount, upath, &fp->f_dnode); fp->f_seekp = 0; -out: if (rc) { f->f_fsdata = NULL; free(fp); @@ -142,7 +129,7 @@ zfs_close(struct open_file *f) static int zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; struct stat sb; size_t n; @@ -216,7 +203,7 @@ zfs_seek(struct open_file *f, off_t offset, int where) static int zfs_stat(struct open_file *f, struct stat *sb) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); @@ -225,7 +212,7 @@ zfs_stat(struct open_file *f, struct stat *sb) static int zfs_readdir(struct open_file *f, struct dirent *d) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; mzap_ent_phys_t mze; struct stat sb; @@ -381,22 +368,31 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size) } } -/* - * Convert a pool guid to a 'unit number' suitable for use with zfs_dev_open. - */ int -zfs_guid_to_unit(uint64_t guid) +zfs_extract_currdev(uint64_t guid, uint64_t rootobj, struct zfs_devdesc *dev) { spa_t *spa; - int unit; - - unit = 0; - STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - if (spa->spa_guid == guid) - return unit; - unit++; + int rv; + + spa = spa_find_by_guid(guid); + if (spa == NULL) + return (ENOENT); + + rv = zfs_spa_init(spa); + if (rv != 0) + return (rv); + strcpy(dev->poolname, spa->spa_name); + if (rootobj == 0 && zfs_get_root(spa, &rootobj)) { + printf("ZFS: can't find root filesystem\n"); + return (EIO); + } + if (zfs_rlookup(spa, rootobj, dev->rootname)) { + printf("ZFS: can't map root filesystem to its name\n"); + return (ENOENT); } - return (-1); + dev->d_dev = &zfs_dev; + dev->d_type = dev->d_dev->dv_type; + return (0); } static int @@ -450,17 +446,14 @@ zfs_dev_print(int verbose) { spa_t *spa; char line[80]; - int unit; if (verbose) { spa_all_status(); return; } - unit = 0; STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - sprintf(line, " zfs%d: %s\n", unit, spa->spa_name); + sprintf(line, " zfs:%s\n", spa->spa_name); pager_output(line); - unit++; } } @@ -471,33 +464,38 @@ static int zfs_dev_open(struct open_file *f, ...) { va_list args; - struct devdesc *dev; - int unit, i; + struct zfs_devdesc *dev; + struct zfsmount *mount; spa_t *spa; + uint64_t rootobj; + int rv; va_start(args, f); - dev = va_arg(args, struct devdesc*); + dev = va_arg(args, struct zfs_devdesc *); va_end(args); - /* - * We mostly ignore the stuff that devopen sends us. For now, - * use the unit to find a pool - later we will override the - * devname parsing so that we can name a pool and a fs within - * the pool. - */ - unit = dev->d_unit; - - i = 0; - STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - if (i == unit) - break; - i++; - } - if (!spa) { + spa = spa_find_by_name(dev->poolname); + if (!spa) return (ENXIO); + rv = zfs_spa_init(spa); + if (rv != 0) + return (rv); + mount = malloc(sizeof(*mount)); + rootobj = 0; + if (dev->rootname[0] != '\0') { + rv = zfs_lookup_dataset(spa, dev->rootname, &rootobj); + if (rv != 0) + return (rv); } - - f->f_devdata = spa; + rv = zfs_mount(spa, rootobj, mount); + if (rv != 0) + return (rv); + if (mount->objset.os_type != DMU_OST_ZFS) { + printf("Unexpected object set type %llu\n", + mount->objset.os_type); + return (EIO); + } + f->f_devdata = mount; free(dev); return (0); } @@ -506,6 +504,7 @@ static int zfs_dev_close(struct open_file *f) { + free(f->f_devdata); f->f_devdata = NULL; return (0); } diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c index 3419564..c6ba500 100644 --- a/sys/boot/zfs/zfsimpl.c +++ b/sys/boot/zfs/zfsimpl.c @@ -36,6 +36,13 @@ __FBSDID("$FreeBSD$"); #include "zfsimpl.h" #include "zfssubr.c" + +struct zfsmount { + spa_t *spa; + objset_phys_t objset; + uint64_t rootobj; +}; + /* * List of all vdevs, chained through v_alllink. */ @@ -626,8 +633,6 @@ spa_find_by_guid(uint64_t guid) return (0); } -#ifdef BOOT2 - static spa_t * spa_find_by_name(const char *name) { @@ -640,8 +645,6 @@ spa_find_by_name(const char *name) return (0); } -#endif - static spa_t * spa_create(uint64_t guid) { @@ -1452,6 +1455,259 @@ objset_get_dnode(spa_t *spa, const objset_phys_t *os, uint64_t objnum, dnode_phy dnode, sizeof(dnode_phys_t)); } +static int +mzap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + const mzap_phys_t *mz; + const mzap_ent_phys_t *mze; + size_t size; + int chunks, i; + + /* + * Microzap objects use exactly one block. Read the whole + * thing. + */ + size = dnode->dn_datablkszsec * 512; + + mz = (const mzap_phys_t *) zap_scratch; + chunks = size / MZAP_ENT_LEN - 1; + + for (i = 0; i < chunks; i++) { + mze = &mz->mz_chunk[i]; + if (value == mze->mze_value) { + strcpy(name, mze->mze_name); + return (0); + } + } + + return (ENOENT); +} + +static void +fzap_name_copy(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, char *name) +{ + size_t namelen; + const zap_leaf_chunk_t *nc; + char *p; + + namelen = zc->l_entry.le_name_length; + + nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk); + p = name; + while (namelen > 0) { + size_t len; + len = namelen; + if (len > ZAP_LEAF_ARRAY_BYTES) + len = ZAP_LEAF_ARRAY_BYTES; + memcpy(p, nc->l_array.la_array, len); + p += len; + namelen -= len; + nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next); + } + + *p = '\0'; +} + +static int +fzap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; + zap_phys_t zh = *(zap_phys_t *) zap_scratch; + fat_zap_t z; + uint64_t *ptrtbl; + uint64_t hash; + int rc; + + if (zh.zap_magic != ZAP_MAGIC) + return (EIO); + + z.zap_block_shift = ilog2(bsize); + z.zap_phys = (zap_phys_t *) zap_scratch; + + /* + * Figure out where the pointer table is and read it in if necessary. + */ + if (zh.zap_ptrtbl.zt_blk) { + rc = dnode_read(spa, dnode, zh.zap_ptrtbl.zt_blk * bsize, + zap_scratch, bsize); + if (rc) + return (rc); + ptrtbl = (uint64_t *) zap_scratch; + } else { + ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0); + } + + hash = zap_hash(zh.zap_salt, name); + + zap_leaf_t zl; + zl.l_bs = z.zap_block_shift; + + off_t off = ptrtbl[hash >> (64 - zh.zap_ptrtbl.zt_shift)] << zl.l_bs; + zap_leaf_chunk_t *zc; + + rc = dnode_read(spa, dnode, off, zap_scratch, bsize); + if (rc) + return (rc); + + zl.l_phys = (zap_leaf_phys_t *) zap_scratch; + + /* + * Make sure this chunk matches our hash. + */ + if (zl.l_phys->l_hdr.lh_prefix_len > 0 + && zl.l_phys->l_hdr.lh_prefix + != hash >> (64 - zl.l_phys->l_hdr.lh_prefix_len)) + return (ENOENT); + + /* + * Hash within the chunk to find our entry. + */ + int shift = (64 - ZAP_LEAF_HASH_SHIFT(&zl) - zl.l_phys->l_hdr.lh_prefix_len); + int h = (hash >> shift) & ((1 << ZAP_LEAF_HASH_SHIFT(&zl)) - 1); + h = zl.l_phys->l_hash[h]; + if (h == 0xffff) + return (ENOENT); + zc = &ZAP_LEAF_CHUNK(&zl, h); + while (zc->l_entry.le_hash != hash) { + if (zc->l_entry.le_next == 0xffff) { + zc = 0; + break; + } + zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next); + } + if (fzap_leaf_value(&zl, zc) == value) { + fzap_name_copy(&zl, zc, name); + return (0); + } + + return (ENOENT); +} + +static int +zap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + int rc; + uint64_t zap_type; + size_t size = dnode->dn_datablkszsec * 512; + + rc = dnode_read(spa, dnode, 0, zap_scratch, size); + if (rc) + return (rc); + + zap_type = *(uint64_t *) zap_scratch; + if (zap_type == ZBT_MICRO) + return mzap_rlookup(spa, dnode, name, value); + else + return fzap_rlookup(spa, dnode, name, value); +} + +static int +zfs_rlookup(spa_t *spa, uint64_t objnum, char *result) +{ + char name[256]; + char component[256]; + uint64_t dir_obj, parent_obj, child_dir_zapobj; + dnode_phys_t child_dir_zap, dataset, dir, parent; + dsl_dir_phys_t *dd; + dsl_dataset_phys_t *ds; + char *p; + int len; + + p = &name[sizeof(name) - 1]; + *p = '\0'; + + if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { + printf("ZFS: can't find dataset %llu\n", objnum); + return (EIO); + } + ds = (dsl_dataset_phys_t *)&dataset.dn_bonus; + dir_obj = ds->ds_dir_obj; + + for (;;) { + if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0) + return (EIO); + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + /* Actual loop condition. */ + parent_obj = dd->dd_parent_obj; + if (parent_obj == 0) + break; + + if (objset_get_dnode(spa, &spa->spa_mos, parent_obj, &parent) != 0) + return (EIO); + dd = (dsl_dir_phys_t *)&parent.dn_bonus; + child_dir_zapobj = dd->dd_child_dir_zapobj; + if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) + return (EIO); + if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0) + return (EIO); + + len = strlen(component); + p -= len; + memcpy(p, component, len); + --p; + *p = '/'; + + /* Actual loop iteration. */ + dir_obj = parent_obj; + } + + if (*p != '\0') + ++p; + strcpy(result, p); + + return (0); +} + +static int +zfs_lookup_dataset(spa_t *spa, const char *name, uint64_t *objnum) +{ + char element[256]; + uint64_t dir_obj, child_dir_zapobj; + dnode_phys_t child_dir_zap, dir; + dsl_dir_phys_t *dd; + const char *p, *q; + + if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) + return (EIO); + if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &dir_obj)) + return (EIO); + + p = name; + for (;;) { + if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) + return (EIO); + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + while (*p == '/') + p++; + /* Actual loop condition #1. */ + if (*p == '\0') + break; + + q = strchr(p, '/'); + if (q) { + memcpy(element, p, q - p); + element[q - p] = '\0'; + p = q + 1; + } else { + strcpy(element, p); + p += strlen(p); + } + + child_dir_zapobj = dd->dd_child_dir_zapobj; + if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) + return (EIO); + + /* Actual loop condition #2. */ + if (zap_lookup(spa, &child_dir_zap, element, &dir_obj) != 0) + return (ENOENT); + } + + *objnum = dd->dd_head_dataset_obj; + return (0); +} + /* * Find the object set given the object number of its dataset object * and return its details in *objset @@ -1481,11 +1737,13 @@ zfs_mount_dataset(spa_t *spa, uint64_t objnum, objset_phys_t *objset) * dataset if there is none and return its details in *objset */ static int -zfs_mount_root(spa_t *spa, objset_phys_t *objset) +zfs_get_root(spa_t *spa, uint64_t *objid) { dnode_phys_t dir, propdir; uint64_t props, bootfs, root; + *objid = 0; + /* * Start with the MOS directory object. */ @@ -1501,8 +1759,10 @@ zfs_mount_root(spa_t *spa, objset_phys_t *objset) && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 && zap_lookup(spa, &propdir, "bootfs", &bootfs) == 0 && bootfs != 0) - return zfs_mount_dataset(spa, bootfs, objset); - + { + *objid = bootfs; + return (0); + } /* * Lookup the root dataset directory */ @@ -1517,29 +1777,42 @@ zfs_mount_root(spa_t *spa, objset_phys_t *objset) * to find the dataset object and from that the object set itself. */ dsl_dir_phys_t *dd = (dsl_dir_phys_t *) &dir.dn_bonus; - return zfs_mount_dataset(spa, dd->dd_head_dataset_obj, objset); + *objid = dd->dd_head_dataset_obj; + return (0); } static int -zfs_mount_pool(spa_t *spa) +zfs_mount(spa_t *spa, uint64_t rootobj, struct zfsmount *mount) { + mount->spa = spa; + /* - * Find the MOS and work our way in from there. + * Find the root object set if not explicitly provided */ - if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { - printf("ZFS: can't read MOS\n"); + if (rootobj == 0 && zfs_get_root(spa, &rootobj)) { + printf("ZFS: can't find root filesystem\n"); return (EIO); } - /* - * Find the root object set - */ - if (zfs_mount_root(spa, &spa->spa_root_objset)) { - printf("Can't find root filesystem - giving up\n"); + if (zfs_mount_dataset(spa, rootobj, &mount->objset)) { + printf("ZFS: can't open root filesystem\n"); return (EIO); } + mount->rootobj = rootobj; + + return (0); +} + +static int +zfs_spa_init(spa_t *spa) +{ + + if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { + printf("ZFS: can't read MOS of pool %s\n", spa->spa_name); + return (EIO); + } return (0); } @@ -1599,10 +1872,11 @@ zfs_dnode_stat(spa_t *spa, dnode_phys_t *dn, struct stat *sb) * Lookup a file and return its dnode. */ static int -zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) +zfs_lookup(const struct zfsmount *mount, const char *upath, dnode_phys_t *dnode) { int rc; uint64_t objnum, rootnum, parentnum; + spa_t *spa; dnode_phys_t dn; const char *p, *q; char element[256]; @@ -1610,16 +1884,17 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) int symlinks_followed = 0; struct stat sb; - if (spa->spa_root_objset.os_type != DMU_OST_ZFS) { + spa = mount->spa; + if (mount->objset.os_type != DMU_OST_ZFS) { printf("ZFS: unexpected object set type %llu\n", - spa->spa_root_objset.os_type); + mount->objset.os_type); return (EIO); } /* * Get the root directory dnode. */ - rc = objset_get_dnode(spa, &spa->spa_root_objset, MASTER_NODE_OBJ, &dn); + rc = objset_get_dnode(spa, &mount->objset, MASTER_NODE_OBJ, &dn); if (rc) return (rc); @@ -1627,7 +1902,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) if (rc) return (rc); - rc = objset_get_dnode(spa, &spa->spa_root_objset, rootnum, &dn); + rc = objset_get_dnode(spa, &mount->objset, rootnum, &dn); if (rc) return (rc); @@ -1660,7 +1935,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) return (rc); objnum = ZFS_DIRENT_OBJ(objnum); - rc = objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn); + rc = objset_get_dnode(spa, &mount->objset, objnum, &dn); if (rc) return (rc); @@ -1702,7 +1977,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) objnum = rootnum; else objnum = parentnum; - objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn); + objset_get_dnode(spa, &mount->objset, objnum, &dn); } } diff --git a/sys/cddl/boot/zfs/zfsimpl.h b/sys/cddl/boot/zfs/zfsimpl.h index 9f24cc0..c39f18b 100644 --- a/sys/cddl/boot/zfs/zfsimpl.h +++ b/sys/cddl/boot/zfs/zfsimpl.h @@ -1327,5 +1327,4 @@ typedef struct spa { struct uberblock spa_uberblock; /* best uberblock so far */ vdev_list_t spa_vdevs; /* list of all toplevel vdevs */ objset_phys_t spa_mos; /* MOS for this pool */ - objset_phys_t spa_root_objset; /* current mounted ZPL objset */ } spa_t; diff --git a/tools/tools/zfsboottest/zfsboottest.c b/tools/tools/zfsboottest/zfsboottest.c index 4b29f94..3058046 100644 --- a/tools/tools/zfsboottest/zfsboottest.c +++ b/tools/tools/zfsboottest/zfsboottest.c @@ -89,7 +89,11 @@ main(int argc, char** argv) char buf[512], hash[33]; MD5_CTX ctx; struct stat sb; + struct zfsmount zfsmnt; dnode_phys_t dn; +#if 0 + uint64_t rootobj; +#endif spa_t *spa; off_t off; ssize_t n; @@ -138,14 +142,28 @@ main(int argc, char** argv) exit(1); } - if (zfs_mount_pool(spa)) { - fprintf(stderr, "can't mount pool\n"); + if (zfs_spa_init(spa)) { + fprintf(stderr, "can't init pool\n"); exit(1); } +#if 0 + if (zfs_get_root(spa, &rootobj)) { + fprintf(stderr, "can't get root\n"); + exit(1); + } + + if (zfs_mount(spa, rootobj, &zfsmnt)) { +#else + if (zfs_mount(spa, 0, &zfsmnt)) { + fprintf(stderr, "can't mount\n"); + exit(1); + } +#endif + printf("\n"); for (++i, failures = 0; i < argc; i++) { - if (zfs_lookup(spa, argv[i], &dn)) { + if (zfs_lookup(&zfsmnt, argv[i], &dn)) { fprintf(stderr, "%s: can't lookup\n", argv[i]); failures++; continue; commit d9377e2ddfd149a81531bd557d6511ebace96583 Author: Andriy Gapon Date: Thu Jun 10 10:18:19 2010 +0300 zfs boot: try to set vfs.root.mountfrom from currdev as a fallback diff --git a/sys/boot/common/boot.c b/sys/boot/common/boot.c index c6ab681..8ea06dd 100644 --- a/sys/boot/common/boot.c +++ b/sys/boot/common/boot.c @@ -311,12 +311,12 @@ getrootmount(char *rootdev) if (getenv("vfs.root.mountfrom") != NULL) return(0); + error = 1; sprintf(lbuf, "%s/etc/fstab", rootdev); if ((fd = open(lbuf, O_RDONLY)) < 0) - return(1); + goto notfound; /* loop reading lines from /etc/fstab What was that about sscanf again? */ - error = 1; while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) { if ((lbuf[0] == 0) || (lbuf[0] == '#')) continue; @@ -377,6 +377,20 @@ getrootmount(char *rootdev) break; } close(fd); + +notfound: + if (error) { + const char *currdev; + + currdev = getenv("currdev"); + if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) { + cp = strdup(currdev); + cp[strlen(cp) - 1] = '\0'; + setenv("vfs.root.mountfrom", cp, 0); + error = 0; + } + } + return(error); }