Refuse a directory whose last entries cannot be named as a parent
A parent is an entry index PLUS ONE in two bytes, so entry 65535 has no parent number: adding one wraps to zero, and zero is the root. Eight entries to a block, so 8192 directory blocks reaches it and SplitDisk formatted that happily. It does not fail by refusing, which is why it was worth chasing rather than reasoning about. Reproduced on a disk built for it: mkdir /deep/child, with /deep at entry 65535, printed 'Made "/deep/child" as entry 0' and put child in the ROOT. Listing /deep then showed nothing, because the search is for a parent of 65536 and the entry carries zero - so the same mkdir succeeded again, and again, and five entries called /child piled up in the root. Duplicate names in one directory are the one thing rename refuses outright, on the grounds that a search answers with whichever it meets first and the rest can never be reached; this manufactured them one per attempt. 8191 blocks is the most, giving 65528 entries. Refused when formatting and again when reading, in both implementations, because a disk claiming more was made by something that never checked. On the machine only the high byte of the count has to be looked at: anything from 0x20 up is too many. Three checks, all of which fail with their guard removed. The machine's disk claims the size rather than having it, so the test image is 64 blocks that lie rather than sixteen megabytes that do not - mounting is refused at the geometry, which is read out of block 0.
This commit is contained in:
@@ -237,6 +237,23 @@ because it is the only thing that makes the difference between them real. A disk
|
||||
readable by anything that has never heard of a directory right up until it actually has
|
||||
one.
|
||||
|
||||
**A disk may have at most 8,191 directory blocks**, which is 65,528 entries, and that
|
||||
number comes from the parent field rather than from anything about size. A parent is an
|
||||
index *plus one* in two bytes, so entry 65,535 has no parent number at all: adding one
|
||||
wraps to zero, and zero means the root.
|
||||
|
||||
The failure is worth describing, because it is the shape of failure this format has to
|
||||
watch for. Such an entry does not refuse what is put inside it. It writes a parent of zero
|
||||
and the thing lands in **the root**, while whatever asked is told it went where it asked
|
||||
for. Looking in that directory afterwards finds nothing, because the search is for a
|
||||
parent the entry does not carry - so the same create succeeds again, and again, filling
|
||||
the root with entries of one name. Two entries of one name in one directory is precisely
|
||||
what `rename` refuses on the grounds that a search answers with whichever it meets first
|
||||
and the rest can never be reached again; this made them by the handful, one per attempt.
|
||||
|
||||
Both implementations refuse to format past the bound, and refuse to read a disk that
|
||||
claims it - because a disk claiming it was made by something that never checked.
|
||||
|
||||
Four things are refused, and each refusal is the reason a separate command exists:
|
||||
|
||||
**`rmdir` will not take a file and `delete` will not take a directory.** Neither can be
|
||||
|
||||
@@ -98,6 +98,27 @@ sbfsGeometry:
|
||||
DPUP.0 0d10
|
||||
SETD.1 SbfsDirBlocks
|
||||
CALL sbfsCopyWord
|
||||
|
||||
; A DIRECTORY WHOSE LAST ENTRIES CANNOT BE NAMED AS A PARENT. Eight entries to a block,
|
||||
; and the parent is an index plus one in two bytes, so entry 65535 has no parent number
|
||||
; at all - adding one wraps to zero, and zero is the root.
|
||||
;
|
||||
; It does not fail by refusing. Anything created inside such a directory writes a parent
|
||||
; of zero and lands in the ROOT, while whatever asked reports the path it wanted;
|
||||
; looking there afterwards finds nothing, because the search is for a parent the entry
|
||||
; does not carry, so the same create succeeds over and over and piles up entries of one
|
||||
; name in the root. Two entries of one name in one place is the thing rename refuses
|
||||
; outright, and this made them by the handful.
|
||||
;
|
||||
; 8191 blocks is the most, so anything from 0x2000 up is refused. Only the high byte has
|
||||
; to be looked at to know.
|
||||
SETD.0 SbfsDirBlocks
|
||||
LDA.0
|
||||
INIB 0x20
|
||||
CCF
|
||||
SUB
|
||||
BNC sbfsMountTooBig ; The high byte is 0x20 or more, so more than 8191 blocks.
|
||||
|
||||
SETD.0 SbfsBuffer
|
||||
DPUP.0 0d06
|
||||
SETD.1 SbfsDiskBlocks
|
||||
@@ -108,6 +129,13 @@ sbfsGeometry:
|
||||
ADD ; Q is zero: mounted.
|
||||
RET
|
||||
|
||||
sbfsMountTooBig:
|
||||
RSTA
|
||||
INIB 0d1
|
||||
CCF
|
||||
ADD ; Q is not zero: not a disk this will mount.
|
||||
RET
|
||||
|
||||
; ---- Finding something by path ----
|
||||
;
|
||||
; DP0 points at a path ending in a zero byte: names with '/' between them. A path that
|
||||
|
||||
Reference in New Issue
Block a user