commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[hurd] 01/02: New upstream snapshot


From: Samuel Thibault
Subject: [hurd] 01/02: New upstream snapshot
Date: Sat, 23 May 2015 05:09:40 +0000

This is an automated email from the git hooks/post-receive script.

sthibault pushed a commit to branch master
in repository hurd.

commit 9f3b30b8fc4a79edb7d978d615310f5952ed69a8
Author: Samuel Thibault <address@hidden>
Date:   Fri May 22 20:58:23 2015 +0000

    New upstream snapshot
---
 doc/hurd.texi           |  11 +-
 ext2fs/dir.c            |  79 +++++++-------
 ext2fs/ext2fs.c         |   2 -
 ext2fs/ext2fs.h         |  13 +--
 ext2fs/getblk.c         |  63 ++++++------
 ext2fs/ialloc.c         |  13 +--
 ext2fs/inode.c          | 231 +++++++++--------------------------------
 ext2fs/pager.c          |  52 +++++-----
 ext2fs/truncate.c       |  16 +--
 fatfs/dir.c             |  11 +-
 fatfs/fatfs.h           |  17 ++-
 fatfs/inode.c           | 254 ++++++---------------------------------------
 fatfs/main.c            |   6 +-
 isofs/inode.c           | 267 +++++++-----------------------------------------
 isofs/isofs.h           |  13 ++-
 isofs/lookup.c          |  18 ++--
 isofs/main.c            |  15 +--
 libdiskfs/Makefile      |   2 +-
 libdiskfs/dir-renamed.c |   2 +
 libdiskfs/diskfs.h      |  90 ++++++++++++----
 libdiskfs/init-init.c   |   2 -
 libdiskfs/node-cache.c  | 260 ++++++++++++++++++++++++++++++++++++++++++++++
 libdiskfs/node-drop.c   |  11 +-
 libdiskfs/node-make.c   |   4 +-
 libdiskfs/node-nput.c   |  52 ++++------
 libdiskfs/node-nputl.c  |  12 +--
 libdiskfs/node-nref.c   |   9 +-
 libdiskfs/node-nrefl.c  |   4 +-
 libdiskfs/node-nrele.c  |  48 +++++----
 libdiskfs/node-nrelel.c |   9 +-
 tmpfs/node.c            | 104 +++++++++++++------
 tmpfs/tmpfs.c           |   6 +-
 tmpfs/tmpfs.h           |  20 ++--
 trans/fakeroot.c        |  41 +++++++-
 34 files changed, 808 insertions(+), 949 deletions(-)

diff --git a/doc/hurd.texi b/doc/hurd.texi
index 7e7b5ee..2f36bdc 100644
--- a/doc/hurd.texi
+++ b/doc/hurd.texi
@@ -3780,10 +3780,6 @@ new thread and (eventually) get rid of the old one; the 
old thread won't
 do any more syncs, regardless.
 @end deftypefun
 
address@hidden spin_lock_t diskfs_node_refcnt_lock
-Pager reference count lock.
address@hidden deftypevar
-
 @deftypevar int diskfs_readonly
 Set to zero if the filesystem is currently writable.
 @end deftypevar
@@ -3818,9 +3814,7 @@ Every file or directory is a diskfs @dfn{node}.  The 
following functions
 help your diskfs callbacks manage nodes and their references:
 
 @deftypefun void diskfs_drop_node (@w{struct node address@hidden)
-Node @var{np} now has no more references; clean all state.  The
address@hidden must be held, and will be released upon
-return.  @var{np} must be locked.
+Node @var{np} now has no more references; clean all state.
 @end deftypefun
 
 @deftypefun void diskfs_node_update (@w{struct node address@hidden, @w{int 
@var{wait}})
@@ -4236,14 +4230,13 @@ without real users.
 @deftypefun void diskfs_try_dropping_softrefs (@w{struct node address@hidden)
 Node @var{np} has some light references, but has just lost its last hard
 references.  Take steps so that if any light references can be freed,
-they are.  Both @var{diskfs_node_refcnt_lock} and @var{np} are locked.
+they are.  @var{np} is locked.
 This function will be called after @code{diskfs_lost_hardrefs}.
 @end deftypefun
 
 @deftypefun void diskfs_node_norefs (@w{struct node address@hidden)
 Node @var{np} has no more references; free local state, including
 @address@hidden if it shouldn't be retained.
address@hidden is held.
 @end deftypefun
 
 @deftypefun error_t diskfs_set_hypermetadata (@w{int @var{wait}}, @w{int 
@var{clean}})
diff --git a/ext2fs/dir.c b/ext2fs/dir.c
index 470b7e9..6cdfba2 100644
--- a/ext2fs/dir.c
+++ b/ext2fs/dir.c
@@ -202,8 +202,8 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
 
   diskfs_set_node_atime (dp);
 
-  /* Start the lookup at DP->dn->dir_idx.  */
-  idx = dp->dn->dir_idx;
+  /* Start the lookup at diskfs_node_disknode (DP)->dir_idx.  */
+  idx = diskfs_node_disknode (dp)->dir_idx;
   if (idx * DIRBLKSIZ > dp->dn_stat.st_size)
     idx = 0;                   /* just in case */
   blockaddr = buf + idx * DIRBLKSIZ;
@@ -217,7 +217,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
       err = dirscanblock (blockaddr, dp, idx, name, namelen, type, ds, &inum);
       if (!err)
        {
-         dp->dn->dir_idx = idx;
+         diskfs_node_disknode (dp)->dir_idx = idx;
          break;
        }
       if (err != ENOENT)
@@ -306,7 +306,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
 
       /* Here below are the spec dotdot cases. */
       else if (type == RENAME || type == REMOVE)
-       np = ifind (inum);
+       np = diskfs_cached_ifind (inum);
 
       else if (type == LOOKUP)
        {
@@ -359,7 +359,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
                diskfs_nput (np);
            }
          else if (type == RENAME || type == REMOVE)
-           /* We just did ifind to get np; that allocates
+           /* We just did diskfs_cached_ifind to get np; that allocates
               no new references, so we don't have anything to do */
            ;
          else if (type == LOOKUP)
@@ -484,17 +484,17 @@ dirscanblock (vm_address_t blockaddr, struct node *dp, 
int idx,
 
       /* Because we scanned the entire block, we should write
         down how many entries there were. */
-      if (!dp->dn->dirents)
+      if (!diskfs_node_disknode (dp)->dirents)
        {
-         dp->dn->dirents = malloc ((dp->dn_stat.st_size / DIRBLKSIZ)
-                                   * sizeof (int));
+         diskfs_node_disknode (dp)->dirents =
+           malloc ((dp->dn_stat.st_size / DIRBLKSIZ) * sizeof (int));
          for (i = 0; i < dp->dn_stat.st_size/DIRBLKSIZ; i++)
-           dp->dn->dirents[i] = -1;
+           diskfs_node_disknode (dp)->dirents[i] = -1;
        }
       /* Make sure the count is correct if there is one now. */
-      assert (dp->dn->dirents[idx] == -1
-             || dp->dn->dirents[idx] == nentries);
-      dp->dn->dirents[idx] = nentries;
+      assert (diskfs_node_disknode (dp)->dirents[idx] == -1
+             || diskfs_node_disknode (dp)->dirents[idx] == nentries);
+      diskfs_node_disknode (dp)->dirents[idx] = nentries;
 
       return ENOENT;
     }
@@ -653,7 +653,7 @@ diskfs_direnter_hard (struct node *dp, const char *name, 
struct node *np,
   memcpy (new->name, name, namelen);
 
   /* Mark the directory inode has having been written.  */
-  dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+  diskfs_node_disknode (dp)->info.i_flags &= ~EXT2_BTREE_FL;
   dp->dn_set_mtime = 1;
 
   munmap ((caddr_t) ds->mapbuf, ds->mapextent);
@@ -662,33 +662,34 @@ diskfs_direnter_hard (struct node *dp, const char *name, 
struct node *np,
     {
       /* If we are keeping count of this block, then keep the count up
         to date. */
-      if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
-       dp->dn->dirents[ds->idx]++;
+      if (diskfs_node_disknode (dp)->dirents
+         && diskfs_node_disknode (dp)->dirents[ds->idx] != -1)
+       diskfs_node_disknode (dp)->dirents[ds->idx]++;
     }
   else
     {
       int i;
       /* It's cheap, so start a count here even if we aren't counting
         anything at all. */
-      if (dp->dn->dirents)
+      if (diskfs_node_disknode (dp)->dirents)
        {
-         dp->dn->dirents = realloc (dp->dn->dirents,
-                                    (dp->dn_stat.st_size / DIRBLKSIZ
-                                     * sizeof (int)));
+         diskfs_node_disknode (dp)->dirents =
+           realloc (diskfs_node_disknode (dp)->dirents,
+                    (dp->dn_stat.st_size / DIRBLKSIZ * sizeof (int)));
          for (i = oldsize / DIRBLKSIZ;
               i < dp->dn_stat.st_size / DIRBLKSIZ;
               i++)
-           dp->dn->dirents[i] = -1;
+           diskfs_node_disknode (dp)->dirents[i] = -1;
 
-         dp->dn->dirents[ds->idx] = 1;
+         diskfs_node_disknode (dp)->dirents[ds->idx] = 1;
        }
       else
        {
-         dp->dn->dirents = malloc (dp->dn_stat.st_size / DIRBLKSIZ
-                                   * sizeof (int));
+         diskfs_node_disknode (dp)->dirents =
+           malloc (dp->dn_stat.st_size / DIRBLKSIZ * sizeof (int));
          for (i = 0; i < dp->dn_stat.st_size / DIRBLKSIZ; i++)
-           dp->dn->dirents[i] = -1;
-         dp->dn->dirents[ds->idx] = 1;
+           diskfs_node_disknode (dp)->dirents[i] = -1;
+         diskfs_node_disknode (dp)->dirents[ds->idx] = 1;
        }
     }
 
@@ -720,14 +721,15 @@ diskfs_dirremove_hard (struct node *dp, struct dirstat 
*ds)
     }
 
   dp->dn_set_mtime = 1;
-  dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+  diskfs_node_disknode (dp)->info.i_flags &= ~EXT2_BTREE_FL;
 
   munmap ((caddr_t) ds->mapbuf, ds->mapextent);
 
   /* If we are keeping count of this block, then keep the count up
      to date. */
-  if (dp->dn->dirents && dp->dn->dirents[ds->idx] != -1)
-    dp->dn->dirents[ds->idx]--;
+  if (diskfs_node_disknode (dp)->dirents
+      && diskfs_node_disknode (dp)->dirents[ds->idx] != -1)
+    diskfs_node_disknode (dp)->dirents[ds->idx]--;
 
   diskfs_file_update (dp, diskfs_synchronous);
 
@@ -751,7 +753,7 @@ diskfs_dirrewrite_hard (struct node *dp, struct node *np, 
struct dirstat *ds)
 
   ds->entry->inode = np->cache_id;
   dp->dn_set_mtime = 1;
-  dp->dn->info.i_flags &= ~EXT2_BTREE_FL;
+  diskfs_node_disknode (dp)->info.i_flags &= ~EXT2_BTREE_FL;
 
   munmap ((caddr_t) ds->mapbuf, ds->mapextent);
 
@@ -831,7 +833,7 @@ count_dirents (struct node *dp, block_t nb, char *buf)
   int count = 0;
   error_t err;
 
-  assert (dp->dn->dirents);
+  assert (diskfs_node_disknode (dp)->dirents);
   assert ((nb + 1) * DIRBLKSIZ <= dp->dn_stat.st_size);
 
   err = diskfs_node_rdwr (dp, buf, nb * DIRBLKSIZ, DIRBLKSIZ, 0, 0, &amt);
@@ -848,8 +850,9 @@ count_dirents (struct node *dp, block_t nb, char *buf)
        count++;
     }
 
-  assert (dp->dn->dirents[nb] == -1 || dp->dn->dirents[nb] == count);
-  dp->dn->dirents[nb] = count;
+  assert (diskfs_node_disknode (dp)->dirents[nb] == -1
+         || diskfs_node_disknode (dp)->dirents[nb] == count);
+  diskfs_node_disknode (dp)->dirents[nb] = count;
   return 0;
 }
 
@@ -884,11 +887,11 @@ diskfs_get_directs (struct node *dp,
 
   nblks = dp->dn_stat.st_size/DIRBLKSIZ;
 
-  if (!dp->dn->dirents)
+  if (!diskfs_node_disknode (dp)->dirents)
     {
-      dp->dn->dirents = malloc (nblks * sizeof (int));
+      diskfs_node_disknode (dp)->dirents = malloc (nblks * sizeof (int));
       for (i = 0; i < nblks; i++)
-       dp->dn->dirents[i] = -1;
+       diskfs_node_disknode (dp)->dirents[i] = -1;
     }
 
   /* Scan through the entries to find ENTRY.  If we encounter
@@ -898,7 +901,7 @@ diskfs_get_directs (struct node *dp,
   bufvalid = 0;
   for (blkno = 0; blkno < nblks; blkno++)
     {
-      if (dp->dn->dirents[blkno] == -1)
+      if (diskfs_node_disknode (dp)->dirents[blkno] == -1)
        {
          err = count_dirents (dp, blkno, buf);
          if (err)
@@ -906,11 +909,11 @@ diskfs_get_directs (struct node *dp,
          bufvalid = 1;
        }
 
-      if (curentry + dp->dn->dirents[blkno] > entry)
+      if (curentry + diskfs_node_disknode (dp)->dirents[blkno] > entry)
        /* ENTRY starts in this block. */
        break;
 
-      curentry += dp->dn->dirents[blkno];
+      curentry += diskfs_node_disknode (dp)->dirents[blkno];
 
       bufvalid = 0;
     }
diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c
index beb7cad..d0fdfe7 100644
--- a/ext2fs/ext2fs.c
+++ b/ext2fs/ext2fs.c
@@ -185,8 +185,6 @@ main (int argc, char **argv)
 
   map_hypermetadata ();
 
-  inode_init ();
-
   /* Set diskfs_root_node to the root inode. */
   err = diskfs_cached_lookup (EXT2_ROOT_INO, &diskfs_root_node);
   if (err)
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
index 3422af2..96d8e9d 100644
--- a/ext2fs/ext2fs.h
+++ b/ext2fs/ext2fs.h
@@ -159,9 +159,6 @@ struct disknode
      each DIRBLKSIZE piece of the directory. */
   int *dirents;
 
-  /* Links on hash list. */
-  struct node *hnext, **hprevp;
-
   /* Lock to lock while fiddling with this inode's block allocation info.  */
   pthread_rwlock_t alloc_lock;
 
@@ -419,12 +416,6 @@ dino_deref (struct ext2_inode *inode)
 
 /* Write all active disknodes into the inode pager. */
 void write_all_disknodes ();
-
-/* Lookup node INUM (which must have a reference already) and return it
-   without allocating any new references. */
-struct node *ifind (ino_t inum);
-
-void inode_init (void);
 
 /* ---------------------------------------------------------------- */
 
@@ -503,7 +494,7 @@ record_indir_poke (struct node *node, void *ptr)
   ext2_debug ("(%llu, %p)", node->cache_id, ptr);
   assert (disk_cache_block_is_ref (block));
   global_block_modified (block);
-  pokel_add (&node->dn->indir_pokel, block_ptr, block_size);
+  pokel_add (&diskfs_node_disknode (node)->indir_pokel, block_ptr, block_size);
 }
 
 /* ---------------------------------------------------------------- */
@@ -524,7 +515,7 @@ alloc_sync (struct node *np)
       if (np)
        {
          diskfs_node_update (np, 1);
-         pokel_sync (&np->dn->indir_pokel, 1);
+         pokel_sync (&diskfs_node_disknode (np)->indir_pokel, 1);
        }
       diskfs_set_hypermetadata (1, 0);
     }
diff --git a/ext2fs/getblk.c b/ext2fs/getblk.c
index d7ddb6a..0d0fab1 100644
--- a/ext2fs/getblk.c
+++ b/ext2fs/getblk.c
@@ -49,13 +49,13 @@ void
 ext2_discard_prealloc (struct node *node)
 {
 #ifdef EXT2_PREALLOCATE
-  if (node->dn->info.i_prealloc_count)
+  if (diskfs_node_disknode (node)->info.i_prealloc_count)
     {
-      int i = node->dn->info.i_prealloc_count;
+      int i = diskfs_node_disknode (node)->info.i_prealloc_count;
       ext2_debug ("discarding %d prealloced blocks for inode %d",
                  i, node->cache_id);
-      node->dn->info.i_prealloc_count = 0;
-      ext2_free_blocks (node->dn->info.i_prealloc_block, i);
+      diskfs_node_disknode (node)->info.i_prealloc_count = 0;
+      ext2_free_blocks (diskfs_node_disknode (node)->info.i_prealloc_block, i);
     }
 #endif
 }
@@ -72,12 +72,12 @@ ext2_alloc_block (struct node *node, block_t goal, int zero)
   block_t result;
 
 #ifdef EXT2_PREALLOCATE
-  if (node->dn->info.i_prealloc_count &&
-      (goal == node->dn->info.i_prealloc_block ||
-       goal + 1 == node->dn->info.i_prealloc_block))
+  if (diskfs_node_disknode (node)->info.i_prealloc_count &&
+      (goal == diskfs_node_disknode (node)->info.i_prealloc_block ||
+       goal + 1 == diskfs_node_disknode (node)->info.i_prealloc_block))
     {
-      result = node->dn->info.i_prealloc_block++;
-      node->dn->info.i_prealloc_count--;
+      result = diskfs_node_disknode (node)->info.i_prealloc_block++;
+      diskfs_node_disknode (node)->info.i_prealloc_count--;
       ext2_debug ("preallocation hit (%lu/%lu) => %u",
                  ++alloc_hits, ++alloc_attempts, result);
     }
@@ -95,8 +95,8 @@ ext2_alloc_block (struct node *node, block_t goal, int zero)
                                       EXT2_FEATURE_COMPAT_DIR_PREALLOC))
         ? sblock->s_prealloc_dir_blocks
         : 0,
-        &node->dn->info.i_prealloc_count,
-        &node->dn->info.i_prealloc_block);
+        &diskfs_node_disknode (node)->info.i_prealloc_count,
+        &diskfs_node_disknode (node)->info.i_prealloc_block);
     }
 #else
   result = ext2_new_block (goal, 0, 0);
@@ -124,15 +124,15 @@ inode_getblk (struct node *node, int nr, int create, int 
zero,
 
   assert (0 <= nr && nr < EXT2_N_BLOCKS);
 
-  *result = node->dn->info.i_data[nr];
+  *result = diskfs_node_disknode (node)->info.i_data[nr];
   if (*result)
     return 0;
 
   if (!create)
     return EINVAL;
 
-  if (node->dn->info.i_next_alloc_block == new_block)
-    goal = node->dn->info.i_next_alloc_goal;
+  if (diskfs_node_disknode (node)->info.i_next_alloc_block == new_block)
+    goal = diskfs_node_disknode (node)->info.i_next_alloc_goal;
 
 #ifdef EXT2FS_DEBUG
   hint = goal;
@@ -142,15 +142,16 @@ inode_getblk (struct node *node, int nr, int create, int 
zero,
     {
       for (i = nr - 1; i >= 0; i--)
        {
-         if (node->dn->info.i_data[i])
+         if (diskfs_node_disknode (node)->info.i_data[i])
            {
-             goal = node->dn->info.i_data[i];
+             goal = diskfs_node_disknode (node)->info.i_data[i];
              break;
            }
        }
       if (!goal)
        goal =
-         (node->dn->info.i_block_group * EXT2_BLOCKS_PER_GROUP (sblock))
+         (diskfs_node_disknode (node)->info.i_block_group
+           * EXT2_BLOCKS_PER_GROUP (sblock))
          + sblock->s_first_data_block;
     }
 
@@ -162,15 +163,15 @@ inode_getblk (struct node *node, int nr, int create, int 
zero,
   if (!*result)
     return ENOSPC;
 
-  node->dn->info.i_data[nr] = *result;
+  diskfs_node_disknode (node)->info.i_data[nr] = *result;
 
-  node->dn->info.i_next_alloc_block = new_block;
-  node->dn->info.i_next_alloc_goal = *result;
+  diskfs_node_disknode (node)->info.i_next_alloc_block = new_block;
+  diskfs_node_disknode (node)->info.i_next_alloc_goal = *result;
   node->dn_set_ctime = node->dn_set_mtime = 1;
   node->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
   node->dn_stat_dirty = 1;
 
-  if (diskfs_synchronous || node->dn->info.i_osync)
+  if (diskfs_synchronous || diskfs_node_disknode (node)->info.i_osync)
     diskfs_node_update (node, 1);
 
   return 0;
@@ -197,8 +198,8 @@ block_getblk (struct node *node, block_t block, int nr, int 
create, int zero,
       return EINVAL;
     }
 
-  if (node->dn->info.i_next_alloc_block == new_block)
-    goal = node->dn->info.i_next_alloc_goal;
+  if (diskfs_node_disknode (node)->info.i_next_alloc_block == new_block)
+    goal = diskfs_node_disknode (node)->info.i_next_alloc_goal;
   if (!goal)
     {
       for (i = nr - 1; i >= 0; i--)
@@ -222,13 +223,13 @@ block_getblk (struct node *node, block_t block, int nr, 
int create, int zero,
 
   bh[nr] = *result;
 
-  if (diskfs_synchronous || node->dn->info.i_osync)
+  if (diskfs_synchronous || diskfs_node_disknode (node)->info.i_osync)
     sync_global_ptr (bh, 1);
   else
     record_indir_poke (node, bh);
 
-  node->dn->info.i_next_alloc_block = new_block;
-  node->dn->info.i_next_alloc_goal = *result;
+  diskfs_node_disknode (node)->info.i_next_alloc_block = new_block;
+  diskfs_node_disknode (node)->info.i_next_alloc_goal = *result;
   node->dn_set_ctime = node->dn_set_mtime = 1;
   node->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
   node->dn_stat_dirty = 1;
@@ -260,13 +261,13 @@ ext2_getblk (struct node *node, block_t block, int 
create, block_t *disk_block)
    */
 
   ext2_debug ("block = %u, next = %u, goal = %u", block,
-             node->dn->info.i_next_alloc_block,
-             node->dn->info.i_next_alloc_goal);
+             diskfs_node_disknode (node)->info.i_next_alloc_block,
+             diskfs_node_disknode (node)->info.i_next_alloc_goal);
 
-  if (block == node->dn->info.i_next_alloc_block + 1)
+  if (block == diskfs_node_disknode (node)->info.i_next_alloc_block + 1)
     {
-      node->dn->info.i_next_alloc_block++;
-      node->dn->info.i_next_alloc_goal++;
+      diskfs_node_disknode (node)->info.i_next_alloc_block++;
+      diskfs_node_disknode (node)->info.i_next_alloc_goal++;
     }
 
   b = block;
diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c
index 52212d5..7cfc4f3 100644
--- a/ext2fs/ialloc.c
+++ b/ext2fs/ialloc.c
@@ -316,14 +316,14 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct 
node **node)
     }
   /* Zero out the block pointers in case there's some noise left on disk.  */
   for (block = 0; block < EXT2_N_BLOCKS; block++)
-    if (np->dn->info.i_data[block] != 0)
+    if (diskfs_node_disknode (np)->info.i_data[block] != 0)
       {
-       np->dn->info.i_data[block] = 0;
+       diskfs_node_disknode (np)->info.i_data[block] = 0;
        np->dn_set_ctime = 1;
       }
-  if (np->dn->info_i_translator != 0)
+  if (diskfs_node_disknode (np)->info_i_translator != 0)
     {
-      np->dn->info_i_translator = 0;
+      diskfs_node_disknode (np)->info_i_translator = 0;
       np->dn_set_ctime = 1;
     }
   st->st_mode &= ~S_IPTRANS;
@@ -335,8 +335,9 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct 
node **node)
     }
 
   /* Propagate initial inode flags from the directory, as Linux does.  */
-  np->dn->info.i_flags =
-    ext2_mask_flags(mode, dir->dn->info.i_flags & EXT2_FL_INHERITED);
+  diskfs_node_disknode (np)->info.i_flags =
+    ext2_mask_flags(mode,
+              diskfs_node_disknode (dir)->info.i_flags & EXT2_FL_INHERITED);
 
   st->st_flags = 0;
 
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
index 6b8b749..d83bedc 100644
--- a/ext2fs/inode.c
+++ b/ext2fs/inode.c
@@ -39,118 +39,31 @@
 #define UF_IMMUTABLE 0
 #endif
 
-#define        INOHSZ  8192
-#if    ((INOHSZ&(INOHSZ-1)) == 0)
-#define        INOHASH(ino)    ((ino)&(INOHSZ-1))
-#else
-#define        INOHASH(ino)    (((unsigned)(ino))%INOHSZ)
-#endif
-
-static struct node *nodehash[INOHSZ];
-static size_t nodehash_nr_items;
-
-static error_t read_node (struct node *np);
-
 pthread_spinlock_t generation_lock = PTHREAD_SPINLOCK_INITIALIZER;
 
-/* Initialize the inode hash table. */
-void
-inode_init ()
-{
-  int n;
-  for (n = 0; n < INOHSZ; n++)
-    nodehash[n] = 0;
-}
-
-/* Fetch inode INUM, set *NPP to the node structure;
-   gain one user reference and lock the node.  */
+/* The user must define this function if she wants to use the node
+   cache.  Create and initialize a node.  */
 error_t
-diskfs_cached_lookup (ino_t inum, struct node **npp)
+diskfs_user_make_node (struct node **npp, struct lookup_context *ctx)
 {
-  error_t err;
   struct node *np;
   struct disknode *dn;
 
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
-    if (np->cache_id == inum)
-      {
-       np->references++;
-       pthread_spin_unlock (&diskfs_node_refcnt_lock);
-       pthread_mutex_lock (&np->lock);
-       *npp = np;
-       return 0;
-      }
+  /* Create the new node.  */
+  np = diskfs_make_node_alloc (sizeof *dn);
+  if (np == NULL)
+    return ENOMEM;
 
   /* Format specific data for the new node.  */
-  dn = malloc (sizeof (struct disknode));
-  if (! dn)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return ENOMEM;
-    }
+  dn = diskfs_node_disknode (np);
   dn->dirents = 0;
   dn->dir_idx = 0;
   dn->pager = 0;
   pthread_rwlock_init (&dn->alloc_lock, NULL);
   pokel_init (&dn->indir_pokel, diskfs_disk_pager, disk_cache);
 
-  /* Create the new node.  */
-  np = diskfs_make_node (dn);
-  np->cache_id = inum;
-
-  pthread_mutex_lock (&np->lock);
-
-  /* Put NP in NODEHASH.  */
-  dn->hnext = nodehash[INOHASH(inum)];
-  if (dn->hnext)
-    dn->hnext->dn->hprevp = &dn->hnext;
-  dn->hprevp = &nodehash[INOHASH(inum)];
-  nodehash[INOHASH(inum)] = np;
-  nodehash_nr_items += 1;
-
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
-  /* Get the contents of NP off disk.  */
-  err = read_node (np);
-
-  if (!diskfs_check_readonly () && !np->dn_stat.st_gen)
-    {
-      pthread_spin_lock (&generation_lock);
-      if (++next_generation < diskfs_mtime->seconds)
-       next_generation = diskfs_mtime->seconds;
-      np->dn_stat.st_gen = next_generation;
-      pthread_spin_unlock (&generation_lock);
-      np->dn_set_ctime = 1;
-    }
-
-  if (err)
-    return err;
-  else
-    {
-      *npp = np;
-      return 0;
-    }
-}
-
-/* Lookup node INUM (which must have a reference already) and return it
-   without allocating any new references. */
-struct node *
-ifind (ino_t inum)
-{
-  struct node *np;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
-    {
-      if (np->cache_id != inum)
-       continue;
-
-      assert (np->references);
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return np;
-    }
-  assert (0);
+  *npp = np;
+  return 0;
 }
 
 /* The last reference to a node has gone away; drop
@@ -158,27 +71,22 @@ ifind (ino_t inum)
 void
 diskfs_node_norefs (struct node *np)
 {
-  *np->dn->hprevp = np->dn->hnext;
-  if (np->dn->hnext)
-    np->dn->hnext->dn->hprevp = np->dn->hprevp;
-  nodehash_nr_items -= 1;
-
-  if (np->dn->dirents)
-    free (np->dn->dirents);
-  assert (!np->dn->pager);
+  if (diskfs_node_disknode (np)->dirents)
+    free (diskfs_node_disknode (np)->dirents);
+  assert (!diskfs_node_disknode (np)->pager);
 
   /* Move any pending writes of indirect blocks.  */
-  pokel_inherit (&global_pokel, &np->dn->indir_pokel);
-  pokel_finalize (&np->dn->indir_pokel);
+  pokel_inherit (&global_pokel, &diskfs_node_disknode (np)->indir_pokel);
+  pokel_finalize (&diskfs_node_disknode (np)->indir_pokel);
 
-  free (np->dn);
   free (np);
 }
 
-/* The last hard reference to a node has gone away; arrange to have
-   all the weak references dropped that can be. */
+/* The user must define this function if she wants to use the node
+   cache.  The last hard reference to a node has gone away; arrange to
+   have all the weak references dropped that can be.  */
 void
-diskfs_try_dropping_softrefs (struct node *np)
+diskfs_user_try_dropping_softrefs (struct node *np)
 {
   drop_pager_softrefs (np);
 }
@@ -197,13 +105,14 @@ diskfs_new_hardrefs (struct node *np)
   allow_pager_softrefs (np);
 }
 
-/* Read stat information out of the ext2_inode. */
-static error_t
-read_node (struct node *np)
+/* The user must define this function if she wants to use the node
+   cache.  Read stat information out of the on-disk node.  */
+error_t
+diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
 {
   error_t err;
   struct stat *st = &np->dn_stat;
-  struct disknode *dn = np->dn;
+  struct disknode *dn = diskfs_node_disknode (np);
   struct ext2_inode *di;
   struct ext2_inode_info *info = &dn->info;
 
@@ -339,6 +248,16 @@ read_node (struct node *np)
        linux, some devices).  */
     np->allocsize = 0;
 
+  if (!diskfs_check_readonly () && !np->dn_stat.st_gen)
+    {
+      pthread_spin_lock (&generation_lock);
+      if (++next_generation < diskfs_mtime->seconds)
+       next_generation = diskfs_mtime->seconds;
+      np->dn_stat.st_gen = next_generation;
+      pthread_spin_unlock (&generation_lock);
+      np->dn_set_ctime = 1;
+    }
+
   return 0;
 }
 
@@ -356,7 +275,7 @@ check_high_bits (struct node *np, long l)
      kernels".  Note that our check refuses to change the values, while
      Linux 2.3.42 just silently clears the high bits in an inode it updates,
      even if it was updating it for an unrelated reason.  */
-  if (np->dn->info.i_dtime != 0)
+  if (diskfs_node_disknode (np)->info.i_dtime != 0)
     return 0;
 
   return ((l & ~0xFFFF) == 0) ? 0 : EINVAL;
@@ -422,12 +341,12 @@ write_node (struct node *np)
 
   ext2_debug ("(%llu)", np->cache_id);
 
-  if (np->dn->info.i_prealloc_count)
+  if (diskfs_node_disknode (np)->info.i_prealloc_count)
     ext2_discard_prealloc (np);
 
   if (np->dn_stat_dirty)
     {
-      struct ext2_inode_info *info = &np->dn->info;
+      struct ext2_inode_info *info = &diskfs_node_disknode (np)->info;
 
       assert (!diskfs_readonly);
 
@@ -513,7 +432,7 @@ write_node (struct node *np)
       if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
        di->i_block[0] = st->st_rdev;
       else
-       memcpy (di->i_block, np->dn->info.i_data,
+       memcpy (di->i_block, diskfs_node_disknode (np)->info.i_data,
                EXT2_N_BLOCKS * sizeof di->i_block[0]);
 
       diskfs_end_catch_exception ();
@@ -531,7 +450,7 @@ write_node (struct node *np)
 error_t
 diskfs_node_reload (struct node *node)
 {
-  struct disknode *dn = node->dn;
+  struct disknode *dn = diskfs_node_disknode (node);
 
   if (dn->dirents)
     {
@@ -540,68 +459,11 @@ diskfs_node_reload (struct node *node)
     }
   pokel_flush (&dn->indir_pokel);
   flush_node_pager (node);
-  read_node (node);
+  diskfs_user_read_node (node, NULL);
 
   return 0;
 }
 
-/* For each active node, call FUN.  The node is to be locked around the call
-   to FUN.  If FUN returns non-zero for any node, then immediately stop, and
-   return that value.  */
-error_t
-diskfs_node_iterate (error_t (*fun)(struct node *))
-{
-  error_t err = 0;
-  int n;
-  size_t num_nodes;
-  struct node *node, **node_list, **p;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-
-  /* We must copy everything from the hash table into another data structure
-     to avoid running into any problems with the hash-table being modified
-     during processing (normally we delegate access to hash-table with
-     diskfs_node_refcnt_lock, but we can't hold this while locking the
-     individual node locks).  */
-  num_nodes = nodehash_nr_items;
-
-  /* TODO This method doesn't scale beyond a few dozen nodes and should be
-     replaced.  */
-  node_list = malloc (num_nodes * sizeof (struct node *));
-  if (node_list == NULL)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      ext2_debug ("unable to allocate temporary node table");
-      return ENOMEM;
-    }
-
-  p = node_list;
-  for (n = 0; n < INOHSZ; n++)
-    for (node = nodehash[n]; node; node = node->dn->hnext)
-      {
-       *p++ = node;
-       node->references++;
-      }
-
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
-  p = node_list;
-  while (num_nodes-- > 0)
-    {
-      node = *p++;
-      if (!err)
-       {
-         pthread_mutex_lock (&node->lock);
-         err = (*fun)(node);
-         pthread_mutex_unlock (&node->lock);
-       }
-      diskfs_nrele (node);
-    }
-
-  free (node_list);
-  return err;
-}
-
 /* Write all active disknodes into the ext2_inode pager. */
 void
 write_all_disknodes ()
@@ -612,7 +474,7 @@ write_all_disknodes ()
 
       /* Sync the indirect blocks here; they'll all be done before any
         inodes.  Waiting for them shouldn't be too bad.  */
-      pokel_sync (&node->dn->indir_pokel, 1);
+      pokel_sync (&diskfs_node_disknode (node)->indir_pokel, 1);
 
       diskfs_set_node_times (node);
 
@@ -694,7 +556,7 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
     {
       /* Allocate block for translator */
       blkno =
-       ext2_new_block ((np->dn->info.i_block_group
+       ext2_new_block ((diskfs_node_disknode (np)->info.i_block_group
                         * EXT2_BLOCKS_PER_GROUP (sblock))
                        + sblock->s_first_data_block,
                        0, 0, 0);
@@ -706,7 +568,7 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
        }
 
       di->i_translator = blkno;
-      np->dn->info_i_translator = blkno;
+      diskfs_node_disknode (np)->info_i_translator = blkno;
       record_global_poke (di);
 
       np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
@@ -716,7 +578,7 @@ diskfs_set_translator (struct node *np, const char *name, 
unsigned namelen,
     {
       /* Clear block for translator going away. */
       di->i_translator = 0;
-      np->dn->info_i_translator = 0;
+      diskfs_node_disknode (np)->info_i_translator = 0;
       record_global_poke (di);
       ext2_free_blocks (blkno, 1);
 
@@ -805,7 +667,7 @@ write_symlink (struct node *node, const char *target)
 
   assert (node->dn_stat.st_blocks == 0);
 
-  memcpy (node->dn->info.i_data, target, len);
+  memcpy (diskfs_node_disknode (node)->info.i_data, target, len);
   node->dn_stat.st_size = len - 1;
   node->dn_set_ctime = 1;
   node->dn_set_mtime = 1;
@@ -822,7 +684,8 @@ read_symlink (struct node *node, char *target)
 
   assert (node->dn_stat.st_size < MAX_INODE_SYMLINK);
 
-  memcpy (target, node->dn->info.i_data, node->dn_stat.st_size);
+  memcpy (target, diskfs_node_disknode (node)->info.i_data,
+          node->dn_stat.st_size);
   return 0;
 }
 
diff --git a/ext2fs/pager.c b/ext2fs/pager.c
index a6c4fbb..b56c923 100644
--- a/ext2fs/pager.c
+++ b/ext2fs/pager.c
@@ -137,7 +137,7 @@ find_block (struct node *node, vm_offset_t offset,
 
   if (!*lock)
     {
-      *lock = &node->dn->alloc_lock;
+      *lock = &diskfs_node_disknode (node)->alloc_lock;
       pthread_rwlock_rdlock (*lock);
     }
 
@@ -279,7 +279,7 @@ file_pager_read_page (struct node *node, vm_offset_t page,
     err = do_pending_reads();
 
   if (!err && partial && !*writelock)
-    node->dn->last_page_partially_writable = 1;
+    diskfs_node_disknode (node)->last_page_partially_writable = 1;
 
   if (lock)
     pthread_rwlock_unlock (lock);
@@ -377,16 +377,16 @@ file_pager_write_page (struct node *node, vm_offset_t 
offset, void *buf)
 {
   error_t err = 0;
   struct pending_blocks pb;
-  pthread_rwlock_t *lock = &node->dn->alloc_lock;
+  pthread_rwlock_t *lock = &diskfs_node_disknode (node)->alloc_lock;
   block_t block;
   int left = vm_page_size;
 
   pending_blocks_init (&pb, buf);
 
-  /* Holding NODE->dn->alloc_lock effectively locks NODE->allocsize,
+  /* Holding diskfs_node_disknode (node)->alloc_lock effectively locks 
NODE->allocsize,
      at least for the cases we care about: pager_unlock_page,
      diskfs_grow and diskfs_truncate.  */
-  pthread_rwlock_rdlock (&node->dn->alloc_lock);
+  pthread_rwlock_rdlock (&diskfs_node_disknode (node)->alloc_lock);
 
   if (offset >= node->allocsize)
     left = 0;
@@ -411,7 +411,7 @@ file_pager_write_page (struct node *node, vm_offset_t 
offset, void *buf)
   if (!err)
     pending_blocks_write (&pb);
 
-  pthread_rwlock_unlock (&node->dn->alloc_lock);
+  pthread_rwlock_unlock (&diskfs_node_disknode (node)->alloc_lock);
 
   return err;
 }
@@ -583,7 +583,7 @@ pager_unlock_page (struct user_pager_info *pager, 
vm_offset_t page)
       error_t err;
       volatile int partial_page;
       struct node *node = pager->node;
-      struct disknode *dn = node->dn;
+      struct disknode *dn = diskfs_node_disknode (node);
 
       pthread_rwlock_wrlock (&dn->alloc_lock);
 
@@ -656,7 +656,7 @@ diskfs_grow (struct node *node, off_t size, struct protid 
*cred)
       volatile off_t new_size;
       volatile block_t end_block;
       block_t new_end_block;
-      struct disknode *dn = node->dn;
+      struct disknode *dn = diskfs_node_disknode (node);
 
       pthread_rwlock_wrlock (&dn->alloc_lock);
 
@@ -741,7 +741,7 @@ diskfs_file_update (struct node *node, int wait)
   struct pager *pager;
 
   pthread_spin_lock (&node_to_page_lock);
-  pager = node->dn->pager;
+  pager = diskfs_node_disknode (node)->pager;
   if (pager)
     ports_port_ref (pager);
   pthread_spin_unlock (&node_to_page_lock);
@@ -752,7 +752,7 @@ diskfs_file_update (struct node *node, int wait)
       ports_port_deref (pager);
     }
 
-  pokel_sync (&node->dn->indir_pokel, wait);
+  pokel_sync (&diskfs_node_disknode (node)->indir_pokel, wait);
 
   diskfs_node_update (node, wait);
 }
@@ -762,7 +762,7 @@ void
 flush_node_pager (struct node *node)
 {
   struct pager *pager;
-  struct disknode *dn = node->dn;
+  struct disknode *dn = diskfs_node_disknode (node);
 
   pthread_spin_lock (&node_to_page_lock);
   pager = dn->pager;
@@ -806,9 +806,9 @@ pager_clear_user_data (struct user_pager_info *upi)
       struct pager *pager;
 
       pthread_spin_lock (&node_to_page_lock);
-      pager = upi->node->dn->pager;
+      pager = diskfs_node_disknode (upi->node)->pager;
       if (pager && pager_get_upi (pager) == upi)
-       upi->node->dn->pager = 0;
+       diskfs_node_disknode (upi->node)->pager = 0;
       pthread_spin_unlock (&node_to_page_lock);
 
       diskfs_nrele_light (upi->node);
@@ -1236,7 +1236,7 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
   pthread_spin_lock (&node_to_page_lock);
   do
     {
-      struct pager *pager = node->dn->pager;
+      struct pager *pager = diskfs_node_disknode (node)->pager;
       if (pager)
        {
          /* Because PAGER is not a real reference,
@@ -1245,9 +1245,9 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
             and loop.  The deallocation will complete separately. */
          right = pager_get_port (pager);
          if (right == MACH_PORT_NULL)
-           node->dn->pager = 0;
-         else
-           pager_get_upi (pager)->max_prot |= prot;
+           diskfs_node_disknode (node)->pager = 0;
+         else
+           pager_get_upi (pager)->max_prot |= prot;
        }
       else
        {
@@ -1257,10 +1257,10 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
          upi->node = node;
          upi->max_prot = prot;
          diskfs_nref_light (node);
-         node->dn->pager =
-           pager_create (upi, file_pager_bucket, MAY_CACHE,
-                         MEMORY_OBJECT_COPY_DELAY, 0);
-         if (node->dn->pager == 0)
+         diskfs_node_disknode (node)->pager =
+                   pager_create (upi, file_pager_bucket, MAY_CACHE,
+                                 MEMORY_OBJECT_COPY_DELAY, 0);
+         if (diskfs_node_disknode (node)->pager == 0)
            {
              diskfs_nrele_light (node);
              free (upi);
@@ -1268,8 +1268,8 @@ diskfs_get_filemap (struct node *node, vm_prot_t prot)
              return MACH_PORT_NULL;
            }
 
-         right = pager_get_port (node->dn->pager);
-         ports_port_deref (node->dn->pager);
+         right = pager_get_port (diskfs_node_disknode (node)->pager);
+         ports_port_deref (diskfs_node_disknode (node)->pager);
        }
     }
   while (right == MACH_PORT_NULL);
@@ -1289,7 +1289,7 @@ drop_pager_softrefs (struct node *node)
   struct pager *pager;
 
   pthread_spin_lock (&node_to_page_lock);
-  pager = node->dn->pager;
+  pager = diskfs_node_disknode (node)->pager;
   if (pager)
     ports_port_ref (pager);
   pthread_spin_unlock (&node_to_page_lock);
@@ -1311,7 +1311,7 @@ allow_pager_softrefs (struct node *node)
   struct pager *pager;
 
   pthread_spin_lock (&node_to_page_lock);
-  pager = node->dn->pager;
+  pager = diskfs_node_disknode (node)->pager;
   if (pager)
     ports_port_ref (pager);
   pthread_spin_unlock (&node_to_page_lock);
@@ -1331,7 +1331,7 @@ diskfs_get_filemap_pager_struct (struct node *node)
 {
   /* This is safe because pager can't be cleared; there must be
      an active mapping for this to be called. */
-  return node->dn->pager;
+  return diskfs_node_disknode (node)->pager;
 }
 
 /* Shutdown all the pagers (except the disk pager). */
diff --git a/ext2fs/truncate.c b/ext2fs/truncate.c
index 63d2295..15e5541 100644
--- a/ext2fs/truncate.c
+++ b/ext2fs/truncate.c
@@ -100,7 +100,7 @@ free_block_run_finish (struct free_block_run *fbr)
 static void
 trunc_direct (struct node *node, block_t end, struct free_block_run *fbr)
 {
-  block_t *blocks = node->dn->info.i_data;
+  block_t *blocks = diskfs_node_disknode (node)->info.i_data;
 
   ext2_debug ("truncating direct blocks from %d", end);
 
@@ -230,7 +230,7 @@ force_delayed_copies (struct node *node, off_t length)
   struct pager *pager;
 
   pthread_spin_lock (&node_to_page_lock);
-  pager = node->dn->pager;
+  pager = diskfs_node_disknode (node)->pager;
   if (pager)
     ports_port_ref (pager);
   pthread_spin_unlock (&node_to_page_lock);
@@ -260,7 +260,7 @@ enable_delayed_copies (struct node *node)
   struct pager *pager;
 
   pthread_spin_lock (&node_to_page_lock);
-  pager = node->dn->pager;
+  pager = diskfs_node_disknode (node)->pager;
   if (pager)
     ports_port_ref (pager);
   pthread_spin_unlock (&node_to_page_lock);
@@ -322,7 +322,7 @@ diskfs_truncate (struct node *node, off_t length)
 
   force_delayed_copies (node, length);
 
-  pthread_rwlock_wrlock (&node->dn->alloc_lock);
+  pthread_rwlock_wrlock (&diskfs_node_disknode (node)->alloc_lock);
 
   /* Update the size on disk; fsck will finish freeing blocks if necessary
      should we crash. */
@@ -335,7 +335,7 @@ diskfs_truncate (struct node *node, off_t length)
   if (!err)
     {
       block_t end = boffs_block (round_block (length)), offs;
-      block_t *bptrs = node->dn->info.i_data;
+      block_t *bptrs = diskfs_node_disknode (node)->info.i_data;
       struct free_block_run fbr;
 
       free_block_run_init (&fbr, node);
@@ -355,8 +355,8 @@ diskfs_truncate (struct node *node, off_t length)
 
       /* Set our last_page_partially_writable to a pessimistic state -- it
         won't hurt if is wrong.  */
-      node->dn->last_page_partially_writable =
-       trunc_page (node->allocsize) != node->allocsize;
+      diskfs_node_disknode (node)->last_page_partially_writable =
+               trunc_page (node->allocsize) != node->allocsize;
 
       diskfs_end_catch_exception ();
     }
@@ -368,7 +368,7 @@ diskfs_truncate (struct node *node, off_t length)
   /* Now we can permit delayed copies again. */
   enable_delayed_copies (node);
 
-  pthread_rwlock_unlock (&node->dn->alloc_lock);
+  pthread_rwlock_unlock (&diskfs_node_disknode (node)->alloc_lock);
 
   return err;
 }
diff --git a/fatfs/dir.c b/fatfs/dir.c
index 5a38c63..66c95d2 100644
--- a/fatfs/dir.c
+++ b/fatfs/dir.c
@@ -342,7 +342,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
 
       /* Here below are the spec dotdot cases.  */
       else if (type == RENAME || type == REMOVE)
-        np = ifind (inum);
+        np = diskfs_cached_ifind (inum);
 
       else if (type == LOOKUP)
         {
@@ -395,7 +395,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
                 diskfs_nput (np);
             }
           else if (type == RENAME || type == REMOVE)
-            /* We just did ifind to get np; that allocates
+            /* We just did diskfs_cached_ifind to get np; that allocates
                no new references, so we don't have anything to do.  */
             ;
           else if (type == LOOKUP)
@@ -728,11 +728,12 @@ diskfs_dirrewrite_hard (struct node *dp, struct node *np, 
struct dirstat *ds)
   entry_key.dir_inode = dp->cache_id;
   entry_key.dir_offset = ((int) ds->entry) - ((int) ds->mapbuf);
   err = vi_rlookup (entry_key, &inode, &vinode, 0);
-  
   assert (err != EINVAL);
-  
+  if (err)
+    return err;
+
   /*  Lookup the node, we already have a reference.  */
-  oldnp = ifind (inode);
+  oldnp = diskfs_cached_ifind (inode);
 
   assert (ds->type == RENAME);
   assert (ds->stat == HERE_TIS);
diff --git a/fatfs/fatfs.h b/fatfs/fatfs.h
index 9d38546..3c3d836 100644
--- a/fatfs/fatfs.h
+++ b/fatfs/fatfs.h
@@ -36,9 +36,6 @@ struct disknode
 {
   cluster_t start_cluster;
 
-  /* Links on hash list.  */
-  struct node *hnext, **hprevp;
-
   /* The inode as returned by virtual inode management routines.  */
   inode_t inode;
 
@@ -71,6 +68,18 @@ struct disknode
   int dir_idx;
 };
 
+struct lookup_context
+{
+  /* The inode as returned by virtual inode management routines.  */
+  inode_t inode;
+
+  /* Use BUF as the directory file map.  */
+  vm_address_t buf;
+
+  /* Directory this node was allocated in (used by diskfs_alloc_node).  */
+  struct node *dir;
+};
+
 struct user_pager_info
 {
   struct node *node;
@@ -117,8 +126,6 @@ void flush_node_pager (struct node *node);
 
 void write_all_disknodes ();
 
-struct node *ifind (ino_t inum);
-
 error_t fat_get_next_cluster (cluster_t cluster, cluster_t *next_cluster);
 void fat_to_unix_filename (const char *, char *);
 
diff --git a/fatfs/inode.c b/fatfs/inode.c
index ed6f3f0..610f357 100644
--- a/fatfs/inode.c
+++ b/fatfs/inode.c
@@ -37,54 +37,21 @@
 #define UF_IMMUTABLE 0
 #endif
 
-#define INOHSZ  512
-#if     ((INOHSZ&(INOHSZ-1)) == 0)
-#define INOHASH(ino)    ((ino)&(INOHSZ-1))
-#else
-#define INOHASH(ino)    (((unsigned)(ino))%INOHSZ)
-#endif
-
-static struct node *nodehash[INOHSZ];
-static size_t nodehash_nr_items;
-
-static error_t read_node (struct node *np, vm_address_t buf);
-
-/* Initialize the inode hash table.  */
-void
-inode_init ()
-{
-  int n;
-  for (n = 0; n < INOHSZ; n++)
-    nodehash[n] = 0;
-}
-
-/* Fetch inode INUM, set *NPP to the node structure; gain one user
-   reference and lock the node.  */
+/* The user must define this function if she wants to use the node
+   cache.  Create and initialize a node.  */
 error_t
-diskfs_cached_lookup (ino64_t inum, struct node **npp)
+diskfs_user_make_node (struct node **npp, struct lookup_context *ctx)
 {
-  error_t err;
   struct node *np;
   struct disknode *dn;
 
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
-    if (np->cache_id == inum)
-      {
-        np->references++;
-        pthread_spin_unlock (&diskfs_node_refcnt_lock);
-        pthread_mutex_lock (&np->lock);
-        *npp = np;
-        return 0;
-      }
+  /* Create the new node.  */
+  np = diskfs_make_node_alloc (sizeof *dn);
+  if (np == NULL)
+    return ENOMEM;
 
   /* Format specific data for the new node.  */
-  dn = malloc (sizeof (struct disknode));
-  if (! dn)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return ENOMEM;
-    }
+  dn = np->dn;
   dn->pager = 0;
   dn->first = 0;
   dn->last = 0;
@@ -93,34 +60,11 @@ diskfs_cached_lookup (ino64_t inum, struct node **npp)
   dn->chain_extension_lock = PTHREAD_SPINLOCK_INITIALIZER;
   pthread_rwlock_init (&dn->alloc_lock, NULL);
   pthread_rwlock_init (&dn->dirent_lock, NULL);
-  
-  /* Create the new node.  */
-  np = diskfs_make_node (dn);
-  np->cache_id = inum;
-  np->dn->inode = vi_lookup(inum);
 
-  pthread_mutex_lock (&np->lock);
-  
-  /* Put NP in NODEHASH.  */
-  dn->hnext = nodehash[INOHASH(inum)];
-  if (dn->hnext)
-    dn->hnext->dn->hprevp = &dn->hnext;
-  dn->hprevp = &nodehash[INOHASH(inum)];
-  nodehash[INOHASH(inum)] = np;
-  nodehash_nr_items += 1;
-
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-  
-  /* Get the contents of NP off disk.  */
-  err = read_node (np, 0);
-
-  if (err)
-    return err;
-  else
-    {
-      *npp = np;
-      return 0;
-    }
+  dn->inode = ctx->inode;
+  dn->dirnode = ctx->dir;
+  *npp = np;
+  return 0;
 }
 
 /* Fetch inode INUM, set *NPP to the node structure;
@@ -130,83 +74,8 @@ error_t
 diskfs_cached_lookup_in_dirbuf (int inum, struct node **npp, vm_address_t buf)
 {
   error_t err;
-  struct node *np;
-  struct disknode *dn;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
-    if (np->cache_id == inum)
-      {
-        np->references++;
-        pthread_spin_unlock (&diskfs_node_refcnt_lock);
-        pthread_mutex_lock (&np->lock);
-        *npp = np;
-        return 0;
-      }
-
-  /* Format specific data for the new node.  */
-  dn = malloc (sizeof (struct disknode));
-  if (! dn)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return ENOMEM;
-    }
-  dn->pager = 0;
-  dn->first = 0;
-  dn->last = 0;
-  dn->length_of_chain = 0;
-  dn->chain_complete = 0;
-  dn->chain_extension_lock = PTHREAD_SPINLOCK_INITIALIZER;
-  pthread_rwlock_init (&dn->alloc_lock, NULL);
-  pthread_rwlock_init (&dn->dirent_lock, NULL);
-  
-  /* Create the new node.  */
-  np = diskfs_make_node (dn);
-  np->cache_id = inum;
-  np->dn->inode = vi_lookup(inum);
-
-  pthread_mutex_lock (&np->lock);
-  
-  /* Put NP in NODEHASH.  */
-  dn->hnext = nodehash[INOHASH(inum)];
-  if (dn->hnext)
-    dn->hnext->dn->hprevp = &dn->hnext;
-  dn->hprevp = &nodehash[INOHASH(inum)];
-  nodehash[INOHASH(inum)] = np;
-  nodehash_nr_items += 1;
-
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-  
-  /* Get the contents of NP off disk.  */
-  err = read_node (np, buf);
-
-  if (err)
-    return err;
-  else
-    {
-      *npp = np;
-      return 0;
-    }
-}
-
-/* Lookup node INUM (which must have a reference already) and return
-   it without allocating any new references.  */
-struct node *
-ifind (ino_t inum)
-{
-  struct node *np;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  for (np = nodehash[INOHASH(inum)]; np; np = np->dn->hnext)
-    {
-      if (np->cache_id != inum)
-        continue;
-
-      assert (np->references);
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return np;
-    }
-  assert (0);
+  struct lookup_context ctx = { buf: buf, inode: vi_lookup (inum) };
+  return diskfs_cached_lookup_context (inum, npp, &ctx);
 }
 
 /* The last reference to a node has gone away; drop it from the hash
@@ -216,11 +85,6 @@ diskfs_node_norefs (struct node *np)
 {
   struct cluster_chain *last = np->dn->first;
 
-  *np->dn->hprevp = np->dn->hnext;
-  if (np->dn->hnext)
-    np->dn->hnext->dn->hprevp = np->dn->hprevp;
-  nodehash_nr_items -= 1;
-
   while (last)
     {
       struct cluster_chain *next = last->next;
@@ -231,25 +95,19 @@ diskfs_node_norefs (struct node *np)
   if (np->dn->translator)
     free (np->dn->translator);
 
-  /* It is safe to unlock diskfs_node_refcnt_lock here for a while because
-     all references to the node have been deleted.  */
   if (np->dn->dirnode)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      diskfs_nrele (np->dn->dirnode);
-      pthread_spin_lock (&diskfs_node_refcnt_lock);
-    }
+    diskfs_nrele (np->dn->dirnode);
 
   assert (!np->dn->pager);
 
-  free (np->dn);
   free (np);
 }
 
-/* The last hard reference to a node has gone away; arrange to have
-   all the weak references dropped that can be.  */
+/* The user must define this function if she wants to use the node
+   cache.  The last hard reference to a node has gone away; arrange to
+   have all the weak references dropped that can be.  */
 void
-diskfs_try_dropping_softrefs (struct node *np)
+diskfs_user_try_dropping_softrefs (struct node *np)
 {
   drop_pager_softrefs (np);
 }
@@ -268,9 +126,10 @@ diskfs_new_hardrefs (struct node *np)
   allow_pager_softrefs (np);
 }
 
-/* Read stat information out of the directory entry. */
-static error_t
-read_node (struct node *np, vm_address_t buf)
+/* The user must define this function if she wants to use the node
+   cache.  Read stat information out of the on-disk node.  */
+error_t
+diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
 {
   /* XXX This needs careful investigation.  */
   error_t err;
@@ -281,6 +140,7 @@ read_node (struct node *np, vm_address_t buf)
   struct vi_key vk = vi_key(np->dn->inode);
   vm_prot_t prot = VM_PROT_READ;
   memory_object_t memobj;
+  vm_address_t buf = ctx->buf;
   vm_size_t buflen = 0;
   int our_buf = 0;
 
@@ -317,7 +177,7 @@ read_node (struct node *np, vm_address_t buf)
          /* FIXME: We know intimately that the parent dir is locked
             by libdiskfs.  The only case it is not locked is for NFS
             (fsys_getfile) and we disabled that.  */
-         dp = ifind (vk.dir_inode);
+         dp = diskfs_cached_ifind (vk.dir_inode);
          assert (dp);
       
          /* Map in the directory contents. */
@@ -339,7 +199,7 @@ read_node (struct node *np, vm_address_t buf)
   /* Files in fatfs depend on the directory that hold the file.  */
   np->dn->dirnode = dp;
   if (dp)
-    dp->references++;
+    refcounts_ref (&dp->refcounts, NULL);
 
   pthread_rwlock_rdlock (&np->dn->dirent_lock);
 
@@ -530,7 +390,7 @@ error_t
 diskfs_node_reload (struct node *node)
 {
   struct cluster_chain *last = node->dn->first;
-
+  static struct lookup_context ctx = { buf: 0 };
   while (last)
     {
       struct cluster_chain *next = last->next;
@@ -538,57 +398,8 @@ diskfs_node_reload (struct node *node)
       last = next;
     }
   flush_node_pager (node);
-  read_node (node, 0);
-
-  return 0;
-}
 
-/* For each active node, call FUN.  The node is to be locked around the call
-   to FUN.  If FUN returns non-zero for any node, then immediately stop, and
-   return that value.  */
-error_t
-diskfs_node_iterate (error_t (*fun)(struct node *))
-{
-  error_t err = 0;
-  int n;
-  size_t num_nodes;
-  struct node *node, **node_list, **p;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-
-  /* We must copy everything from the hash table into another data structure
-     to avoid running into any problems with the hash-table being modified
-     during processing (normally we delegate access to hash-table with
-     diskfs_node_refcnt_lock, but we can't hold this while locking the
-     individual node locks).  */
-
-  num_nodes = nodehash_nr_items;
-
-  node_list = alloca (num_nodes * sizeof (struct node *));
-  p = node_list;
-  for (n = 0; n < INOHSZ; n++)
-    for (node = nodehash[n]; node; node = node->dn->hnext)
-      {
-        *p++ = node;
-        node->references++;
-      }
-
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
-  p = node_list;
-  while (num_nodes-- > 0)
-    {
-      node = *p++;
-      if (!err)
-        {
-          pthread_mutex_lock (&node->lock);
-          err = (*fun)(node);
-          pthread_mutex_unlock (&node->lock);
-        }
-      diskfs_nrele (node);
-    }
-
-  return err;
+  return diskfs_user_read_node (node, &ctx);
 }
 
 /* Write all active disknodes into the ext2_inode pager. */
@@ -766,7 +577,8 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct 
node **node)
   ino_t inum;
   inode_t inode;
   struct node *np;
-  
+  struct lookup_context ctx = { dir: dir };
+
   assert (!diskfs_readonly);
 
   /* FIXME: We use a magic key here that signals read_node that we are
@@ -775,13 +587,11 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct 
node **node)
   if (err)
     return err;
 
-  err = diskfs_cached_lookup (inum, &np);
+  err = diskfs_cached_lookup_context (inum, &np, &ctx);
   if (err)
     return err;
 
-  /* FIXME: We know that readnode couldn't put this in.  */
-  np->dn->dirnode = dir;
-  dir->references++;
+  refcounts_ref (&dir->refcounts, NULL);
 
   *node = np;
   return 0;
diff --git a/fatfs/main.c b/fatfs/main.c
index 2bbcdfa..00c56bc 100644
--- a/fatfs/main.c
+++ b/fatfs/main.c
@@ -143,7 +143,7 @@ fetch_root ()
 {
   error_t err;
   ino_t inum;
-  inode_t inode;
+  struct lookup_context ctx;
 
   memset (&dr_root_node, 0, sizeof(struct dirrect));
 
@@ -196,13 +196,13 @@ fetch_root ()
      from the vi_zero_key (in the dir_offset value) as well as all
      normal virtual inode keys (in the dir_inode value).  Enter the
      disknode into the inode table.  */
-  err = vi_new ((struct vi_key) {0, 1}, &inum, &inode);
+  err = vi_new ((struct vi_key) {0, 1}, &inum, &ctx.inode);
   assert_perror (err);
 
   /* Allocate a node for the root directory disknode in
      diskfs_root_node.  */
   if (!err)
-    err = diskfs_cached_lookup (inum, &diskfs_root_node);
+    err = diskfs_cached_lookup_context (inum, &diskfs_root_node, &ctx);
 
   assert_perror (err);
 
diff --git a/isofs/inode.c b/isofs/inode.c
index 247d8ac..eef2a6a 100644
--- a/isofs/inode.c
+++ b/isofs/inode.c
@@ -31,55 +31,6 @@
    record for symlinks and zero length files, and file_start otherwise.
    Only for hard links to zero length files we get extra inodes.  */
 
-#define        INOHSZ  512
-#if    ((INOHSZ&(INOHSZ-1)) == 0)
-#define        INOHASH(ino)    ((ino>>8)&(INOHSZ-1))
-#else
-#define        INOHASH(ino)    (((unsigned)(ino>>8))%INOHSZ)
-#endif
-
-struct node_cache
-{
-  struct dirrect *dr;          /* somewhere in disk_image */
-  off_t file_start;            /* start of file */
-
-  off_t id;                    /* UNIQUE identifier.  */
-
-  struct node *np;             /* if live */
-};
-
-static int node_cache_size = 0;
-static int node_cache_alloced = 0;
-struct node_cache *node_cache = 0;
-
-/* Forward */
-static error_t read_disknode (struct node *,
-                             struct dirrect *, struct rrip_lookup *);
-
-
-/* See if node with identifier ID is in the cache.  If so, return it,
-   with one additional reference. diskfs_node_refcnt_lock must be held
-   on entry to the call, and will be released iff the node was found
-   in the cache. */
-void
-inode_cache_find (off_t id, struct node **npp)
-{
-  int i;
-
-  for (i = 0; i < node_cache_size; i++)
-    if (node_cache[i].id == id
-       && node_cache[i].np)
-      {
-       *npp = node_cache[i].np;
-       (*npp)->references++;
-       pthread_spin_unlock (&diskfs_node_refcnt_lock);
-       pthread_mutex_lock (&(*npp)->lock);
-       return;
-      }
-  *npp = 0;
-}
-
-
 /* Determine if we use file_start or struct dirrect * as node id.  */
 int
 use_file_start_id (struct dirrect *record, struct rrip_lookup *rr)
@@ -91,127 +42,35 @@ use_file_start_id (struct dirrect *record, struct 
rrip_lookup *rr)
   return 1;
 }
 
-/* Enter NP into the cache.  The directory entry we used is DR, the
-   cached Rock-Ridge info RR. diskfs_node_refcnt_lock must be held. */
-void
-cache_inode (struct node *np, struct dirrect *record,
-           struct rrip_lookup *rr)
-{
-  int i;
-  struct node_cache *c = 0;
-  off_t id;
-
-  if (use_file_start_id (record, rr))
-    id = np->dn->file_start << store->log2_block_size;
-  else
-    id = (off_t) ((void *) record - (void *) disk_image);
-
-  /* First see if there's already an entry. */
-  for (i = 0; i < node_cache_size; i++)
-    if (node_cache[i].id == id)
-      break;
-
-  if (i == node_cache_size)
-    {
-      if (node_cache_size >= node_cache_alloced)
-       {
-         if (!node_cache_alloced)
-           {
-             /* Initialize */
-             node_cache_alloced = 10;
-             node_cache = malloc (sizeof (struct node_cache) * 10);
-           }
-         else
-           {
-             node_cache_alloced *= 2;
-             node_cache = realloc (node_cache,
-                                   sizeof (struct node_cache)
-                                   * node_cache_alloced);
-           }
-         assert (node_cache);
-       }
-      node_cache_size++;
-    }
-
-  c = &node_cache[i];
-  c->id = id;
-  c->dr = record;
-  c->file_start = np->dn->file_start;
-  c->np = np;
-
-  /* PLUS 1 so that we don't store zero cache ID's (not allowed by diskfs) */
-  np->cache_id = i + 1;
-}
-
-/* Fetch inode with cache id ID; set *NPP to the node structure;
-   gain one user reference and lock the node. */
+/* The user must define this function if she wants to use the node
+   cache.  Create and initialize a node.  */
 error_t
-diskfs_cached_lookup (ino_t id, struct node **npp)
+diskfs_user_make_node (struct node **npp, struct lookup_context *ctx)
 {
-  struct node *np;
   error_t err;
+  struct node *np;
+  struct disknode *dn;
 
-  /* Cache ID's are incremented when presented to diskfs
-     to avoid presenting zero cache ID's. */
-  id--;
-
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  assert (id < node_cache_size);
-
-  np = node_cache[id].np;
+  /* Create the new node.  */
+  np = diskfs_make_node_alloc (sizeof *dn);
+  if (np == NULL)
+    return ENOMEM;
 
-  if (!np)
+  /* Format specific data for the new node.  */
+  dn = diskfs_node_disknode (np);
+  dn->fileinfo = 0;
+  dn->dr = ctx->dr;
+  err = calculate_file_start (ctx->dr, &dn->file_start, &ctx->rr);
+  if (err)
     {
-      struct node_cache *c = &node_cache[id];
-      struct rrip_lookup rr;
-      struct disknode *dn;
-
-      rrip_lookup (node_cache[id].dr, &rr, 1);
-
-      /* We should never cache the wrong directory entry */
-      assert (!(rr.valid & VALID_CL));
-
-      dn = malloc (sizeof (struct disknode));
-      if (!dn)
-       {
-         pthread_spin_unlock (&diskfs_node_refcnt_lock);
-         release_rrip (&rr);
-         return ENOMEM;
-       }
-      dn->fileinfo = 0;
-      dn->dr = c->dr;
-      dn->file_start = c->file_start;
-      np = diskfs_make_node (dn);
-      if (!np)
-       {
-         free (dn);
-         pthread_spin_unlock (&diskfs_node_refcnt_lock);
-         release_rrip (&rr);
-         return ENOMEM;
-       }
-      np->cache_id = id + 1;   /* see above for rationale for increment */
-      pthread_mutex_lock (&np->lock);
-      c->np = np;
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
-      err = read_disknode (np, node_cache[id].dr, &rr);
-      if (!err)
-       *npp = np;
-
-      release_rrip (&rr);
-
+      diskfs_nrele (np);
       return err;
     }
 
-
-  np->references++;
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-  pthread_mutex_lock (&np->lock);
   *npp = np;
   return 0;
 }
 
-
 /* Return Epoch-based time from a seven byte according to 9.1.5 */
 char *
 isodate_915 (char *c, struct timespec *ts)
@@ -285,6 +144,9 @@ calculate_file_start (struct dirrect *record, off_t 
*file_start,
     *file_start = rr->realfilestart;
   else
     {
+      if (record == NULL)
+        return ENOENT;
+
       err = diskfs_catch_exception ();
       if (err)
        return err;
@@ -297,82 +159,40 @@ calculate_file_start (struct dirrect *record, off_t 
*file_start,
   return 0;
 }
 
-
-/* Load the inode with directory entry RECORD and cached Rock-Ridge
-   info RR into NP.  The directory entry is at OFFSET in BLOCK.  */
+/* Given RECORD and RR, calculate the cache id.  */
 error_t
-load_inode (struct node **npp, struct dirrect *record,
-           struct rrip_lookup *rr)
+cache_id (struct dirrect *record, struct rrip_lookup *rr, ino_t *idp)
 {
   error_t err;
   off_t file_start;
-  struct disknode *dn;
-  struct node *np;
-
   err = calculate_file_start (record, &file_start, rr);
   if (err)
     return err;
+
   if (rr->valid & VALID_CL)
     record = rr->realdirent;
 
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-
-  /* First check the cache */
   if (use_file_start_id (record, rr))
-    inode_cache_find (file_start << store->log2_block_size, npp);
+    *idp = file_start << store->log2_block_size;
   else
-    inode_cache_find ((off_t) ((void *) record - (void *) disk_image), npp);
-
-  if (*npp)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return 0;
-    }
-
-  /* Create a new node */
-  dn = malloc (sizeof (struct disknode));
-  if (!dn)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return ENOMEM;
-    }
-  dn->fileinfo = 0;
-  dn->dr = record;
-  dn->file_start = file_start;
-
-  np = diskfs_make_node (dn);
-  if (!np)
-    {
-      free (dn);
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      return ENOMEM;
-    }
-
-  pthread_mutex_lock (&np->lock);
-
-  cache_inode (np, record, rr);
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
-  err = read_disknode (np, record, rr);
-  *npp = np;
-  return err;
+    *idp = (off_t) ((void *) record - (void *) disk_image);
+  return 0;
 }
 
-
-/* Read stat information from the directory entry at DR and the
-   contents of RL. */
-static error_t
-read_disknode (struct node *np, struct dirrect *dr,
-              struct rrip_lookup *rl)
+/* The user must define this function if she wants to use the node
+   cache.  Read stat information out of the on-disk node.  */
+error_t
+diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
 {
   error_t err;
   struct stat *st = &np->dn_stat;
+  /* Read stat information from the directory entry at DR and the
+     contents of RL. */
+  struct dirrect *dr = ctx->dr;
+  struct rrip_lookup *rl = &ctx->rr;
   st->st_fstype = FSTYPE_ISO9660;
   st->st_fsid = getpid ();
-  if (use_file_start_id (dr, rl))
-    st->st_ino = (ino_t) np->dn->file_start << store->log2_block_size;
-  else
-    st->st_ino = (ino_t) ((void *) dr - (void *) disk_image);
+  st->st_ino = np->cache_id;
   st->st_gen = 0;
   st->st_rdev = 0;
 
@@ -505,21 +325,18 @@ error_t (*diskfs_read_symlink_hook) (struct node *, char 
*)
 void
 diskfs_node_norefs (struct node *np)
 {
-  assert (node_cache[np->cache_id - 1].np == np);
-  node_cache[np->cache_id - 1].np = 0;
-
   if (np->dn->translator)
     free (np->dn->translator);
 
   assert (!np->dn->fileinfo);
-  free (np->dn);
   free (np);
 }
 
-/* The last hard reference to a node has gone away; arrange to have
-   all the weak references dropped that can be.  */
+/* The user must define this function if she wants to use the node
+   cache.  The last hard reference to a node has gone away; arrange to
+   have all the weak references dropped that can be.  */
 void
-diskfs_try_dropping_softrefs (struct node *np)
+diskfs_user_try_dropping_softrefs (struct node *np)
 {
   drop_pager_softrefs (np);
 }
@@ -581,14 +398,6 @@ diskfs_validate_author_change (struct node *np, uid_t 
author)
   return EROFS;
 }
 
-error_t
-diskfs_node_iterate (error_t (*fun)(struct node *))
-{
-  /* We never actually have to do anything, because this function
-     is only used for things that have to do with read-write media. */
-  return 0;
-}
-
 void
 diskfs_write_disknode (struct node *np, int wait)
 {
diff --git a/isofs/isofs.h b/isofs/isofs.h
index 68a94e9..3f6690b 100644
--- a/isofs/isofs.h
+++ b/isofs/isofs.h
@@ -56,6 +56,15 @@ struct user_pager_info
     struct pager *p;
 };
 
+struct lookup_context
+{
+  /* The directory record.  Points somewhere into the disk_image.  */
+  struct dirrect *dr;
+
+  /* The results of an rrip_scan_lookup call for this node.  */
+  struct rrip_lookup rr;
+};
+
 /* The physical media */
 extern struct store *store;
 
@@ -87,7 +96,9 @@ void drop_pager_softrefs (struct node *);
 void allow_pager_softrefs (struct node *);
 void create_disk_pager (void);
 
-error_t load_inode (struct node **, struct dirrect *, struct rrip_lookup *);
+/* Given RECORD and RR, calculate the cache id.  */
+error_t cache_id (struct dirrect *record, struct rrip_lookup *rr, ino_t *idp);
+
 error_t calculate_file_start (struct dirrect *, off_t *, struct rrip_lookup *);
 
 char *isodate_915 (char *, struct timespec *);
diff --git a/isofs/lookup.c b/isofs/lookup.c
index e51b9cb..f375212 100644
--- a/isofs/lookup.c
+++ b/isofs/lookup.c
@@ -70,12 +70,12 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
                    struct node **npp, struct dirstat *ds, struct protid *cred)
 {
   error_t err = 0;
-  struct dirrect *record;
+  struct lookup_context ctx;
   int namelen;
   int spec_dotdot;
   void *buf;
   void *blockaddr;
-  struct rrip_lookup rr;
+  ino_t id;
 
   if ((type == REMOVE) || (type == RENAME))
     assert (npp);
@@ -99,7 +99,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
        blockaddr < buf + dp->dn_stat.st_size;
        blockaddr += logical_sector_size)
     {
-      err = dirscanblock (blockaddr, name, namelen, &record, &rr);
+      err = dirscanblock (blockaddr, name, namelen, &ctx.dr, &ctx.rr);
 
       if (!err)
        break;
@@ -115,6 +115,10 @@ diskfs_lookup_hard (struct node *dp, const char *name, 
enum lookup_type type,
   if (err)
     return err;
 
+  err = cache_id (ctx.dr, &ctx.rr, &id);
+  if (err)
+    return err;
+
   /* Load the inode */
   if (namelen == 2 && name[0] == '.' && name[1] == '.')
     {
@@ -125,7 +129,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
          /* renames and removes can't get this far. */
          assert (type == LOOKUP);
          diskfs_nput (dp);
-         err = load_inode (npp, record, &rr);
+         err = diskfs_cached_lookup_context (id, npp, &ctx);
        }
       else
        {
@@ -133,7 +137,7 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
             we are permanently read-only, so things are necessarily
             quiescent.  Just be careful to honor the locking order. */
          pthread_mutex_unlock (&dp->lock);
-         err = load_inode (npp, record, &rr);
+         err = diskfs_cached_lookup_context (id, npp, &ctx);
          pthread_mutex_lock (&dp->lock);
        }
     }
@@ -143,9 +147,9 @@ diskfs_lookup_hard (struct node *dp, const char *name, enum 
lookup_type type,
       diskfs_nref (dp);
     }
   else
-    err = load_inode (npp, record, &rr);
+    err = diskfs_cached_lookup_context (id, npp, &ctx);
 
-  release_rrip (&rr);
+  release_rrip (&ctx.rr);
   return err;
 }
 
diff --git a/isofs/main.c b/isofs/main.c
index 4f531f7..95c90fe 100644
--- a/isofs/main.c
+++ b/isofs/main.c
@@ -44,20 +44,23 @@ int diskfs_maxsymlinks = 8;
 static void
 fetch_root ()
 {
-  struct rrip_lookup rl;
-  struct dirrect *dr;
+  struct lookup_context ctx;
+  ino_t id;
   error_t err;
 
-  dr = (struct dirrect *) sblock->root;
+  ctx.dr = (struct dirrect *) sblock->root;
 
   /* First check for SUSP and all relevant extensions */
-  rrip_initialize (dr);
+  rrip_initialize (ctx.dr);
 
   /* Now rescan the node for real */
-  rrip_lookup (dr, &rl, 1);
+  rrip_lookup (ctx.dr, &ctx.rr, 1);
+
+  err = cache_id (ctx.dr, &ctx.rr, &id);
+  assert_perror (err);
 
   /* And fetch the node. */
-  err = load_inode (&diskfs_root_node, dr, &rl);
+  err = diskfs_cached_lookup_context (id, &diskfs_root_node, &ctx);
   assert_perror (err);
 
   pthread_mutex_unlock (&diskfs_root_node->lock);
diff --git a/libdiskfs/Makefile b/libdiskfs/Makefile
index 996e86a..47b9339 100644
--- a/libdiskfs/Makefile
+++ b/libdiskfs/Makefile
@@ -41,7 +41,7 @@ OTHERSRCS = conch-fetch.c conch-set.c dir-clear.c dir-init.c 
dir-renamed.c \
        extern-inline.c \
        node-create.c node-drop.c node-make.c node-rdwr.c node-update.c \
        node-nref.c node-nput.c node-nrele.c node-nrefl.c node-nputl.c \
-       node-nrelel.c \
+       node-nrelel.c node-cache.c \
        peropen-make.c peropen-rele.c protid-make.c protid-rele.c \
        init-init.c init-startup.c init-first.c init-main.c \
        rdwr-internal.c boot-start.c demuxer.c node-times.c shutdown.c \
diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c
index 9e37e23..a4878f3 100644
--- a/libdiskfs/dir-renamed.c
+++ b/libdiskfs/dir-renamed.c
@@ -94,6 +94,8 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, const 
char *fromname,
   ds = buf;
   err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
   assert (err != EAGAIN);      /* <-> assert (TONAME != "..") */
+  if (err && err != ENOENT)
+    goto out;
 
   if (tnp == fnp)
     {
diff --git a/libdiskfs/diskfs.h b/libdiskfs/diskfs.h
index 2818225..82a16b4 100644
--- a/libdiskfs/diskfs.h
+++ b/libdiskfs/diskfs.h
@@ -80,24 +80,31 @@ struct peropen
    filesystem.  */
 struct node
 {
-  struct node *next, **prevp;
+  /* Links on hash list. */
+  struct node *hnext, **hprevp;
 
   struct disknode *dn;
 
   io_statbuf_t dn_stat;
 
+  /* Flags.  */
+  unsigned int
+
   /* Stat has been modified if one of the following four fields
      is nonzero.  Also, if one of the dn_set_?time fields is nonzero,
      the appropriate dn_stat.st_?tim field needs to be updated. */
-  int dn_set_ctime;
-  int dn_set_atime;
-  int dn_set_mtime;
-  int dn_stat_dirty;
+    dn_set_ctime:1,
+    dn_set_atime:1,
+    dn_set_mtime:1,
+    dn_stat_dirty:1,
+
+  /* Indicate whether the author is tracking the uid because the
+     on-disk file format does not encode a separate author.  */
+    author_tracks_uid:1;
 
   pthread_mutex_t lock;
 
-  int references;              /* hard references */
-  int light_references;                /* light references */
+  refcounts_t refcounts;
 
   mach_port_t sockaddr;                /* address for S_IFSOCK shortcut */
 
@@ -118,8 +125,6 @@ struct node
   loff_t allocsize;
 
   ino64_t cache_id;
-
-  int author_tracks_uid;
 };
 
 struct diskfs_control
@@ -198,8 +203,6 @@ extern volatile struct mapped_time_value *diskfs_mtime;
    be done by format independent code. */
 extern int diskfs_synchronous;
 
-extern pthread_spinlock_t diskfs_node_refcnt_lock;
-
 extern int pager_port_type;
 
 /* Whether the filesystem is currently writable or not. */
@@ -448,14 +451,16 @@ error_t diskfs_alloc_node (struct node *dp, mode_t mode, 
struct node **np);
 void diskfs_free_node (struct node *np, mode_t mode);
 
 /* Node NP has no more references; free local state, including *NP
-   if it isn't to be retained.  diskfs_node_refcnt_lock is held. */
+   if it isn't to be retained.  */
 void diskfs_node_norefs (struct node *np);
 
-/* The user must define this function.  Node NP has some light
+/* The user must define this function unless she wants to use the node
+   cache.  See the section `Node cache' below.  Node NP has some light
    references, but has just lost its last hard references.  Take steps
    so that if any light references can be freed, they are.  NP is locked
    as is the pager refcount lock.  This function will be called after
-   diskfs_lost_hardrefs.  */
+   diskfs_lost_hardrefs.  An additional light reference is acquired by
+   libdiskfs across calls to this function.  */
 void diskfs_try_dropping_softrefs (struct node *np);
 
 /* The user must define this funcction.  Node NP has some light
@@ -514,7 +519,8 @@ void diskfs_write_disknode (struct node *np, int wait);
    then return only after the physical media has been completely updated.  */
 void diskfs_file_update (struct node *np, int wait);
 
-/* The user must define this function.  For each active node, call
+/* The user must define this function unless she wants to use the node
+   cache.  See the section `Node cache' below.  For each active node, call
    FUN.  The node is to be locked around the call to FUN.  If FUN
    returns non-zero for any node, then immediately stop, and return
    that value. */
@@ -586,6 +592,36 @@ error_t (*diskfs_read_symlink_hook)(struct node *np, char 
*target);
 error_t diskfs_get_source (struct protid *cred,
                            char *source, size_t source_len);
 
+/* Libdiskfs contains a node cache.
+
+   Using it relieves the user of implementing diskfs_cached_lookup,
+   diskfs_node_iterate, and diskfs_try_dropping_softrefs.
+
+   In order to use it, she must implement the following functions with
+   the prefix `diskfs_user_'.  */
+
+/* This can be used to provide additional context to
+   diskfs_user_make_node and diskfs_user_read_node in case of cache
+   misses.  */
+struct lookup_context;
+
+/* The user must define this function if she wants to use the node
+   cache.  Create and initialize a node.  */
+error_t diskfs_user_make_node (struct node **npp, struct lookup_context *ctx);
+
+/* The user must define this function if she wants to use the node
+   cache.  Read stat information out of the on-disk node.  */
+error_t diskfs_user_read_node (struct node *np, struct lookup_context *ctx);
+
+/* The user must define this function if she wants to use the node
+   cache.  The last hard reference to a node has gone away; arrange to
+   have all the weak references dropped that can be.  */
+void diskfs_user_try_dropping_softrefs (struct node *np);
+
+/* Lookup node INUM (which must have a reference already) and return it
+   without allocating any new references. */
+struct node *diskfs_cached_ifind (ino_t inum);
+
 /* The library exports the following functions for general use */
 
 /* Call this after arguments have been parsed to initialize the library.
@@ -611,9 +647,8 @@ void diskfs_spawn_first_thread (ports_demuxer_type demuxer);
    diskfs_init_completed once it has a valid proc and auth port. */
 void diskfs_start_bootstrap ();
 
-/* Node NP now has no more references; clean all state.  The
-   _diskfs_node_refcnt_lock must be held, and will be released
-   upon return.  NP must be locked.  */
+/* Node NP now has no more references; clean all state.  NP must be
+   locked.  */
 void diskfs_drop_node (struct node *np);
 
 /* Set on disk fields from NP->dn_stat; update ctime, atime, and mtime
@@ -697,9 +732,11 @@ struct node *diskfs_make_node_alloc (size_t size);
    this value for offset calculations.  */
 extern const size_t _diskfs_sizeof_struct_node;
 
+#if defined(__USE_EXTERN_INLINES) || defined(DISKFS_DEFINE_EXTERN_INLINE)
+
 /* Return the address of the disknode for NODE.  NODE must have been
    allocated using diskfs_make_node_alloc.  */
-static inline struct disknode *
+DISKFS_EXTERN_INLINE struct disknode *
 diskfs_node_disknode (struct node *node)
 {
   return (struct disknode *) ((char *) node + _diskfs_sizeof_struct_node);
@@ -707,12 +744,14 @@ diskfs_node_disknode (struct node *node)
 
 /* Return the address of the node for DISKNODE.  DISKNODE must have
    been allocated using diskfs_make_node_alloc.  */
-static inline struct node *
+DISKFS_EXTERN_INLINE struct node *
 diskfs_disknode_node (struct disknode *disknode)
 {
   return (struct node *) ((char *) disknode - _diskfs_sizeof_struct_node);
 }
 
+#endif /* Use extern inlines.  */
+
 
 /* The library also exports the following functions; they are not generally
    useful unless you are redefining other functions the library provides. */
@@ -804,9 +843,18 @@ error_t diskfs_dirrewrite (struct node *dp, struct node 
*oldnp,
 error_t diskfs_dirremove (struct node *dp, struct node *np,
                          const char *name, struct dirstat *ds);
 
-/* Return the node corresponding to CACHE_ID in *NPP. */
+/* The user must define this function unless she wants to use the node
+   cache.  See the section `Node cache' above.  Return the node
+   corresponding to CACHE_ID in *NPP. */
 error_t diskfs_cached_lookup (ino64_t cache_id, struct node **npp);
 
+/* Return the node corresponding to CACHE_ID in *NPP.  In case of a
+   cache miss, use CTX to create it and load it from the disk.  See
+   the section `Node cache' above.  */
+error_t diskfs_cached_lookup_context (ino_t inum, struct node **npp,
+                                     struct lookup_context *ctx);
+
+
 /* Create a new node. Give it MODE; if that includes IFDIR, also
    initialize `.' and `..' in the new directory.  Return the node in NPP.
    CRED identifies the user responsible for the call.  If NAME is nonzero,
diff --git a/libdiskfs/init-init.c b/libdiskfs/init-init.c
index ffb99e0..357960b 100644
--- a/libdiskfs/init-init.c
+++ b/libdiskfs/init-init.c
@@ -41,8 +41,6 @@ int _diskfs_noatime;
 
 struct hurd_port _diskfs_exec_portcell;
 
-pthread_spinlock_t diskfs_node_refcnt_lock = PTHREAD_SPINLOCK_INITIALIZER;
-
 pthread_spinlock_t _diskfs_control_lock = PTHREAD_SPINLOCK_INITIALIZER;
 int _diskfs_ncontrol_ports;
 
diff --git a/libdiskfs/node-cache.c b/libdiskfs/node-cache.c
new file mode 100644
index 0000000..6b70da8
--- /dev/null
+++ b/libdiskfs/node-cache.c
@@ -0,0 +1,260 @@
+/* Inode cache.
+
+   Copyright (C) 1994-2015 Free Software Foundation, Inc.
+
+   This file is part of the GNU Hurd.
+
+   The GNU Hurd is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2, or (at
+   your option) any later version.
+
+   The GNU Hurd is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the GNU Hurd.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "priv.h"
+
+#define        INOHSZ  8192
+#if    ((INOHSZ&(INOHSZ-1)) == 0)
+#define        INOHASH(ino)    ((ino)&(INOHSZ-1))
+#else
+#define        INOHASH(ino)    (((unsigned)(ino))%INOHSZ)
+#endif
+
+/* The nodehash is a cache of nodes.
+
+   Access to nodehash and nodehash_nr_items is protected by
+   nodecache_lock.
+
+   Every node in the nodehash carries a light reference.  When we are
+   asked to give up that light reference, we reacquire our lock
+   momentarily to check whether someone else reacquired a reference
+   through the nodehash.  */
+static struct node *nodehash[INOHSZ];
+static size_t nodehash_nr_items;
+static pthread_rwlock_t nodecache_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+/* Initialize the inode hash table. */
+static void __attribute__ ((constructor))
+nodecache_init ()
+{
+}
+
+/* Lookup node with inode number INUM.  Returns NULL if the node is
+   not found in the node cache.  */
+static struct node *
+lookup (ino_t inum)
+{
+  struct node *np;
+  for (np = nodehash[INOHASH(inum)]; np; np = np->hnext)
+    if (np->cache_id == inum)
+      return np;
+  return NULL;
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+   gain one user reference and lock the node.  */
+error_t __attribute__ ((weak))
+diskfs_cached_lookup (ino_t inum, struct node **npp)
+{
+  return diskfs_cached_lookup_context (inum, npp, NULL);
+}
+
+/* Fetch inode INUM, set *NPP to the node structure;
+   gain one user reference and lock the node.  */
+error_t
+diskfs_cached_lookup_context (ino_t inum, struct node **npp,
+                             struct lookup_context *ctx)
+{
+  error_t err;
+  struct node *np, *tmp;
+
+  pthread_rwlock_rdlock (&nodecache_lock);
+  np = lookup (inum);
+  if (np)
+    goto gotit;
+  pthread_rwlock_unlock (&nodecache_lock);
+
+  err = diskfs_user_make_node (&np, ctx);
+  if (err)
+    return err;
+
+  np->cache_id = inum;
+  pthread_mutex_lock (&np->lock);
+
+  /* Put NP in NODEHASH.  */
+  pthread_rwlock_wrlock (&nodecache_lock);
+  tmp = lookup (inum);
+  if (tmp)
+    {
+      /* We lost a race.  */
+      diskfs_nput (np);
+      np = tmp;
+      goto gotit;
+    }
+
+  np->hnext = nodehash[INOHASH(inum)];
+  if (np->hnext)
+    np->hnext->hprevp = &np->hnext;
+  np->hprevp = &nodehash[INOHASH(inum)];
+  nodehash[INOHASH(inum)] = np;
+  diskfs_nref_light (np);
+  nodehash_nr_items += 1;
+  pthread_rwlock_unlock (&nodecache_lock);
+
+  /* Get the contents of NP off disk.  */
+  err = diskfs_user_read_node (np, ctx);
+  if (err)
+    return err;
+  else
+    {
+      *npp = np;
+      return 0;
+    }
+
+ gotit:
+  diskfs_nref (np);
+  pthread_rwlock_unlock (&nodecache_lock);
+  pthread_mutex_lock (&np->lock);
+  *npp = np;
+  return 0;
+}
+
+/* Lookup node INUM (which must have a reference already) and return it
+   without allocating any new references. */
+struct node *
+diskfs_cached_ifind (ino_t inum)
+{
+  struct node *np;
+
+  pthread_rwlock_rdlock (&nodecache_lock);
+  np = lookup (inum);
+  pthread_rwlock_unlock (&nodecache_lock);
+
+  assert (np);
+  return np;
+}
+
+void __attribute__ ((weak))
+diskfs_try_dropping_softrefs (struct node *np)
+{
+  pthread_rwlock_wrlock (&nodecache_lock);
+  if (np->hprevp != NULL)
+    {
+      /* Check if someone reacquired a reference through the
+        nodehash.  */
+      struct references result;
+      refcounts_references (&np->refcounts, &result);
+
+      if (result.hard > 0)
+       {
+         /* A reference was reacquired through a hash table lookup.
+            It's fine, we didn't touch anything yet. */
+         pthread_rwlock_unlock (&nodecache_lock);
+         return;
+       }
+
+      *np->hprevp = np->hnext;
+      if (np->hnext)
+       np->hnext->hprevp = np->hprevp;
+      np->hnext = NULL;
+      np->hprevp = NULL;
+      nodehash_nr_items -= 1;
+      diskfs_nrele_light (np);
+    }
+  pthread_rwlock_unlock (&nodecache_lock);
+
+  diskfs_user_try_dropping_softrefs (np);
+}
+
+/* For each active node, call FUN.  The node is to be locked around the call
+   to FUN.  If FUN returns non-zero for any node, then immediately stop, and
+   return that value.  */
+error_t __attribute__ ((weak))
+diskfs_node_iterate (error_t (*fun)(struct node *))
+{
+  error_t err = 0;
+  int n;
+  size_t num_nodes;
+  struct node *node, **node_list, **p;
+
+  pthread_rwlock_rdlock (&nodecache_lock);
+
+  /* We must copy everything from the hash table into another data structure
+     to avoid running into any problems with the hash-table being modified
+     during processing (normally we delegate access to hash-table with
+     nodecache_lock, but we can't hold this while locking the
+     individual node locks).  */
+  /* XXX: Can we?  */
+  num_nodes = nodehash_nr_items;
+
+  /* TODO This method doesn't scale beyond a few dozen nodes and should be
+     replaced.  */
+  node_list = malloc (num_nodes * sizeof (struct node *));
+  if (node_list == NULL)
+    {
+      pthread_rwlock_unlock (&nodecache_lock);
+      error (0, 0, "unable to allocate temporary node table");
+      return ENOMEM;
+    }
+
+  p = node_list;
+  for (n = 0; n < INOHSZ; n++)
+    for (node = nodehash[n]; node; node = node->hnext)
+      {
+       *p++ = node;
+
+       /* We acquire a hard reference for node, but without using
+          diskfs_nref.  We do this so that diskfs_new_hardrefs will not
+          get called.  */
+       refcounts_ref (&node->refcounts, NULL);
+      }
+
+  pthread_rwlock_unlock (&nodecache_lock);
+
+  p = node_list;
+  while (num_nodes-- > 0)
+    {
+      node = *p++;
+      if (!err)
+       {
+         pthread_mutex_lock (&node->lock);
+         err = (*fun)(node);
+         pthread_mutex_unlock (&node->lock);
+       }
+      diskfs_nrele (node);
+    }
+
+  free (node_list);
+  return err;
+}
+
+/* The user must define this function if she wants to use the node
+   cache.  Create and initialize a node.  */
+error_t __attribute__ ((weak))
+diskfs_user_make_node (struct node **npp, struct lookup_context *ctx)
+{
+  assert (! "diskfs_user_make_node not implemented");
+}
+
+/* The user must define this function if she wants to use the node
+   cache.  Read stat information out of the on-disk node.  */
+error_t __attribute__ ((weak))
+diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
+{
+  assert (! "diskfs_user_read_node not implemented");
+}
+
+/* The user must define this function if she wants to use the node
+   cache.  The last hard reference to a node has gone away; arrange to
+   have all the weak references dropped that can be.  */
+void __attribute__ ((weak))
+diskfs_user_try_dropping_softrefs (struct node *np)
+{
+  assert (! "diskfs_user_try_dropping_softrefs not implemented");
+}
diff --git a/libdiskfs/node-drop.c b/libdiskfs/node-drop.c
index 83eb590..455031b 100644
--- a/libdiskfs/node-drop.c
+++ b/libdiskfs/node-drop.c
@@ -31,9 +31,8 @@ free_modreqs (struct modreq *mr)
 }
 
 
-/* Node NP now has no more references; clean all state.  The
-   diskfs_node_refcnt_lock must be held, and will be released
-   upon return.  NP must be locked.  */
+/* Node NP now has no more references; clean all state.  NP must be
+   locked.  */
 void
 diskfs_drop_node (struct node *np)
 {
@@ -60,8 +59,7 @@ diskfs_drop_node (struct node *np)
             and an nput.  The next time through, this routine
             will notice that the size is zero, and not have to
             do anything. */
-         np->references++;
-         pthread_spin_unlock (&diskfs_node_refcnt_lock);
+         refcounts_unsafe_ref (&np->refcounts, NULL);
          diskfs_truncate (np, 0);
          
          /* Force allocsize to zero; if truncate consistently fails this
@@ -93,6 +91,7 @@ diskfs_drop_node (struct node *np)
 
   assert (!np->sockaddr);
 
+  pthread_mutex_unlock(&np->lock);
+  pthread_mutex_destroy(&np->lock);
   diskfs_node_norefs (np);
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
 }
diff --git a/libdiskfs/node-make.c b/libdiskfs/node-make.c
index ff0cc0d..7bc1d85 100644
--- a/libdiskfs/node-make.c
+++ b/libdiskfs/node-make.c
@@ -27,10 +27,10 @@ init_node (struct node *np, struct disknode *dn)
   np->dn_set_atime = 0;
   np->dn_set_mtime = 0;
   np->dn_stat_dirty = 0;
+  np->author_tracks_uid = 0;
 
   pthread_mutex_init (&np->lock, NULL);
-  np->references = 1;
-  np->light_references = 0;
+  refcounts_init (&np->refcounts, 1, 0);
   np->owner = 0;
   np->sockaddr = MACH_PORT_NULL;
 
diff --git a/libdiskfs/node-nput.c b/libdiskfs/node-nput.c
index 5043ad1..d23c103 100644
--- a/libdiskfs/node-nput.c
+++ b/libdiskfs/node-nput.c
@@ -26,56 +26,44 @@
 void
 diskfs_nput (struct node *np)
 {
-  int tried_drop_softrefs = 0;
+  struct references result;
 
- loop:
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references + np->light_references == 0)
-    diskfs_drop_node (np);
-  else if (np->references == 0 && !tried_drop_softrefs)
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  /* While we call the diskfs_try_dropping_softrefs, we need to hold
+     one reference.  We use a weak reference for this purpose, which
+     we acquire by demoting our hard reference to a weak one.  */
+  refcounts_demote (&np->refcounts, &result);
 
+  if (result.hard == 0)
+    {
       /* This is our cue that something akin to "last process closes file"
         in the POSIX.1 sense happened, so make sure any pending node time
         updates now happen in a timely fashion.  */
       diskfs_set_node_times (np);
-
       diskfs_lost_hardrefs (np);
       if (!np->dn_stat.st_nlink)
        {
-         /* There are no links.  If there are soft references that
-            can be dropped, we can't let them postpone deallocation.
-            So attempt to drop them.  But that's a user-supplied
-            routine, which might result in further recursive calls to
-            the ref-counting system.  So we have to reacquire our
-            reference around the call to forestall disaster. */
-         pthread_spin_lock (&diskfs_node_refcnt_lock);
-         np->references++;
-         pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
          if (np->sockaddr != MACH_PORT_NULL)
            {
              mach_port_deallocate (mach_task_self (), np->sockaddr);
              np->sockaddr = MACH_PORT_NULL;
            }
 
+         /* There are no links.  If there are soft references that
+            can be dropped, we can't let them postpone deallocation.
+            So attempt to drop them.  But that's a user-supplied
+            routine, which might result in further recursive calls to
+            the ref-counting system.  This is not a problem, as we
+            hold a weak reference ourselves. */
          diskfs_try_dropping_softrefs (np);
-
-         /* But there's no value in looping forever in this
-            routine; only try to drop soft refs once. */
-         tried_drop_softrefs = 1;
-
-         /* Now we can drop the reference back... */
-         goto loop;
        }
       pthread_mutex_unlock (&np->lock);
     }
+
+  /* Finally get rid of our reference.  */
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
+    diskfs_drop_node (np);
   else
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      pthread_mutex_unlock (&np->lock);
-    }
+    pthread_mutex_unlock (&np->lock);
 }
diff --git a/libdiskfs/node-nputl.c b/libdiskfs/node-nputl.c
index 1959665..8dac16e 100644
--- a/libdiskfs/node-nputl.c
+++ b/libdiskfs/node-nputl.c
@@ -25,14 +25,10 @@
 void
 diskfs_nput_light (struct node *np)
 {
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  assert (np->light_references);
-  np->light_references--;
-  if (np->references + np->light_references == 0)
+  struct references result;
+  refcounts_deref_weak (&np->refcounts, &result);
+  if (result.hard == 0 && result.weak == 0)
     diskfs_drop_node (np);
   else
-    {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
-      pthread_mutex_unlock (&np->lock);
-    }
+    pthread_mutex_unlock (&np->lock);
 }
diff --git a/libdiskfs/node-nref.c b/libdiskfs/node-nref.c
index 13cea05..766a69c 100644
--- a/libdiskfs/node-nref.c
+++ b/libdiskfs/node-nref.c
@@ -26,12 +26,9 @@
 void
 diskfs_nref (struct node *np)
 {
-  int new_hardref;
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  np->references++;
-  new_hardref = (np->references == 1);
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
-  if (new_hardref)
+  struct references result;
+  refcounts_ref (&np->refcounts, &result);
+  if (result.hard == 1)
     {
       pthread_mutex_lock (&np->lock);
       diskfs_new_hardrefs (np);
diff --git a/libdiskfs/node-nrefl.c b/libdiskfs/node-nrefl.c
index 9692247..f7a823d 100644
--- a/libdiskfs/node-nrefl.c
+++ b/libdiskfs/node-nrefl.c
@@ -24,7 +24,5 @@
 void
 diskfs_nref_light (struct node *np)
 {
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  np->light_references++;
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  refcounts_ref_weak (&np->refcounts, NULL);
 }
diff --git a/libdiskfs/node-nrele.c b/libdiskfs/node-nrele.c
index cc68089..d962846 100644
--- a/libdiskfs/node-nrele.c
+++ b/libdiskfs/node-nrele.c
@@ -28,38 +28,36 @@
 void
 diskfs_nrele (struct node *np)
 {
-  int tried_drop_softrefs = 0;
+  struct references result;
 
- loop:
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  assert (np->references);
-  np->references--;
-  if (np->references + np->light_references == 0)
-    {
-      pthread_mutex_lock (&np->lock);
-      diskfs_drop_node (np);
-    }
-  else if (np->references == 0)
+  /* While we call the diskfs_try_dropping_softrefs, we need to hold
+     one reference.  We use a weak reference for this purpose, which
+     we acquire by demoting our hard reference to a weak one.  */
+  refcounts_demote (&np->refcounts, &result);
+
+  if (result.hard == 0)
     {
       pthread_mutex_lock (&np->lock);
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
       diskfs_lost_hardrefs (np);
-      if (!np->dn_stat.st_nlink && !tried_drop_softrefs)
+      if (!np->dn_stat.st_nlink)
        {
-         /* Same issue here as in nput; see that for explanation */
-         pthread_spin_lock (&diskfs_node_refcnt_lock);
-         np->references++;
-         pthread_spin_unlock (&diskfs_node_refcnt_lock);
-
+         /* There are no links.  If there are soft references that
+            can be dropped, we can't let them postpone deallocation.
+            So attempt to drop them.  But that's a user-supplied
+            routine, which might result in further recursive calls to
+            the ref-counting system.  This is not a problem, as we
+            hold a weak reference ourselves. */
          diskfs_try_dropping_softrefs (np);
-         tried_drop_softrefs = 1;
-
-         /* Now we can drop the reference back... */
-         pthread_mutex_unlock (&np->lock);
-         goto loop;
        }
       pthread_mutex_unlock (&np->lock);
     }
-  else
-    pthread_spin_unlock (&diskfs_node_refcnt_lock);
+
+  /* Finally get rid of our reference.  */
+  refcounts_deref_weak (&np->refcounts, &result);
+
+  if (result.hard == 0 && result.weak == 0)
+    {
+      pthread_mutex_lock (&np->lock);
+      diskfs_drop_node (np);
+    }
 }
diff --git a/libdiskfs/node-nrelel.c b/libdiskfs/node-nrelel.c
index ee53b22..dc4f920 100644
--- a/libdiskfs/node-nrelel.c
+++ b/libdiskfs/node-nrelel.c
@@ -26,14 +26,11 @@
 void
 diskfs_nrele_light (struct node *np)
 {
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  assert (np->light_references);
-  np->light_references--;
-  if (np->references + np->light_references == 0)
+  struct references result;
+  refcounts_deref_weak (&np->refcounts, &result);
+  if (result.hard == 0 && result.weak == 0)
     {
       pthread_mutex_lock (&np->lock);
       diskfs_drop_node (np);
     }
-  else
-    pthread_spin_unlock (&diskfs_node_refcnt_lock);
 }
diff --git a/tmpfs/node.c b/tmpfs/node.c
index 02d7a60..2a4489c 100644
--- a/tmpfs/node.c
+++ b/tmpfs/node.c
@@ -29,8 +29,18 @@
 unsigned int num_files;
 static unsigned int gen;
 
+/* all_nodes is a list of all nodes.
+
+   Access to all_nodes and all_nodes_nr_items is protected by
+   all_nodes_lock.
+
+   Every node in all_nodes carries a light reference.  When we are
+   asked to give up that light reference, we reacquire our lock
+   momentarily to check whether someone else reacquired a
+   reference.  */
 struct node *all_nodes;
 static size_t all_nodes_nr_items;
+pthread_rwlock_t all_nodes_lock = PTHREAD_RWLOCK_INITIALIZER;
 
 error_t
 diskfs_alloc_node (struct node *dp, mode_t mode, struct node **npp)
@@ -40,18 +50,17 @@ diskfs_alloc_node (struct node *dp, mode_t mode, struct 
node **npp)
   dn = calloc (1, sizeof *dn);
   if (dn == 0)
     return ENOSPC;
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  if (round_page (tmpfs_space_used + sizeof *dn) / vm_page_size
+
+  if (round_page (get_used () + sizeof *dn) / vm_page_size
       > tmpfs_page_limit)
     {
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
+      pthread_rwlock_unlock (&all_nodes_lock);
       free (dn);
       return ENOSPC;
     }
   dn->gen = gen++;
-  ++num_files;
-  tmpfs_space_used += sizeof *dn;
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  __atomic_add_fetch (&num_files, 1, __ATOMIC_RELAXED);
+  adjust_used (sizeof *dn);
 
   dn->type = IFTODT (mode & S_IFMT);
   return diskfs_cached_lookup ((ino_t) (uintptr_t) dn, npp);
@@ -75,15 +84,19 @@ diskfs_free_node (struct node *np, mode_t mode)
       free (np->dn->u.lnk);
       break;
     }
+
+  pthread_rwlock_wrlock (&all_nodes_lock);
   *np->dn->hprevp = np->dn->hnext;
   if (np->dn->hnext != 0)
     np->dn->hnext->dn->hprevp = np->dn->hprevp;
   all_nodes_nr_items -= 1;
+  pthread_rwlock_unlock (&all_nodes_lock);
+
   free (np->dn);
   np->dn = 0;
 
-  --num_files;
-  tmpfs_space_used -= sizeof *np->dn;
+  __atomic_sub_fetch (&num_files, 1, __ATOMIC_RELAXED);
+  adjust_used (-sizeof *np->dn);
 }
 
 void
@@ -117,14 +130,6 @@ diskfs_node_norefs (struct node *np)
          np->dn->u.chr = np->dn_stat.st_rdev;
          break;
        }
-
-      /* Remove this node from the cache list rooted at `all_nodes'.  */
-      *np->dn->hprevp = np->dn->hnext;
-      if (np->dn->hnext != 0)
-       np->dn->hnext->dn->hprevp = np->dn->hprevp;
-      all_nodes_nr_items -= 1;
-      np->dn->hnext = 0;
-      np->dn->hprevp = 0;
     }
 
   free (np);
@@ -167,30 +172,34 @@ diskfs_cached_lookup (ino_t inum, struct node **npp)
 
   assert (npp);
 
+  pthread_rwlock_rdlock (&all_nodes_lock);
   if (dn->hprevp != 0)         /* There is already a node.  */
-    {
-      np = *dn->hprevp;
-      assert (np->dn == dn);
-      assert (*dn->hprevp == np);
-
-      diskfs_nref (np);
-    }
+    goto gotit;
   else
     /* Create the new node.  */
     {
       struct stat *st;
+      pthread_rwlock_unlock (&all_nodes_lock);
 
       np = diskfs_make_node (dn);
       np->cache_id = (ino_t) (uintptr_t) dn;
 
-      pthread_spin_lock (&diskfs_node_refcnt_lock);
+      pthread_rwlock_wrlock (&all_nodes_lock);
+      if (dn->hprevp != NULL)
+        {
+          /* We lost a race.  */
+          diskfs_nrele (np);
+          goto gotit;
+        }
+
       dn->hnext = all_nodes;
       if (dn->hnext)
        dn->hnext->dn->hprevp = &dn->hnext;
       dn->hprevp = &all_nodes;
       all_nodes = np;
       all_nodes_nr_items += 1;
-      pthread_spin_unlock (&diskfs_node_refcnt_lock);
+      diskfs_nref_light (np);
+      pthread_rwlock_unlock (&all_nodes_lock);
 
       st = &np->dn_stat;
       memset (st, 0, sizeof *st);
@@ -220,6 +229,16 @@ diskfs_cached_lookup (ino_t inum, struct node **npp)
   pthread_mutex_lock (&np->lock);
   *npp = np;
   return 0;
+
+ gotit:
+  np = *dn->hprevp;
+  assert (np->dn == dn);
+  assert (*dn->hprevp == np);
+  diskfs_nref (np);
+  pthread_rwlock_unlock (&all_nodes_lock);
+  pthread_mutex_lock (&np->lock);
+  *npp = np;
+  return 0;
 }
 
 error_t
@@ -229,12 +248,12 @@ diskfs_node_iterate (error_t (*fun) (struct node *))
   size_t num_nodes;
   struct node *node, **node_list, **p;
 
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
+  pthread_rwlock_rdlock (&all_nodes_lock);
 
   /* We must copy everything from the hash table into another data structure
      to avoid running into any problems with the hash-table being modified
      during processing (normally we delegate access to hash-table with
-     diskfs_node_refcnt_lock, but we can't hold this while locking the
+     all_nodes_lock, but we can't hold this while locking the
      individual node locks).  */
 
   num_nodes = all_nodes_nr_items;
@@ -243,10 +262,14 @@ diskfs_node_iterate (error_t (*fun) (struct node *))
   for (node = all_nodes; node != 0; node = node->dn->hnext)
     {
       *p++ = node;
-      node->references++;
+
+      /* We acquire a hard reference for node, but without using
+        diskfs_nref.  We do this so that diskfs_new_hardrefs will not
+        get called.  */
+      refcounts_ref (&node->refcounts, NULL);
     }
 
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  pthread_rwlock_unlock (&all_nodes_lock);
 
   p = node_list;
   while (num_nodes-- > 0)
@@ -272,6 +295,27 @@ diskfs_node_iterate (error_t (*fun) (struct node *))
 void
 diskfs_try_dropping_softrefs (struct node *np)
 {
+  pthread_rwlock_wrlock (&all_nodes_lock);
+  if (np->cache_id != 0)
+    {
+      /* Check if someone reacquired a reference.  */
+      struct references result;
+      refcounts_references (&np->refcounts, &result);
+
+      if (result.hard > 0)
+       {
+         /* A reference was reacquired.  It's fine, we didn't touch
+            anything yet. */
+         pthread_rwlock_unlock (&all_nodes_lock);
+         return;
+       }
+
+      /* Just let go of the weak reference.  The node will be removed
+        from all_nodes in diskfs_free_node.  */
+      np->cache_id = 0;
+      diskfs_nrele_light (np);
+    }
+  pthread_rwlock_unlock (&all_nodes_lock);
 }
 
 /* The user must define this funcction.  Node NP has some light
@@ -447,7 +491,7 @@ diskfs_grow (struct node *np, off_t size, struct protid 
*cred)
 
   off_t set_size = size;
   size = round_page (size);
-  if (round_page (tmpfs_space_used + size - np->allocsize)
+  if (round_page (get_used () + size - np->allocsize)
       / vm_page_size > tmpfs_page_limit)
     return ENOSPC;
 
diff --git a/tmpfs/tmpfs.c b/tmpfs/tmpfs.c
index 1b5b374..fd1c9aa 100644
--- a/tmpfs/tmpfs.c
+++ b/tmpfs/tmpfs.c
@@ -67,10 +67,8 @@ diskfs_set_statfs (struct statfs *st)
   st->f_bsize = vm_page_size;
   st->f_blocks = tmpfs_page_limit;
 
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  st->f_files = num_files;
-  pages = round_page (tmpfs_space_used) / vm_page_size;
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  st->f_files = __atomic_load_n (&num_files, __ATOMIC_RELAXED);
+  pages = round_page (get_used ()) / vm_page_size;
 
   st->f_bfree = pages < tmpfs_page_limit ? tmpfs_page_limit - pages : 0;
   st->f_bavail = st->f_bfree;
diff --git a/tmpfs/tmpfs.h b/tmpfs/tmpfs.h
index b3c636d..ad47200 100644
--- a/tmpfs/tmpfs.h
+++ b/tmpfs/tmpfs.h
@@ -69,17 +69,25 @@ struct tmpfs_dirent
   char name[0];
 };
 
-extern unsigned int num_files;
-extern off_t tmpfs_page_limit, tmpfs_space_used;
-
+extern off_t tmpfs_page_limit;
 extern mach_port_t default_pager;
 
+/* These two must be accessed using atomic operations.  */
+extern unsigned int num_files;
+extern off_t tmpfs_space_used;
+
+/* Convenience function to adjust tmpfs_space_used.  */
 static inline void
 adjust_used (off_t change)
 {
-  pthread_spin_lock (&diskfs_node_refcnt_lock);
-  tmpfs_space_used += change;
-  pthread_spin_unlock (&diskfs_node_refcnt_lock);
+  __atomic_add_fetch (&num_files, change, __ATOMIC_RELAXED);
+}
+
+/* Convenience function to get tmpfs_space_used.  */
+static inline off_t
+get_used (void)
+{
+  return __atomic_load_n (&num_files, __ATOMIC_RELAXED);
 }
 
 #endif
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
index a223bc4..63303a0 100644
--- a/trans/fakeroot.c
+++ b/trans/fakeroot.c
@@ -75,6 +75,9 @@ new_node (file_t file, mach_port_t idport, int locked, int 
openmodes,
 {
   error_t err;
   struct netnode *nn;
+
+  assert ((openmodes & ~(O_RDWR|O_EXEC)) == 0);
+
   *np = netfs_make_node_alloc (sizeof *nn);
   if (*np == 0)
     {
@@ -203,14 +206,16 @@ check_openmodes (struct netnode *nn, int newmodes, file_t 
file)
 {
   error_t err = 0;
 
+  assert ((newmodes & ~(O_RDWR|O_EXEC)) == 0);
+
   if (newmodes &~ nn->openmodes)
     {
       /* The user wants openmodes we haven't tried before.  */
 
       if (file != MACH_PORT_NULL && (nn->openmodes & ~newmodes))
        {
-         /* Intersecting sets.
-            We need yet another new peropen on this node.  */
+         /* Intersecting sets with no inclusion. `file' doesn't fit either,
+            we need yet another new peropen on this node.  */
          mach_port_deallocate (mach_task_self (), file);
          file = MACH_PORT_NULL;
        }
@@ -390,7 +395,7 @@ netfs_S_dir_lookup (struct protid *diruser,
   else
     {
       pthread_spin_unlock (&netfs_node_refcnt_lock);
-      err = new_node (file, idport, 1, flags, &np);
+      err = new_node (file, idport, 1, flags & (O_RDWR|O_EXEC), &np);
       pthread_mutex_unlock (&dnp->lock);
       if (!err)
        {
@@ -428,6 +433,20 @@ netfs_S_dir_lookup (struct protid *diruser,
   return err;
 }
 
+/* The user may define this function.  Attempt to set the passive
+   translator record for FILE to ARGZ (of length ARGZLEN) for user
+   CRED. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *np,
+                     char *argz, size_t argzlen)
+{
+  return file_set_translator (netfs_node_netnode (np)->file,
+                             FS_TRANS_EXCL|FS_TRANS_SET,
+                             FS_TRANS_EXCL|FS_TRANS_SET, 0,
+                             argz, argzlen,
+                             MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
+}
+
 /* These callbacks are used only by the standard netfs_S_dir_lookup,
    which we do not use.  But the shared library requires us to define them.  */
 error_t
@@ -525,14 +544,28 @@ real_from_fake_mode (mode_t mode)
 error_t
 netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode)
 {
+  struct netnode *nn;
+  mode_t real_mode;
+
   if ((mode & S_IFMT) == 0)
     mode |= np->nn_stat.st_mode & S_IFMT;
   if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
     return EOPNOTSUPP;
 
+  /* Make sure that `check_openmodes' will still always be able to reopen
+     it.  */
+  real_mode = mode;
+  nn = netfs_node_netnode (np);
+  if (nn->openmodes & O_READ)
+    real_mode |= S_IRUSR;
+  if (nn->openmodes & O_WRITE)
+    real_mode |= S_IWUSR;
+  if (nn->openmodes & O_EXEC)
+    real_mode |= S_IXUSR;
+
   /* We don't bother with error checking since the fake mode change should
      always succeed--worst case a later open will get EACCES.  */
-  (void) file_chmod (netfs_node_netnode (np)->file, mode);
+  (void) file_chmod (nn->file, real_mode);
   set_faked_attribute (np, FAKE_MODE);
   np->nn_stat.st_mode = mode;
   return 0;

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-hurd/hurd.git



reply via email to

[Prev in Thread] Current Thread [Next in Thread]