ISO 9660 Guide — Images lesen und erstellen

Dieser Guide zeigt den praktischen Einsatz von std.iso für typische Aufgaben: ISO-Images auslesen, eigene Images für Software-Distribution oder Embedded-Systeme bauen und das Format sinnvoll mit anderen Archiv-Units kombinieren.

std.iso (Unit-Referenz) · Archiv-Übersicht · Welche Unit?


Wann ISO 9660 einsetzen?

ISO 9660 ist kein allgemeines Archivformat — es hat einen spezifischen Anwendungsbereich:

Szenario ISO 9660? Alternative
Bootfähiges CD/DVD/USB-Image bauen ja
Firmware-Update als selbstlesendes Image ausliefern ja
Read-only Dateisystem für Embedded-Geräte ja
Vorhandene ISO-Images (Linux-ISOs, Software-DVDs) auslesen ja
Allgemeines Archiv mit Kompression nein std.zip / std.tar
Archiv mit Unterverzeichnissen (schreiben) nein std.zip / std.tar
Austauschformat zwischen Systemen nein std.zip (universell)

ISO 9660 ist die richtige Wahl wenn das Ziel ein bootfähiges Medium ist, ein Read-only-Dateisystem gebraucht wird oder das Image von einem BIOS/UEFI, einem DVD-Laufwerk oder einem virtuellen Laufwerk gemountet werden soll.


ISO-Image lesen

Inhalt auflisten

IsoOpen liest das gesamte Image in den Speicher und traversiert den Verzeichnisbaum per BFS. Das Ergebnis ist eine flache Liste aller Dateien mit vollständigen Pfaden.

import std.iso;
import std.alloc;
import std.io;

fn IsoAuflisten(pfad: pchar): void {
    var img: int64 := IsoOpen(pfad as int64);
    if img == 0 then {
        PrintLn("Kein gültiges ISO 9660-Image"c);
        return;
    }

    var n: int64 := IsoCount(img);
    Print("Dateien: "c); PrintLn(IntToStr(n)c);

    var i: int64 := 0;
    while i < n do {
        Print(IsoName(img, i) as pchar);
        Print("  ("c);
        Print(IntToStr(IsoSize(img, i))c);
        PrintLn(" Bytes)"c);
        i := i + 1;
    }

    IsoClose(img);
}

IsoName gibt vollständige Pfade zurück inklusive Verzeichnisanteil:

BOOT/GRUB/GRUB.CFG
BOOT/GRUB/GRUB.EFI
EFI/BOOT/BOOTX64.EFI
README.TXT
AUTORUN.INF

Die Dateinamen enthalten das ISO 9660 Version-Suffix ;1 nicht — isoStripVersion entfernt es beim Einlesen automatisch.

Einzelne Datei extrahieren

fn IsoDateiLesen(pfad: pchar, name: pchar): void {
    var img: int64 := IsoOpen(pfad as int64);
    if img == 0 then { return; }

    var idx: int64 := IsoFind(img, name as int64);
    if idx < 0 then {
        Print("Nicht gefunden: "c); PrintLn(name);
        IsoClose(img);
        return;
    }

    var sz:  int64 := IsoSize(img, idx);
    var buf: int64 := alloc(sz + 1);
    var n:   int64 := IsoRead(img, idx, buf, sz);

    if n >= 0 then {
        poke8(buf + n, 0);
        PrintLn(buf as pchar);
    }

    free(buf, sz + 1);
    IsoClose(img);
}

Alle Dateien in Verzeichnis extrahieren

Da IsoName vollständige Pfade liefert, lassen sich alle Dateien eines bestimmten Unterverzeichnisses filtern:

import std.iso;
import std.alloc;
import std.string;
import std.fs;

fn IsoPfadExtrahieren(imgPfad: pchar, prefix: pchar, zielOrdner: pchar): void {
    var img: int64 := IsoOpen(imgPfad as int64);
    if img == 0 then { return; }

    var plen:  int64 := StrLen(prefix as int64);
    var n:     int64 := IsoCount(img);
    var i:     int64 := 0;

    while i < n do {
        var name: pchar := IsoName(img, i) as pchar;
        // Prüfen ob Name mit prefix beginnt
        if StrStartsWith(name as int64, prefix as int64) == 1 then {
            var sz:  int64 := IsoSize(img, i);
            var buf: int64 := alloc(sz);
            IsoRead(img, idx, buf, sz);

            // Zieldatei: zielOrdner + "/" + name
            var outPfad: int64 := StrConcat(zielOrdner as int64, "/"c as int64);
            outPfad := StrConcat(outPfad, name as int64);
            WriteFile(outPfad, buf, sz);

            free(buf, sz);
            free(outPfad, StrLen(outPfad) + 1);
        }
        i := i + 1;
    }

    IsoClose(img);
}


ISO-Image erstellen

Einfaches Image

Der Writer legt alle Dateien im Root-Verzeichnis ab. Namen werden automatisch auf ISO 9660 Level 1 normalisiert: Kleinbuchstaben → Großbuchstaben, Sonderzeichen → _.

import std.iso;
import std.alloc;

fn EinfachesIso(): void {
    var w: int64 := IsoWriterNew();

    var readme: pchar := "Mein Lyx-Projekt v1.0\n"c;
    IsoWriterAdd(w, "README.TXT"c as int64, readme as int64, 22);

    var cfg: pchar := "version=1\ndebug=0\n"c;
    IsoWriterAdd(w, "config.ini"c as int64, cfg as int64, 18);
    // → wird im Image zu CONFIG.INI

    var rc: int64 := IsoWriterSave(w, "release.iso"c as int64);
    if rc != ISO_ERR_OK then {
        PrintLn("Fehler beim Erzeugen des ISO-Images"c);
    }

    IsoWriterFree(w);
}

Datei aus Dateisystem einlesen und ins Image packen

IsoWriterAdd kopiert den Datenpuffer nicht — der Zeiger muss bis zum IsoWriterSave-Aufruf gültig bleiben. Bei mehreren Dateien aus dem Dateisystem alle zuerst laden, dann erst speichern:

import std.iso;
import std.alloc;
import std.fs;

fn IsoAusDateien(outPfad: pchar): int64 {
    var w: int64 := IsoWriterNew();

    // Alle Puffer halten bis IsoWriterSave
    var sz1:  int64 := FileSize("build/firmware.bin"c as int64);
    var buf1: int64 := alloc(sz1);
    ReadFile("build/firmware.bin"c as int64, buf1, sz1);
    IsoWriterAdd(w, "FIRMWARE.BIN"c as int64, buf1, sz1);

    var sz2:  int64 := FileSize("docs/manual.pdf"c as int64);
    var buf2: int64 := alloc(sz2);
    ReadFile("docs/manual.pdf"c as int64, buf2, sz2);
    IsoWriterAdd(w, "MANUAL.PDF"c as int64, buf2, sz2);

    var rc: int64 := IsoWriterSave(w, outPfad as int64);

    // Erst nach Save freigeben
    free(buf1, sz1);
    free(buf2, sz2);
    IsoWriterFree(w);

    return rc;
}


Dateinamen-Normalisierung

ISO 9660 Level 1 erlaubt nur Großbuchstaben, Ziffern, Unterstrich und Punkt. IsoWriterAdd normalisiert automatisch:

Eingabe-Name Im Image
README.txt README.TXT
boot-loader.bin BOOT_LOADER.BIN
mein datei.cfg MEIN_DATEI.CFG
v1.0-release_notes.txt V1.0_RELEASE_NOTES.TXT
name-länger-als-36-zeichen-geht-nicht.txt ISO_ERR_NAMETOOLONG (Fehler)

Eindeutigkeit: Wenn zwei Eingabe-Namen nach Normalisierung identisch werden (z.B. boot.bin und BOOT.BIN), überschreibt der zweite Eintrag im Root-Verzeichnis den ersten nicht — beide landen als separate Einträge mit identischem normalisierten Namen. Das Image ist dann technisch gültig, aber der zweite Eintrag kann vom Reader nur über den Index, nicht über IsoFind erreicht werden.


ISO + TAR: Unterverzeichnisse simulieren

Da der Writer nur ein flaches Root-Verzeichnis unterstützt, lässt sich ein TAR-Archiv als Träger für eine vollständige Verzeichnisstruktur ins ISO packen:

import std.iso;
import std.tar;
import std.alloc;
import std.fs;

fn IsoMitTar(outIso: pchar): void {
    // 1. TAR mit Unterverzeichnisstruktur bauen
    var t: int64 := TarWriterNew();
    TarWriterAdd(t, "etc/config.txt"c as int64, "debug=0\n"c as int64, 8);
    TarWriterAdd(t, "bin/app"c as int64,         "..."c as int64, 3);
    TarWriterAdd(t, "lib/runtime.so"c as int64,  "..."c as int64, 3);
    TarWriterSave(t, "/tmp/payload.tar"c as int64);
    TarWriterFree(t);

    // 2. TAR-Datei ins ISO packen
    var tsz: int64  := FileSize("/tmp/payload.tar"c as int64);
    var tbuf: int64 := alloc(tsz);
    ReadFile("/tmp/payload.tar"c as int64, tbuf, tsz);

    var w: int64 := IsoWriterNew();
    IsoWriterAdd(w, "PAYLOAD.TAR"c as int64, tbuf, tsz);

    var readme: pchar := "Inhalt: payload.tar entpacken\n"c;
    IsoWriterAdd(w, "README.TXT"c as int64, readme as int64, 30);

    IsoWriterSave(w, outIso as int64);

    free(tbuf, tsz);
    IsoWriterFree(w);
}

Das ISO enthält dann PAYLOAD.TAR und README.TXT im Root. Das TAR innerhalb des ISO bringt die vollständige Verzeichnisstruktur.


Fehlerbehandlung

fn IsoMitFehlerbehandlung(pfad: pchar): void {
    var w: int64 := IsoWriterNew();

    var rc: int64 := IsoWriterAdd(w,
        "dieser-dateiname-ist-definitiv-zu-lang-fuer-iso9660.txt"c as int64,
        "Inhalt"c as int64, 6);

    if rc == ISO_ERR_NAMETOOLONG then {
        PrintLn("Name > 36 Zeichen — kürzen oder umbenennen"c);
        IsoWriterFree(w);
        return;
    }
    if rc == ISO_ERR_FULL then {
        PrintLn("Maximale Eintragsanzahl (4096) erreicht"c);
        IsoWriterFree(w);
        return;
    }

    rc := IsoWriterSave(w, pfad as int64);
    if rc == ISO_ERR_IO then {
        PrintLn("I/O-Fehler — Schreibrechte oder Speicherplatz prüfen"c);
    }

    IsoWriterFree(w);
}

Fehlercode Bedeutung Lösung
ISO_ERR_NOTISO Datei ist kein ISO 9660-Image PVD-Signatur CD001 fehlt
ISO_ERR_IO Lese- oder Schreibfehler Pfad, Rechte, Speicherplatz prüfen
ISO_ERR_CORRUPT Image beschädigt Andere Quelle verwenden
ISO_ERR_FULL 4096 Einträge erreicht Image aufteilen
ISO_ERR_NAMETOOLONG Name > 36 Zeichen oder leer Namen kürzen

Einschränkungen im Überblick

Einschränkung Details
Unterverzeichnisse (Writer) Nicht unterstützt — nur Flat-Root. Workaround: TAR im ISO
Dateiname (Writer) Max. 36 Zeichen, nur A–Z 0–9 _ .
Einträge Max. 4096 Dateien, max. 256 Verzeichnisse (Reader)
Dateigröße Durch verfügbaren RAM beim Lesen begrenzt (Image wird vollständig geladen)
Kompression Keine — ISO 9660 komprimiert nicht
Joliet / Rock Ridge Nicht unterstützt — lange Dateinamen und Unix-Permissions fehlen
ZIP64 / große Images Kein inhärentes Limit, aber Einzeldateien müssen in den RAM passen
Bootfähigkeit Image ist strukturell korrekt; El Torito Boot-Record wird nicht geschrieben

Weiterführend

Letzte Aktualisierung: 2026-06-13