summaryrefslogtreecommitdiffstats
path: root/sys/boot/zfs/zfs.c
diff options
context:
space:
mode:
authoravg <avg@FreeBSD.org>2012-05-12 09:03:30 +0000
committeravg <avg@FreeBSD.org>2012-05-12 09:03:30 +0000
commita1cf7817fdc316198beb7e881a16684b20ea9ab8 (patch)
treedf061398b6750e42dc38884e817fdece2299ea9e /sys/boot/zfs/zfs.c
parent8f2a8ac2b1384a6b3dd4d5d11d4522ea728f9882 (diff)
downloadFreeBSD-src-a1cf7817fdc316198beb7e881a16684b20ea9ab8.zip
FreeBSD-src-a1cf7817fdc316198beb7e881a16684b20ea9ab8.tar.gz
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
Diffstat (limited to 'sys/boot/zfs/zfs.c')
-rw-r--r--sys/boot/zfs/zfs.c298
1 files changed, 191 insertions, 107 deletions
diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c
index 97f924f..c90e1c7 100644
--- a/sys/boot/zfs/zfs.c
+++ b/sys/boot/zfs/zfs.c
@@ -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)
+static int
+zfs_dev_init(void)
{
- spa_t *spa;
- int unit;
-
- unit = 0;
- STAILQ_FOREACH(spa, &zfs_pools, spa_link) {
- if (spa->spa_guid == guid)
- return unit;
- unit++;
- }
- return (-1);
+ zfs_init();
+ if (archsw.arch_zfs_probe == NULL)
+ return (ENXIO);
+ archsw.arch_zfs_probe();
+ return (0);
}
-#if defined(__amd64__) || defined(__i386__)
-static int
-zfs_dev_init(void)
+int
+zfs_probe_dev(const char *devname, uint64_t *pool_guid)
{
- char devname[512];
- int unit, slice;
+ spa_t *spa;
int fd;
+ int ret;
- /*
- * 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);
- }
- }
-
+ 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_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,
- .dv_close = zfs_dev_close,
+ .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);
+}
OpenPOWER on IntegriCloud