summaryrefslogtreecommitdiffstats
path: root/sys/boot/zfs/zfsimpl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/zfs/zfsimpl.c')
-rw-r--r--sys/boot/zfs/zfsimpl.c354
1 files changed, 332 insertions, 22 deletions
diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c
index 55c6e28..d70112e 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,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 (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 (spa->spa_inited)
+ return (0);
+ 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);
+ }
+ 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);
}
}
OpenPOWER on IntegriCloud