HAMMER 60I/Many: Mirroring
authorMatthew Dillon <dillon@dragonflybsd.org>
Wed, 9 Jul 2008 10:29:20 +0000 (10:29 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Wed, 9 Jul 2008 10:29:20 +0000 (10:29 +0000)
* Flesh out the mirroring code a lot.  It is now 90% done.

* Change the way PFS's are managed.  The PFS management structure is no
  longer associated with the PFS root inode.  Instead all PFS management
  structures, for all PFSs, are associated with the master root inode.

  This allows a mirroring slave to also mirror the root inode itself.

* Remove the directory localization hacks used to link a PFS to its parent.
  PFS's are no longer linked to their parent.  Instead, a special
  @@PFS softlink is created.

* The master/slave mode must now be selected when a PFS is created and
  cannot be changed.

* PFSs are accessed via a special @@PFS softlink created in the parent
  directory.  HAMMER manipulates this special softlink in the readlink()
  command and returns a longer form to the kernel which changes whenever
  the snapshot TID changes.

  This allows updates to slave PFSs by mirroring commands to not create
  stale system caches.  Every time you CD through the special softlink
  you get the most recent completed snapshot of the PFS.

* Userland now manipulates PFS ids as a simple integer in the range 0-65535.

sys/vfs/hammer/hammer.h
sys/vfs/hammer/hammer_disk.h
sys/vfs/hammer/hammer_inode.c
sys/vfs/hammer/hammer_ioctl.c
sys/vfs/hammer/hammer_ioctl.h
sys/vfs/hammer/hammer_mirror.c
sys/vfs/hammer/hammer_ondisk.c
sys/vfs/hammer/hammer_reblock.c
sys/vfs/hammer/hammer_subs.c
sys/vfs/hammer/hammer_vnops.c

index b5c1161..94f23da 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.107 2008/07/08 04:34:41 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer.h,v 1.108 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * This header file contains structures used internally by the HAMMERFS
@@ -178,6 +178,7 @@ struct hammer_pseudofs_inmem {
        RB_ENTRY(hammer_pseudofs_inmem) rb_node;
        struct hammer_lock      lock;
        u_int32_t               localization;
+       hammer_tid_t            create_tid;
        udev_t                  fsid_udev;
        struct hammer_pseudofs_data pfsd;
 };
@@ -288,7 +289,7 @@ typedef struct hammer_inode *hammer_inode_t;
 #define HAMMER_INODE_DDIRTY    0x0001  /* in-memory ino_data is dirty */
                                        /* (not including atime/mtime) */
 #define HAMMER_INODE_RSV_INODES        0x0002  /* hmp->rsv_inodes bumped */
-#define HAMMER_INODE_PFSD      0x0004  /* obj_asof set based on pfsd */
+#define HAMMER_INODE_UNUSED0004        0x0004
 #define HAMMER_INODE_XDIRTY    0x0008  /* in-memory records */
 #define HAMMER_INODE_ONDISK    0x0010  /* inode is on-disk (else not yet) */
 #define HAMMER_INODE_FLUSH     0x0020  /* flush on last ref */
@@ -843,7 +844,8 @@ u_int32_t hammer_to_unix_xid(uuid_t *uuid);
 void hammer_guid_to_uuid(uuid_t *uuid, u_int32_t guid);
 void   hammer_time_to_timespec(u_int64_t xtime, struct timespec *ts);
 u_int64_t hammer_timespec_to_time(struct timespec *ts);
-hammer_tid_t hammer_str_to_tid(const char *str);
+hammer_tid_t hammer_str_to_tid(const char *str, int *ispfs,
+                       u_int32_t *localizationp);
 hammer_tid_t hammer_alloc_objid(hammer_mount_t hmp, hammer_inode_t dip);
 void hammer_clear_objid(hammer_inode_t dip);
 void hammer_destroy_objid_cache(hammer_mount_t hmp);
@@ -997,7 +999,8 @@ void hammer_wait_inode(hammer_inode_t ip);
 
 int  hammer_create_inode(struct hammer_transaction *trans, struct vattr *vap,
                        struct ucred *cred, struct hammer_inode *dip,
-                       int pseudofs, struct hammer_inode **ipp);
+                       hammer_pseudofs_inmem_t pfsm,
+                       struct hammer_inode **ipp);
 void hammer_rel_inode(hammer_inode_t ip, int flush);
 int hammer_reload_inode(hammer_inode_t ip, void *arg __unused);
 int hammer_ino_rb_compare(hammer_inode_t ip1, hammer_inode_t ip2);
@@ -1025,8 +1028,12 @@ int  hammer_ip_sync_data(hammer_cursor_t cursor, hammer_inode_t ip,
                        int64_t offset, void *data, int bytes);
 int  hammer_ip_sync_record(hammer_transaction_t trans, hammer_record_t rec);
 int  hammer_ip_sync_record_cursor(hammer_cursor_t cursor, hammer_record_t rec);
-int  hammer_load_pseudofs(hammer_transaction_t trans, hammer_inode_t ip);
-int  hammer_save_pseudofs(hammer_transaction_t trans, hammer_inode_t ip);
+hammer_pseudofs_inmem_t  hammer_load_pseudofs(hammer_transaction_t trans,
+                       u_int32_t localization, int *errorp);
+int  hammer_mkroot_pseudofs(hammer_transaction_t trans, struct ucred *cred,
+                       hammer_pseudofs_inmem_t pfsm);
+int  hammer_save_pseudofs(hammer_transaction_t trans,
+                       hammer_pseudofs_inmem_t pfsm);
 void hammer_rel_pseudofs(hammer_mount_t hmp, hammer_pseudofs_inmem_t pfsm);
 int hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
                        struct ucred *cred);
@@ -1065,7 +1072,7 @@ int hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
 int hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
                        struct hammer_ioc_mirror_rw *mirror);
 int hammer_ioc_set_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
-                        struct hammer_ioc_pseudofs_rw *pfs);
+                       struct ucred *cred, struct hammer_ioc_pseudofs_rw *pfs);
 int hammer_ioc_get_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
                         struct hammer_ioc_pseudofs_rw *pfs);
 
index e927dc4..9d3a142 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.47 2008/07/07 03:49:50 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_disk.h,v 1.48 2008/07/09 10:29:20 dillon Exp $
  */
 
 #ifndef VFS_HAMMER_DISK_H_
@@ -537,13 +537,13 @@ typedef struct hammer_volume_ondisk *hammer_volume_ondisk_t;
 #define HAMMER_RECTYPE_DB              0x0012
 #define HAMMER_RECTYPE_EXT             0x0013  /* ext attributes */
 #define HAMMER_RECTYPE_FIX             0x0014  /* fixed attribute */
+#define HAMMER_RECTYPE_PFS             0x0015  /* PFS management */
 #define HAMMER_RECTYPE_MOVED           0x8000  /* special recovery flag */
 #define HAMMER_RECTYPE_MAX             0xFFFF
 
 #define HAMMER_RECTYPE_CLEAN_START     HAMMER_RECTYPE_EXT
 
 #define HAMMER_FIXKEY_SYMLINK          1
-#define HAMMER_FIXKEY_PSEUDOFS         2
 
 #define HAMMER_OBJTYPE_UNKNOWN         0       /* (never exists on-disk) */
 #define HAMMER_OBJTYPE_DIRECTORY       1
index c0a53d8..bb6515f 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_inode.c,v 1.95 2008/07/07 03:49:50 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_inode.c,v 1.96 2008/07/09 10:29:20 dillon Exp $
  */
 
 #include "hammer.h"
@@ -223,6 +223,7 @@ hammer_get_vnode(struct hammer_inode *ip, struct vnode **vpp)
        hammer_mount_t hmp;
        struct vnode *vp;
        int error = 0;
+       u_int8_t obj_type;
 
        hmp = ip->hmp;
 
@@ -241,8 +242,9 @@ hammer_get_vnode(struct hammer_inode *ip, struct vnode **vpp)
                        hammer_ref(&ip->lock);
                        vp = *vpp;
                        ip->vp = vp;
-                       vp->v_type =
-                               hammer_get_vnode_type(ip->ino_data.obj_type);
+
+                       obj_type = ip->ino_data.obj_type;
+                       vp->v_type = hammer_get_vnode_type(obj_type);
 
                        hammer_inode_wakereclaims(ip);
 
@@ -370,7 +372,10 @@ loop:
        hammer_ref(&ip->lock);
 
        /*
-        * Locate the on-disk inode.
+        * Locate the on-disk inode.  If this is a PFS root we always
+        * access the current version of the root inode and (if it is not
+        * a master) always access information under it with a snapshot
+        * TID.
         */
 retry:
        hammer_init_cursor(trans, &cursor, (dip ? &dip->cache[0] : NULL), NULL);
@@ -381,6 +386,7 @@ retry:
        cursor.key_beg.delete_tid = 0;
        cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
        cursor.key_beg.obj_type = 0;
+
        cursor.asof = iinfo.obj_asof;
        cursor.flags = HAMMER_CURSOR_GET_LEAF | HAMMER_CURSOR_GET_DATA |
                       HAMMER_CURSOR_ASOF;
@@ -429,7 +435,10 @@ retry:
                        ip->pfsm = dip->pfsm;
                        hammer_ref(&ip->pfsm->lock);
                } else {
-                       *errorp = hammer_load_pseudofs(trans, ip);
+                       ip->pfsm = hammer_load_pseudofs(trans,
+                                                       ip->obj_localization,
+                                                       errorp);
+                       *errorp = 0;    /* ignore ENOENT */
                }
        }
 
@@ -460,59 +469,36 @@ retry:
 
 /*
  * Create a new filesystem object, returning the inode in *ipp.  The
- * returned inode will be referenced.
+ * returned inode will be referenced.  The inode is created in-memory.
  *
- * The inode is created in-memory.
+ * If pfsm is non-NULL the caller wishes to create the root inode for
+ * a master PFS.
  */
 int
 hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
                    struct ucred *cred, hammer_inode_t dip,
-                   int pseudofs, struct hammer_inode **ipp)
+                   hammer_pseudofs_inmem_t pfsm, struct hammer_inode **ipp)
 {
        hammer_mount_t hmp;
        hammer_inode_t ip;
        uid_t xuid;
-       u_int32_t localization;
        int error;
 
        hmp = trans->hmp;
 
-       /*
-        * Assign the localization domain.  If if dip is NULL we are creating
-        * a pseudo-fs and must locate an unused localization domain.
-        */
-       if (pseudofs) {
-               for (localization = HAMMER_DEF_LOCALIZATION;
-                    localization < HAMMER_LOCALIZE_PSEUDOFS_MASK;
-                    localization += HAMMER_LOCALIZE_PSEUDOFS_INC) {
-                       ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT,
-                                             hmp->asof, localization,
-                                             0, &error);
-                       if (ip == NULL) {
-                               if (error != ENOENT)
-                                       return(error);
-                               break;
-                       }
-                       if (ip)
-                               hammer_rel_inode(ip, 0);
-               }
-       } else {
-               localization = dip->obj_localization;
-       }
-
        ip = kmalloc(sizeof(*ip), M_HAMMER, M_WAITOK|M_ZERO);
        ++hammer_count_inodes;
        ++hmp->count_inodes;
 
-       /*
-        * Allocate a new object id.  If creating a new pseudo-fs the
-        * obj_id is 1.
-        */
-       if (pseudofs)
+       if (pfsm) {
+               KKASSERT(pfsm->localization != 0);
                ip->obj_id = HAMMER_OBJID_ROOT;
-       else
+               ip->obj_localization = pfsm->localization;
+       } else {
+               KKASSERT(dip != NULL);
                ip->obj_id = hammer_alloc_objid(hmp, dip);
-       ip->obj_localization = localization;
+               ip->obj_localization = dip->obj_localization;
+       }
 
        KKASSERT(ip->obj_id != 0);
        ip->obj_asof = hmp->asof;
@@ -538,8 +524,10 @@ hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
         * the child.  We will do this even for pseudo-fs creation... the
         * sysad can turn it off.
         */
-       ip->ino_data.uflags = dip->ino_data.uflags &
-                             (SF_NOHISTORY|UF_NOHISTORY|UF_NODUMP);
+       if (dip) {
+               ip->ino_data.uflags = dip->ino_data.uflags &
+                                     (SF_NOHISTORY|UF_NOHISTORY|UF_NODUMP);
+       }
 
        ip->ino_leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
        ip->ino_leaf.base.localization = ip->obj_localization +
@@ -559,15 +547,21 @@ hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
        /*
         * Setup the ".." pointer.  This only needs to be done for directories
         * but we do it for all objects as a recovery aid.
-        *
+        */
+       if (dip)
+               ip->ino_data.parent_obj_id = dip->ino_leaf.base.obj_id;
+#if 0
+       /*
         * The parent_obj_localization field only applies to pseudo-fs roots.
+        * XXX this is no longer applicable, PFSs are no longer directly
+        * tied into the parent's directory structure.
         */
-       ip->ino_data.parent_obj_id = dip->ino_leaf.base.obj_id;
        if (ip->ino_data.obj_type == HAMMER_OBJTYPE_DIRECTORY &&
            ip->obj_id == HAMMER_OBJID_ROOT) {
                ip->ino_data.ext.obj.parent_obj_localization = 
                                                dip->obj_localization;
        }
+#endif
 
        switch(ip->ino_leaf.base.obj_type) {
        case HAMMER_OBJTYPE_CDEV:
@@ -583,9 +577,13 @@ hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
         * Calculate default uid/gid and overwrite with information from
         * the vap.
         */
-       xuid = hammer_to_unix_xid(&dip->ino_data.uid);
-       xuid = vop_helper_create_uid(hmp->mp, dip->ino_data.mode, xuid, cred,
-                                    &vap->va_mode);
+       if (dip) {
+               xuid = hammer_to_unix_xid(&dip->ino_data.uid);
+               xuid = vop_helper_create_uid(hmp->mp, dip->ino_data.mode,
+                                            xuid, cred, &vap->va_mode);
+       } else {
+               xuid = 0;
+       }
        ip->ino_data.mode = vap->va_mode;
 
        if (vap->va_vaflags & VA_UID_UUID_VALID)
@@ -599,17 +597,24 @@ hammer_create_inode(hammer_transaction_t trans, struct vattr *vap,
                ip->ino_data.gid = vap->va_gid_uuid;
        else if (vap->va_gid != (gid_t)VNOVAL)
                hammer_guid_to_uuid(&ip->ino_data.gid, vap->va_gid);
-       else
+       else if (dip)
                ip->ino_data.gid = dip->ino_data.gid;
 
        hammer_ref(&ip->lock);
 
-       if (dip->obj_localization == ip->obj_localization) {
+       if (pfsm) {
+               ip->pfsm = pfsm;
+               hammer_ref(&pfsm->lock);
+               error = 0;
+       } else if (dip->obj_localization == ip->obj_localization) {
                ip->pfsm = dip->pfsm;
                hammer_ref(&ip->pfsm->lock);
                error = 0;
        } else {
-               error = hammer_load_pseudofs(trans, ip);
+               ip->pfsm = hammer_load_pseudofs(trans,
+                                               ip->obj_localization,
+                                               &error);
+               error = 0;      /* ignore ENOENT */
        }
 
        if (error) {
@@ -647,83 +652,84 @@ hammer_free_inode(hammer_inode_t ip)
 }
 
 /*
- * Retrieve pseudo-fs data.
+ * Retrieve pseudo-fs data.  NULL will never be returned.
+ *
+ * If an error occurs *errorp will be set and a default template is returned,
+ * otherwise *errorp is set to 0.  Typically when an error occurs it will
+ * be ENOENT.
  */
-int
-hammer_load_pseudofs(hammer_transaction_t trans, hammer_inode_t ip)
+hammer_pseudofs_inmem_t
+hammer_load_pseudofs(hammer_transaction_t trans,
+                    u_int32_t localization, int *errorp)
 {
        hammer_mount_t hmp = trans->hmp;
+       hammer_inode_t ip;
        hammer_pseudofs_inmem_t pfsm;
        struct hammer_cursor cursor;
-       int error;
        int bytes;
 
 retry:
-       pfsm = RB_LOOKUP(hammer_pfs_rb_tree, &hmp->rb_pfsm_root,
-                        ip->obj_localization);
+       pfsm = RB_LOOKUP(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, localization);
        if (pfsm) {
-               KKASSERT(ip->pfsm == NULL);
-               ip->pfsm = pfsm;
                hammer_ref(&pfsm->lock);
-               return(0);
+               *errorp = 0;
+               return(pfsm);
+       }
+
+       /*
+        * PFS records are stored in the root inode (not the PFS root inode,
+        * but the real root).  Avoid an infinite recursion if loading
+        * the PFS for the real root.
+        */
+       if (localization) {
+               ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT,
+                                     HAMMER_MAX_TID,
+                                     HAMMER_DEF_LOCALIZATION, 0, errorp);
+       } else {
+               ip = NULL;
        }
 
        pfsm = kmalloc(sizeof(*pfsm), M_HAMMER, M_WAITOK | M_ZERO);
-       pfsm->localization = ip->obj_localization;
+       pfsm->localization = localization;
        pfsm->pfsd.unique_uuid = trans->rootvol->ondisk->vol_fsid;
        pfsm->pfsd.shared_uuid = pfsm->pfsd.unique_uuid;
 
-       hammer_init_cursor(trans, &cursor, NULL, NULL);
-       cursor.key_beg.localization = ip->obj_localization +
+       hammer_init_cursor(trans, &cursor, (ip ? &ip->cache[1] : NULL), ip);
+       cursor.key_beg.localization = HAMMER_DEF_LOCALIZATION +
                                      HAMMER_LOCALIZE_MISC;
        cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
        cursor.key_beg.create_tid = 0;
        cursor.key_beg.delete_tid = 0;
-       cursor.key_beg.rec_type = HAMMER_RECTYPE_FIX;
+       cursor.key_beg.rec_type = HAMMER_RECTYPE_PFS;
        cursor.key_beg.obj_type = 0;
-       cursor.key_beg.key = HAMMER_FIXKEY_PSEUDOFS;
+       cursor.key_beg.key = localization;
        cursor.asof = HAMMER_MAX_TID;
        cursor.flags |= HAMMER_CURSOR_ASOF;
 
-       error = hammer_btree_lookup(&cursor);
-       if (error == 0) {
-               error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_DATA);
-               if (error == 0) {
+       if (ip)
+               *errorp = hammer_ip_lookup(&cursor);
+       else
+               *errorp = hammer_btree_lookup(&cursor);
+       if (*errorp == 0) {
+               *errorp = hammer_ip_resolve_data(&cursor);
+               if (*errorp == 0) {
                        bytes = cursor.leaf->data_len;
                        if (bytes > sizeof(pfsm->pfsd))
                                bytes = sizeof(pfsm->pfsd);
                        bcopy(cursor.data, &pfsm->pfsd, bytes);
                }
-       } else if (error == ENOENT) {
-               error = 0;
        }
-
        hammer_done_cursor(&cursor);
 
-       if (error == 0) {
-               pfsm->fsid_udev = hammer_fsid_to_udev(&pfsm->pfsd.shared_uuid);
-               hammer_ref(&pfsm->lock);
-               if (RB_INSERT(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, pfsm)) {
-                       kfree(pfsm, M_HAMMER);
-                       goto retry;
-               }
-               ip->pfsm = pfsm;
-
-               /*
-                * Certain aspects of the pseudofs configuration are reflected
-                * in the inode.
-                */
-               if (pfsm->pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
-                       ip->flags |= HAMMER_INODE_RO;
-                       ip->flags |= HAMMER_INODE_PFSD;
-               } else if (pfsm->pfsd.master_id >= 0) {
-                       ip->flags |= HAMMER_INODE_PFSD;
-               }
-       } else {
-               kprintf("cannot load pfsm error %d\n", error);
+       pfsm->fsid_udev = hammer_fsid_to_udev(&pfsm->pfsd.shared_uuid);
+       hammer_ref(&pfsm->lock);
+       if (ip)
+               hammer_rel_inode(ip, 0);
+       if (RB_INSERT(hammer_pfs_rb_tree, &hmp->rb_pfsm_root, pfsm)) {
                kfree(pfsm, M_HAMMER);
+               goto retry;
        }
-       return(error);
+       return(pfsm);
 }
 
 /*
@@ -731,25 +737,26 @@ retry:
  * on-disk pseudo-fs data but we have to delete in-memory versions.
  */
 int
-hammer_save_pseudofs(hammer_transaction_t trans, hammer_inode_t ip)
+hammer_save_pseudofs(hammer_transaction_t trans, hammer_pseudofs_inmem_t pfsm)
 {
        struct hammer_cursor cursor;
-       hammer_pseudofs_inmem_t pfsm;
        hammer_record_t record;
+       hammer_inode_t ip;
        int error;
 
+       ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT, HAMMER_MAX_TID,
+                             HAMMER_DEF_LOCALIZATION, 0, &error);
 retry:
-       pfsm = ip->pfsm;
        pfsm->fsid_udev = hammer_fsid_to_udev(&pfsm->pfsd.shared_uuid);
        hammer_init_cursor(trans, &cursor, &ip->cache[1], ip);
        cursor.key_beg.localization = ip->obj_localization +
                                      HAMMER_LOCALIZE_MISC;
-       cursor.key_beg.obj_id = ip->obj_id;
+       cursor.key_beg.obj_id = HAMMER_OBJID_ROOT;
        cursor.key_beg.create_tid = 0;
        cursor.key_beg.delete_tid = 0;
-       cursor.key_beg.rec_type = HAMMER_RECTYPE_FIX;
+       cursor.key_beg.rec_type = HAMMER_RECTYPE_PFS;
        cursor.key_beg.obj_type = 0;
-       cursor.key_beg.key = HAMMER_FIXKEY_PSEUDOFS;
+       cursor.key_beg.key = pfsm->localization;
        cursor.asof = HAMMER_MAX_TID;
        cursor.flags |= HAMMER_CURSOR_ASOF;
 
@@ -772,8 +779,8 @@ retry:
 
                record->leaf.base.localization = ip->obj_localization +
                                                 HAMMER_LOCALIZE_MISC;
-               record->leaf.base.rec_type = HAMMER_RECTYPE_FIX;
-               record->leaf.base.key = HAMMER_FIXKEY_PSEUDOFS;
+               record->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
+               record->leaf.base.key = pfsm->localization;
                record->leaf.data_len = sizeof(pfsm->pfsd);
                bcopy(&pfsm->pfsd, record->data, sizeof(pfsm->pfsd));
                error = hammer_ip_add_record(trans, record);
@@ -781,30 +788,37 @@ retry:
        hammer_done_cursor(&cursor);
        if (error == EDEADLK)
                goto retry;
-       if (error == 0) {
-               /*
-                * Certain aspects of the pseudofs configuration are reflected
-                * in the inode.  Note that we cannot mess with the as-of or
-                * clear the read-only state.
-                *
-                * If this inode represented a slave snapshot its asof will
-                * be set to a snapshot tid.  When clearing slave mode any
-                * re-access of the inode via the parent directory will
-                * wind up using a different asof and thus will instantiate
-                * a new inode.
-                */
-               if (pfsm->pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
-                       ip->flags |= HAMMER_INODE_RO;
-                       ip->flags |= HAMMER_INODE_PFSD;
-               } else if (pfsm->pfsd.master_id >= 0) {
-                       ip->flags |= HAMMER_INODE_PFSD;
-               } else {
-                       ip->flags &= ~HAMMER_INODE_PFSD;
-               }
+       hammer_rel_inode(ip, 0);
+       return(error);
+}
+
+/*
+ * Create a root directory for a PFS if one does not alredy exist.
+ */
+int
+hammer_mkroot_pseudofs(hammer_transaction_t trans, struct ucred *cred,
+                      hammer_pseudofs_inmem_t pfsm)
+{
+       hammer_inode_t ip;
+       struct vattr vap;
+       int error;
+
+       ip = hammer_get_inode(trans, NULL, HAMMER_OBJID_ROOT, HAMMER_MAX_TID,
+                             pfsm->localization, 0, &error);
+       if (ip == NULL) {
+               vattr_null(&vap);
+               vap.va_mode = 0755;
+               vap.va_type = VDIR;
+               error = hammer_create_inode(trans, &vap, cred, NULL, pfsm, &ip);
        }
+       if (ip)
+               hammer_rel_inode(ip, 0);
        return(error);
 }
 
+/*
+ * Release a reference on a PFS
+ */
 void
 hammer_rel_pseudofs(hammer_mount_t hmp, hammer_pseudofs_inmem_t pfsm)
 {
index 3dfcc03..5af7cb2 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.25 2008/07/02 21:57:54 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.c,v 1.26 2008/07/09 10:29:20 dillon Exp $
  */
 
 #include "hammer.h"
@@ -79,7 +79,7 @@ hammer_ioctl(hammer_inode_t ip, u_long com, caddr_t data, int fflag,
                break;
        case HAMMERIOC_SET_PSEUDOFS:
                if (error == 0) {
-                       error = hammer_ioc_set_pseudofs(&trans, ip,
+                       error = hammer_ioc_set_pseudofs(&trans, ip, cred,
                                    (struct hammer_ioc_pseudofs_rw *)data);
                }
                break;
index 3c7ce15..c615311 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.h,v 1.17 2008/07/07 00:24:31 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_ioctl.h,v 1.18 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * HAMMER ioctl's.  This file can be #included from userland
@@ -201,7 +201,7 @@ struct hammer_ioc_synctid {
  */
 struct hammer_ioc_pseudofs_rw {
        struct hammer_ioc_head  head;
-       u_int32_t               pseudoid;
+       int                     pfs_id;
        u_int32_t               bytes;
        u_int32_t               version;
        u_int32_t               flags;
@@ -218,6 +218,8 @@ struct hammer_ioc_pseudofs_rw {
 #define HAMMER_IOC_PFS_MIRROR_FLAGS    0x0020
 #define HAMMER_IOC_PFS_LABEL           0x0040
 
+#define HAMMER_MAX_PFS                 65536
+
 /*
  * HAMMERIOC_MIRROR_READ/WRITE
  */
@@ -231,6 +233,9 @@ struct hammer_ioc_mirror_rw {
        void                    *ubuf;          /* user buffer */
        int                     count;          /* current size */
        int                     size;           /* max size */
+       int                     pfs_id;         /* PFS id being read/written */
+       int                     reserved01;
+       uuid_t                  shared_uuid;    /* validator for safety */
 };
 
 /*
index 8cdd83e..6c08d3b 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_mirror.c,v 1.8 2008/07/07 03:49:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_mirror.c,v 1.9 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * HAMMER mirroring ioctls - serialize and deserialize modifications made
@@ -55,6 +55,10 @@ static int hammer_mirror_localize_data(hammer_data_ondisk_t data,
  * to the transaction id range are returned.  Mirroring code keeps track
  * of the last transaction id fully scanned and can efficiently pick up
  * where it left off if interrupted.
+ *
+ * The PFS is identified in the mirror structure.  The passed ip is just
+ * some directory in the overall HAMMER filesystem and has nothing to
+ * do with the PFS.
  */
 int
 hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
@@ -69,6 +73,9 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
        int error;
        int data_len;
        int bytes;
+       u_int32_t localization;
+
+       localization = (u_int32_t)mirror->pfs_id << 16;
 
        if ((mirror->key_beg.localization | mirror->key_end.localization) &
            HAMMER_LOCALIZE_PSEUDOFS_MASK) {
@@ -78,7 +85,7 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip,
                return(EINVAL);
 
        mirror->key_cur = mirror->key_beg;
-       mirror->key_cur.localization += ip->obj_localization;
+       mirror->key_cur.localization += localization;
        bzero(&mrec, sizeof(mrec));
 
 retry:
@@ -89,7 +96,7 @@ retry:
        }
        cursor.key_beg = mirror->key_cur;
        cursor.key_end = mirror->key_end;
-       cursor.key_end.localization += ip->obj_localization;
+       cursor.key_end.localization += localization;
 
        cursor.flags |= HAMMER_CURSOR_END_INCLUSIVE;
        cursor.flags |= HAMMER_CURSOR_BACKEND;
@@ -200,7 +207,10 @@ failed:
  * Copy records from userland to the target mirror.  Records which already
  * exist may only have their delete_tid updated.
  *
- * The passed ip is the root ip of the pseudofs
+ * The PFS is identified in the mirror structure.  The passed ip is just
+ * some directory in the overall HAMMER filesystem and has nothing to
+ * do with the PFS.  In fact, there might not even be a root directory for
+ * the PFS yet!
  */
 int
 hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
@@ -213,6 +223,9 @@ hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip,
        u_int32_t rec_crc;
        int error;
        char *uptr;
+       u_int32_t localization;
+
+       localization = (u_int32_t)mirror->pfs_id << 16;
 
        if (mirror->size < 0 || mirror->size > 0x70000000)
                return(EINVAL);
@@ -259,7 +272,7 @@ retry:
                 * by hammer_mirror_write().
                 */
                mrec.leaf.base.localization &= HAMMER_LOCALIZE_MASK;
-               mrec.leaf.base.localization += ip->obj_localization;
+               mrec.leaf.base.localization += localization;
 
                /*
                 * Locate the record.
@@ -324,7 +337,7 @@ hammer_mirror_check(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec)
        hammer_btree_leaf_elm_t leaf = cursor->leaf;
 
        if (leaf->base.delete_tid != mrec->leaf.base.delete_tid) {
-               if (leaf->base.delete_tid != 0)
+               if (mrec->leaf.base.delete_tid != 0)
                        return(1);
        }
        return(0);
@@ -386,9 +399,10 @@ hammer_mirror_write(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec,
        int error;
        int doprop;
 
-       /*
-        * Skip records related to the root inode other then
-        * directory entries.
+#if 0
+       /* 
+        * removed: all records are now duplicated, including the root
+        * inode.
         */
        if (mrec->leaf.base.obj_id == HAMMER_OBJID_ROOT) {
                if (mrec->leaf.base.rec_type == HAMMER_RECTYPE_INODE ||
@@ -396,6 +410,7 @@ hammer_mirror_write(hammer_cursor_t cursor, struct hammer_ioc_mrecord *mrec,
                        return(0);
                }
        }
+#endif
 
        trans = cursor->trans;
        data_buffer = NULL;
@@ -513,35 +528,14 @@ hammer_mirror_localize_data(hammer_data_ondisk_t data,
 }
 
 /*
- * Set mirroring/pseudo-fs information
+ * Auto-detect the pseudofs.
  */
-int
-hammer_ioc_set_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
-                       struct hammer_ioc_pseudofs_rw *pfs)
+static
+void
+hammer_mirror_autodetect(struct hammer_ioc_pseudofs_rw *pfs, hammer_inode_t ip)
 {
-       hammer_pseudofs_inmem_t pfsm;
-       int error;
-
-       pfsm = ip->pfsm;
-       error = 0;
-
-       if (pfs->pseudoid != ip->obj_localization)
-               error = EINVAL;
-       if (pfs->bytes != sizeof(pfsm->pfsd))
-               error = EINVAL;
-       if (pfs->version != HAMMER_IOC_PSEUDOFS_VERSION)
-               error = EINVAL;
-       if (error == 0 && pfs->ondisk) {
-               if (ip->obj_id != HAMMER_OBJID_ROOT)
-                       error = EINVAL;
-               if (error == 0) {
-                       error = copyin(pfs->ondisk, &ip->pfsm->pfsd,
-                                      sizeof(ip->pfsm->pfsd));
-               }
-               if (error == 0)
-                       error = hammer_save_pseudofs(trans, ip);
-       }
-       return(error);
+       if (pfs->pfs_id == -1)
+               pfs->pfs_id = (int)(ip->obj_localization >> 16);
 }
 
 /*
@@ -552,30 +546,76 @@ hammer_ioc_get_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
                        struct hammer_ioc_pseudofs_rw *pfs)
 {
        hammer_pseudofs_inmem_t pfsm;
+       u_int32_t localization;
        int error;
 
-       pfs->pseudoid = ip->obj_localization;
+       hammer_mirror_autodetect(pfs, ip);
+       if (pfs->pfs_id < 0 || pfs->pfs_id >= HAMMER_MAX_PFS)
+               return(EINVAL);
+       localization = (u_int32_t)pfs->pfs_id << 16;
        pfs->bytes = sizeof(struct hammer_pseudofs_data);
        pfs->version = HAMMER_IOC_PSEUDOFS_VERSION;
 
+       pfsm = hammer_load_pseudofs(trans, localization, &error);
+       if (error) {
+               hammer_rel_pseudofs(trans->hmp, pfsm);
+               return(error);
+       }
+
        /*
-        * Update pfsm->sync_end_tid if a master
+        * If the PFS is a master the sync tid is set by normal operation
+        * rather then the mirroring code, and will always track the
+        * real HAMMER filesystem.
         */
-       pfsm = ip->pfsm;
        if (pfsm->pfsd.master_id >= 0)
                pfsm->pfsd.sync_end_tid = trans->rootvol->ondisk->vol0_next_tid;
 
        /*
-        * Return PFS information for root inodes only.
+        * Copy out to userland.
         */
        error = 0;
-       if (pfs->ondisk) {
-               if (ip->obj_id != HAMMER_OBJID_ROOT)
-                       error = EINVAL;
-               if (error == 0) {
-                       error = copyout(&ip->pfsm->pfsd, pfs->ondisk,
-                                       sizeof(ip->pfsm->pfsd));
-               }
+       if (pfs->ondisk && error == 0)
+               error = copyout(&pfsm->pfsd, pfs->ondisk, sizeof(pfsm->pfsd));
+       hammer_rel_pseudofs(trans->hmp, pfsm);
+       return(error);
+}
+
+/*
+ * Set mirroring/pseudo-fs information
+ */
+int
+hammer_ioc_set_pseudofs(hammer_transaction_t trans, hammer_inode_t ip,
+                       struct ucred *cred, struct hammer_ioc_pseudofs_rw *pfs)
+{
+       hammer_pseudofs_inmem_t pfsm;
+       int error;
+       u_int32_t localization;
+
+       error = 0;
+       hammer_mirror_autodetect(pfs, ip);
+       if (pfs->pfs_id < 0 || pfs->pfs_id >= HAMMER_MAX_PFS)
+               error = EINVAL;
+       if (pfs->bytes != sizeof(pfsm->pfsd))
+               error = EINVAL;
+       if (pfs->version != HAMMER_IOC_PSEUDOFS_VERSION)
+               error = EINVAL;
+       if (error == 0 && pfs->ondisk) {
+               /*
+                * Load the PFS so we can modify our in-core copy.
+                */
+               localization = (u_int32_t)pfs->pfs_id << 16;
+               pfsm = hammer_load_pseudofs(trans, localization, &error);
+               error = copyin(pfs->ondisk, &pfsm->pfsd, sizeof(pfsm->pfsd));
+
+               /*
+                * Save it back, create a root inode if we are in master
+                * mode and no root exists.
+                */
+               if (error == 0)
+                       error = hammer_mkroot_pseudofs(trans, cred, pfsm);
+               if (error == 0)
+                       error = hammer_save_pseudofs(trans, pfsm);
+               hammer_rel_pseudofs(trans->hmp, pfsm);
        }
        return(error);
 }
index 78cf32c..7e977e2 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.66 2008/07/07 03:49:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_ondisk.c,v 1.67 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * Manage HAMMER's on-disk structures.  These routines are primarily
@@ -1289,6 +1289,7 @@ hammer_alloc_data(hammer_transaction_t trans, int32_t data_len,
                case HAMMER_RECTYPE_DIRENTRY:
                case HAMMER_RECTYPE_EXT:
                case HAMMER_RECTYPE_FIX:
+               case HAMMER_RECTYPE_PFS:
                        zone = HAMMER_ZONE_META_INDEX;
                        break;
                case HAMMER_RECTYPE_DATA:
index 7a21bb1..ca8a3d8 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_reblock.c,v 1.24 2008/07/05 18:59:28 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_reblock.c,v 1.25 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * HAMMER reblocker - This code frees up fragmented physical space
@@ -230,6 +230,7 @@ hammer_reblock_helper(struct hammer_ioc_reblock *reblock,
                break;
        case HAMMER_RECTYPE_EXT:
        case HAMMER_RECTYPE_FIX:
+       case HAMMER_RECTYPE_PFS:
        case HAMMER_RECTYPE_DIRENTRY:
                iocflags = HAMMER_IOC_DO_DIRS;
                break;
index c1dbb3d..5f17a33 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.31 2008/07/07 03:49:51 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.32 2008/07/09 10:29:20 dillon Exp $
  */
 /*
  * HAMMER structural locking
@@ -453,11 +453,19 @@ hammer_directory_namekey(const void *name, int len)
 }
 
 hammer_tid_t
-hammer_str_to_tid(const char *str)
+hammer_str_to_tid(const char *str, int *ispfs, u_int32_t *localizationp)
+
 {
        hammer_tid_t tid;
+       char *ptr;
 
-       tid = strtouq(str, NULL, 0);                    /* full TID */
+       tid = strtouq(str, &ptr, 0);
+       if (*ptr == ':') {
+               *ispfs = 1;
+               *localizationp = strtoul(ptr + 1, NULL, 0) << 16;
+       } else {
+               *ispfs = 0;
+       }
        return(tid);
 }
 
index 5674fcf..c1050e2 100644 (file)
@@ -31,7 +31,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * 
- * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.83 2008/07/07 22:42:35 dillon Exp $
+ * $DragonFly: src/sys/vfs/hammer/hammer_vnops.c,v 1.84 2008/07/09 10:29:20 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -595,7 +595,7 @@ hammer_vop_ncreate(struct vop_ncreate_args *ap)
         */
 
        error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
-                                   dip, 0, &nip);
+                                   dip, NULL, &nip);
        if (error) {
                hkprintf("hammer_create_inode error %d\n", error);
                hammer_done_transaction(&trans);
@@ -717,7 +717,6 @@ hammer_vop_getattr(struct vop_getattr_args *ap)
        default:
                break;
        }
-
        return(0);
 }
 
@@ -755,20 +754,43 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
        ncp = ap->a_nch->ncp;
        asof = dip->obj_asof;
        nlen = ncp->nc_nlen;
-       flags = dip->flags;
+       flags = dip->flags & HAMMER_INODE_RO;
        ispfs = 0;
 
        hammer_simple_transaction(&trans, dip->hmp);
 
        for (i = 0; i < nlen; ++i) {
                if (ncp->nc_name[i] == '@' && ncp->nc_name[i+1] == '@') {
-                       asof = hammer_str_to_tid(ncp->nc_name + i + 2);
-                       flags |= HAMMER_INODE_RO;
+                       asof = hammer_str_to_tid(ncp->nc_name + i + 2,
+                                                &ispfs, &localization);
+                       if (asof != HAMMER_MAX_TID)
+                               flags |= HAMMER_INODE_RO;
                        break;
                }
        }
        nlen = i;
 
+       /*
+        * If this is a PFS softlink we dive into the PFS
+        */
+       if (ispfs && nlen == 0) {
+               ip = hammer_get_inode(&trans, dip, HAMMER_OBJID_ROOT,
+                                     asof, localization,
+                                     flags, &error);
+               if (error == 0) {
+                       error = hammer_get_vnode(ip, &vp);
+                       hammer_rel_inode(ip, 0);
+               } else {
+                       vp = NULL;
+               }
+               if (error == 0) {
+                       vn_unlock(vp);
+                       cache_setvp(ap->a_nch, vp);
+                       vrele(vp);
+               }
+               goto done;
+       }
+
        /*
         * If there is no path component the time extension is relative to
         * dip.
@@ -834,13 +856,6 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
                        if (nlen == cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF &&
                            bcmp(ncp->nc_name, cursor.data->entry.name, nlen) == 0) {
                                obj_id = cursor.data->entry.obj_id;
-
-                               /*
-                                * Force relookups whenever a PFS root is
-                                * accessed.
-                                */
-                               if (obj_id == HAMMER_OBJID_ROOT)
-                                       ispfs = 1;
                                localization = cursor.data->entry.localization;
                                break;
                        }
@@ -852,15 +867,6 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
                ip = hammer_get_inode(&trans, dip, obj_id,
                                      asof, localization,
                                      flags, &error);
-               if (ispfs && asof > ip->pfsm->pfsd.sync_end_tid) {
-                       asof = ip->pfsm->pfsd.sync_end_tid;
-                       hammer_rel_inode(ip, 0);
-                       ip = hammer_get_inode(&trans, dip, obj_id,
-                                             asof, localization,
-                                             flags, &error);
-               }
-
-
                if (error == 0) {
                        error = hammer_get_vnode(ip, &vp);
                        hammer_rel_inode(ip, 0);
@@ -870,8 +876,6 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap)
                if (error == 0) {
                        vn_unlock(vp);
                        cache_setvp(ap->a_nch, vp);
-                       if (ispfs)
-                               cache_settimeout(ap->a_nch, 0);
                        vrele(vp);
                }
        } else if (error == ENOENT) {
@@ -1035,7 +1039,7 @@ hammer_vop_nmkdir(struct vop_nmkdir_args *ap)
         * returned inode will be referenced but not locked.
         */
        error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
-                                   dip, 0, &nip);
+                                   dip, NULL, &nip);
        if (error) {
                hkprintf("hammer_mkdir error %d\n", error);
                hammer_done_transaction(&trans);
@@ -1085,7 +1089,6 @@ hammer_vop_nmknod(struct vop_nmknod_args *ap)
        struct hammer_inode *nip;
        struct nchandle *nch;
        int error;
-       int pseudofs;
 
        nch = ap->a_nch;
        dip = VTOI(ap->a_dvp);
@@ -1106,9 +1109,8 @@ hammer_vop_nmknod(struct vop_nmknod_args *ap)
         *
         * If mknod specifies a directory a pseudo-fs is created.
         */
-       pseudofs = (ap->a_vap->va_type == VDIR);
        error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
-                                   dip, pseudofs, &nip);
+                                   dip, NULL, &nip);
        if (error) {
                hammer_done_transaction(&trans);
                *ap->a_vpp = NULL;
@@ -1195,6 +1197,7 @@ hammer_vop_readdir(struct vop_readdir_args *ap)
        off_t *cookies;
        off_t saveoff;
        int r;
+       int dtype;
 
        ip = VTOI(ap->a_vp);
        uio = ap->a_uio;
@@ -1280,9 +1283,13 @@ hammer_vop_readdir(struct vop_readdir_args *ap)
                if (base->obj_id != ip->obj_id)
                        panic("readdir: bad record at %p", cursor.node);
 
+               /*
+                * Convert pseudo-filesystems into softlinks
+                */
+               dtype = hammer_get_dtype(cursor.leaf->base.obj_type);
                r = vop_write_dirent(
                             &error, uio, cursor.data->entry.obj_id,
-                            hammer_get_dtype(cursor.leaf->base.obj_type),
+                            dtype,
                             cursor.leaf->data_len - HAMMER_ENTRY_NAME_OFF ,
                             (void *)cursor.data->entry.name);
                if (r)
@@ -1332,16 +1339,57 @@ hammer_vop_readlink(struct vop_readlink_args *ap)
        struct hammer_transaction trans;
        struct hammer_cursor cursor;
        struct hammer_inode *ip;
+       char buf[32];
+       u_int32_t localization;
+       hammer_pseudofs_inmem_t pfsm;
        int error;
 
        ip = VTOI(ap->a_vp);
 
+       /*
+        * Special softlink for PFS access, created by hammer pfs-create
+        */
+
+       if (ip->obj_id == HAMMER_OBJID_ROOT && ip->obj_localization &&
+           ip->obj_asof == HAMMER_MAX_TID) {
+               ksnprintf(buf, sizeof(buf), "@@0x%016llx:0x%04x",
+                       ip->pfsm->pfsd.sync_end_tid,
+                       ip->obj_localization >> 16);
+               error = uiomove(buf, strlen(buf), ap->a_uio);
+               return(error);
+       }
+
        /*
         * Shortcut if the symlink data was stuffed into ino_data.
+        *
+        * Also expand special @@PFSxxxxx softlinks.
         */
        if (ip->ino_data.size <= HAMMER_INODE_BASESYMLEN) {
-               error = uiomove(ip->ino_data.ext.symlink,
-                               ip->ino_data.size, ap->a_uio);
+               char *ptr;
+               int bytes;
+
+               ptr = ip->ino_data.ext.symlink;
+               bytes = (int)ip->ino_data.size;
+               if (bytes == 10 && strncmp(ptr, "@@PFS", 5) == 0) {
+                       hammer_simple_transaction(&trans, ip->hmp);
+                       bcopy(ptr + 5, buf, 5);
+                       buf[5] = 0;
+                       localization = strtoul(buf, NULL, 10) << 16;
+                       pfsm = hammer_load_pseudofs(&trans, localization,
+                                                   &error);
+                       if (error == 0) {
+                               ksnprintf(buf, sizeof(buf),
+                                        "@@0x%016llx:%05d",
+                                        pfsm->pfsd.sync_end_tid,
+                                        localization >> 16);
+                               ptr = buf;
+                               bytes = strlen(buf);
+                       }
+                       if (pfsm)
+                               hammer_rel_pseudofs(trans.hmp, pfsm);
+                       hammer_done_transaction(&trans);
+               }
+               error = uiomove(ptr, bytes, ap->a_uio);
                return(error);
        }
 
@@ -1806,7 +1854,7 @@ hammer_vop_nsymlink(struct vop_nsymlink_args *ap)
         */
 
        error = hammer_create_inode(&trans, ap->a_vap, ap->a_cred,
-                                   dip, 0, &nip);
+                                   dip, NULL, &nip);
        if (error) {
                hammer_done_transaction(&trans);
                *ap->a_vpp = NULL;