D2: the machine walks a path

sbfsFind takes a path where it used to take a name: names with '/' between
them, walked from the root, with '.' and '..'. Each name is looked for among
the entries whose parent is where the walk has got to. A bare name is a path of
one name, so everything written before directories existed still works and
still costs one walk of the directory.

sbfsMount takes either version. On a version one disk every entry has zeroes
where a parent goes and the walk starts at zero, so the comparison always
agrees - which is how a flat disk reads correctly here with nothing done to it.

PROGRAMS DID NOT HAVE TO BE TAUGHT ANY OF THIS. Resolution sits inside
sbfsFind, below the services, so every osFile call keeps its signature and a
path is simply a longer name. Type, More, Edit and the assembler gained
subdirectories without a line changing in any of them.

Four things this turned up, none of which was the path walk:

load copied the path into a buffer sized for a NAME, so anything over 22
characters was cut short - and cut short into a path that often still resolved.
"/Apps/Deep/../../Apps/Say.sbx" became "/Apps/Deep/../../Apps/" and reported
that the program was a directory. That is the whole of what looked like a bug
in '..', and it cost most of the time here.

load on a directory SUCCEEDED. A directory has no blocks, so reading it reads
nothing and leaves the staging area holding whatever was staged last - which,
if that was a program, still says SBEX and still has a working entry address.
It handed back the program before it. Refused outright now.

delete and rename on a directory are refused, and save refuses one up front
rather than failing at the rename and leaving a temporary behind. Deleting a
directory frees an entry index, and a parent IS an index, so the next file
created would take it and inherit the children.

create writes the parent rather than leaving it zero by luck. It would be zero
- delete wipes all thirty two bytes and a fresh entry never had any - but that
is a fact about two other routines, and a file appearing inside a directory it
was never put in is not a failure anybody would think to look for.

dir marks directories and counts them apart from files, because at this point
it was calling them files of no bytes.

Two hazards written down in the design note turned out not to be real, and both
were checked rather than argued about:

The lookup cache holding 22 bytes of a longer path cannot hand back the wrong
file - textSame wants both strings to end in the same place, so a cut down
entry misses. It can never HIT either, though, so every path longer than a name
went to the disk every time; it holds a whole path now.

The allocator stepping over directories changes nothing any test can see. A
directory has no start as well as no blocks, so its bounds are nought to nought
and no candidate begins before it ends. The four instructions stay, with a
comment saying they are not load bearing today and why they are there anyway.

makedisks.sh resolves its build path before it cds. Given a relative one it
carried on and quietly built disks missing some of their files, which is how
the tree fixture lost a file and sent me looking for a bug in '..'.

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-24 19:08:26 -04:00
co-authored by Claude Opus 5
parent 78e9eef472
commit 36a1b07b5b
10 changed files with 891 additions and 76 deletions
+38 -4
View File
@@ -17,6 +17,9 @@ for itself.
- Interactive Shell: Read commands from the SplitBit console and continue until `exit` or
the end of input.
- SBFS Filesystem: Mount, list, read, write, delete, and rename files on a SplitBit disk.
- Paths: Anywhere a filename is taken, a path may be given instead - names with `/`
between them, walked from the root, with `.` and `..`. Directories are read but not yet
made; the host tool makes them.
- Loadable Applications: Validate SBEX files, copy their Program and Data segments into
the addresses for which they were assembled, and start them at their declared entry
point.
@@ -83,7 +86,7 @@ CosmOS currently provides these built-in commands:
| Command | Description |
| -- | -- |
| `dir` | List the files on the mounted disk and their sizes. |
| `load <file>` | Read and validate an SBEX application, then place its code and data where its header requests. |
| `load <path>` | Read and validate an SBEX application, then place its code and data where its header requests. |
| `run [words]` | Start the loaded application and make the rest of the line available to it as an argument. |
| `<name> [words]` | Any word the shell does not recognise is looked for on the disk as `<name>.sbx`, and loaded and started if it is there. |
| `delete <file>` | Remove a file from the filesystem and release its blocks. |
@@ -130,6 +133,34 @@ facility and as a test that CosmOS correctly restores its Stack and vector table
every run; and `load` is how the monitor puts an arbitrary file in front of itself, which
is a thing typing a name deliberately cannot do.
### Paths:
Everywhere CosmOS takes a filename it will take a path: names with `/` between them,
walked from the root, with `.` meaning where you are and `..` meaning the directory above.
`..` from the root is the root. A bare name is a path of one name, so nothing written
before directories existed had to change.
```text
> load /Apps/Snake.sbx
> Type /Notes/today.txt
```
**Programs did not have to be taught any of this.** Path resolution lives inside
`sbfsFind`, below the services, so `osFileInfo`, `osFileBlock`, `osFileSave`,
`osFileDelete` and `osFileRename` all still take a pointer to a name - and a path is
simply a longer name. `Type`, `More`, `Edit` and the assembler gained subdirectories
without a line being changed in any of them.
Each *name* along a path is still the 22 characters a directory entry holds, and a longer
one is refused rather than cut short, because a name cut to 22 characters is a different
name that might well be some other file's.
At this stage CosmOS **reads** directories and does not make them. There is no `cd`, no
`mkdir`, and no working directory: every path is from the root. Files a program writes go
in the root. Deleting or renaming a directory is refused - deleting one would free its
entry index, and since a parent is written as an index, the next file created would take
that index and inherit its children.
### Starting An Application By Name:
A word the shell has no command for is not immediately an error. Before saying so, the
@@ -150,15 +181,18 @@ trustworthy.
cannot be started by typing what it is called, whatever happens to be inside it. Only
`load` reaches a file by its literal name.
A path works here too, so `/Apps/Say hello` starts `/Apps/Say.sbx` and gives it `hello`.
**A file that is found but is broken says so.** If `notes.sbx` exists and is not an SBEX
program, typing `notes` reports `not a program` rather than `I do not know: notes`.
Reporting an unknown command about a file that is sitting on the disk would send somebody
looking in the wrong place.
Names are matched exactly, including case, because every other name on the filesystem is.
A directory entry holds twenty two characters and four are spoken for by the extension, so
eighteen is the longest a bare name can be; a longer word is reported as unknown, which is
the truth, since no file of that name can exist.
What limits the typed word is the buffer it is built in rather than the format: each name
along a path is still twenty two characters, and the path walker refuses a longer one
rather than cutting it down. A word that will not fit is reported as unknown, which is the
truth, since nothing the shell can reach is called that.
CosmOS also boots without a disk. It reports that no filesystem was found, leaves the
shell and memory monitor available, and refuses commands that require a mounted disk