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 2d18e7ec4aea27821c826173ed44c143987ac848 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. zfs support should be architecture independent and is provided in a separate library, but architectures wishing to use this zfs support still have to provide some glue code and their devdesc should be compatible with zfs_devdesc. libi386 unconditionally includes zfs support, but some zfs-specific functions are stubbed out as weak symbols. The strong definitions are provided in libzfsboot. This change mean that the size of i386_devspec becomes much larger to match zfs_devspec. 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..a3a9e18 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -9,6 +9,7 @@ SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ elf64_freebsd.c \ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c +SRCS+= ../../zfs/devicename_stubs.c # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c index d6f3ac1..0a1b5e1 100644 --- a/sys/boot/i386/libi386/devicename.c +++ b/sys/boot/i386/libi386/devicename.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include "bootstrap.h" #include "libi386.h" +#include "../zfs/libzfs.h" static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path); @@ -171,7 +172,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 +192,9 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) if (path != NULL) *path = (*cp == 0) ? cp : cp + 1; break; - + case DEVT_ZFS: + err = zfs_parsedev((struct zfs_devdesc *)idev, devspec, path); + break; default: err = EINVAL; goto fail; @@ -247,9 +249,10 @@ i386_fmtdev(void *vdev) break; case DEVT_NET: - case DEVT_ZFS: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; + case DEVT_ZFS: + return(zfs_fmtdev(vdev)); } return(buf); } diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h index b8842ba..08b9c4b 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,12 @@ struct i386_devdesc { void *data; } bioscd; + struct + { + void *data; + char poolname[256]; + char rootname[256]; + } zfs; } 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..4ef8e48 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -42,9 +42,14 @@ __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 +#define KARGS_FLAGS_ZFS_DS_GUID 0x8 /* Arguments passed in from the boot1/boot2 loader */ static struct @@ -52,14 +57,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 +271,22 @@ extract_currdev(void) "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev); new_currdev.d_unit = 0; } - 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); - } + if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { + if ((kargs->bootflags & KARGS_FLAGS_ZFS_DS_GUID) != 0) + zfs_extract_currdev(kargs->zfspool, kargs->zfsroot, + (struct zfs_devdesc *)&new_currdev); + else + zfs_extract_currdev(kargs->zfspool, 0, + (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); } 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..82e0d61 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -42,8 +42,10 @@ __FBSDID("$FreeBSD$"); #include "util.h" #include "cons.h" -/* Hint to loader that we came from ZFS */ +/* Hint to loader that we came from ZFS. */ #define KARGS_FLAGS_ZFS 0x4 +/* Indicate that a GUID of boot dataset is provided. */ +#define KARGS_FLAGS_ZFS_DS_GUID 0x8 #define PATH_DOTCONFIG "/boot.config" #define PATH_CONFIG "/boot/config" @@ -92,8 +94,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 +536,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 +571,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 +617,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; @@ -681,10 +694,12 @@ load(void) bootinfo.bi_kernelname = VTOP(kname); __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), bootdev, - KARGS_FLAGS_ZFS, + KARGS_FLAGS_ZFS | KARGS_FLAGS_ZFS_DS_GUID, (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 +751,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 +773,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_stubs.c b/sys/boot/zfs/devicename_stubs.c new file mode 100644 index 0000000..41bf907 --- /dev/null +++ b/sys/boot/zfs/devicename_stubs.c @@ -0,0 +1,47 @@ +/*- + * 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 "libzfs.h" + +__attribute__((weak)) +int +zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) +{ + return (EINVAL); +} + +__attribute__((weak)) +char * +zfs_fmtdev(void *vdev) +{ + static char buf[128]; + + 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..ce12e26 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); } - return (-1); + if (zfs_rlookup(spa, rootobj, dev->rootname)) { + printf("ZFS: can't map root filesystem to its name\n"); + return (ENOENT); + } + 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); } @@ -528,3 +527,63 @@ struct devsw zfs_dev = { .dv_print = zfs_dev_print, .dv_cleanup = NULL }; + +int +zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) +{ + const char *end; + const char *np; + const char *sep; + 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) +{ + static char buf[2 * ZFS_MAXNAMELEN + 8]; + struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; + + 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/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 a1a978761a519fa042a5617f069687d4c92a83e6 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); }