hammer2 - Refactor file unlink w/open descriptor
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 11 Dec 2013 21:33:11 +0000 (13:33 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 11 Dec 2013 21:50:09 +0000 (13:50 -0800)
* Remove the H2 special cases for the HAMMER2_CHAIN_DELETED flag.  These
  special cases existed to deal with the unlink-open-file case, but hackish
  and didn't deal with all the issues (such as bulk-free scan code losing
  track of the unlinked open file).

* Change the unlink case when the file is still open or mmap'd.  Instead of
  deleting the inode H2 will now move the inode to a hidden directory in
  the mount root.  This way the bulk freeing code still sees it.

  When the inode is moved it is re-keyed to its inode number, thus guaranteed
  not to conflict with anything else sitting in the hidden directory.

* Add code to create the hidden directory in the mount root if it does not
  exist.

* Add code to wipe the contents of the hidden directory on-mount (as a crash
  recovery mechanic).

sys/vfs/hammer2/hammer2.h
sys/vfs/hammer2/hammer2_chain.c
sys/vfs/hammer2/hammer2_disk.h
sys/vfs/hammer2/hammer2_flush.c
sys/vfs/hammer2/hammer2_inode.c
sys/vfs/hammer2/hammer2_ioctl.c
sys/vfs/hammer2/hammer2_vfsops.c
sys/vfs/hammer2/hammer2_vnops.c

index 70aacf0..1e0c7a0 100644 (file)
@@ -240,7 +240,7 @@ RB_PROTOTYPE(hammer2_chain_tree, hammer2_chain, rbnode, hammer2_chain_cmp);
 #define HAMMER2_CHAIN_MOVED            0x00000080      /* bref changed */
 #define HAMMER2_CHAIN_IOFLUSH          0x00000100      /* bawrite on put */
 #define HAMMER2_CHAIN_DEFERRED         0x00000200      /* on a deferral list */
-#define HAMMER2_CHAIN_DESTROYED                0x00000400      /* destroying inode */
+#define HAMMER2_CHAIN_UNLINKED         0x00000400      /* delete on reclaim */
 #define HAMMER2_CHAIN_VOLUMESYNC       0x00000800      /* needs volume sync */
 #define HAMMER2_CHAIN_UNUSED01000      0x00001000
 #define HAMMER2_CHAIN_MOUNTED          0x00002000      /* PFS is mounted */
@@ -562,6 +562,8 @@ struct hammer2_pfsmount {
        struct mount            *mp;
        hammer2_cluster_t       cluster;
        hammer2_inode_t         *iroot;         /* PFS root inode */
+       hammer2_inode_t         *ihidden;       /* PFS hidden directory */
+       struct lock             lock;           /* PFS lock for certain ops */
        hammer2_off_t           inode_count;    /* copy of inode_count */
        ccms_domain_t           ccms_dom;
        struct netexport        export;         /* nfs export */
@@ -734,14 +736,15 @@ hammer2_inode_t *hammer2_inode_create(hammer2_trans_t *trans,
                        hammer2_chain_t **chainp, int *errorp);
 int hammer2_inode_connect(hammer2_trans_t *trans, int hlink,
                        hammer2_inode_t *dip, hammer2_chain_t **chainp,
-                       const uint8_t *name, size_t name_len);
+                       const uint8_t *name, size_t name_len,
+                       hammer2_key_t key);
 hammer2_inode_t *hammer2_inode_common_parent(hammer2_inode_t *fdip,
                        hammer2_inode_t *tdip);
 void hammer2_inode_fsync(hammer2_trans_t *trans, hammer2_inode_t *ip,
                        hammer2_chain_t **parentp);
 int hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip,
                        const uint8_t *name, size_t name_len, int isdir,
-                       int *hlinkp);
+                       int *hlinkp, struct nchandle *nch);
 int hammer2_hardlink_consolidate(hammer2_trans_t *trans, hammer2_inode_t *ip,
                        hammer2_chain_t **chainp,
                        hammer2_inode_t *tdip, int linkcnt);
@@ -749,6 +752,7 @@ int hammer2_hardlink_deconsolidate(hammer2_trans_t *trans, hammer2_inode_t *dip,
                        hammer2_chain_t **chainp, hammer2_chain_t **ochainp);
 int hammer2_hardlink_find(hammer2_inode_t *dip,
                        hammer2_chain_t **chainp, hammer2_chain_t **ochainp);
+void hammer2_inode_install_hidden(hammer2_pfsmount_t *pmp);
 
 /*
  * hammer2_chain.c
index ef7b579..8ea0e9c 100644 (file)
@@ -2910,6 +2910,8 @@ hammer2_chain_duplicate(hammer2_trans_t *trans, hammer2_chain_t **parentp,
        nchain->inode_reason = ochain->inode_reason + 0x100000;
        if (ochain->flags & HAMMER2_CHAIN_INITIAL)
                atomic_set_int(&nchain->flags, HAMMER2_CHAIN_INITIAL);
+       if (ochain->flags & HAMMER2_CHAIN_UNLINKED)
+               atomic_set_int(&nchain->flags, HAMMER2_CHAIN_UNLINKED);
 
        /*
         * Fixup (copy) any embedded data.  Non-embedded data relies on the
@@ -3066,7 +3068,8 @@ hammer2_chain_delete_duplicate(hammer2_trans_t *trans, hammer2_chain_t **chainp,
        nchain->inode_count += ochain->inode_count;
        atomic_set_int(&nchain->flags,
                       ochain->flags & (HAMMER2_CHAIN_INITIAL |
-                                       HAMMER2_CHAIN_FORCECOW));
+                                       HAMMER2_CHAIN_FORCECOW |
+                                       HAMMER2_CHAIN_UNLINKED));
        atomic_set_int(&ochain->flags, HAMMER2_CHAIN_FORCECOW);
        nchain->inode_reason = ochain->inode_reason + 0x1000;
 
index d87d381..4153148 100644 (file)
@@ -682,6 +682,9 @@ typedef struct hammer2_bmap_data hammer2_bmap_data_t;
 #define HAMMER2_INODE_MAXNAME          256     /* maximum name in bytes */
 #define HAMMER2_INODE_VERSION_ONE      1
 
+#define HAMMER2_INODE_HIDDENDIR                16      /* special inode */
+#define HAMMER2_INODE_START            1024    /* dynamically allocated */
+
 struct hammer2_inode_data {
        uint16_t        version;        /* 0000 inode data version */
        uint16_t        reserved02;     /* 0002 */
index da2e7c3..9ab2eb6 100644 (file)
@@ -78,28 +78,16 @@ static void hammer2_rollup_stats(hammer2_chain_t *parent,
  * Can we ignore a chain for the purposes of flushing modifications
  * to the media?
  *
- * Normally we don't bother to flush deleted chains.  However, a chain
- * associated with a deleted inode may still be active due to open
- * descriptors.  A flush is needed in this situation to prevent unbounded
- * memory use by the filesystem.
- *
- * This test is fragile as active inodes can still be delete-duplicated and
- * the older deleted chains are, in fact, actual deletions.  These will be
- * flagged DUPLICATED.  The live inode at the end of the del-dup chain
- * will be marked destroyed on last close.
- *
- * XXX inode deletions need to be deleted normally and then duplicated to
- *     a special 'live-but-unlinked' spot somewhere until last-close.
- * XXX bulk free code has a conniption fit too.
+ * This code is no degenerate.  We used to have to distinguish between
+ * deleted chains and deleted chains associated with inodes that were
+ * still open.  This mechanic has been fixed so the function is now
+ * a simple test.
  */
 static __inline
 int
 h2ignore_deleted(hammer2_flush_info_t *info, hammer2_chain_t *chain)
 {
-       return (chain->delete_tid <= info->sync_tid &&
-               (chain->bref.type != HAMMER2_BREF_TYPE_INODE ||
-                (chain->flags & HAMMER2_CHAIN_DUPLICATED) ||
-                (chain->flags & HAMMER2_CHAIN_DESTROYED)));
+       return (chain->delete_tid <= info->sync_tid);
 }
 
 #if 0
@@ -236,8 +224,11 @@ hammer2_trans_init(hammer2_trans_t *trans, hammer2_pfsmount_t *pmp,
                        }
                }
        }
-       if (flags & HAMMER2_TRANS_NEWINODE)
+       if (flags & HAMMER2_TRANS_NEWINODE) {
+               if (hmp->voldata.inode_tid < HAMMER2_INODE_START)
+                       hmp->voldata.inode_tid = HAMMER2_INODE_START;
                trans->inode_tid = hmp->voldata.inode_tid++;
+       }
        hammer2_voldata_unlock(hmp, 0);
 }
 
@@ -919,7 +910,7 @@ retry:
        /*
         * Issue flush.
         *
-        * A DESTROYED node that reaches this point must be flushed for
+        * A DELETED node that reaches this point must be flushed for
         * synchronization point consistency.
         *
         * Update bref.mirror_tid, clear MODIFIED, and set MOVED.
@@ -1178,53 +1169,6 @@ hammer2_chain_flush_scan1(hammer2_chain_t *child, void *data)
        hammer2_chain_unlock(parent);
        hammer2_chain_lock(child, HAMMER2_RESOLVE_MAYBE);
 
-#if 0
-       /*
-        * This isn't working atm, it seems to be causing necessary
-        * updates to be thrown away, probably due to aliasing, resulting
-        * base_insert/base_delete panics.
-        */
-       /*
-        * The DESTROYED flag can only be initially set on an unreferenced
-        * deleted inode and will propagate downward via the mechanic below.
-        * Such inode chains have been deleted for good and should no longer
-        * be subject to delete/duplication.
-        *
-        * This optimization allows the inode reclaim (destroy unlinked file
-        * on vnode reclamation after last close) to be flagged by just
-        * setting HAMMER2_CHAIN_DESTROYED at the top level and then will
-        * cause the chains to be terminated and related buffers to be
-        * invalidated and not flushed out.
-        *
-        * We have to be careful not to propagate the DESTROYED flag if
-        * the destruction occurred after our flush sync_tid.
-        */
-       if (parent->delete_tid <= trans->sync_tid &&
-           (parent->flags & HAMMER2_CHAIN_DESTROYED) &&
-           (child->flags & HAMMER2_CHAIN_DESTROYED) == 0) {
-               /*
-                * Force downward recursion by bringing update_hi up to
-                * at least sync_tid, and setting the DESTROYED flag.
-                * Parent's mirror_tid has not yet been updated.
-                *
-                * We do not mark the child DELETED because this would
-                * cause unnecessary modifications/updates.  Instead, the
-                * DESTROYED flag propagates downward and causes the flush
-                * to ignore any pre-existing modified chains.
-                *
-                * Vnode reclamation may have forced update_hi to MAX_TID
-                * (we do this because there was no transaction at the time).
-                * In this situation bring it down to something reasonable
-                * so the elements being destroyed can be retired.
-                */
-               atomic_set_int(&child->flags, HAMMER2_CHAIN_DESTROYED);
-               spin_lock(&child->core->cst.spin);
-               if (child->core->update_hi < trans->sync_tid)
-                       child->core->update_hi = trans->sync_tid;
-               spin_unlock(&child->core->cst.spin);
-       }
-#endif
-
        /*
         * No recursion needed if neither the child or anything under it
         * was messed with.
@@ -1238,6 +1182,12 @@ hammer2_chain_flush_scan1(hammer2_chain_t *child, void *data)
        }
 
        /*
+        * XXX delete child if parent is deleted.  Propagate deletion
+        *     downward.  TODO
+        */
+
+
+       /*
         * Re-check original pre-lock conditions after locking.
         */
        if (child->modify_tid > trans->sync_tid) {
@@ -1283,9 +1233,6 @@ skip:
                 * Special optimization matching similar tests done in
                 * flush_core, scan1, and scan2.  Avoid updating the block
                 * table in the parent if the parent is no longer visible.
-                * A deleted parent is no longer visible unless it's an
-                * inode (in which case it might have an open fd).. the
-                * DESTROYED flag must also be checked for inodes.
                 */
                ;
        } else if (child->delete_tid <= trans->sync_tid &&
@@ -1409,7 +1356,7 @@ hammer2_chain_flush_scan2(hammer2_chain_t *child, void *data)
        /*
         * The parent's blockref to the child must be deleted or updated.
         *
-        * This point is not reached on successful DESTROYED optimizations
+        * This point is not reached on successful DELETED optimizations
         * but can be reached on recursive deletions and restricted flushes.
         *
         * The chain_modify here may delete-duplicate the block.  This can
@@ -1475,24 +1422,12 @@ hammer2_chain_flush_scan2(hammer2_chain_t *child, void *data)
        }
 
        /*
-        * Don't bother updating a deleted + destroyed parent's blockrefs.
-        * We MUST update deleted + non-destroyed parent's blockrefs since
-        * they could represent an open file.
+        * Don't bother updating a deleted parent's blockrefs.
         *
         * Otherwise, we need to be COUNTEDBREFS synchronized for the
         * hammer2_base_*() functions.
         *
-        * NOTE: We optimize this by noting that only 'inode' chains require
-        *       this treatment.  When a file with an open descriptor is
-        *       deleted only its inode is marked deleted.  Other deletions,
-        *       such as indirect block deletions, will no longer be visible
-        *       to the live filesystem and do not need to be updated.
-        *
-        *       rm -rf's generally wind up setting DESTROYED on the way down
-        *       and the result is typically that no disk I/O is needed at all
-        *       when rm -rf'ing an entire directory topology.
-        *
-        *       This test must match the similar one in flush_core.
+        * This test must match the similar one in flush_core.
         */
 #if FLUSH_DEBUG
        kprintf("SCAN2 base=%p pass=%d PARENT %p.%d DTID=%016jx SYNC=%016jx\n",
index 59d73c4..a476063 100644 (file)
 
 #include "hammer2.h"
 
+static void hammer2_inode_move_to_hidden(hammer2_trans_t *trans,
+                                        hammer2_chain_t **chainp,
+                                        hammer2_tid_t inum);
+
 RB_GENERATE2(hammer2_inode_tree, hammer2_inode, rbnode, hammer2_inode_cmp,
             hammer2_tid_t, inum);
 
@@ -902,19 +906,17 @@ retry:
 int
 hammer2_inode_connect(hammer2_trans_t *trans, int hlink,
                      hammer2_inode_t *dip, hammer2_chain_t **chainp,
-                     const uint8_t *name, size_t name_len)
+                     const uint8_t *name, size_t name_len,
+                     hammer2_key_t lhc)
 {
        hammer2_inode_data_t *ipdata;
        hammer2_chain_t *nchain;
        hammer2_chain_t *parent;
        hammer2_chain_t *ochain;
        hammer2_key_t key_dummy;
-       hammer2_key_t lhc;
        int cache_index = -1;
        int error;
 
-       ochain = *chainp;
-
        /*
         * Since ochain is either disconnected from the topology or represents
         * a hardlink terminus which is always a parent of or equal to dip,
@@ -923,27 +925,37 @@ hammer2_inode_connect(hammer2_trans_t *trans, int hlink,
         * WARNING! Must use inode_lock_ex() on dip to handle a stale
         *          dip->chain cache.
         */
+       ochain = *chainp;
        parent = hammer2_inode_lock_ex(dip);
        /*parent = hammer2_chain_lookup_init(dip->chain, 0);*/
 
-       lhc = hammer2_dirhash(name, name_len);
-
        /*
-        * Locate the inode or indirect block to create the new
-        * entry in.  At the same time check for key collisions
-        * and iterate until we don't get one.
+        * If name is non-NULL we calculate lhc, else we use the passed-in
+        * lhc.
         */
-       error = 0;
-       while (error == 0) {
-               nchain = hammer2_chain_lookup(&parent, &key_dummy,
-                                             lhc, lhc, &cache_index, 0);
-               if (nchain == NULL)
-                       break;
-               if ((lhc & HAMMER2_DIRHASH_LOMASK) == HAMMER2_DIRHASH_LOMASK)
-                       error = ENOSPC;
-               hammer2_chain_unlock(nchain);
-               nchain = NULL;
-               ++lhc;
+       if (name) {
+               lhc = hammer2_dirhash(name, name_len);
+
+               /*
+                * Locate the inode or indirect block to create the new
+                * entry in.  At the same time check for key collisions
+                * and iterate until we don't get one.
+                */
+               error = 0;
+               while (error == 0) {
+                       nchain = hammer2_chain_lookup(&parent, &key_dummy,
+                                                     lhc, lhc,
+                                                     &cache_index, 0);
+                       if (nchain == NULL)
+                               break;
+                       if ((lhc & HAMMER2_DIRHASH_LOMASK) ==
+                           HAMMER2_DIRHASH_LOMASK) {
+                               error = ENOSPC;
+                       }
+                       hammer2_chain_unlock(nchain);
+                       nchain = NULL;
+                       ++lhc;
+               }
        }
 
        if (error == 0) {
@@ -1125,6 +1137,14 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip,
  * isdir determines whether a directory/non-directory check should be made.
  * No check is made if isdir is set to -1.
  *
+ * isopen specifies whether special unlink-with-open-descriptor handling
+ * must be performed.  If set to -1 the caller is deleting a PFS and we
+ * check whether the chain is mounted or not (chain->pmp != NULL).  1 is
+ * implied if it is mounted.
+ *
+ * If isopen is 1 and nlinks drops to 0 this function must move the chain
+ * to a special hidden directory until last-close occurs on the file.
+ *
  * NOTE!  The underlying file can still be active with open descriptors
  *       or if the chain is being manually held (e.g. for rename).
  *
@@ -1134,7 +1154,7 @@ hammer2_inode_repoint(hammer2_inode_t *ip, hammer2_inode_t *pip,
 int
 hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip,
                    const uint8_t *name, size_t name_len,
-                   int isdir, int *hlinkp)
+                   int isdir, int *hlinkp, struct nchandle *nch)
 {
        hammer2_inode_data_t *ipdata;
        hammer2_chain_t *parent;
@@ -1254,7 +1274,9 @@ hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip,
         */
        if (ochain) {
                /*
-                * Delete the original hardlink pointer.
+                * Delete the original hardlink pointer unconditionally.
+                * (any open descriptors will migrate to the hardlink
+                * target and have no affect on this operation).
                 *
                 * NOTE: parent from above is NULL when ochain != NULL
                 *       so we can reuse it.
@@ -1262,28 +1284,42 @@ hammer2_unlink_file(hammer2_trans_t *trans, hammer2_inode_t *dip,
                hammer2_chain_lock(ochain, HAMMER2_RESOLVE_ALWAYS);
                hammer2_chain_delete(trans, ochain, 0);
                hammer2_chain_unlock(ochain);
+       }
 
-               /*
-                * Then decrement nlinks on hardlink target, deleting
-                * the target when nlinks drops to 0.
-                */
-               hammer2_chain_modify(trans, &chain, 0);
-               --chain->data->ipdata.nlinks;
-               if (chain->data->ipdata.nlinks == 0)
+       /*
+        * Decrement nlinks on the hardlink target (or original file if
+        * there it was not hardlinked).  Delete the target when nlinks
+        * reaches 0 with special handling if (isopen) is set.
+        *
+        * NOTE! In DragonFly the vnops function calls cache_unlink() after
+        *       calling us here to clean out the namecache association,
+        *       (which does not represent a ref for the open-test), and to
+        *       force finalization of the vnode if/when the last ref gets
+        *       dropped.
+        */
+       hammer2_chain_modify(trans, &chain, 0);
+       ipdata = &chain->data->ipdata;
+       --ipdata->nlinks;
+       kprintf("file %s nlinks %ld\n", ipdata->filename, ipdata->nlinks);
+       if ((int64_t)ipdata->nlinks < 0)        /* XXX debugging */
+               ipdata->nlinks = 0;
+       if (ipdata->nlinks == 0) {
+               if ((chain->flags & HAMMER2_CHAIN_PFSROOT) && chain->pmp) {
+                       error = EINVAL;
+                       kprintf("hammer2: PFS \"%s\" cannot be deleted "
+                               "while still mounted\n",
+                               ipdata->filename);
+                       goto done;
+               }
+               if (nch && cache_isopen(nch)) {
+                       kprintf("WARNING: unlinking open file\n");
+                       atomic_set_int(&chain->flags, HAMMER2_CHAIN_UNLINKED);
+                       hammer2_inode_move_to_hidden(trans, &chain,
+                                                    ipdata->inum);
+               } else {
                        hammer2_chain_delete(trans, chain, 0);
-       } else {
-               /*
-                * Otherwise this was not a hardlink and we can just
-                * remove the entry and decrement nlinks.
-                *
-                * NOTE: *_get() integrates chain's lock into the inode lock.
-                */
-               hammer2_chain_modify(trans, &chain, 0);
-               ipdata = &chain->data->ipdata;
-               --ipdata->nlinks;
-               hammer2_chain_delete(trans, chain, 0);
+               }
        }
-
        error = 0;
 done:
        if (chain)
@@ -1297,6 +1333,119 @@ done:
 }
 
 /*
+ * This is called from the mount code to initialize pmp->ihidden
+ */
+void
+hammer2_inode_install_hidden(hammer2_pfsmount_t *pmp)
+{
+       hammer2_trans_t trans;
+       hammer2_chain_t *parent;
+       hammer2_chain_t *chain;
+       hammer2_chain_t *scan;
+       hammer2_inode_data_t *ipdata;
+       hammer2_key_t key_dummy;
+       hammer2_key_t key_next;
+       int cache_index;
+       int error;
+       int count;
+
+       if (pmp->ihidden)
+               return;
+
+       /*
+        * Find the hidden directory
+        */
+       bzero(&key_dummy, sizeof(key_dummy));
+       hammer2_trans_init(&trans, pmp, NULL, 0);
+
+       parent = hammer2_inode_lock_ex(pmp->iroot);
+       chain = hammer2_chain_lookup(&parent, &key_dummy,
+                                    HAMMER2_INODE_HIDDENDIR,
+                                    HAMMER2_INODE_HIDDENDIR,
+                                    &cache_index, 0);
+       if (chain) {
+               pmp->ihidden = hammer2_inode_get(pmp, pmp->iroot, chain);
+               hammer2_inode_ref(pmp->ihidden);
+
+               /*
+                * Remove any unlinked files which were left open as-of
+                * any system crash.
+                */
+               count = 0;
+               scan = hammer2_chain_lookup(&chain, &key_next,
+                                           0, HAMMER2_MAX_TID,
+                                           &cache_index,
+                                           HAMMER2_LOOKUP_NODATA);
+               while (scan) {
+                       if (scan->bref.type == HAMMER2_BREF_TYPE_INODE) {
+                               hammer2_chain_delete(&trans, scan, 0);
+                               ++count;
+                       }
+                       scan = hammer2_chain_next(&chain, scan, &key_next,
+                                                  0, HAMMER2_MAX_TID,
+                                                  &cache_index,
+                                                  HAMMER2_LOOKUP_NODATA);
+               }
+
+               hammer2_inode_unlock_ex(pmp->ihidden, chain);
+               hammer2_inode_unlock_ex(pmp->iroot, parent);
+               hammer2_trans_done(&trans);
+               kprintf("hammer2: PFS loaded hidden dir, "
+                       "removed %d dead entries\n", count);
+               return;
+       }
+
+       /*
+        * Create the hidden directory
+        */
+       error = hammer2_chain_create(&trans, &parent, &chain,
+                                    HAMMER2_INODE_HIDDENDIR, 0,
+                                    HAMMER2_BREF_TYPE_INODE,
+                                    HAMMER2_INODE_BYTES);
+       hammer2_inode_unlock_ex(pmp->iroot, parent);
+       hammer2_chain_modify(&trans, &chain, 0);
+       ipdata = &chain->data->ipdata;
+       ipdata->type = HAMMER2_OBJTYPE_DIRECTORY;
+       ipdata->inum = HAMMER2_INODE_HIDDENDIR;
+       ipdata->nlinks = 1;
+       kprintf("hammer2: PFS root missing hidden directory, creating\n");
+
+       pmp->ihidden = hammer2_inode_get(pmp, pmp->iroot, chain);
+       hammer2_inode_ref(pmp->ihidden);
+       hammer2_inode_unlock_ex(pmp->ihidden, chain);
+       hammer2_trans_done(&trans);
+}
+
+/*
+ * If an open file is unlinked H2 needs to retain the file in the topology
+ * to ensure that its backing store is not recovered by the bulk free scan.
+ * This also allows us to avoid having to special-case the CHAIN_DELETED flag.
+ *
+ * To do this the file is moved to a hidden directory in the PFS root and
+ * renamed.  The hidden directory must be created if it does not exist.
+ */
+static
+void
+hammer2_inode_move_to_hidden(hammer2_trans_t *trans, hammer2_chain_t **chainp,
+                            hammer2_tid_t inum)
+{
+       hammer2_chain_t *chain;
+       hammer2_pfsmount_t *pmp;
+       int error;
+
+       chain = *chainp;
+       pmp = chain->pmp;
+       KKASSERT(pmp != NULL);
+       KKASSERT(pmp->ihidden != NULL);
+
+       hammer2_chain_delete(trans, chain, 0);
+        error = hammer2_inode_connect(trans, 0,
+                                      pmp->ihidden, chainp,
+                                     NULL, 0, inum);
+       KKASSERT(error == 0);
+}
+
+/*
  * Given an exclusively locked inode and chain we consolidate its chain
  * for hardlink creation, adding (nlinks) to the file's link count and
  * potentially relocating the inode to a directory common to ip->pip and tdip.
index ae4b74a..76c9724 100644 (file)
@@ -548,7 +548,7 @@ hammer2_ioctl_pfs_delete(hammer2_inode_t *ip, void *data)
        hammer2_trans_init(&trans, ip->pmp, NULL, 0);
        error = hammer2_unlink_file(&trans, hmp->sroot,
                                    pfs->name, strlen(pfs->name),
-                                   2, NULL);
+                                   2, NULL, NULL);
        hammer2_trans_done(&trans);
 
        return (error);
index 012d35a..0edeacd 100644 (file)
@@ -405,6 +405,8 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
                                if (error)
                                        break;
                        }
+                       hammer2_inode_install_hidden(pmp);
+
                        return error;
                }
        }
@@ -582,7 +584,7 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
 
        kmalloc_create(&pmp->minode, "HAMMER2-inodes");
        kmalloc_create(&pmp->mmsg, "HAMMER2-pfsmsg");
-
+       lockinit(&pmp->lock, "pfslk", 0, 0);
        spin_init(&pmp->inum_spin);
        RB_INIT(&pmp->inum_tree);
 
@@ -708,6 +710,11 @@ hammer2_vfs_mount(struct mount *mp, char *path, caddr_t data,
        hammer2_cluster_reconnect(pmp, fp);
 
        /*
+        * With the cluster operational install ihidden.
+        */
+       hammer2_inode_install_hidden(pmp);
+
+       /*
         * Finish setup
         */
        vfs_getnewfsid(mp);
@@ -1428,27 +1435,35 @@ hammer2_vfs_unmount(struct mount *mp, int mntflags)
                pmp->wthread_td = NULL;
        }
 
-       for (i = 0; i < pmp->cluster.nchains; ++i) {
-               hmp = pmp->cluster.chains[i]->hmp;
-
-               hammer2_vfs_unmount_hmp1(mp, hmp);
+       /*
+        * Cleanup our reference on ihidden.
+        */
+       if (pmp->ihidden) {
+               hammer2_inode_drop(pmp->ihidden);
+               pmp->ihidden = NULL;
+       }
 
-               /*
-                * Cleanup the root and super-root chain elements
-                * (which should be clean).
-                */
-               if (pmp->iroot) {
+       /*
+        * Cleanup our reference on iroot.  iroot is (should) not be needed
+        * by the flush code.
+        */
+       if (pmp->iroot) {
 #if REPORT_REFS_ERRORS
-                       if (pmp->iroot->refs != 1)
-                               kprintf("PMP->IROOT %p REFS WRONG %d\n",
-                                       pmp->iroot, pmp->iroot->refs);
+               if (pmp->iroot->refs != 1)
+                       kprintf("PMP->IROOT %p REFS WRONG %d\n",
+                               pmp->iroot, pmp->iroot->refs);
 #else
-                       KKASSERT(pmp->iroot->refs == 1);
+               KKASSERT(pmp->iroot->refs == 1);
 #endif
-                       /* ref for pmp->iroot */
-                       hammer2_inode_drop(pmp->iroot);
-                       pmp->iroot = NULL;
-               }
+               /* ref for pmp->iroot */
+               hammer2_inode_drop(pmp->iroot);
+               pmp->iroot = NULL;
+       }
+
+       for (i = 0; i < pmp->cluster.nchains; ++i) {
+               hmp = pmp->cluster.chains[i]->hmp;
+
+               hammer2_vfs_unmount_hmp1(mp, hmp);
 
                rchain = pmp->cluster.chains[i];
                if (rchain) {
index 8297b6d..bbf3d8c 100644 (file)
@@ -277,7 +277,7 @@ hammer2_vop_inactive(struct vop_inactive_args *ap)
        /*
         * Check for deleted inodes and recycle immediately.
         */
-       if (parent->flags & HAMMER2_CHAIN_DELETED) {
+       if (parent->flags & HAMMER2_CHAIN_UNLINKED) {
                hammer2_inode_unlock_ex(ip, parent);
                vrecycle(vp);
        } else {
@@ -305,7 +305,7 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap)
                return(0);
 
        /*
-        * Set update_hi so we can detect and propagate the DESTROYED
+        * Set update_hi so we can detect and propagate the DELETED
         * bit in the flush code.
         *
         * ip->chain might be stale, correct it before checking as older
@@ -322,7 +322,7 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap)
 
        /*
         * The final close of a deleted file or directory marks it for
-        * destruction.  The DESTROYED flag allows the flusher to shortcut
+        * destruction.  The DELETED flag allows the flusher to shortcut
         * any modified blocks still unflushed (that is, just ignore them).
         *
         * HAMMER2 usually does not try to optimize the freemap by returning
@@ -335,10 +335,11 @@ hammer2_vop_reclaim(struct vop_reclaim_args *ap)
         */
        vp->v_data = NULL;
        ip->vp = NULL;
-       if (chain->flags & HAMMER2_CHAIN_DELETED) {
-               atomic_set_int(&chain->flags, HAMMER2_CHAIN_DESTROYED);
+       if (chain->flags & HAMMER2_CHAIN_UNLINKED) {
+               kprintf("unlink on reclaim: %s\n", chain->data->ipdata.filename);
                hammer2_trans_init(&trans, ip->pmp, NULL,
                                   HAMMER2_TRANS_BUFCACHE);
+               hammer2_chain_delete(&trans, chain, 0);
                hammer2_chain_setsubmod(&trans, chain);
                spin_lock(&chain->core->cst.spin);
                if (chain->core->update_hi < trans.sync_tid)
@@ -1536,7 +1537,7 @@ hammer2_vop_nlink(struct vop_nlink_args *ap)
         */
        error = hammer2_inode_connect(&trans, 1,
                                      dip, &chain,
-                                     name, name_len);
+                                     name, name_len, 0);
        if (error == 0) {
                cache_setunresolved(ap->a_nch);
                cache_setvp(ap->a_nch, ap->a_vp);
@@ -1753,11 +1754,14 @@ hammer2_vop_nremove(struct vop_nremove_args *ap)
        ncp = ap->a_nch->ncp;
        name = ncp->nc_name;
        name_len = ncp->nc_nlen;
+
        hammer2_chain_memory_wait(dip->pmp);
        hammer2_trans_init(&trans, dip->pmp, NULL, 0);
-       error = hammer2_unlink_file(&trans, dip, name, name_len, 0, NULL);
+       error = hammer2_unlink_file(&trans, dip, name, name_len,
+                                   0, NULL, ap->a_nch);
        hammer2_trans_done(&trans);
        if (error == 0) {
+               kprintf("cache_unlink\n");
                cache_unlink(ap->a_nch);
        }
        return (error);
@@ -1787,7 +1791,8 @@ hammer2_vop_nrmdir(struct vop_nrmdir_args *ap)
 
        hammer2_chain_memory_wait(dip->pmp);
        hammer2_trans_init(&trans, dip->pmp, NULL, 0);
-       error = hammer2_unlink_file(&trans, dip, name, name_len, 1, NULL);
+       error = hammer2_unlink_file(&trans, dip, name, name_len,
+                                   1, NULL, ap->a_nch);
        hammer2_trans_done(&trans);
        if (error == 0) {
                cache_unlink(ap->a_nch);
@@ -1859,7 +1864,8 @@ hammer2_vop_nrename(struct vop_nrename_args *ap)
        /*
         * Remove target if it exists
         */
-       error = hammer2_unlink_file(&trans, tdip, tname, tname_len, -1, NULL);
+       error = hammer2_unlink_file(&trans, tdip, tname, tname_len,
+                                   -1, NULL, ap->a_tnch);
        if (error && error != ENOENT)
                goto done;
        cache_setunresolved(ap->a_tnch);
@@ -1890,10 +1896,15 @@ hammer2_vop_nrename(struct vop_nrename_args *ap)
         * a hardlink the HARDLINK pointer object will be removed but the
         * hardlink will stay intact.
         *
+        * Always pass nch as NULL because we intend to reconnect the inode,
+        * so we don't want hammer2_unlink_file() to rename it to the hidden
+        * open-but-unlinked directory.
+        *
         * The target chain may be marked DELETED but will not be destroyed
         * since we retain our hold on ip and chain.
         */
-       error = hammer2_unlink_file(&trans, fdip, fname, fname_len, -1, &hlink);
+       error = hammer2_unlink_file(&trans, fdip, fname, fname_len,
+                                   -1, &hlink, NULL);
        KKASSERT(error != EAGAIN);
        if (error)
                goto done;
@@ -1915,7 +1926,7 @@ hammer2_vop_nrename(struct vop_nrename_args *ap)
        hammer2_chain_refactor(&chain);
        error = hammer2_inode_connect(&trans, hlink,
                                      tdip, &chain,
-                                     tname, tname_len);
+                                     tname, tname_len, 0);
        chain->inode_reason = 5;
        if (error == 0) {
                KKASSERT(chain != NULL);