Refuse a streamed file that commits more than it reserved
osFileStart sets an extent aside and osFileWrite refuses a block index outside it, so writing off the end was already barred. Committing a larger size was not, and reaches the same neighbour by simply claiming it: a directory entry is the only record of what a file owns, so an entry claiming a block it was never given owns it, and so does whatever owned it before. Both files then look perfectly well formed. The free count went backwards past zero on the same path. Found by ChatGPT's review of the streaming work, in NOTES.md. I had bounded the index because writing off the end was the obvious way to reach a neighbour, and had not noticed that the other end of the same reservation was open. THE SIZE IS COMPARED, NOT THE ROOM IT TAKES UP. One block and a tail occupies exactly what two whole blocks occupy, so bounding the blocks alone would let a file reserve the first, commit the second, claim no block it was not given, and still report two hundred and forty six bytes that were never written to it - whatever the disk had there before. Checked before anything is touched, which is why the temporary is found twice. The old file is deleted a few lines down and a refusal after that point would have destroyed the thing it was protecting. AND IT CAUGHT A REAL ONE IMMEDIATELY. The assembler reserves the file plus room for its vectors, and asked for four bytes per vector DECLARED - which looks like a safe bound and is not, because a device is declared during the SECOND pass, in the line that implements it. A program with a device installs a vector that was not counted when the room was measured. CosmOS reserved 14,163 bytes and committed 14,167, writing four bytes past what it had been given on every build since S2. It landed inside the last block it owned, and would not have if the boundary had fallen four bytes earlier. It reserves against the vector table's LIMIT now, which cannot go stale whenever things are counted. Claim.asm is the program that tries it: reserve one block and a tail of ten, write them, then tell osFileDone the file came to two whole blocks. The refusal and the honest commit that follows are both recorded. 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
19ab36a201
commit
a7d3e09d94
@@ -0,0 +1,10 @@
|
||||
CosmOS
|
||||
> claiming more than was reserved was refused
|
||||
and the size it really came to was taken
|
||||
finished
|
||||
> Claim.sbx 572
|
||||
claim.dat 266
|
||||
2 files
|
||||
> halted
|
||||
Execution halted.
|
||||
[exit 0]
|
||||
@@ -0,0 +1,3 @@
|
||||
Claim
|
||||
dir
|
||||
exit
|
||||
@@ -194,6 +194,33 @@ awk 'BEGIN { for (i = 0; i < 30; i++) printf "line %02d: ABCDEFGHIJKLMNOPQRSTUVW
|
||||
"$ROOT/Programs/CosmOS/Apps/More.asm" -o "$WORK/More.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/type.img" "$WORK/More.sbx" >/dev/null
|
||||
|
||||
# A disk for Copy and Compare. The files mark all three shapes a streamed tool has to
|
||||
# distinguish: no blocks, an exact whole block, and a part block. The large pair says the
|
||||
# programs do not quietly depend on Data Memory being able to hold either input, and they
|
||||
# differ at one byte while keeping the same length so Compare has to inspect content rather
|
||||
# than stopping at the descriptor.
|
||||
"$TOOL" format "$DISKS/copycompare.img" 1024 4 >/dev/null
|
||||
"$TOOL" mkdir "$DISKS/copycompare.img" /Apps >/dev/null
|
||||
"$TOOL" mkdir "$DISKS/copycompare.img" /Input >/dev/null
|
||||
"$TOOL" mkdir "$DISKS/copycompare.img" /Output >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Copy.asm" -o "$WORK/Copy.sbx" >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Compare.asm" -o "$WORK/Compare.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" "$WORK/Copy.sbx" /Apps/Copy.sbx >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" "$WORK/Compare.sbx" /Apps/Compare.sbx >/dev/null
|
||||
: > copy-empty.dat
|
||||
awk 'BEGIN { for (i = 0; i < 256; i++) printf "%c", 65 + (i % 26) }' > copy-exact.dat
|
||||
awk 'BEGIN { for (i = 0; i < 257; i++) printf "%c", 65 + (i % 26) }' > copy-tail.dat
|
||||
awk 'BEGIN { for (i = 0; i < 84000; i++) printf "%c", 65 + (i % 26) }' > copy-large.dat
|
||||
awk 'BEGIN { for (i = 0; i < 84000; i++) printf "%c", (i == 65535 ? 33 : 65 + (i % 26)) }' \
|
||||
> copy-different.dat
|
||||
"$TOOL" put "$DISKS/copycompare.img" copy-empty.dat /Input/empty.dat >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" copy-exact.dat /Input/exact.dat >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" copy-tail.dat /Input/tail.dat >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" copy-large.dat /Input/large.dat >/dev/null
|
||||
"$TOOL" put "$DISKS/copycompare.img" copy-different.dat /Input/different.dat >/dev/null
|
||||
|
||||
# A disk with directories on it, which is the whole of what version two adds. Built by the
|
||||
# host tool, because at this rung the machine can read a tree and not yet make one - and
|
||||
# that split is the point: the two implementations are checked against each other rather
|
||||
@@ -219,6 +246,13 @@ awk 'BEGIN { for (i = 0; i < 30; i++) printf "line %02d: ABCDEFGHIJKLMNOPQRSTUVW
|
||||
printf 'this is not a program' > rooted.txt
|
||||
"$TOOL" put "$DISKS/tree.img" rooted.txt >/dev/null
|
||||
|
||||
# A disk for the program that tries to commit a file bigger than it reserved. Its own,
|
||||
# because what it leaves behind is a file whose size is the thing being checked.
|
||||
"$TOOL" format "$DISKS/claim.img" 64 2 >/dev/null
|
||||
"$ROOT/Assembler" -I "$ROOT/Programs/CosmOS/Source" \
|
||||
"$ROOT/Programs/CosmOS/Apps/Claim.asm" -o "$WORK/Claim.sbx" >/dev/null
|
||||
"$TOOL" put "$DISKS/claim.img" "$WORK/Claim.sbx" >/dev/null
|
||||
|
||||
# A blank disk for the machine to build a tree on itself. It starts as a version ONE disk
|
||||
# with nothing at all on it, because half of what this checks is that making the first
|
||||
# directory raises the version - the number says what is on a disk rather than what made
|
||||
|
||||
@@ -329,6 +329,10 @@ cosmosType | CosmOS/Source/cosmos.asm | run | cosmosTyp
|
||||
# with Space, and one line with Return. The input is intentionally packed so that the one
|
||||
# byte the pager consumes leaves the next shell command immediately behind it.
|
||||
cosmosMore | CosmOS/Source/cosmos.asm | run | cosmosMore.in | - | disks/type.img
|
||||
# Copy and Compare exercise both halves of block streaming together. Empty, exact-block,
|
||||
# part-block and 84,000-byte files are copied through one buffer; Compare checks the copies
|
||||
# and a same-sized file whose only difference is deep into the input.
|
||||
cosmosCopyCompare | CosmOS/Source/cosmos.asm | run | cosmosCopyCompare.in | - | disks/copycompare.img
|
||||
# Reading a disk that has directories on it. The machine can walk a path at this point but
|
||||
# cannot make a directory, so the disk is built by the host tool and read here - which is
|
||||
# the two implementations checking each other rather than either checking itself.
|
||||
@@ -348,6 +352,23 @@ cosmosTree | CosmOS/Source/cosmos.asm | run | cosmosTre
|
||||
# hold no blocks and must therefore be in nobody's way when a run of free ones is wanted.
|
||||
# The listing afterwards says where it landed and how big it is.
|
||||
cosmosTreeWrite | CosmOS/Source/cosmos.asm | run | cosmosTreeWrite.in | - | disks/treewrite.img
|
||||
# Committing a streamed file bigger than the room reserved for it, which must be refused.
|
||||
#
|
||||
# osFileStart sets an extent aside and osFileWrite already refuses a block index outside it,
|
||||
# so writing off the end is barred. This is the same neighbour reached the other way: claim
|
||||
# the extra block at commit rather than write into it. A directory entry is the only record
|
||||
# of what a file owns, so an entry claiming a block it was never given simply owns it, and
|
||||
# so does whatever owned it before - both files then look perfectly well formed.
|
||||
#
|
||||
# The size is compared and not the room it takes up, because those differ: one block and a
|
||||
# tail occupies what two whole blocks occupy, so bounding the blocks alone would allow a
|
||||
# file to report two hundred and forty six bytes that were never written to it. The
|
||||
# reservation here is one block and a tail of ten, and the claim is two whole blocks - the
|
||||
# same two blocks, and a bigger lie.
|
||||
#
|
||||
# The listing at the end is the point: 266 bytes, which is what was reserved and what was
|
||||
# written, after the refusal and the honest commit that follows it.
|
||||
cosmosClaim | CosmOS/Source/cosmos.asm | run | cosmosClaim.in | - | disks/claim.img
|
||||
# The machine building its own tree. It starts with a blank version one disk and makes
|
||||
# every directory on it, which is the half of the filesystem the machine could only read
|
||||
# until now.
|
||||
@@ -422,7 +443,10 @@ app-Stream | CosmOS/Apps/Stream.asm | assemble | -
|
||||
app-Type | CosmOS/Apps/Type.asm | assemble | - | -
|
||||
app-Wander | CosmOS/Apps/Wander.asm | assemble | - | -
|
||||
app-Pour | CosmOS/Apps/Pour.asm | assemble | - | -
|
||||
app-Claim | CosmOS/Apps/Claim.asm | assemble | - | -
|
||||
app-More | CosmOS/Apps/More.asm | assemble | - | -
|
||||
app-Copy | CosmOS/Apps/Copy.asm | assemble | - | -
|
||||
app-Compare | CosmOS/Apps/Compare.asm | assemble | - | -
|
||||
# The assembler that runs on the machine, and its parts. Checked on their own so that a
|
||||
# failure reads as "it does not assemble" rather than as a broken disk image.
|
||||
asm-Asm | CosmOS/Assembler/Asm.asm | assemble | - | -
|
||||
|
||||
Reference in New Issue
Block a user