Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177026669
D60355.1793745835.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
5 KB
Referenced Files
None
Subscribers
None
D60355.1793745835.diff
View Options
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -1236,6 +1236,28 @@
__DEVOLATILE(int *, &rename_restarts), 0,
"Times rename had to restart due to lock contention");
+/*
+ * A directory moved by ufs_rename() to a new parent could not have its ".."
+ * changed to name the new parent, so ".." still names the old one, which
+ * keeps its link. Take back the link added to the new parent, if one was,
+ * and cancel the journal record set up for it, which would otherwise never
+ * be written and would keep the new parent's link count rolled back in
+ * every write of its inode.
+ */
+static void
+ufs_rename_dotdot_undo(struct inode *tdp, struct inode *fip, bool addedlink)
+{
+
+ if (addedlink) {
+ tdp->i_effnlink--;
+ tdp->i_nlink--;
+ DIP_SET_NLINK(tdp, tdp->i_nlink);
+ UFS_INODE_SET_FLAG(tdp, IN_CHANGE);
+ }
+ if (DOINGSUJ(ITOV(tdp)))
+ softdep_revert_link(fip, tdp);
+}
+
/*
* Rename system call.
* rename("foo", "bar");
@@ -1743,13 +1765,16 @@
error = UFS_UPDATE(tdvp, !DOINGSOFTDEP(tdvp) &&
!DOINGASYNC(tdvp));
/* Don't go to bad here as the new link exists. */
- if (error)
+ if (error) {
+ ufs_rename_dotdot_undo(tdp, fip, true);
goto unlockout;
+ }
} else if (DOINGSUJ(tdvp))
/* Journal must account for each new link. */
softdep_setup_dotdot_link(tdp, fip);
SET_I_OFFSET(fip, mastertemplate.dot_reclen);
- (void)ufs_dirrewrite(fip, fdp, newparent, DT_DIR, 0);
+ if (ufs_dirrewrite(fip, fdp, newparent, DT_DIR, 0) != 0)
+ ufs_rename_dotdot_undo(tdp, fip, tip == NULL);
cache_purge(fdvp);
}
error = ufs_dirremove(fdvp, fip, fcnp->cn_flags, false);
diff --git a/tools/test/stress2/misc/rename19.sh b/tools/test/stress2/misc/rename19.sh
new file mode 100755
--- /dev/null
+++ b/tools/test/stress2/misc/rename19.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Maksym Sobolyev <sobomax@FreeBSD.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# rename(2) of a directory to a new parent when ".." cannot be rewritten.
+#
+# ufs_rename() adds a link to the new parent for the moved directory's
+# "..", and with SU+J a journal record (jaddref) for it, and then rewrites
+# "..". When that rewrite failed, for example on a read error, ".." still
+# named the old parent, but the new parent kept the extra link and the
+# journal record was never written. With SU+J the record kept the new
+# parent's link count rolled back in every write of its inode, so the inode
+# block was redirtied forever: unmount failed with EAGAIN once fsync gave up
+# on it, and a forced unmount after I/O errors retried forever.
+# Without the journal, fsck found the new parent's link count too high.
+#
+# The read of the moved directory's block is made to fail with gnop(8),
+# after everything else the rename needs is cached. fsck_ffs then has to
+# report the stale "..", which shows that the rewrite failed, and nothing
+# else.
+
+[ `id -u` -ne 0 ] && echo "Must be root!" && exit 1
+
+. ../default.cfg
+
+kldstat -q -m g_nop || gnop load || exit 1
+log=/tmp/rename19.log
+s=0
+
+for flags in -U -j; do
+ set -e
+ mount | grep -q "on $mntpoint " && umount -f $mntpoint
+ mdconfig -l | grep -q "md$mdstart" && mdconfig -d -u $mdstart
+ mdconfig -a -t swap -s 1g -u $mdstart
+ newfs $flags /dev/md$mdstart > /dev/null
+ gnop create /dev/md$mdstart
+ mount /dev/md$mdstart.nop $mntpoint
+ mkdir -p $mntpoint/a/d $mntpoint/b
+ # Drop the cached blocks, then cache all but the block of a/d.
+ umount $mntpoint
+ mount /dev/md$mdstart.nop $mntpoint
+ set +e
+ ls -la $mntpoint $mntpoint/a $mntpoint/b > /dev/null
+ stat $mntpoint/a/d > /dev/null
+
+ gnop configure -r 100 md$mdstart.nop
+ mv $mntpoint/a/d $mntpoint/b/d
+ gnop configure -r 0 md$mdstart.nop
+
+ umount $mntpoint &
+ upid=$!
+ t=0
+ while kill -0 $upid 2>/dev/null; do
+ sleep 1
+ t=$((t + 1))
+ if [ $t -eq 120 ]; then
+ echo "FAIL ($flags): umount $mntpoint is stuck"
+ procstat -kk $upid
+ # The mount point stays busy, leave everything as is.
+ exit 1
+ fi
+ done
+ if ! wait $upid; then
+ # The suspension gave up on the inode block of b.
+ echo "FAIL ($flags): umount $mntpoint failed"
+ exit 1
+ fi
+
+ fsck_ffs -fn /dev/md$mdstart.nop > $log 2>&1
+ if ! grep -q "'\.\.'" $log; then
+ echo "SKIP ($flags): the rewrite of \"..\" did not fail"
+ cat $log
+ elif grep -q "LINK COUNT" $log; then
+ echo "FAIL ($flags): link count left wrong"
+ cat $log
+ s=1
+ fi
+ gnop destroy md$mdstart.nop
+ mdconfig -d -u $mdstart
+done
+rm -f $log
+exit $s
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 3, 10:43 PM (14 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41387717
Default Alt Text
D60355.1793745835.diff (5 KB)
Attached To
Mode
D60355: ufs: undo the new parent's link when ufs_rename() cannot rewrite ".."
Attached
Detach File
Event Timeline