Die 40-Ordner-Zeitbombe entschärft

Spätestens seit es Festplatten für den ST gibt, schlagen sich die Anwender mit dem tückischen und allseitig bekannten 40-Ordner-Fehler herum. Allerlei Nothilfen wie FOLDRxxx geistern durch die Programmwelt, doch gibt es auch einen einfachen Programmiertrick, um das Auftauchen des unheilvollen Fehlers zu verzögern und sehr unwahrscheinlich zu machen.

Der “40-Ordner-Fehler” ist einer der berüchtigtsten Fehler der TOS-Versionen 1.0 und 1.2. Der Fehler beruht, kurz gesagt, darauf, daß eine vom GEMDOS zur Beschleunigung des Dateizugriffs geführte Verzeichnisliste überläuft und dieser Überlauf nur unzureichend abgefangen ist (s. dazu z.B. [1], S. 472ff. oder [2]).

Programme wie FOLDRxxx von ATARI schaffen hier insofern Abhilfe, als der zur Verfügung stehende Verzeichnisspeicher vergrößert wird. Einen Überblick über die Anzahl der restlichen noch verfügbaren Ordnerplätze kann man mit dem gleichfalls von ATARI gelieferten Programm DIRSLEFT.TOS erhalten.

Sehenden Auges in die Katastrophe

Mit der so geschaffenen Lage könnte man sich einigermaßen abfinden, gäbe es nicht einen weiteren GEMDOS-Fehler, der dem Verzeichnisbaum zu unaufhaltsamem Wachstum verhilft und so auch trotz vergrößerten Verzeichnisspeichers die Katastrophe unausweichlich herbeiführt. Von diesem Fehler, der von A. Esser [2], Heft 7 und 8/1988, als “einer der schwerwiegendsten Fehler des ganzen GEMDOS” bezeichnet wird, soll im folgenden die Rede sein und vor allem von einem äußerst einfachen Trick zu seiner Verhinderung.

Einen Freund und Helfer hat der 40-Ordner-Fehler in einem Initialisierungsfehler bei einigen wichtigen und häufig benutzten GEMDOS-Funktionen, z.B.

GEMDOS 57 (Dcreate)
GEMDOS 60 (Fcreate)
GEMDOS 61 (Fopen)
GEMDOS 75 (Pexec)
GEMDOS 78 (Fsfirst)

usw.

Wird von diesen Operationen ein im Verzeichnisbaum noch nicht vorhandener Ordner angesprochen, wird dieser fehlerhaft in den Verzeichnisbaum eingebaut, so daß er von nachfolgenden GEMDOS-Operationen, die den gleichen Ordner ansprechen, nicht mehr erkannt werden kann und infolgedessen erneut eingebaut wird. Auf diese Weise kann auch ein mit FOLDRxxx erweiterter Verzeichnisspeicher sehr schnell verbraucht und der “40-Ordner-Fehler” ausgelöst werden.

Agent Provocateur

Um eigene Experimente ausführen zu können, benötigt man eine Möglichkeit, den Initialisierungsfehler, etwa für Wurzelverzeichnisse, provozieren zu können. Dies leistet das kleine GFA-BASIC-Programm in Listing 1, wobei die Lücke in der fünften Zeile für die spätere Fehlerverhinderung vorgesehen ist.

Bei der Auswahl des zu testenden Laufwerks ist zu beachten, daß einerseits der Initialisierungsfehler beim Ansprechen gut gefüllter Wurzelverzeichnisse besonders schnell zur Katastrophe führt, während andererseits gewisse TOS-Funktionen eine “Immunisierung” bewirken, indem sie eine fehlerfreie Initialisierung für den angesprochenen Ordner ausführen, z.B.

Ein derart geöffnetes Verzeichnis wird bei erneutem Auftreten als bereits vorhanden erkannt und ist damit gegen den zu provozierenden Fehler immun!

Will man also umgekehrt den Fehler provozieren, so muß man alle “Immunisierungen” möglichst vermeiden. Man schließt daher am besten alle überflüssigen Fenster im Desktop und fixiert diesen Zustand per ARBEIT SICHERN. Nach dem anschließenden Booten kann das obige Testprogramm gestartet werden, wobei natürlich alle dazu erforderlichen Laufwerke für den Test ausfallen.

Der Grund für die mögliche Immunisierung von Ordnern besteht darin, daß die letztgenannten Betriebssystemfunktionen den angesprochenen Ordner vollständig durchsuchen, was (s. [2]) einen ordnungsgemäßen Einbau des geöffneten Ordners in den Verzeichnisbaum zur Folge hat.

PRINT AT(10,10);"Zu testendes Laufwerk (C, D,...): "' pfad$=UPPER$(INPUT$(1)) 
filename$=pfad$+":\*.*"
PRINT filename$
'Lücke für die Immunisierungsanweisung 
REPEAT
    INC i%
    e%=GEMDOS(78,L:VARPTR(filename$),-1)
    PRINT AT(10,12);i%''e%
UNTIL e%<0 OR INKEY$>""
PRINT "GEMDOS-Fehler:"'e%

Listing 1: Wie man den 40-Ordner-Fehler provoziert

Und die Moral von der Geschieht’?

So entsteht natürlich die Frage, ob man nicht diese immunisierende Wirkung generell dazu benutzen kann, um den GEMDOS-Fehler auszuschalten. Das ist in der Tat leicht möglich: Bei der GEMDOS-Funktion FSFIRST (78) wird der durch pfad$ angesprochene Ordner vollständig durchsucht, wenn man dieser Funktion den Pfad

pfad$+dummy$

übergibt, wobei dummy$ eine Datei bezeichnet, die in dem Ordner garantiert nicht vorhanden ist. So wird der Dateiname “))).)))” wohl nur von ausgesprochenen Esoterikern verwendet. Bei allen anderen löst der Aufruf von GEMDOS(78) mit dem Pfad

pfad$+”\))).)))”

den gewünschten Effekt aus.

[Anm.: Bei weiteren Experimenten stellte sich heraus, daß es ausreicht, Fsfirst nur den Pfad zu übergeben, also etwa “D:\” oder “F:\EBENE1\TIEFER\’. Damit wird auch der unwahrscheinliche Fall ausgeschlossen, daß eine eventuell doch vorhandene Datei mit Namen “))).)))” gefunden wird. -CB-]

Fügt man daher in die Lücke für die Immunisierungsanweisungen die Befehle

noname$=pfad$+":\))).)))""
VOID GEMDOS(78,L:VARPTR(noname$),-1)

ein, so wird das Wurzelverzeichnis “immunisiert”, d.h. bei den weiteren GEMDOS-Operationen bezüglich des hier angesprochenen Wurzelverzeichnisses wächst der GEMDOS-Verzeichnisbaum nicht weiter an, der 40-Ordner-Fehler wird wesentlich verzögert.

Hilfe zur Selbsthilfe

Welche allgemeine Konsequenz ergibt sich aus diesen Überlegungen?

Wegen der immunisierenden Wirkung der GEMDOS-Funktion FSFIRST mit dem oben konstruierten Dummy-Pfad genügt der einmalige (!) Aufruf dieser Routine für jeden in einem Programmlauf auftretenden Ordner. Kann man die auftretenden Ordner in der Programmierungsphase nicht vorhersehen, so sollte man den Immunisierungsbefehl vor jeder kritischen GEMDOS-Anweisung ausführen, also z.B.

...
pfad$=... ! Pfad zum Ordner
...
noname$=pfad$+”\))).)))”
VOID GEMDOS(78,L:VARPTR(noname$),-1) 
OPEN “I”,#1,pfad$+"\"+filename$

Man beachte also, daß auch Hochsprachen-Dateibefehle wie OPEN intern auf GEMDOS zurückgreifen! Es ist klar, daß dieses Vorgehen analog auch in allen anderen Sprachen möglich ist, die Zugriff zu den GEMDOS-Funktionen bieten.

Bleibt zu wünschen, daß von den Hoch-sprachen-Anbietern die Immunisierung gleich in die dort vorhandenen GEMDOS-Adaptionen (z.B. EXIST, OPEN, EXEC usw. in GFA-BASIC) eingebaut wird, so daß die Immunisierung in diesen Sprachen künftig nicht mehr explizit in Erscheinung tritt. Dazu ist, wie in [2] genauer begründet, im wesentlichen die Korrektur eines einzigen Bits erforderlich - ein Problem des “gewußt wo”. Der Normalanwender kann sich bis dahin mit dem oben beschriebenen Trick behelfen.

In TOS 1.4 scheint der Freund und Helfer des 40-Ordner-Fehlers diesen verlassen zu haben; generell wurde die Verwaltung des internen Ordnerspeichers stark überarbeitet und verbessert. Listing 2 zeigt eine Version des Demo-Programms samt Immunisierung für OMIKRON.BASIC, Listing 3 bedient die Turbo-C-Freunde unter uns. Und um falsche Begeisterung im Keime zu ersticken: Der 40-Ordner-Fehler selbst ist mit dem beschriebenen Trick nicht behoben, aber doch sein wichtigster Zulieferer außer Gefecht gesetzt. Verwendet man nun noch FOLDRxxx, ist der 40-Ordner-Fehler selbst im alten TOS in praxi verschwunden.

(Prof.Dr. G. Bruhn/CB)

Literatur:

[1] Brod/Stepper: SCHEIBENKLEISTER II, MAXON, 1989, hier insbesondere Abschnitt 14.1.2 (“FOLDRxxx - Atari Baba und die vierzig Ordner’’)

[2] A. Esser: Auf der Schwelle zum Licht, Directory-Verwaltung Teile I und 2 in “ST-Computer” 7/88 und 8/89.

    ' =================================================================
    ' Demo-Programm für TOS-Fehler und seine Vermeidung in OMIKRON.BASIC
    ' =================================================================
    PRINT @(10,10);"Zu testendes Laufwerk (C, D,...): ";
    Pfad$= UPPER$( INPUT$(1))
    Filename$=Pfad$+":\*.*"
    PRINT Filename$
    Noname$=Pfad$+":\))).)))"REM Möglich wäre auch pfad$+":\"
    N%L= LPEEK( VARPTR(Noname$))+ LPEEK( SEGPTR +28)
    '
    GEMDOS (R%L,78, HIGH(N%L), LOW(N%L),-1)
    ' Dies ist der Immunisierungsbefehl.
    ' Ihn wegzulassen, führt in der nachfolgenden Repeat-Schleife 
    ' zum 40-Ordner-Fehler, wenn das gewählte Laufwerk seit dem Booten 
    ' noch nicht anderweitig immunisiert wurde (via Desktop oder 
    ' Dateiauswahlbox). Ferner muß das Wurzelverzeichnis mindestens 
    ' z w e i   Ordner enthalten.
    '
    A%L= LPEEK( VARPTR(Filename$))+ LPEEK( SEGPTR +28)
    REPEAT
        I%=I%+1
        GEMDOS (E%L,78, HIGH(A%L), LOW(A%L),-1)
        PRINT @(12,10);I%,E%L 
    UNTIL E%L<>0 OR INKEY$ <>""
    PRINT "GEMDOS-Fehler ";E%L

Listing 2: Demoprogramm in OMIKRON .BASIC

/* (c) MAXON Computer GmbH */
#include <stdio.h>
#include <tos.h>
#include <ctype.h>
#include <string.h>

#define OUT(a)          Cconout(a)
#define CLS             OUT(0x1b);OUT('E')
#define PRINTAT(a,b)    OUT(0x1b);OUT('Y');OUT(b+32);OUT(a+32)

void immunisiere(char *); 
void main(void);

/*      void immunisiere(char *)
**      Immunisiert ein Verzeichnis gegen den im Artikel beschriebenen
**      Fehler. Dazu muß die Routine mit dem passenden Pfad aufgerufen
**      werden. Will man das Wurzelverzeichnis von D:
**      immunisieren, lautet der Pfad "D:\".
*/
void immunisiere(pfad) 
char *pfad;
{
    Fsfirst(pfad, -1);
}

void main()
{
    char c, dname[8]; 
    int error,i = 0;
    DTA *mydta;

    CLS; puts("Immunisierung gegen den 40-Ordner-Fehler"); 
    puts("Idee: Prof. Dr. G. Bruhn, C-Version: Claus Brod");

    puts("\nZu testendes Laufwerk (C, D,...): "); 
    dname[0] = toupper(Cnecin());

    strcpy(&dname[1], ":\\");

    puts("\nImmunisieren (j/n)?"); 
    do
        c = toupper(Cnecin()); 
    while ( (c != 'J') && (c != 'N') );

    if (c == 'J')
        immunisiere(dname);

    strcpy(&dname[3], "*.*");

    mydta = Fgetdta(); 
    do 
    {
        error = Fsfirst(dname, -1);
        PRINTAT(10,12); printf("%d %d %s\n", i++, error, mydta->d_fname);
    } while ( (error >= 0) && (Bconstat(2) == 0) );

    printf("GEMDOS-Fehler %d\n", error);
}

Listing 3: Die Turbo C-Version



Aus: ST-Computer 02 / 1990, Seite 170

Links

Copyright-Bestimmungen: siehe Über diese Seite