Read and write NDFS (Norsk Data File System) disk images from Sintran-III minicomputers.
Three standalone libraries with identical APIs, plus a CLI tool:
| Library | Language | Tests | Install |
|---|---|---|---|
| ndfs-ts | TypeScript | 288 | npm install norskdata-ndfs |
| ndfs-py | Python | 328 | pip install norskdata-ndfs |
| ndfs-c | C99 | 166 | cmake .. && make |
| ndtool | CLI (C) | -- | Built with libndfs |
Total: 782 tests. No external dependencies in any library.
- Read and write files to/from NDFS disk images (contiguous, indexed, sub-indexed allocation with sparse hole support)
- Create new images from templates (360KB floppy, 1.2MB floppy, 75MB SMD, 74MB Winchester, custom)
- User management (add, remove, quota with quotaadd/quotadel, password clearing, friends list/add/remove, max 256 users)
- ND-100 even parity (strip on read, set on write -- proper calculated parity per byte, not mark parity)
- XAT sidecar files for preserving NDFS metadata (permissions, file types, dates) when copying to/from host filesystems
- Boot loader detection (BPUN, FLOMON, Binary formats)
- SINTRAN initial commands (read / write / repair the
@LIST-INITIAL-COMMANDSbuffer that lives at kernel symbol INIBU insideSEGFIL0) - Filesystem check (fsck: bitmap consistency, orphaned blocks, cross-links, quota verification)
- Interactive shell (ndtool --shell: ls, cat, hexdump, edit, get, put, rm, mv, bitmap, fsck, stat, users, save)
import { NdfsFileSystem, ImageTemplate } from 'norskdata-ndfs';
// Read a text file with parity stripped
const ndfs = new NdfsFileSystem(imageData);
const text = ndfs.readFile('SYSTEM/STARTUP:MODE', 'strip');
// Write with ND-100 even parity
ndfs.writeFile('RONNY/SOURCE:C', sourceBytes, 'set');from ndfs import NdfsFileSystem, ImageTemplate, ImageCreationOptions
# Create a new floppy image
ndfs = NdfsFileSystem.create_image(ImageCreationOptions(
template=ImageTemplate.Floppy360KB,
directory_name='MY-DISK',
))
# Write with parity, read with parity stripped
ndfs.write_file('SYSTEM/HELLO:TEXT', b'Hello!', parity='set')
text = ndfs.read_file('SYSTEM/HELLO:TEXT', parity='strip')#include <ndfs/ndfs.h>
ndfs_filesystem_t *fs = NULL;
ndfs_image_options_t opts;
ndfs_image_options_init(&opts);
opts.template_type = NDFS_TMPL_FLOPPY_360KB;
ndfs_create_image(&fs, &opts);
// Write with parity
ndfs_write_file_parity(fs, "SYSTEM/HELLO:TEXT",
(const uint8_t *)"Hello!", 6, NDFS_PARITY_SET);
// Read with parity stripped
uint8_t *data; size_t size;
ndfs_read_file_parity(fs, "SYSTEM/HELLO:TEXT",
NDFS_PARITY_STRIP, &data, &size);The commands SINTRAN runs first at every restart (@LIST-INITIAL-COMMANDS) live
at the kernel symbol INIBU inside (SYSTEM)SEGFIL0:DATA — no file form. Read,
replace, or repair them offline (see docs/SINTRAN-INITIAL-COMMANDS-SPEC.md):
const ic = ndfs.readInitialCommands(); // -> { version, byteOffset, commands, ... } | null
ndfs.writeInitialCommands([ // replace the whole buffer, in place
'ENTER-DIRECTORY,,DISC-SCSI-1,0', 'SET-AVAILABLE',
]);
// If the buffer header was clobbered so the locator can't see it:
ndfs.repairInitialCommands([ 'ENTER-DIRECTORY,,DISC-SCSI-1,0', 'SET-AVAILABLE' ]);ic = ndfs.read_initial_commands() # -> InitialCommands | None
ndfs.write_initial_commands(['ENTER-DIRECTORY,,DISC-SCSI-1,0', 'SET-AVAILABLE'])ndtool -t disk.ndfs # List all files
ndtool -i -v disk.ndfs # Info with bitmap visualization
ndtool --fsck disk.ndfs # Full filesystem check
ndtool -x -p -d -l -o output/ disk.ndfs # Extract all, strip parity, lowercase
ndtool -p --put source.c RONNY/SOURCE:C disk.ndfs # Copy in with parity
ndtool --create floppy360 --name MYDISK new.ndfs # Create new image
ndtool --shell disk.ndfs # Interactive modeSee the ndtool README for the full command reference.
The ND-100 stores text files with even parity. Bit 7 of each byte is set so the total number of 1-bits is even:
'H' = 0x48 (01001000) -> 2 ones (even) -> bit 7 = 0 -> 0x48
' ' = 0x20 (00100000) -> 1 one (odd) -> bit 7 = 1 -> 0xA0
'W' = 0x57 (01010111) -> 5 ones (odd) -> bit 7 = 1 -> 0xD7
Applies to text types: :MODE, :SYMB, :TEXT, :C, :BATC, :FORT, :PLAN, etc. Does not apply to binary types: :PROG, :BPUN, :DATA, :VTM.
NDFS files carry rich metadata (3-tier permissions, file type flags, ND timestamps, user ownership) that has no equivalent on modern filesystems. XAT sidecar files preserve this metadata as JSON when files are extracted:
SYSTEM/README:TEXT --> README.TEXT (file data)
README.TEXT.xat (NDFS metadata as JSON)
When copying back, the .xat file restores all metadata. Essential for archival round-trips and ND-100 development workflows. See the format spec for details.
See NDFS-FORMAT.md for the complete binary format specification covering master blocks, block pointers, user/object entries, allocation bitmaps, boot sectors, and XAT sidecar files.
This library was originally reverse-engineered from disk images without the producing
code. It has since been corrected against the real SINTRAN III kernel (the carved
006-S3FS filesystem segment of an L-VSX-500 system) plus a set of real ND disks.
See KERNEL-VERIFIED-CORRECTIONS.md for the full list with kernel addresses and disk evidence. The headline corrections:
- The extended-info checksum is an ADDITIVE SUM, not XOR (the XOR form matched the sample disk only by coincidence).
- There is no "valid low byte only" checksum state — the kernel compares all 16 bits.
- Allocation runs HIGH → LOW, bounded by the declared capacity, not upward from block 7.
- Bit-file placement is
9 * floor(floor(pages/2) / 9)— a track boundary — notpages/2. - Flag-word bit 15 = "directory entered"; system number 0 means no owner.
- Every real drive reserves a bad-sector spare region, so the device is always larger than the declared capacity — and the spare is a property of the drive, never a percentage.
# TypeScript
cd ndfs-ts && npm install && npm test
# Python
cd ndfs-py && PYTHONPATH=src python -m pytest tests/ -v
# C library + ndtool
cd ndfs-c && mkdir build && cd build && cmake .. && make && ./ndfs_testsOr, from the repository root, use the convenience Makefile:
make # build the ndtool CLI (Release)
make release # build ndtool, statically linked where supported
make test # build + run the C unit tests
make help # list all targetsPre-built ndtool binaries for Windows (x64), Linux (x64 / arm64), and
macOS (Apple Silicon) are attached to every
GitHub Release.
Each archive contains a single self-contained binary with no external runtime
dependencies. Releases are built automatically by the
release workflow when a v* tag is pushed.
MIT - Copyright (c) 1985-2026 Ronny Hansen, HackerCorp Labs