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. arch_zfs_probe method is used to discover all disk devices that may be part of ZFS pool(s). 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 larger to match zfs_devspec. Backward-compatibility shims are provided for recently added sparc64 zfs boot support. Currently that architecture still works the old way and does not support the new features. TODO: - clear up pool root filesystem vs pool bootfs filesystem distinction - update sparc64 support - set vfs.root.mountfrom based on currdev (for zfs) Mid-future TODO: - loader sub-menu for selecting alternative boot environment Distant future TODO: - support accessing snapshots, using a snapshot as readonly root Reviewed by: marius (sparc64), Gavin Mu <gavin.mu@gmail.com> (sparc64) Tested by: Florian Wagner <florian@wagner-flo.net> (x86), marius (sparc64) No objections: fs@, hackers@ MFC after: 1 month
This commit is contained in:
parent
8521fde5fc
commit
1702e62f67
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=235329
@ -317,6 +317,9 @@ struct arch_switch
|
||||
#else
|
||||
void (*arch_loadseg)(void *eh, void *ph, uint64_t delta);
|
||||
#endif
|
||||
|
||||
/* Probe ZFS pool(s), if needed. */
|
||||
void (*arch_zfs_probe)(void);
|
||||
};
|
||||
extern struct arch_switch archsw;
|
||||
|
||||
|
@ -9,6 +9,8 @@ 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
|
||||
.PATH: ${.CURDIR}/../../zfs
|
||||
SRCS+= devicename_stubs.c
|
||||
|
||||
# Enable PXE TFTP or NFS support, not both.
|
||||
.if defined(LOADER_TFTP_SUPPORT)
|
||||
|
@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/disklabel.h>
|
||||
#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,11 @@ 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, np, path);
|
||||
if (err != 0)
|
||||
goto fail;
|
||||
break;
|
||||
default:
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
@ -247,9 +251,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);
|
||||
}
|
||||
|
@ -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;
|
||||
uint64_t pool_guid;
|
||||
uint64_t root_guid;
|
||||
} zfs;
|
||||
} d_kind;
|
||||
};
|
||||
|
||||
|
@ -30,6 +30,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <stand.h>
|
||||
#include <bootstrap.h>
|
||||
#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,
|
||||
@ -70,10 +69,6 @@ struct devsw *devsw[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
#if defined(LOADER_ZFS_SUPPORT)
|
||||
extern struct fs_ops zfs_fsops;
|
||||
#endif
|
||||
|
||||
struct fs_ops *file_system[] = {
|
||||
&ufs_fsops,
|
||||
&ext2fs_fsops,
|
||||
|
@ -44,6 +44,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include "libi386/libi386.h"
|
||||
#include "btxv86.h"
|
||||
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
#include "../zfs/libzfs.h"
|
||||
#endif
|
||||
|
||||
CTASSERT(sizeof(struct bootargs) == BOOTARGS_SIZE);
|
||||
CTASSERT(offsetof(struct bootargs, bootinfo) == BA_BOOTINFO);
|
||||
CTASSERT(offsetof(struct bootargs, bootflags) == BA_BOOTFLAGS);
|
||||
@ -62,6 +66,9 @@ static void extract_currdev(void);
|
||||
static int isa_inb(int port);
|
||||
static void isa_outb(int port, int value);
|
||||
void exit(int code);
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
static void i386_zfs_probe(void);
|
||||
#endif
|
||||
|
||||
/* from vers.c */
|
||||
extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
|
||||
@ -153,6 +160,9 @@ main(void)
|
||||
archsw.arch_readin = i386_readin;
|
||||
archsw.arch_isainb = isa_inb;
|
||||
archsw.arch_isaoutb = isa_outb;
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
archsw.arch_zfs_probe = i386_zfs_probe;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* March through the device switch probing for things.
|
||||
@ -196,8 +206,11 @@ main(void)
|
||||
static void
|
||||
extract_currdev(void)
|
||||
{
|
||||
struct i386_devdesc new_currdev;
|
||||
int biosdev = -1;
|
||||
struct i386_devdesc new_currdev;
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
struct zfs_boot_args *zargs;
|
||||
#endif
|
||||
int biosdev = -1;
|
||||
|
||||
/* Assume we are booting from a BIOS disk by default */
|
||||
new_currdev.d_dev = &biosdisk;
|
||||
@ -218,6 +231,24 @@ extract_currdev(void)
|
||||
new_currdev.d_kind.biosdisk.partition = 0;
|
||||
biosdev = -1;
|
||||
}
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
} else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) {
|
||||
zargs = NULL;
|
||||
/* check for new style extended argument */
|
||||
if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0)
|
||||
zargs = (struct zfs_boot_args *)(kargs + 1);
|
||||
|
||||
if (zargs != NULL && zargs->size >= sizeof(*zargs)) {
|
||||
/* sufficient data is provided */
|
||||
new_currdev.d_kind.zfs.pool_guid = zargs->pool;
|
||||
new_currdev.d_kind.zfs.root_guid = zargs->root;
|
||||
} else {
|
||||
/* old style zfsboot block */
|
||||
new_currdev.d_kind.zfs.pool_guid = kargs->zfspool;
|
||||
new_currdev.d_kind.zfs.root_guid = 0;
|
||||
}
|
||||
new_currdev.d_dev = &zfs_dev;
|
||||
#endif
|
||||
} else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) {
|
||||
/* The passed-in boot device is bad */
|
||||
new_currdev.d_kind.biosdisk.slice = -1;
|
||||
@ -238,7 +269,7 @@ extract_currdev(void)
|
||||
biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */
|
||||
}
|
||||
new_currdev.d_type = new_currdev.d_dev->dv_type;
|
||||
|
||||
|
||||
/*
|
||||
* If we are booting off of a BIOS disk and we didn't succeed in determining
|
||||
* which one we booted off of, just use disk0: as a reasonable default.
|
||||
@ -249,33 +280,11 @@ 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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
|
||||
@ -342,3 +351,30 @@ isa_outb(int port, int value)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
static void
|
||||
i386_zfs_probe(void)
|
||||
{
|
||||
char devname[32];
|
||||
int unit, slice;
|
||||
|
||||
/*
|
||||
* Open all the disks we can find and see if we can reconstruct
|
||||
* ZFS pools from them. Bogusly assumes that the disks are named
|
||||
* diskN, diskNpM or diskNsM.
|
||||
*/
|
||||
for (unit = 0; unit < MAXBDDEV; unit++) {
|
||||
sprintf(devname, "disk%d:", unit);
|
||||
if (zfs_probe_dev(devname, NULL) == ENXIO)
|
||||
continue;
|
||||
for (slice = 1; slice <= 128; slice++) {
|
||||
sprintf(devname, "disk%dp%d:", unit, slice);
|
||||
zfs_probe_dev(devname, NULL);
|
||||
}
|
||||
for (slice = 1; slice <= 4; slice++) {
|
||||
sprintf(devname, "disk%ds%d:", unit, slice);
|
||||
zfs_probe_dev(devname, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include "cons.h"
|
||||
#include "bootargs.h"
|
||||
|
||||
#include "libzfs.h"
|
||||
|
||||
#define PATH_DOTCONFIG "/boot.config"
|
||||
#define PATH_CONFIG "/boot/config"
|
||||
#define PATH_BOOT3 "/boot/zfsloader"
|
||||
@ -91,9 +93,12 @@ static const unsigned char dev_maj[NDEV] = {30, 4, 2};
|
||||
static char cmd[512];
|
||||
static char cmddup[512];
|
||||
static char kname[1024];
|
||||
static char rootname[256];
|
||||
static int comspeed = SIOSPD;
|
||||
static struct bootinfo bootinfo;
|
||||
static uint32_t bootdev;
|
||||
static struct zfs_boot_args zfsargs;
|
||||
static struct zfsmount zfsmount;
|
||||
|
||||
vm_offset_t high_heap_base;
|
||||
uint32_t bios_basemem, bios_extmem, high_heap_size;
|
||||
@ -170,7 +175,7 @@ zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t
|
||||
/*
|
||||
* Current ZFS pool
|
||||
*/
|
||||
spa_t *spa;
|
||||
static spa_t *spa;
|
||||
|
||||
/*
|
||||
* A wrapper for dskread that doesn't have to worry about whether the
|
||||
@ -209,7 +214,7 @@ 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) {
|
||||
printf("Invalid %s\n", "format");
|
||||
printf("Invalid format\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -529,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));
|
||||
}
|
||||
@ -567,11 +574,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))
|
||||
@ -607,7 +620,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,12 +695,16 @@ load(void)
|
||||
}
|
||||
bootinfo.bi_esymtab = VTOP(p);
|
||||
bootinfo.bi_kernelname = VTOP(kname);
|
||||
zfsargs.size = sizeof(zfsargs);
|
||||
zfsargs.pool = zfsmount.spa->spa_guid;
|
||||
zfsargs.root = zfsmount.rootobj;
|
||||
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
|
||||
bootdev,
|
||||
KARGS_FLAGS_ZFS,
|
||||
KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
|
||||
(uint32_t) spa->spa_guid,
|
||||
(uint32_t) (spa->spa_guid >> 32),
|
||||
VTOP(&bootinfo));
|
||||
VTOP(&bootinfo),
|
||||
zfsargs);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -738,7 +756,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;
|
||||
@ -760,17 +778,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))
|
||||
|
@ -74,6 +74,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include "libofw.h"
|
||||
#include "dev_net.h"
|
||||
|
||||
#define MAXBDDEV 31
|
||||
|
||||
extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
|
||||
|
||||
enum {
|
||||
@ -141,7 +143,7 @@ static vm_offset_t heapva;
|
||||
static phandle_t root;
|
||||
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
static int zfs_dev_init(void);
|
||||
static int sparc64_zfs_dev_init(void);
|
||||
#include "zfs.c"
|
||||
#endif
|
||||
|
||||
@ -157,7 +159,7 @@ struct devsw *devsw[] = {
|
||||
&netdev,
|
||||
#endif
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
&zfs_dev,
|
||||
&zfs_dev_compat,
|
||||
#endif
|
||||
0
|
||||
};
|
||||
@ -733,7 +735,7 @@ tlb_init_sun4u(void)
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
|
||||
static int
|
||||
zfs_dev_init(void)
|
||||
sparc64_zfs_dev_init(void)
|
||||
{
|
||||
struct vtoc8 vtoc;
|
||||
char devname[512];
|
||||
@ -868,6 +870,13 @@ main(int (*openfirm)(void *))
|
||||
env_setenv("loaddev", EV_VOLATILE, bootpath,
|
||||
env_noset, env_nounset);
|
||||
|
||||
#ifdef LOADER_ZFS_SUPPORT
|
||||
/*
|
||||
* Patch up ZFS.
|
||||
*/
|
||||
zfs_dev_compat.dv_init = sparc64_zfs_dev_init;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialize devices.
|
||||
*/
|
||||
|
47
sys/boot/zfs/devicename_stubs.c
Normal file
47
sys/boot/zfs/devicename_stubs.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <stand.h>
|
||||
#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);
|
||||
}
|
66
sys/boot/zfs/libzfs.h
Normal file
66
sys/boot/zfs/libzfs.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*-
|
||||
* Copyright (c) 2012 Andriy Gapon <avg@FreeBSD.org>
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef _BOOT_LIBZFS_H_
|
||||
#define _BOOT_LIBZFS_H_
|
||||
|
||||
#define ZFS_MAXNAMELEN 256
|
||||
|
||||
/*
|
||||
* 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;
|
||||
uint64_t pool_guid;
|
||||
uint64_t root_guid;
|
||||
};
|
||||
|
||||
struct zfs_boot_args
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t reserved;
|
||||
uint64_t pool;
|
||||
uint64_t root;
|
||||
};
|
||||
|
||||
int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec,
|
||||
const char **path);
|
||||
char *zfs_fmtdev(void *vdev);
|
||||
int zfs_probe_dev(const char *devname, uint64_t *pool_guid);
|
||||
|
||||
extern struct devsw zfs_dev;
|
||||
extern struct fs_ops zfs_fsops;
|
||||
|
||||
#endif /*_BOOT_LIBZFS_H_*/
|
@ -43,9 +43,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <stand.h>
|
||||
#include <bootstrap.h>
|
||||
|
||||
#include "zfsimpl.c"
|
||||
#include "libzfs.h"
|
||||
|
||||
#define MAXBDDEV 31
|
||||
#include "zfsimpl.c"
|
||||
|
||||
static int zfs_open(const char *path, struct open_file *f);
|
||||
static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
|
||||
@ -56,6 +56,7 @@ static int zfs_stat(struct open_file *f, struct stat *sb);
|
||||
static int zfs_readdir(struct open_file *f, struct dirent *d);
|
||||
|
||||
struct devsw zfs_dev;
|
||||
struct devsw zfs_dev_compat;
|
||||
|
||||
struct fs_ops zfs_fsops = {
|
||||
"zfs",
|
||||
@ -85,35 +86,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)
|
||||
if (f->f_dev != &zfs_dev && f->f_dev != &zfs_dev_compat)
|
||||
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 +128,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 +202,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 +211,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,68 +367,33 @@ 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)
|
||||
{
|
||||
spa_t *spa;
|
||||
int unit;
|
||||
|
||||
unit = 0;
|
||||
STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
|
||||
if (spa->spa_guid == guid)
|
||||
return unit;
|
||||
unit++;
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if defined(__amd64__) || defined(__i386__)
|
||||
static int
|
||||
zfs_dev_init(void)
|
||||
zfs_dev_init(void)
|
||||
{
|
||||
char devname[512];
|
||||
int unit, slice;
|
||||
int fd;
|
||||
|
||||
/*
|
||||
* Open all the disks we can find and see if we can reconstruct
|
||||
* ZFS pools from them. Bogusly assumes that the disks are named
|
||||
* diskN, diskNpM or diskNsM.
|
||||
*/
|
||||
zfs_init();
|
||||
for (unit = 0; unit < MAXBDDEV; unit++) {
|
||||
sprintf(devname, "disk%d:", unit);
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we find a vdev, the zfs code will eat the fd, otherwise
|
||||
* we close it.
|
||||
*/
|
||||
if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0))
|
||||
close(fd);
|
||||
|
||||
for (slice = 1; slice <= 128; slice++) {
|
||||
sprintf(devname, "disk%dp%d:", unit, slice);
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
sprintf(devname, "disk%ds%d:", unit, slice);
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
}
|
||||
if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0))
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (archsw.arch_zfs_probe == NULL)
|
||||
return (ENXIO);
|
||||
archsw.arch_zfs_probe();
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_probe_dev(const char *devname, uint64_t *pool_guid)
|
||||
{
|
||||
spa_t *spa;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return (ENXIO);
|
||||
ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa);
|
||||
if (ret != 0)
|
||||
close(fd);
|
||||
else if (pool_guid != NULL)
|
||||
*pool_guid = spa->spa_guid;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Print information about ZFS pools
|
||||
@ -452,54 +403,85 @@ 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++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to open the pool described by (dev) for use by (f).
|
||||
*/
|
||||
static int
|
||||
static int
|
||||
zfs_dev_open_spa(struct open_file *f, spa_t *spa, uint64_t root_guid)
|
||||
{
|
||||
struct zfsmount *mount;
|
||||
int rv;
|
||||
|
||||
rv = zfs_spa_init(spa);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
mount = malloc(sizeof(*mount));
|
||||
rv = zfs_mount(spa, root_guid, mount);
|
||||
if (rv != 0) {
|
||||
free(mount);
|
||||
return (rv);
|
||||
}
|
||||
if (mount->objset.os_type != DMU_OST_ZFS) {
|
||||
printf("Unexpected object set type %llu\n",
|
||||
mount->objset.os_type);
|
||||
free(mount);
|
||||
return (EIO);
|
||||
}
|
||||
f->f_devdata = mount;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_dev_open(struct open_file *f, ...)
|
||||
{
|
||||
va_list args;
|
||||
struct zfs_devdesc *dev;
|
||||
spa_t *spa;
|
||||
int rv;
|
||||
|
||||
va_start(args, f);
|
||||
dev = va_arg(args, struct zfs_devdesc *);
|
||||
va_end(args);
|
||||
|
||||
spa = spa_find_by_guid(dev->pool_guid);
|
||||
if (!spa)
|
||||
return (ENXIO);
|
||||
rv = zfs_dev_open_spa(f, spa, dev->root_guid);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
free(dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_dev_open_compat(struct open_file *f, ...)
|
||||
{
|
||||
va_list args;
|
||||
struct devdesc *dev;
|
||||
int unit, i;
|
||||
spa_t *spa;
|
||||
int rv;
|
||||
|
||||
va_start(args, f);
|
||||
dev = va_arg(args, struct devdesc*);
|
||||
dev = va_arg(args, struct 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_unit(dev->d_unit);
|
||||
if (!spa)
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
f->f_devdata = spa;
|
||||
rv = zfs_dev_open_spa(f, spa, 0);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
free(dev);
|
||||
return (0);
|
||||
}
|
||||
@ -508,6 +490,7 @@ static int
|
||||
zfs_dev_close(struct open_file *f)
|
||||
{
|
||||
|
||||
free(f->f_devdata);
|
||||
f->f_devdata = NULL;
|
||||
return (0);
|
||||
}
|
||||
@ -520,13 +503,114 @@ zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, si
|
||||
}
|
||||
|
||||
struct devsw zfs_dev = {
|
||||
.dv_name = "zfs",
|
||||
.dv_type = DEVT_ZFS,
|
||||
.dv_name = "zfs",
|
||||
.dv_type = DEVT_ZFS,
|
||||
.dv_init = zfs_dev_init,
|
||||
.dv_strategy = zfs_dev_strategy,
|
||||
.dv_open = zfs_dev_open,
|
||||
.dv_close = zfs_dev_close,
|
||||
.dv_strategy = zfs_dev_strategy,
|
||||
.dv_open = zfs_dev_open,
|
||||
.dv_close = zfs_dev_close,
|
||||
.dv_ioctl = noioctl,
|
||||
.dv_print = zfs_dev_print,
|
||||
.dv_cleanup = NULL
|
||||
};
|
||||
|
||||
struct devsw zfs_dev_compat = {
|
||||
.dv_name = "zfs",
|
||||
.dv_type = DEVT_ZFS,
|
||||
.dv_init = zfs_dev_init,
|
||||
.dv_strategy = zfs_dev_strategy,
|
||||
.dv_open = zfs_dev_open_compat,
|
||||
.dv_close = zfs_dev_close,
|
||||
.dv_ioctl = noioctl,
|
||||
.dv_print = zfs_dev_print,
|
||||
.dv_cleanup = NULL
|
||||
};
|
||||
|
||||
int
|
||||
zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path)
|
||||
{
|
||||
static char rootname[ZFS_MAXNAMELEN];
|
||||
static char poolname[ZFS_MAXNAMELEN];
|
||||
spa_t *spa;
|
||||
const char *end;
|
||||
const char *np;
|
||||
const char *sep;
|
||||
int rv;
|
||||
|
||||
np = devspec;
|
||||
if (*np != ':')
|
||||
return (EINVAL);
|
||||
np++;
|
||||
end = strchr(np, ':');
|
||||
if (end == NULL)
|
||||
return (EINVAL);
|
||||
sep = strchr(np, '/');
|
||||
if (sep == NULL || sep >= end)
|
||||
sep = end;
|
||||
memcpy(poolname, np, sep - np);
|
||||
poolname[sep - np] = '\0';
|
||||
if (sep < end) {
|
||||
sep++;
|
||||
memcpy(rootname, sep, end - sep);
|
||||
rootname[end - sep] = '\0';
|
||||
}
|
||||
else
|
||||
rootname[0] = '\0';
|
||||
|
||||
spa = spa_find_by_name(poolname);
|
||||
if (!spa)
|
||||
return (ENXIO);
|
||||
rv = zfs_spa_init(spa);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
dev->pool_guid = spa->spa_guid;
|
||||
if (rootname[0] != '\0') {
|
||||
rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
} else
|
||||
dev->root_guid = 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 rootname[ZFS_MAXNAMELEN];
|
||||
static char buf[2 * ZFS_MAXNAMELEN + 8];
|
||||
struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev;
|
||||
spa_t *spa;
|
||||
|
||||
buf[0] = '\0';
|
||||
if (dev->d_type != DEVT_ZFS)
|
||||
return (buf);
|
||||
|
||||
spa = spa_find_by_guid(dev->pool_guid);
|
||||
if (spa == NULL) {
|
||||
printf("ZFS: can't find pool by guid\n");
|
||||
return (buf);
|
||||
}
|
||||
if (zfs_spa_init(spa) != 0) {
|
||||
printf("ZFS: can't init pool\n");
|
||||
return (buf);
|
||||
}
|
||||
if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) {
|
||||
printf("ZFS: can't find root filesystem\n");
|
||||
return (buf);
|
||||
}
|
||||
if (zfs_rlookup(spa, dev->root_guid, rootname)) {
|
||||
printf("ZFS: can't find filesystem by guid\n");
|
||||
return (buf);
|
||||
}
|
||||
|
||||
if (rootname[0] == '\0')
|
||||
sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name);
|
||||
else
|
||||
sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name,
|
||||
rootname);
|
||||
return (buf);
|
||||
}
|
||||
|
@ -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,6 +645,36 @@ spa_find_by_name(const char *name)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifndef BOOT2
|
||||
static spa_t *
|
||||
spa_find_by_unit(int unit)
|
||||
{
|
||||
spa_t *spa;
|
||||
|
||||
STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
|
||||
if (unit == 0)
|
||||
return (spa);
|
||||
unit--;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_guid_to_unit(uint64_t guid)
|
||||
{
|
||||
spa_t *spa;
|
||||
int unit;
|
||||
|
||||
unit = 0;
|
||||
STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
|
||||
if (spa->spa_guid == guid)
|
||||
return (unit);
|
||||
unit++;
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static spa_t *
|
||||
@ -1452,6 +1487,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 +1769,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 +1791,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 +1809,45 @@ 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 (rootobj == 0 && zfs_get_root(spa, &rootobj)) {
|
||||
printf("ZFS: can't find root filesystem\n");
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
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 (spa->spa_inited)
|
||||
return (0);
|
||||
if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
|
||||
printf("ZFS: can't read MOS\n");
|
||||
printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
|
||||
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");
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
spa->spa_inited = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1599,10 +1907,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 +1919,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 +1937,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 +1970,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 +2012,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user