The branch main has been updated by allanjude:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=a849842f510af48717e35ff709623e0dd1b80b20

commit a849842f510af48717e35ff709623e0dd1b80b20
Author:     Allan Jude <allanj...@freebsd.org>
AuthorDate: 2022-11-26 18:11:13 +0000
Commit:     Allan Jude <allanj...@freebsd.org>
CommitDate: 2023-03-14 14:18:29 +0000

    loader: Add support for booting from a ZFS snapshot
    
    When booting from a snapshot we need to follow a different code path
    to turn the objset ID into the name, and for forward lookups we need
    to walk the parent's snapnames_zap.
    
    With this, it is possible to set the pools BOOTFS property to a
    snapshot and boot with a read-only filesystem of that snapshot.
    
    Reviewed by:    tsoome, rew, imp
    Sponsored By:   Beckhoff Automation GmbH & Co. KG
    Sponsored By:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D38600
---
 stand/libsa/zfs/zfsimpl.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 2 deletions(-)

diff --git a/stand/libsa/zfs/zfsimpl.c b/stand/libsa/zfs/zfsimpl.c
index 36c90613e827..76063e76225f 100644
--- a/stand/libsa/zfs/zfsimpl.c
+++ b/stand/libsa/zfs/zfsimpl.c
@@ -3068,11 +3068,12 @@ zfs_rlookup(const 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;
+       dnode_phys_t child_dir_zap, snapnames_zap, dataset, dir, parent;
        dsl_dir_phys_t *dd;
        dsl_dataset_phys_t *ds;
        char *p;
        int len;
+       boolean_t issnap = B_FALSE;
 
        p = &name[sizeof(name) - 1];
        *p = '\0';
@@ -3083,6 +3084,8 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char 
*result)
        }
        ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
        dir_obj = ds->ds_dir_obj;
+       if (ds->ds_snapnames_zapobj == 0)
+               issnap = B_TRUE;
 
        for (;;) {
                if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir) != 0)
@@ -3098,6 +3101,34 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char 
*result)
                    &parent) != 0)
                        return (EIO);
                dd = (dsl_dir_phys_t *)&parent.dn_bonus;
+               if (issnap == B_TRUE) {
+                       /*
+                        * The dataset we are looking up is a snapshot
+                        * the dir_obj is the parent already, we don't want
+                        * the grandparent just yet. Reset to the parent.
+                        */
+                       dd = (dsl_dir_phys_t *)&dir.dn_bonus;
+                       /* Lookup the dataset to get the snapname ZAP */
+                       if (objset_get_dnode(spa, spa->spa_mos,
+                           dd->dd_head_dataset_obj, &dataset))
+                               return (EIO);
+                       ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
+                       if (objset_get_dnode(spa, spa->spa_mos,
+                           ds->ds_snapnames_zapobj, &snapnames_zap) != 0)
+                               return (EIO);
+                       /* Get the name of the snapshot */
+                       if (zap_rlookup(spa, &snapnames_zap, component,
+                           objnum) != 0)
+                               return (EIO);
+                       len = strlen(component);
+                       p -= len;
+                       memcpy(p, component, len);
+                       --p;
+                       *p = '@';
+                       issnap = B_FALSE;
+                       continue;
+               }
+
                child_dir_zapobj = dd->dd_child_dir_zapobj;
                if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
                    &child_dir_zap) != 0)
@@ -3127,9 +3158,11 @@ zfs_lookup_dataset(const 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;
+       dnode_phys_t child_dir_zap, snapnames_zap, dir, dataset;
        dsl_dir_phys_t *dd;
+       dsl_dataset_phys_t *ds;
        const char *p, *q;
+       boolean_t issnap = B_FALSE;
 
        if (objset_get_dnode(spa, spa->spa_mos,
            DMU_POOL_DIRECTORY_OBJECT, &dir))
@@ -3160,6 +3193,25 @@ zfs_lookup_dataset(const spa_t *spa, const char *name, 
uint64_t *objnum)
                        p += strlen(p);
                }
 
+               if (issnap == B_TRUE) {
+                       if (objset_get_dnode(spa, spa->spa_mos,
+                           dd->dd_head_dataset_obj, &dataset))
+                               return (EIO);
+                       ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
+                       if (objset_get_dnode(spa, spa->spa_mos,
+                           ds->ds_snapnames_zapobj, &snapnames_zap) != 0)
+                               return (EIO);
+                       /* Actual loop condition #2. */
+                       if (zap_lookup(spa, &snapnames_zap, element,
+                           sizeof (dir_obj), 1, &dir_obj) != 0)
+                               return (ENOENT);
+                       *objnum = dir_obj;
+                       return (0);
+               } else if ((q = strchr(element, '@')) != NULL) {
+                       issnap = B_TRUE;
+                       element[q - element] = '\0';
+                       p = q + 1;
+               }
                child_dir_zapobj = dd->dd_child_dir_zapobj;
                if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
                    &child_dir_zap) != 0)

Reply via email to