CRDIR - How SINTRAN III CREATE-DIRECTORY lays down a fresh directory device¶
Scope: the on-disk "boot sector" / directory-label creation (Phase 7 of the
Filesystem RE plan). This walks, in order, everything
@CREATE-DIRECTORY writes onto a fresh directory device: the raw boot area vs the
structured label, the directory name + flags, the allocation and placement of the
bit file (bitmap) / object file / user file, the three-pattern bad-page test and
relocation, the reserved blocks and initial bitmap state, and the master-block
checksum.
Rule of evidence (same as the foundation doc):
- VERIFIED - proven from carved SINTRAN L
006-S3FSCRDIR/ALBITbytes, from real diskSMD0.IMGbytes, or from official ND documentation. - INFERRED - deduced from the NDFS C oracle (
image_creator.c) or docs, not yet byte-proven inCRDIR. - OPEN - not resolvable statically; needs a live create-directory trace.
Octal is the primary radix for on-disk addresses and code addresses; multi-byte on-disk values are big-endian.
0. Sources used here¶
| Source | Role | Location |
|---|---|---|
006-S3FS CRDIR = 136741B (extent 136741B..137477B) + ALBIT = 137500B (137500B..137730B) |
carved SINTRAN L bytes - primary ground truth for the code | ../../tools/sintran-segment-carver/versions/L-VSX-500/segments/006-S3FS.bin, symbols in .../re/segments-ref/006-S3FS/006-S3FS.symbols.txt |
SMD0.IMG volume PACK-ONE |
real freshly-structured directory device - ground truth for the produced layout | ~/repos/nd100x/SMD0.IMG, page 0 master block at byte 2016 |
image_creator.c (ndfs_create_image) |
independent behavioural oracle - builds a valid fresh image | ~/repos/norskdata-ndfs/ndfs-c/src/image_creator.c |
master_block.c (ndfs_mb_write, ndfs_mb_write_extended) |
oracle for label field offsets + checksum | ~/repos/norskdata-ndfs/ndfs-c/src/master_block.c |
ND-60.128.5 @CREATE-DIRECTORY (p.63) + ND-60.050.06 Users Guide |
official behaviour | ../../Reference-Manuals/ND-60.128.5 EN SINTRAN III Reference Manual.md line 2071; ../../Reference-Manuals/ND-60.050.06 SINTRAN III Users Guide.md line 5119 |
The CRDIR decode¶
CRDIR is a JPL-dispatch routine: its subroutine calls go P-relative indirect
through an in-body pointer table interleaved with the code (this is exactly
OPEN-Q5 from the foundation doc). Decode recipe (VERIFIED to resolve to real
FILSYS symbols):
python3 -c "d=bytearray(open('006-S3FS.bin','rb').read());d[0::2],d[1::2]=d[1::2],d[0::2];open('/tmp/fs.le','wb').write(d)"
nd100-dis -a -o -b 11264 /tmp/fs.le | awk '$1>="136741"&&$1<="137731"'
Each JPL I nn / JMP I nn reads the pointer word at (P+nn) and jumps to its
contents. Resolving every pointer word in CRDIR (136741B..137477B) and ALBIT
(137500B..137730B) against FILSYS-SYMBOLS yields the call graph in the tables
below. Callees that land below the segment load base 26000B (003752,
010500, 010506, 010421, 001224, 003776) are resident SINTRAN
routines out of the 006-S3FS window - noted as OPEN for exact naming;
137454/137457/137466/137232/137116 are local error/return handlers
inside CRDIR (each does STA ,B 2 to store a status code, then falls into the
release/return tail).
1. Raw boot area vs the structured label¶
VERIFIED (real disk SMD0.IMG, page 0):
Page 0 is one 1KW page = 2048 bytes. The structured directory label lives at the top of page 0; everything before it is the raw boot area:
| Byte range (page 0) | Word (octal) | Contents |
|---|---|---|
| 0 .. 1999 | 0B .. 1747B | Raw boot code / FLOMON/BPUN bootstrap (or zero on a pure segment/data disk). On PACK-ONE the boot format is "None". |
| 2000 .. 2015 | 1750B | Extended-info block (16 bytes): checksum, 3 reserved words, flag word, last-system-number, pages-available. |
| 2016 .. 2047 | 1760B | Master block / directory label (32 bytes): directory name + 3 block pointers + unreserved-pages. |
So the label does not sit at the front of the device - it is written into the tail of the boot page, leaving the low ~1KW for the boot loader. This matches the Users Guide: "the directory name is written on the first page of the device" (ND-60.050.06 line 5119).
CRDIR builds the label in an in-core directory datafield and writes the whole
page out at the end via WDIRS (write directory to disk, 40221B) - see §7.
The label field writes are done by the master-block accessors GNAMT/PNAMT
(name), GDIRT/PDIRT/PDDRT (datafield words), and GDIRA (get the datafield
base) - all called from CRDIR (VERIFIED, call graph §7).
2. Directory name, status bits and pointers in the master block¶
VERIFIED master-block layout (real SMD0.IMG bytes, offsets relative to byte
2016; corroborated by master_block.c ndfs_mb_write):
000007e0: 5041 434b 2d4f 4e45 2700 0000 0000 0000 "PACK-ONE'" name (2016)
000007f0: 4000 48fc 4000 48fe 0000 4824 0000 2ca4 3 block ptrs + unreserved
| Rel. off | Field | Real bytes | Decoded |
|---|---|---|---|
| 0x00 | Directory name (16 bytes, 0x27 '-terminated) |
50 41 43 4B 2D 4F 4E 45 27 .. |
PACK-ONE |
| 0x10 | object_file_ptr |
40 00 48 FC |
type 1 INDEXED, block 44374B (18684) |
| 0x14 | user_file_ptr |
40 00 48 FE |
type 1 INDEXED, block 44376B (18686) |
| 0x18 | bit_file_ptr |
00 00 48 24 |
type 0 CONTIGUOUS, block 44044B (18468) |
| 0x1C | unreserved_pages |
00 00 2C A4 |
26244B (11428) |
- Name:
CRDIRgets it viaGNAMT(50223B) and copies it into the datafield withCOPYS(copy string, 30104B) - both VERIFIED in the call graph. The name is upper-cased and0x27-terminated. Max 16 chars incl. hyphen (doc). - Status / special flags: the pointer type is the flag that matters here.
Each pointer is a 4-byte big-endian word whose top 2 bits = type
(0 contiguous, 1 indexed, 2 sub-indexed, 3 reserved) and bottom 30 bits =
block ID (
block_pointer.h, VERIFIED). CRDIR always writes the bit file CONTIGUOUS (type 0) and the object/user files INDEXED (type 1) - VERIFIED from the real disk bytes and matched by the oracle (image_creator.clines 161-166). - The
has_flomon/ boot-format state is a property of the raw boot area (an!bootstrap marker at the front of page 0), not a bit in the master block (master_block.c detect_flomon). INFERRED thatCRDIRleaves the boot area as written by the loader; not separately proven fromCRDIRbytes (OPEN).
3. Bit file, object file, user file - allocation, placement, pointers¶
3.1 Placement (VERIFIED from the real disk + doc)¶
- Bit file at block 44044B (18468),
18468 / 38400 = 0.481-> "in the middle of the directory ... starting from the beginning of a track" (ND-60.128.5 line 2071; ND-60.050.06 line 5119). VERIFIED. - Object file at 44374B (18684) and user file at 44376B (18686) - two blocks apart, placed together just past mid-disk (VERIFIED real disk).
Placing the bit file mid-disk is the reason a default contiguous file can be at
most half the disk (doc). The user may override the bit-file address with the last
@CREATE-DIRECTORY parameter (doc).
UPDATE - the bit-file formula is now VERIFIED from bytes.
ALBIT(137526B-137532B) computesbit_file = 9 * floor(floor(pages/2)/9)- i.e.floor(pages/2)rounded down to a multiple of 9 pages - which is byte-exact:PACK-ONE36945 -> 18468 (NDFSpages/2= 18472, off by 4). The layout branch is keyed on whether the last@CREATE-DIRECTORYparameter (a bit address) is supplied, not on device type. The object/user default-path offset (bit+216 on the SMD) still does not fit any simple rule and stays OPEN. Full derivation, byte evidence, the all-image data table, and the NDFS "replacepages/2with THIS" recommendation are increate-directory-placement.md.INFERRED (oracle, superseded above for the bit file):
image_creator.cbuild_custom_specusesbit_file_block = pages/2, then places the object/user index blocks immediately after the bitmap span (object = bf + bitmap_pages,user = object + 2). The NDFS built-inspec_smd_75mbhardcodes 18684/18686/18472 to approximate this real disk.
3.2 What CRDIR writes for each structure¶
VERIFIED call graph (the three structures, in CRDIR body order):
| Structure | CRDIR actions (resolved callees) |
|---|---|
| Bit file | ALBIT (137500B) is called at 137145B - allocates + tests + builds the bitmap page(s) (§4). ALPAG (50627B, allocate/mark-used page) at 137324B; WBFBU (write bit-file buffer, 50565B) at 137167B/137354B. |
| Object / user files | index+data blocks written via GDEVB (get device buffer, 34557B), RCBLO/WCBLO (read/write core block, 35766B/36357B), SETBL (set block, 30164B), CL1DB (clear one disk buffer, 35240B). PDDRT/GDDRT (put/get directory datafield, 50127B/50121B) store the pointers into the datafield. |
| Master block | pointers assembled in the datafield, then WDIRS (40221B) at 137403B writes page 0 (§7). |
INFERRED (oracle) - the index-block shape the object/user pointers reference:
each INDEXED file is a one-page index block whose first 4-byte pointer points
to the file's first data page (image_creator.c lines 182-208):
- user file index block -> user data page =
user_file_block + 1; that data page holds the first user entry, "SYSTEM" (ndfs_ue_init,user_index = 0,pages_reserved = pages/2). - object file index block -> object data page =
object_file_block + 1; the object data page is all zeros (no files yet).
Whether CRDIR seeds a SYSTEM user entry into the fresh user file is not
confirmable from the CRDIR bytes alone (the user-write worker WUSER 53410B is
not called directly inside the CRDIR window) - marked OPEN. The doc only
guarantees the label is written; the initial SYSTEM account is INFERRED
(oracle).
4. The bad-page test (three write/compare patterns) + relocation¶
VERIFIED (doc) - ND-60.128.5 line 2071:
"The bit file pages are tested by performing write and compare with three standard test patterns before being cleared. If there is a bad page in the bit file area the file system automatically locates the bit file on an adjacent track."
and for the rest of the disk (line 2073): on non-ECC / unformatted-for-sparing disks "every page is tested by performing a read and compare from the disk. Any page in error is marked as used in the bit map." On ECC disks formatted with spare-track re-allocation (TYPE C bit set) the whole-disk test is skipped.
VERIFIED (ALBIT bytes, 137500B..137730B) - the three-pattern loop is real:
ALBITcomputes the bit-file page span from the disk size viaGSIZE(37101B, called at 137524B/137535B) and the bits/word split (MPY 20= octal 20 = 16 bits per bitmap word, VERIFIED opcode at 137710B;RDIVfor the word/bit index).- Outer loop over the bit-file pages: counter at
B+7, boundB+21(137611BLDA ,B 7/ 137612BLDT ,B 21/ 137613BSKP IF DT GRE SA). - Inner loop = exactly three iterations (137622B..137650B): counter
B+14, compared against the literal 3 (SAT 3at 137624B,SKP IF DT GRE SA, i = 0,1,2). Per iteration it: loads pattern[i] from an in-body pattern table (LDA I ,X 66at 137636B), fills the page buffer with it viaSETBL(30164B, 137640B), and writes it to the disk page viaWCBLO(write core block, 36357B, 137643B). The read-back/compare section follows at 137660B..137717B (BSKP ONEbit tests at 137664B), and on mismatch the page's bit is computed and set used.
This is a byte-for-byte confirmation of the doc's "write and compare with three standard test patterns." The three literal pattern values are loaded from the in-body table but were not extracted statically - OPEN.
Relocation to an adjacent track on a bad bit-file page (doc): CRDIR prints a
diagnostic (OUTRC 40730B + OCTAL 40336B at 137302B..137315B - "output value in
octal", the reported bad-page address) and re-runs allocation. The exact
"advance the bit-file base to the next track and retry" arithmetic is INFERRED
(doc) and not pinned in the carved bytes - OPEN (needs a live trace of a disk
with an injected bad page).
5. Reserved blocks (0-6) and initial bitmap state¶
VERIFIED (oracle + code):
- The freshly built bitmap marks these pages used (
image_creator.clines 214-246): page 0 (master block), the user file index block + its data page, the object file index block + its data page, and every page of the bitmap itself. Everything else starts free (0). - One bit per page, 0 = free / 1 = used (
bit_file.h). VERIFIED. Cross-check:ndtool -i SMD0.IMG-> 38400 total / 14277 used / 24123 free.
INFERRED (NDFS): blocks 0-6 are reserved; the allocator never hands out a
block below 7 (NDFS_FIRST_ALLOC_BLOCK = 7, enforced in bit_file.c lines
116/158 - it starts scanning at 7 and rejects start_block < 7). Note this is
enforced by the allocator, not by pre-marking 1-6 used in the fresh bitmap
(image_creator.c only marks page 0). Whether real SINTRAN CRDIR reserves
exactly 7 blocks, and whether it pre-marks 1-6, is OPEN (the doc does not
state the reserved count; ALPAG at 137324B marks system pages but the exact set
was not traced).
6. Master-block checksum¶
VERIFIED (master_block.c ndfs_mb_write_extended, corroborated by SMD0.IMG):
the checksum lives in the extended-info block at byte 2000 (word 1750B), not in
the 32-byte label. Algorithm:
checksum = (pages_lo XOR pages_hi XOR flag_word
XOR reserved1 XOR reserved2 XOR reserved3)
+ last_system_number (16-bit wrap)
Real SMD0.IMG extended info (10b7 0000 0000 0000 8000 0066 0000 9051):
pages_available = 0x9051 = 36945 (pages_lo=0x9051, pages_hi=0),
flag_word=0x8000, reserved1..3=0, last_system_number=0x66=102:
(0x9051 ^ 0 ^ 0x8000 ^ 0 ^ 0 ^ 0) + 0x66 = 0x1051 + 0x66 = 0x10B7 -> matches the
stored 10B7. VERIFIED.
INFERRED / OPEN: the checksum + extended-info block is a property of disks big
enough to carry it (image_creator.c: written when pages > 1000, i.e.
ext_valid; floppies omit it). The exact CRDIR instruction that computes and
stores this checksum was not isolated in the carved window (the extended-info
write is likely folded into the datafield-to-disk path via WDIRS) - OPEN.
7. The confirmed create sequence¶
VERIFIED ordering from the CRDIR call graph (P-relative-indirect callees
resolved to FILSYS symbols; -> = the resolved routine):
CRDIR addr |
Call | Meaning |
|---|---|---|
| 136745B | -> 003752 (resident) |
entry / save-context helper (OPEN name) |
| 136752B | -> CLPAR (44777B) |
parse/clear the create parameters |
| 136754B | -> COLDE (132072B) |
(cold-entry helper) |
| 136761B | -> GNAMT (50223B) |
fetch the directory name |
| 136772B | -> TUSSY (53047B) |
test user is SYSTEM (permission - doc rule 1) |
| 137000B | -> GDIRI (47402B) |
get directory index |
| 137007B | -> GDIRE (131732B) |
get directory entry |
| 137035B.. | -> GDIRT (50124B) |
get directory datafield word (repeated) |
| 137046B | MON 124 |
ForceReserve the directory/device |
| 137054B | -> GSSIZ |
get system/segment size |
| 137056B | -> GDIRA (30225B) |
get datafield base |
| 137065B | -> COPYS (30104B) |
copy the name into the datafield |
| 137100B | -> GDEVB (34557B) |
get device buffer |
| 137102B | -> WTAPE (36511B) |
device write helper |
| 137105B | -> CL1DB (35240B) |
clear a disk buffer |
| 137140B.. | -> PDDRT (50127B) |
put directory datafield word (repeated) |
| 137145B | -> ALBIT (137500B) |
allocate + bad-page-test + build the bit file (§4) |
| 137153B | -> GSIZE (37101B) |
get size |
| 137167B | -> WBFBU (50565B) |
write bit-file buffer to disk |
| 137251B | -> RCBLO (35766B) |
read core block |
| 137253B | -> RELBU (35476B) |
release buffer |
| 137272B | -> GDEVB / 137273B -> WCBLO (36357B) |
write core block (object/user structures) |
| 137302B.. | -> OUTRC (40730B) + -> OCTAL (40336B) |
print bad-page/diagnostic value in octal |
| 137324B | -> ALPAG (50627B) |
mark system pages used in the bitmap |
| 137354B | -> WBFBU |
write bit-file buffer |
| 137366B | -> GDIRA / 137375B -> WCBLO |
assemble + write directory blocks |
| 137401B | -> CL1DB |
clear disk buffer |
| 137403B | -> WDIRS (40221B) |
write the directory label (page 0) to disk |
| 137417B | MON 125 |
ForceRelease the directory/device |
| 137420B..137421B | SAA -34 / JMP I -> 003776 |
store return status, return |
So the create order is: permission-check + reserve -> build the label in the datafield (name + flags) -> allocate & bad-page-test the bit file (three patterns) -> write bit-file + object/user structures -> mark system pages used -> write the label page (WDIRS) -> release. VERIFIED.
flowchart TB
A["Parse params<br/>CLPAR"] --> B["Check user = SYSTEM<br/>TUSSY (doc rule 1)"]
B --> C["MON 124 ForceReserve<br/>the device"]
C --> D["Build label in datafield:<br/>name (GNAMT/COPYS) + type flags"]
D --> E["ALBIT: allocate bit file mid-disk,<br/>write+compare THREE test patterns,<br/>mark bad pages used, else clear"]
E --> F["Write bit-file buffer (WBFBU) +<br/>object/user index+data blocks (WCBLO)"]
F --> G["ALPAG: mark system pages USED<br/>(page 0, index/data pages, bitmap)"]
G --> H["WDIRS: write master block /<br/>label to page 0"]
H --> I["MON 125 ForceRelease"]
classDef blue fill:#E3F2FD,stroke:#0D47A1,color:#0D47A1;
classDef teal fill:#E0F7FA,stroke:#00838F,color:#00838F;
classDef green fill:#E8F5E9,stroke:#2E7D32,color:#2E7D32;
classDef orange fill:#FFF3E0,stroke:#E65100,color:#E65100;
classDef purple fill:#F3E5F5,stroke:#7B1FA2,color:#7B1FA2;
class A,B blue
class C,I purple
class D,H orange
class E,G teal
class F green
Resulting on-disk layout (VERIFIED from SMD0.IMG)¶
flowchart TB
subgraph DISK["Directory device PACK-ONE - 38400 pages (113000B)"]
direction TB
P0["Page 0 - boot area + label<br/>bytes 0-1999 boot code<br/>byte 2000 extended info (checksum)<br/>byte 2016 master block / label"]
MID["... free data pages ..."]
BF["Bit file (bitmap)<br/>block 44044B (18468) CONTIGUOUS<br/>~mid-disk, on a track boundary<br/>1 bit/page, 0=free 1=used"]
MID2["... free data pages ..."]
OF["Object file index+data<br/>block 44374B (18684) INDEXED"]
UF["User file index+data<br/>block 44376B (18686) INDEXED"]
END["... free data pages ..."]
end
P0 -->|bit_file_ptr| BF
P0 -->|object_file_ptr| OF
P0 -->|user_file_ptr| UF
classDef blue fill:#E3F2FD,stroke:#0D47A1,color:#0D47A1;
classDef teal fill:#E0F7FA,stroke:#00838F,color:#00838F;
classDef green fill:#E8F5E9,stroke:#2E7D32,color:#2E7D32;
classDef purple fill:#F3E5F5,stroke:#7B1FA2,color:#7B1FA2;
classDef orange fill:#FFF3E0,stroke:#E65100,color:#E65100;
class P0 orange
class BF,MID,MID2,END teal
class OF green
class UF purple
8. What stays INFERRED / OPEN (needs a live create-directory trace)¶
- Exact object/user placement math - real disk uses 18684/18686 (past the
mid-disk bit file at 18468); the NDFS
bf+bitmap_pagesformula does not reproduce this. OPEN. - Whether
CRDIRseeds the SYSTEM user entry and zeroes the object data page- guaranteed by the oracle, not by the doc, and
WUSERis not called in theCRDIRwindow. OPEN.
- guaranteed by the oracle, not by the doc, and
- The three literal test-pattern values loaded by
ALBIT(the count 3 is VERIFIED; the values are in an in-body table not extracted statically). OPEN. - Bad-page relocation arithmetic ("locate the bit file on an adjacent track")
- doc-stated, diagnostic print path VERIFIED, but the retry math not pinned. OPEN.
- Reserved-block count / whether blocks 1-6 are pre-marked used - NDFS uses 7;
the doc is silent;
ALPAGmarks system pages but the exact set was not traced. OPEN. - The extended-info checksum store instruction inside
CRDIR(algorithm and result VERIFIED; the storing opcode not isolated). OPEN. - Resident callees
003752,010500,010506,010421,001224,003776are below the006-S3FSload base and were not named. OPEN.
A live @CREATE-DIRECTORY trace (breakpoint at 136741B, single-step the reserve
-> ALBIT -> WDIRS path, dump the target pages after each write) resolves all seven.
Related: Filesystem foundation (code map, Phase 7 exit criteria) - code-logic - boot-creation.md (the separate boot half: the page-0 bootstrap per device class, which this doc deliberately leaves as-written).