rmdir, or a rename onto an empty directory, clears the directory and
drops its last link, but the node stays as long as someone holds it: a
port on it, or a process's working directory. Nothing stopped a
create, a link or a rename through such a reference from entering a
name in it. The name is in a directory nothing reaches; when the node
goes, its blocks are freed, and the file entered stays allocated with
no name at all (e2fsck: "Unattached inode"). With a rename, the file's
old name is gone too.
diskfs_direnter now refuses with ENOENT, as Linux does, when the
directory has no links. Its callers already undo the link they took on
an error. diskfs_rename_dir checks before it rewrites "..", as its
error path after the rewrite does not undo it.
How to reproduce:
$ mkdir /tmp/d && cd /tmp/d && rmdir /tmp/d && touch x; echo $?
0
The create in the removed directory succeeds; with this change it fails
with ENOENT.
---
libdiskfs/dir-renamed.c | 7 +++++++
libdiskfs/direnter.c | 9 +++++++++
2 files changed, 16 insertions(+)
diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c
index 17efebb04..2a7ff51f6 100644
--- a/libdiskfs/dir-renamed.c
+++ b/libdiskfs/dir-renamed.c
@@ -105,6 +105,13 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp,
const char *fromname,
fnp = NULL;
goto out;
}
+ /* TDP was removed (see diskfs_direnter): fail before ".." changes. */
+ if (tdp->dn_stat.st_nlink == 0)
+ {
+ err = ENOENT;
+ fnp = NULL;
+ goto out;
+ }
if (tnp && excl)
{
err = EEXIST;
diff --git a/libdiskfs/direnter.c b/libdiskfs/direnter.c
index 17cc618d2..93bc2a814 100644
--- a/libdiskfs/direnter.c
+++ b/libdiskfs/direnter.c
@@ -38,6 +38,15 @@ diskfs_direnter (struct node *dp,
{
error_t err;
+ /* A directory that was removed, by rmdir or by a rename onto it, stays
+ reachable through the ports and working directories still on it, but
+ nothing reaches what is entered in it. */
+ if (dp->dn_stat.st_nlink == 0)
+ {
+ diskfs_drop_dirstat (dp, ds);
+ return ENOENT;
+ }
+
err = diskfs_direnter_hard (dp, name, np, ds, cred);
if (err)
return err;
--
2.56.0