Anyone up for checking for a symlink bug on their NeXT hardware?

jktw

New Tinkerer
May 31, 2026
6
0
1
The NeXT cube that had been sitting in my basement "just worked" after I added a new battery, a BlueSCSI and unplugged the MO drive. I found [this list](https://github.com/rjbrown99/next/wiki/NeXTStep-Software-Ports) of software to add ssh, newer gcc etc, and worked through the list. Cool!

After that was done, I wanted to find the latest version of curl I could build with gcc 2.95.3. I figured out the right flags for curl 7.17 myself, but things started getting weird around version 7.20 (released in 2010!). I asked AI to help over ssh, and to make a long story short, one of the problems was that the configure script would fail consistently with subs.awk:2 invalid char '' in expression. Evidently the subs.awk file was corrupted, and Claude identified it as a bug with symlinks in the NeXT kernel. I asked it to write a test script to reproduce the bug.

Is anyone interested to run this on their NeXT hardware to confirm? I'm running 3.3 with CISCUserPatch3 installed and ~30 year old capacitors.

Code:
#!/bin/sh
#
# symlink-ghost.sh - test for the stale symlink size bug seen on NeXTSTEP 3.3.
#
# Deleting a symbolic link appears to free its inode without clearing the
# inode's size.  The next file created on that inode then starts out with
# the symlink target's length, filled with NUL bytes, even when it was
# opened with O_TRUNC.
#
# The test creates a symlink in a fresh directory, deletes it, then creates
# an empty file in another fresh directory.  The filesystem normally hands
# that file the inode just freed by the symlink.  On an affected system the
# "empty" file is 20 bytes of NULs.
#
# Usage:  sh symlink-ghost.sh [directory]
#         The directory defaults to the current one; it must be writable.
#         Run it once per filesystem you want to check.
#
# Exit status: 0 = not affected, 1 = bug seen, 2 = test could not run,
#              3 = inconclusive (the freed inode was never reused).

dir=${1-.}
cd "$dir" || exit 2
target=0123456789ABCDEFGHIJ        # 20 characters
runs=5

hostinfo 2>/dev/null | sed -n '1,2p'
echo "Testing in `pwd`"

bad=0
reused=0
i=1
while [ $i -le $runs ]; do
    d1=ghost$$.$i.a
    d2=ghost$$.$i.b
    mkdir $d1 || exit 2
    ln -s $target $d1/link || { rmdir $d1; exit 2; }
    sino=`ls -id $d1/link | awk '{print $1}'`
    rm -f $d1/link
    rmdir $d1

    mkdir $d2 || exit 2
    : > $d2/empty
    fino=`ls -id $d2/empty | awk '{print $1}'`
    [ "$sino" = "$fino" ] && reused=`expr $reused + 1`

    if [ -s $d2/empty ]; then
        bad=`expr $bad + 1`
        echo "run $i: BUG - new empty file is not empty" \
            "(symlink inode $sino, file inode $fino)"
        ls -l $d2/empty
        od -c $d2/empty | sed -n '1p'
    else
        echo "run $i: ok (symlink inode $sino, file inode $fino)"
    fi
    rm -rf $d2
    i=`expr $i + 1`
done

echo
if [ $bad -gt 0 ]; then
    echo "AFFECTED: $bad of $runs empty files inherited the deleted symlink's size."
    exit 1
elif [ $reused -eq 0 ]; then
    echo "INCONCLUSIVE: the freed symlink inode was never reused; try another directory."
    exit 3
else
    echo "NOT AFFECTED: $reused of $runs files reused the symlink's inode and were empty."
    exit 0
fi