diskfs_alloc_node returns the new node locked and with a hard
reference. When diskfs_create_node then fails before entering the node
in the directory, for instance because diskfs_init_dir finds no free
block for a new directory, the error path cleared the mode and link
count and returned without releasing the node! The node stayed locked
for good, and its inode stayed allocated on disk until e2fsck freed it.
The next diskfs_node_iterate then waits for that lock forever. Without
a journal this stops the periodic sync until reboot silently.
With a journal the periodic sync holds a handle while it waits, so the commit
cannot drain and every new RPC blocks behind it.
Running mkdir on a full filesystem is enough to trigger it.
Release the node with diskfs_nput. As it is unlinked, diskfs_drop_node
truncates it and frees its inode. Keep the file type in st_mode rather
than clearing it, since diskfs_drop_node passes the mode to
diskfs_free_node, and ext2fs uses it to undo the used directories count
that ext2_alloc_inode took for a directory.
---
libdiskfs/node-create.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libdiskfs/node-create.c b/libdiskfs/node-create.c
index 28d2c6e34..56d548f75 100644
--- a/libdiskfs/node-create.c
+++ b/libdiskfs/node-create.c
@@ -137,11 +137,16 @@ diskfs_create_node (struct node *dir,
if (err)
{
change_err:
- np->dn_stat.st_mode = 0;
+ /* Hand the inode back: dropping the last reference to an unlinked
+ node truncates and frees it. Keep the file type so that
+ diskfs_free_node undoes what diskfs_alloc_node accounted for it,
+ such as the count of used directories. */
+ np->dn_stat.st_mode = mode & S_IFMT;
np->dn_stat.st_nlink = 0;
if (name)
diskfs_drop_dirstat (dir, ds);
*newnode = NULL;
+ diskfs_nput (np);
return err;
}
--
2.56.0