8e389c03f8
Реализовать двухпанельный Commander от платформенного PoC до этапов P6-P20: EMM-каталог, сортировку и выбор, операции с файлами и деревьями, транзакционное копирование, метаданные, политику конфликтов и предварительную проверку свободного места. Добавить проектную документацию, HDD/MAME-сценарии и проверенные артефакты. Расширить libc операцией bank_write_page, исправлением режима O_RDONLY и связанными регрессионными проверками.
118 lines
6.2 KiB
Markdown
118 lines
6.2 KiB
Markdown
# Sprinter Commander: рекурсивный F5 одного каталога 0.2/P15
|
||
|
||
Дата: 9 сентября 2026 года.
|
||
|
||
Статус: основной и отрицательные сценарии `PASS` в MAME 0.288 / BIOS 3.06 /
|
||
DSS 1.71.57 на HDD `D:`.
|
||
|
||
## Реализованный контракт
|
||
|
||
Если в активной панели нет выбранной группы и cursor стоит на обычном
|
||
каталоге, F5 рекурсивно создаёт одноимённое дерево в каталоге passive panel:
|
||
|
||
- обход breadth-first, без рекурсии C-стека;
|
||
- каждый файл проходит прежний crash-safe `temp -> commit` и общий 4-КБ
|
||
copy-buffer в выделенной странице W3;
|
||
- одновременно открыты не более source и temp;
|
||
- пустые каталоги создаются;
|
||
- existing target не объединяется и не перезаписывается;
|
||
- Esc проверяется между каталогами и на каждом 4-КБ шаге файла;
|
||
- при cancel/error уже committed-файлы и созданные каталоги остаются, temp
|
||
текущего файла удаляется;
|
||
- CWD процесса восстанавливается и passive panel перечитывается после
|
||
изменения target.
|
||
|
||
Начиная с P16 выбранные каталоги также добавляются как несколько roots в ту
|
||
же очередь. Этот документ фиксирует более узкий single-root срез P15.
|
||
|
||
## Память обхода
|
||
|
||
`sc_tree` на время одной операции выделяет одну дополнительную EMM-страницу.
|
||
Первые 256 байт сохраняют исходный CWD. Остаток содержит 1008 узлов по 16
|
||
байт: `parent index + DOS 8.3 component + depth`. Абсолютный путь строится
|
||
перед операцией по цепочке parent; фиксированный предел глубины — 32, а общий
|
||
предел пути остаётся 255 символов.
|
||
|
||
4-КБ данные файла не смешиваются с traversal queue и не размещаются в W2.
|
||
W3 отображается только внутри `bank_read/write` и `bank_read_page/
|
||
bank_write_page`; вызовы сами восстанавливают прежнюю страницу.
|
||
|
||
До добавления root проверяется, что каталог passive panel не совпадает с
|
||
source root и не лежит внутри него. Это запрещает саморастущее копирование.
|
||
|
||
## Проверка
|
||
|
||
Fixture включает пустой каталог, файлы размером 0, 15, 16 и 4097 байт и
|
||
цепочку из 16 вложенных уровней. MAME сообщил точный итог:
|
||
|
||
```text
|
||
Tree copied: 4 files; 19 dirs.
|
||
```
|
||
|
||
После выхода source и target извлечены из CHD и сравнены как полные деревья:
|
||
|
||
```text
|
||
PASS: recursive tree copied exactly; depth=16; no temp.
|
||
```
|
||
|
||
Повторный F5 остановился до изменения файлов: DSS вернул `EISDIR` (`errno
|
||
15`) для уже существующего target-каталога. Контрольные состояния:
|
||
|
||
- [source TREE и пустой target](../artifacts/mame-p15/tree-copy/before.png);
|
||
- [успех 4 files / 19 dirs](../artifacts/mame-p15/tree-copy/success.png);
|
||
- [repeat остановлен на existing target](../artifacts/mame-p15/tree-copy/repeat-existing-target.png);
|
||
- [passive panel перечитана](../artifacts/mame-p15/tree-copy/passive-refreshed.png);
|
||
- [Esc удалил temp большого файла](../artifacts/mame-p15/tree-copy/cancel-clean.png);
|
||
- [target внутри source отклонён до mutation](../artifacts/mame-p15/tree-copy/self-target-refused.png);
|
||
- [чистый возврат в DSS](../artifacts/mame-p15/tree-copy/clean-dss-return.png).
|
||
|
||
Отдельный cancel-fixture начал копирование `BIG.BIN` размером 4 МБ и подал
|
||
Esc до commit. Target root остался как определённый частичный результат,
|
||
файл и temp отсутствуют, source не изменён:
|
||
|
||
```text
|
||
PASS: tree cancel removed active temp; source intact; no BIG commit.
|
||
```
|
||
|
||
Self-target fixture расположил passive panel в `SOURCE\TREE\INNER`. Операция
|
||
остановилась с `errno 14` (`EINVAL` отображается libc на родной DSS
|
||
`EUNKOP`) до первого `mkdir`; `INNER` остался пустым:
|
||
|
||
```text
|
||
PASS: target-inside-source refused before mutation.
|
||
```
|
||
|
||
## Компоновка и размер
|
||
|
||
Конфигурация `--memory big --safe --max-allocs 3000`:
|
||
|
||
| Область | После P14 | После P15 | Изменение |
|
||
|---|---:|---:|---:|
|
||
| `_CODE` W2 | 9503 | 9503 | 0 |
|
||
| данные W2 | 3268 | 3524 | +256 |
|
||
| свободная куча W2 | 2077 | 1821 | -256 |
|
||
| BANK1 | 6477 | 9687 | +3210 |
|
||
| BANK2 | 13388 | 14547 | +1159 |
|
||
| `SPRCMD.EXE` | 43080 | 43080 | 0 |
|
||
|
||
256 байт W2 — единственный `ffblk_t` traversal engine. Крупная очередь
|
||
динамическая и в эти числа не входит. BANK1/BANK2 имеют соответственно 6697
|
||
и 1837 байт запаса; межбанковый аудит чист.
|
||
|
||
## Воспроизведение
|
||
|
||
```sh
|
||
make hdd-p15-tree-copy ALLOCS=3000
|
||
tests/run_mame_hdd.sh build/hdd/p15_tree_copy.chd \
|
||
tests/mame_p15_tree_copy.lua artifacts/mame-p15/local 60
|
||
tests/check_p15_tree_copy_hdd.sh build/hdd/p15_tree_copy.chd
|
||
|
||
make hdd-p15-tree-cancel hdd-p15-tree-self ALLOCS=3000
|
||
tests/run_mame_hdd.sh build/hdd/p15_tree_cancel.chd \
|
||
tests/mame_p15_tree_cancel.lua artifacts/mame-p15/cancel 48
|
||
tests/check_p15_tree_cancel_hdd.sh build/hdd/p15_tree_cancel.chd
|
||
tests/run_mame_hdd.sh build/hdd/p15_tree_self.chd \
|
||
tests/mame_p15_tree_self.lua artifacts/mame-p15/self 48
|
||
tests/check_p15_tree_self_hdd.sh build/hdd/p15_tree_self.chd
|
||
```
|