Add Copy and Compare, which stream in both directions at once

Copy joins the read and write streaming services: source and destination are
both larger than Data Memory while the program owns one block. Compare reads
two files through separate blocks and ignores the bytes past a short final
block, which belong to neither file. Between them they exercise empty,
exact-block, part-block and 84,000 byte files, and the host extracts the copy
afterwards so that two native programs agreeing with each other is not the only
oracle.

Written by ChatGPT, as their headers record, along with the agree.sh section
and the manifest entry that drive them.

THIS SHOULD HAVE COME FIRST. The commit before it staged whole files rather
than the hunks it meant, so its manifest already names these two programs while
their source was still untracked - that commit will not build on its own. Left
in place rather than rewritten, since the pair is right and only their order is
wrong.

NOTES.md is their review of the streaming work. The first item in it is fixed
by the commit before this one; the rest are still open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-25 20:21:43 -04:00
co-authored by Claude Opus 5
parent a7d3e09d94
commit 54f5cfe8a4
7 changed files with 731 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
# Delete me after these things are fixed
Temporary notes from the SBFS v2 and CosmOS review, in priority order.
## Streaming writes: reject a final size larger than the reservation
`osFileStart` reserves an extent and `osFileWrite` correctly refuses a block index outside
that extent. `sbfsStreamDone`, however, appears to accept a final block/tail count larger
than the extent reserved at the start. It writes the larger size into the descriptor and
then subtracts that size from the reservation when updating the free-block count.
A caller could reserve one block, finish while claiming two, and make the file descriptor
claim a neighboring block that was never allocated and may belong to another file.
Before deleting the old destination or modifying the temporary entry, compare the final
extent (`whole blocks + a possible tail block`) with the reserved extent. Refuse the commit
if the final extent is larger. Add tests for a final size that is smaller than, equal to,
and larger than the reservation, including tail/no-tail boundaries.
## Protect the temporary-file namespace
Whole-file saves and streamed writes use the fixed names `sbfs.part` and `sbfs.out`.
Starting another operation deletes an existing entry with the corresponding name as stale
temporary output, but these are otherwise legal user filenames. A real user file with one
of those names can therefore be deleted.
Possible resolutions:
- Reserve these names and refuse ordinary creation under them.
- Use a spare descriptor flag to identify temporary entries, and clean up by identity
rather than by name.
- Move temporary artifacts into a defined `/tmp` policy where appropriate. A stream's
temporary currently needs to be in the destination directory because rename changes a
name but not a parent, so `/tmp` alone may require adding a move operation.
Whatever rule is chosen should be enforced in both SplitDisk and the native SBFS code and
documented as part of the format or CosmOS policy.
## Correct stale CosmOS memory-map documentation
`Programs/CosmOS/README.md` currently gives overlapping Data Memory ranges:
```text
CosmOS: 0x0000 through 0x3FFF
Application: 0x2000 and above
```
The intended 8 KiB system allocation appears to be `0x0000` through `0x1FFF`, with
applications at `0x2000` and above. The minimal application in the same section still uses
the old bases (`0x2000` Program and `0x1000` Data), while current applications use `0x4000`
and `0x2000`. The opening comments in `cosmos.asm` and several monitor examples also retain
old addresses.
The README says `make test` checks these boundaries, but the overlapping table passed.
Strengthen `Tests/docs.sh` so the documented system endpoints and application bases are
parsed and checked for agreement/non-overlap.
## Establish one portable path-length limit
SplitDisk carries paths up to 511 characters, while the native path machinery appears to
use a 255-byte limit. Paths are not stored on disk, so this does not change the format, but
the host can construct a tree addressable by a path that CosmOS cannot express in one
operation.
Declare a portable CosmOS/SBFS path limit and have SplitDisk enforce it when modifying an
image. The native 255-byte limit is reasonable; the important property is agreement and
documentation. Component names remain limited to 22 bytes independently.
## Bound the number of directory entries
The parent field is 16 bits and stores `descriptor index + 1`, with zero reserved for the
root. Descriptor index 65535 therefore cannot be represented as a parent because adding
one wraps to zero. SplitDisk currently accepts directory sizes large enough to exceed the
representable parent domain.
Define and validate a maximum directory-block/entry count such that every directory entry
can be named as a parent. Apply the check while formatting and mounting/reading malformed
images in both implementations.
## Document the save/stream crash guarantee precisely
The temporary-first ordering protects the old file from expected failures such as no
contiguous run or a refused block write. It is not power-loss atomic: a reset between
deleting the old entry and renaming the temporary can lose the destination and leave the
temporary behind.
That is an appropriate tradeoff for SBFS; it does not call for a journal. Document the
guarantee as protection against normal operation failures rather than full crash
atomicity. A future boot cleanup or small consistency checker could reclaim unmistakable
temporary entries, especially if they receive a descriptor flag.
## Design strengths worth preserving
- Parent-as-index-plus-one makes every version one entry a valid root child without
conversion.
- Directories consume one descriptor and no data blocks, leaving the descriptor array as
the complete allocation map.
- Files remain contiguous and the block allocator remains ignorant of hierarchy.
- Path resolution below the service boundary gave existing applications directories
without changing their interfaces.
- The current directory is a two-byte identity rather than a stored string, and the shell
restores it after applications run.
- The independent host/native implementations and byte-identical disk agreement tests are
unusually strong validation of the written format.
- `osFileStart`/`osFileWrite`/`osFileFetch`/`osFileDone` provide the bounded-memory output
abstraction needed by assemblers, compilers, linkers, and future sequential pipelines.