D1: teach SplitDisk directories, without moving a byte
SBFS version two adds directories out of space each entry had already set aside: two of the four reserved bytes become a parent, and one of the seven spare flag bits says an entry is a directory. The entry is still thirty two bytes, so it still divides two hundred and fifty six and still never straddles a block, and nothing in the block layer knows anything happened. A directory is an entry with no blocks. That is what keeps the flat array of entries the whole allocation map, which is the property the format is built on: with files laid down contiguously, every block is inside some entry's range or it is not, and an entry with no range is in nobody's way. There is still no allocation table to consult and none to keep right. THE PARENT IS AN INDEX PLUS ONE, so zero means the root. A version one disk has zeroes in those bytes, and "in the root" is exactly where every file on a flat disk is - so a version one image is already a valid version two image, with nothing to convert and no tool to convert it with. A disk is at the lowest version that describes what is on it. format makes a version one disk and mkdir is what raises it, so everything built here stays readable by a reader that has never heard of a directory right up until it really does have one. That is what lets this land before the machine knows anything: the whole existing suite passes untouched. The tool gains mkdir and rmdir, and list, put, get and delete take paths. list also now reports entries used against entries available, because a disk has two ceilings and the entry one is the one nobody notices until it bites. rmdir refuses a directory with anything in it, and that is not politeness: parents are entry indices, a freed index gets handed out again, and the children of a removed directory would reappear inside whatever took its place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
f4fb56606e
commit
78e9eef472
@@ -97,6 +97,64 @@ refuses "refuse a file with no run long enough" "$TOOL" put frag.img big.bin
|
||||
head -c 512 /dev/urandom > fits.bin
|
||||
check "but one that fits the gap goes on" "$TOOL" put frag.img fits.bin
|
||||
|
||||
# ---- Directories ----
|
||||
#
|
||||
# Version two, which adds a parent to each entry and a flag bit saying an entry is a
|
||||
# directory. Both come out of bytes the entry had already set aside, so nothing moved and
|
||||
# a version one disk needs no converting: zero in those bytes means the root, which is
|
||||
# exactly where every file on a flat disk is.
|
||||
#
|
||||
# The version is therefore a statement about what is ON a disk rather than about what made
|
||||
# it, and these check that it is only raised when it becomes true.
|
||||
"$TOOL" format tree.img 64 2 >/dev/null 2>&1
|
||||
printf 'a file in the root' > root.txt
|
||||
check "a fresh disk is flat" "$TOOL" put tree.img root.txt
|
||||
version() { "$TOOL" list "$1" 2>/dev/null | head -1 | grep -q "version $2"; }
|
||||
check "and says it is version 1" version tree.img 1
|
||||
check "make a directory" "$TOOL" mkdir tree.img /Apps
|
||||
check "which raises it to version 2" version tree.img 2
|
||||
check "make one inside it" "$TOOL" mkdir tree.img /Apps/Source
|
||||
check "put a file down a path" "$TOOL" put tree.img root.txt /Apps/Source/deep.txt
|
||||
|
||||
# The point of the whole exercise: a name means something different in each place, so the
|
||||
# same one can be used twice without either being in the other's way.
|
||||
check "the same name in two places" "$TOOL" put tree.img root.txt /Apps/root.txt
|
||||
roundTripAt() {
|
||||
"$TOOL" get tree.img "$1" got_deep.txt >/dev/null 2>&1 || return 1
|
||||
cmp -s root.txt got_deep.txt
|
||||
}
|
||||
check "it comes back byte for byte" roundTripAt /Apps/Source/deep.txt
|
||||
check ". and .. walk the path" roundTripAt /Apps/./Source/../root.txt
|
||||
check ".. from the root is the root" roundTripAt /Apps/../../root.txt
|
||||
|
||||
# Each of these is a way the tree could be made to contradict itself, and each is refused
|
||||
# rather than half done.
|
||||
refuses "no file where a directory goes" "$TOOL" put tree.img root.txt /root.txt/x.txt
|
||||
refuses "no putting into thin air" "$TOOL" put tree.img root.txt /Nowhere/x.txt
|
||||
refuses "no duplicate in one directory" "$TOOL" mkdir tree.img /Apps
|
||||
refuses "no getting a directory" "$TOOL" get tree.img /Apps out.bin
|
||||
refuses "delete will not take a directory" "$TOOL" delete tree.img /Apps
|
||||
refuses "rmdir will not take a file" "$TOOL" rmdir tree.img /root.txt
|
||||
refuses "nor the root" "$TOOL" rmdir tree.img /
|
||||
|
||||
# THE REFUSAL THAT MATTERS MOST. Parents are entry indices and a freed index is handed out
|
||||
# again, so removing a directory with things still in it would let the next file created
|
||||
# adopt them. Emptying it first is the only safe order.
|
||||
refuses "no removing an occupied one" "$TOOL" rmdir tree.img /Apps/Source
|
||||
check "empty it first" "$TOOL" delete tree.img /Apps/Source/deep.txt
|
||||
check "then it goes" "$TOOL" rmdir tree.img /Apps/Source
|
||||
|
||||
# A path is names with separators between them, and a name is still twenty two characters.
|
||||
refuses "refuse a 23 character component" "$TOOL" mkdir tree.img /Apps/abcdefghijklmnopqrstuvw
|
||||
refuses "refuse a path naming nothing" "$TOOL" mkdir tree.img /Apps/
|
||||
|
||||
# A directory costs an entry and no blocks at all, which is what keeps the flat array of
|
||||
# entries the whole allocation map. If a directory ever took a block, this would drop.
|
||||
blocksFree() { "$TOOL" list "$1" 2>/dev/null | tail -1 | sed 's/.*used, //; s/ blocks free.*//'; }
|
||||
before=$(blocksFree tree.img)
|
||||
"$TOOL" mkdir tree.img /Empty >/dev/null 2>&1
|
||||
check "a directory costs no blocks" [ "$before" = "$(blocksFree tree.img)" ]
|
||||
|
||||
echo
|
||||
if [ "$FAIL" -eq 0 ]; then
|
||||
echo "All $PASS disk tool checks passed."
|
||||
|
||||
Reference in New Issue
Block a user