← ST-Computer 07 / 1988

Auf der Schwelle zum Licht: Directory-Verwaltung Teil 1

Grundlagen

Heute geht es um die Directory-Verwaltung. Da dies wieder ein sehr umfangreiches Kapitel des GEMDOS ist, haben wir es in zwei Teile aufgeteilt. Im ersten Teil beschÀftigen wir uns mit der internen Verwaltung von Directories (Dateiverzeichnissen), Pfaden und den zugehörigen GEMDOS-Funktionen. Im zweiten Teil, der in der nÀchsten Ausgabe erscheint, geht es dann um Suchoperationen in Directories. Diesmal kommen die Freunde der allseits beliebten GEMDOS-Fehler wieder voll auf ihre Kosten.

Allgemeines

ZunĂ€chst ein paar allgemeine Bemerkungen zur Struktur der Directories (Dateiverzeichnisse) unter GEMDOS. Jedes Laufwerk verfĂŒgt ĂŒber ein Root Directory (Wurzelverzeichnis), welches mit ‘' angesprochen wird. Das Root Directory kann neben "normalen” Anwenderdateien wie Programmen, Texten usw. weitere (Sub-)Directories (Ordner) enthalten, die eigene Namen haben. Jeder Ordner kann selbst wiederum weitere Ordner enthalten, so daß sich eine hierarchische Struktur ergibt.

Ein ĂŒbergeordnetes Directory wird “Parent” genannt, ein Subdirectory ist ein “Child”. Diese Bezeichnungen sind jeweils relativ zu einem anderen Directory gemeint, d.h., ein Child kann selbst wiederum Parent eines anderen Directories sein.

Dateien werden durch ihren Zugriffspfad bezeichnet, der sich aus dem Laufwerk und der Abfolge von durchlaufenen Ordnern vom Root Directory bis zum Directory, in dem sich die Datei befindet, ergibt (z.B. ‘A:\ MEGAMAX\HEADERS\STDIO.H'). Dies ist jedem Anwender vom Arbeiten mit dem Desktop her bekannt.

Bei den dem Programmierer zur VerfĂŒgung stehenden GEMDOS-Funktionen können außer diesen vollstĂ€ndigen Dateipfaden auch nur bestimmte Teile davon angegeben werden. Beim Fehlen der Laufwerkskennung bezieht sich der Pfad auf das sogenannte “Standardlaufwerk”, welches mit ’Dsetdrv’ festgelegt werden kann (z.B. ‘\MEGAMAX\HEADERS\STDIO.H').

Wenn der erste Backslash (‘\’) fehlt, bezieht sich die Ordnerfolge nicht auf das Root Directory, sondern wird an den “Standardpfad”, der mit ‘Dsetpath’ eingestellt wird, angehĂ€ngt (relative Pfadangabe). Z.B. meint bei einem Standardpfad ‘\MEGAMAX\’ fĂŒr Laufwerk A: der Name ‘A:HEADERS\STDIO.H’ die vollstĂ€ndige Bezeichnung ’A:\MEGAMAX\HEADERS\ STDIO.H’. Diese beiden AbkĂŒrzungsmöglichkeiten können auch miteinander kombiniert werden, z.B. könnte beim letzten Beispiel auch nur "HEADERS\ STDIO.H’ angegeben werden, wenn A: Standardlaufwerk ist. Diese letzte Möglichkeit ist in den meisten FĂ€llen zu bevorzugen, da hierbei die absolute Position einer Datei im Dateisystem nicht bekannt zu sein braucht.

Bei der kompletten Angabe von Dateinamen, wie in vielen Programmen leider der Fall, besteht die Gefahr, daß eine Datei z.B. immer nur auf Laufwerk A: gefunden wird, obwohl es sich auf C: oder in einem anderen Ordner befindet.

Weiterhin können die speziellen Directories '.' und '..' verwendet werden. '.' bezeichnet das aktuelle Directory selbst und '..' fĂŒhrt eine Directory-Ebene höher. So ist z.B. ‘HEADERS.\ STDIO.H’ identisch mit ‘HEADERS\STDIO.H’ und ‘..\ERRORS.OUT’ bezeichnet '\MEGAMAX\ERRORS.OUT’, wenn der Standardpfad wieder ‘\MEGAMAX\HEADERS' ist.

GEMDOS verwaltet fĂŒr jedes Laufwerk einen eigenen Standardpfad. Dabei hat sogar jeder Prozeß seine eigenen Standardpfade und Standardlaufwerke. Sie werden an nachgeladene Child-Prozesse "weitervererbt”. Die Pfade des Parent-Prozesses können vom Child-Prozeß nicht verĂ€ndert werden.

Bei der maximalen Schachtelungstiefe von Ordnern gibt es keine prinzipielle Begrenzung von Seiten des GEMDOS aus. In der Praxis gibt es EinschrĂ€nkungen bei der maximalen PfadlĂ€nge (s. 'Dgetpath’) und bei der “internen Speicherverwaltung''.

Aufbau der Directories

Subdirectories sind ganz Ă€hnlich wie "normale” Dateien aufgebaut. Ihr Directory-Eintrag in ihrem Parent Directory unterscheidet sich hauptsĂ€chlich im Dateiattribut. Bei Directories ist hier Bit 4 gesetzt. Außerdem ist als “DateilĂ€nge” immer Null eingetragen. Die Directory-Datei besteht aus lauter 32 Byte großen Directory-EintrĂ€gen. Ihre Struktur ist in Abb. 1 dargestellt.

'dir_name' ist der Dateiname im GEM-DOS-Format. Er ist immer genau 11 Zeichen lang, 8 Zeichen fĂŒr den Hauptteil des Namens, 3 Zeichen fĂŒr die Namenserweiterung (Extender). Zu kurze Namensteile werden mit SPACE aufgefĂŒllt (‘DATEI.X’ wird hier als ‘DATEI X ‘ dargestellt). Jeder Dateiname darf pro Directory nur einmal Vorkommen. Es darf also keine Anwenderdatei und einen Ordner gleichen Namens geben. ‘dir_attr’ ist das "Dateiattribut”, das Auskunft ĂŒber Typ und einige Eigenschaften der Datei gibt. Es wird nĂ€chsten Monat noch diskutiert.

Der timestamp (Zeit und Datum der Erstellung der Datei) findet sich in ‘dir_date’ und ‘dir_time’ (zur ReprĂ€sentation dieser Daten siehe spĂ€tere Folge ĂŒber die “Timer-Funktionen”).

Die Nummer des ersten Clusters der Datei ist ‘dir_stcl’. Die weiteren Clusternummern werden mit der FAT ermittelt (s. MĂ€rz-Ausgabe). ‘dir_flen’ schließlich ist die LĂ€nge der Datei in Bytes.

Timestamp, Startcluster und DateilĂ€nge sind ĂŒbrigens im Intel-Format gespeichert, d.h. die Reihenfolge von höher- und niederwertigen Bytes ist gegenĂŒber dem Motorola-Format gerade vertauscht. Beim Löschen von Dateien wird im Directory nur das erste Zeichen des Dateinamens auf $E5 gesetzt, der Rest bleibt erhalten. Da die zugehörigen Cluster in der FAT aber freigegeben werden, ist ein Rekonstruieren gelöschter Dateien nicht so ohne weiteres möglich.

typedef struct { char dir_name[11]; /* Dateiname */ char dir_attr; /* Attribut */ char dir_dummy [10] ; /* unbenutzt */ unsigned int dir_time; /* Erstellungszeit */ unsigned int dir_date; /* Erstellungsdatum */ int dir_stcl; /* erster Cluster */ long dir_flen; /* DateilÀnge */ } DIR;

Abb. 1: Directory-Eintrag

Ein neu angelegtes Directory besteht zuerst aus einem Cluster, der anfangs gelöscht ist (nur Nullbytes enthÀlt). Sobald mehr Dateien erzeugt werden, als hier Platz haben, wird das Directory automatisch um einen Cluster verlÀngert, der ebenfalls zuerst gelöscht wird.

Eine Ausnahme bildet wiederum das Root Directory. Es hat eine fest vorgegebene LĂ€nge, die nicht ĂŒberschritten werden kann. Das Root Directory wird beim Formatieren komplett mit Nullen gefĂŒllt. Ein Directory wird jedoch nie verkĂŒrzt, auch dann nicht, wenn der letzte Cluster ĂŒberhaupt nicht mehr benutzt wird.

Neue EintrĂ€ge werden immer an der ersten freien Position gemacht. Die EintrĂ€ge haben somit keine bestimmte Reihenfolge. FĂŒr die sortierte Anzeige in den Fenstern ist der Desktop verantwortlich. Jedes Subdirectory hat noch zwei spezielle EintrĂ€ge, die die Dateinamen ‘.’ und ‘’ haben. Sie werden bei Anlegen eines Subdirectory automatisch erzeugt und erscheinen im allgemeinen bei der Anzeige von Directories nicht. '.' verweist auf das Directory selbst (‘dir_stcl’ ist der eigene Start-Cluster), fĂŒhrt zum Parent Directory (‘dir_stcl’ ist der Start-Cluster des Parent Directories). Wenn das Root Directory der Parent ist, enthĂ€lt ‘dir_stcl’ hier Null.

Directory-Dateien

GEMDOS verwaltet Directories ganz Ă€hnlich wie Anwenderdateien. Das bedeutet, jedes Directory hat einen eigenen “File Descriptor” (FD), ĂŒber den die eigentlichen Datei-Zugriffe wie Lesen, Schreiben und Positionieren laufen. Dabei finden die allgemeinen internen Routinen Verwendung, wie sie in der MĂ€rz-Folge beschrieben wurden. FĂŒr die von GEMDOS intern verwalteten Directory-Dateien gibt es keine Datei-Handles und keine “File Control Blocks” (FCB).

Da auf Directories wesentlich hĂ€ufiger zugegriffen werden muß als auf andere Dateien, wird hier ein erhöhter Verwaltungsaufwand betrieben. Dies hat zur Folge, daß '.' und '..' von GEMDOS ĂŒberhaupt nicht benutzt werden. Sie existieren nur aus KompatibilitĂ€tsgrĂŒnden zu PC-DOS.

GEMDOS merkt sich alle Verzeichnisse, auf die irgendwann einmal zugegriffen wurde, um bei weiteren Suchoperationen sofort zu wissen, was mit dem Directory los ist. Dies spart erheblich an Zugriffen auf die ĂŒbergeordneten Directories. Zu jedem GEMDOS bekannten Directory existiert dazu eine Datenstruktur, die ich “Directory Descriptor” (DD) getauft habe. Die DDs sind in einer Baum-Struktur entsprechend der Directory-Hierarchie angeordnet. Jedesmal, wenn eine der GEMDOS-Dateifunktionen ein Directory anspricht, fĂŒr das es noch keinen DD gibt, wird dieser automatisch angelegt und in den Baum einsortiert. GEMDOS geht sogar noch weiter, denn das Bekanntmachen neuer Directories geschieht sozusagen im Vorbeigehen. Bei jeder Suchoperation (also auch beim Suchen gewöhnlicher Dateien) werden nĂ€mlich alle Subdirectories, deren EintrĂ€ge untersucht werden, bekannt gemacht. Da sich GEMDOS nicht fĂŒr V und interessiert, gibt es dafĂŒr auch keine DDs.

“Directory Descriptoren” (DD)

DDs sind nun folgendermaßen aufgebaut (s. Abb. 2). Ähnlich wie die FDs beinhalten sie einige Informationen zum Eintrag des Directories in dessen Parent Directory. Dazu gehören der eigene Name im GEMDOS-Format (‘dd_name’), der erste Cluster (‘dd_stcl’) sowie Erstellungszeit und -datum

(‘dd_time’ und ‘dd_date’). Der Startcluster liegt in GEMDOS-ZĂ€hlung und der timestamp im Intel-Format vor. FĂŒr weitere ErlĂ€uterungen vergleichen Sie bitte mit den ErklĂ€rungen zum FD aus der letzten Folge.

‘dd_dfd’ zeigt auf den FD des Directories und bedarf wohl keiner weiteren ErklĂ€rung. NIL zeigt an, daß das Directory zwar schon erkannt, aber noch kein FD benötigt wurde. Der FD wird erst dann eingerichtet, wenn auch tatsĂ€chlich ein Zugriff auf das Directory stattfindet. Auch eine Referenz auf das zugehörige Laufwerk muß sein: 'dd_dmd’ verweist auf den "Drive Media Descriptor”, der ja auch schon frĂŒher behandelt wurde.

Im letzten Artikel wurde erwĂ€hnt, daß die FDs aller geöffneten Dateien eines Directories in einer Liste verknĂŒpft sind.

‘ dd_fdl ’ zeigt auf den Beginn dieser Liste. Dabei bedeutet NIL, daß keine Dateien geöffnet sind.

Doch kommen wir nun zum eigentlich interessanten Teil, der Verkettung der einzelnen DDs untereinander (Abb. 3). Der RĂŒckverweis auf das eigene Parent Directory geschieht mit ‘dd_pardd’. Das Root Directory hat als einziges keinen Parent; daher steht hier NIL. ‘dd_chdd’ bezeichnet ein Child Directory; wenn noch keins bekannt ist, steht hier NIL. Die einzelnen Directories einer Hierarchie-Ebene sind in einer ‘Child-Liste’ mit ‘dd_link’ verbunden, das Ende der Liste wird durch NIL markiert. Die Reihenfolge der Directories in der Liste ist im allgemeinen umgekehrt wie in ihrem Parent. Das ist aber nicht immer so, daher ist diese Reihenfolge als Undefiniert anzusehen. Mit Hilfe dieser drei Zeiger kann sich GEMDOS also relativ schnell innerhalb der Directory Hierarchie bewegen, ohne daß Zugriffe auf den Massenspeicher notwendig sind.

Da Zugriffe auf das Parent Directory ziemlich hĂ€ufig sind, gibt es noch weitere VerknĂŒpfungen zwischen Parent und Child. In der Abb. 4 sind alle Beziehungen eines Child Directories zu seinem Parent dargestellt. Der FD des Parents kann direkt ĂŒber ‘dd_parfd’ angesprochen werden, ohne Umweg ĂŒber den DD des Parents.

Jedes Directory hat ja einen ganz normalen Eintrag in seinem Parent. ‘dd_parpos’ bezeichnet nun die Dateiposition dieses eigenen Eintrags im Parent. Über ‘dd_pardfd' und ‘dd_parpos’ kann also mit Hilfe von Fseek und Fwrite direkt der eigene Eintrag angesprochen und verĂ€ndert werden. ‘dd_lpos’ zeigt auf die höchste Dateiposition, die bisher bei Suchoperationen in diesem Directory erreicht wurde. Dies ist fĂŒr das automatische Erweitern des DD-Baumes wichtig (s. Teil 2).

Damit können wir auch noch einmal auf die FDs von Anwenderdateien der letzten Folge zurĂŒckkommen. Die Verbindungen mit dem zugehörigen Directory-Descriptor sind in Abb. 5 dargestellt. Es ist praktisch eine vereinfachte Version von Abb. 4.

Hier noch ein Hinweis fĂŒr diejenigen, die sich mit der internen Speicherverwaltung des GEMDOS auskennen. Die DDs benötigen vier 8-Wort-Einheiten. Freigegebene DDs werden daher ebenso wie die FDs in der ‘mifl’-Liste 4 aufbewahrt.

typedef struct { char dd__name [11] ; /* Directory-Name */ int dd_stcl; /* Start-Cluster */ unsigned int dd_time; /* Erstellungs-Zeit */ unsigned int dd_date; /* Erstellungs-Datum */ FD *dd_dfd; /* Zeiger auf eigenen FD */ DD *dd_pardd; /* Zeiger auf DD des parent directory */ DD *dd_chdd; /* Zeiger auf DD eines child directory */ DD *dd_link; /* Zeiger auf DD des nÀchsten childs */ DMD *dd_dmd; /* Zeiger auf zugehörigen DMD */ FD *dd_parfd; /* Zeiger auf FD des parent directory */ long dd_parpos; /* Position des eigenen Eintrags im parent */ long dd_lpos; /* letzte Directory-Suchposition */ FD *dd_fdl; /* Zeiger auf ersten User-Datei-FDi */ } DD;

Abb. 2: Directory Descriptor

Initialisierung des DD-Baumes

Wenn ein Laufwerk zum ersten Mal angeprochen wird, richtet GEMDOS, wie Sie schon wissen, einen “Drive Media Descriptor” (DMD) ein. Dabei wird automatisch der DD fĂŒr das Root Directory erzeugt. Damit ist ein Minimal-Baum vorhanden, der bei nachfolgenden Operationen erweitert wird.

Der Baum wird dabei im Laufe der Zeit immer nur vergrĂ¶ĂŸert. GEMDOS hat quasi ein perfektes GedĂ€chtnis, was die Existenz von Directories angeht. Eine Ausnahme bildet der Mediumwechsel, bei dem der gesamte DD-Baum aufgelöst wird. Hieraus ergeben sich auch Probleme mit der Harddisk. Daeshieri.allg. sehr viele Ordner und nie Medienwechsel gibt, sammeln sich recht viele DDs an, die leicht zu einer Überlastung der internen Speicherverwaltung fĂŒhren können.

Pfad-Handles - schon wieder neue Handles

Auf den DDs baut die Verwaltung der Standardpfade auf. Intern wird jeder Standardpfad ĂŒber ein “Pfad-Handle” angesprochen. Diese Pfad-Handles sind fĂŒr den Programmierer legal nicht zugĂ€nglich. Pfad-Handles liegen zwischen 1 und 39, hinzu kommt noch die Null als Dummy-Handle, das verwendet wird, wenn kein Pfad festgelegt ist.

Der Pfad selbst ist durch einen Zeiger auf den DD des Pfadendes, also des letzten Ordners des Pfades, eindeutig charakterisiert. Die Zuordnung dieser Zeiger zu den Pfad-Handles erfolgt ĂŒber eine Tabelle C‘pathx[]’), die zu den globalen GEMDOS-Variablen gehört (Tab. 1). ‘pathx[5]’ liefert also z.B. den Pfad fĂŒr Pfad-Handle 5. Die GĂŒltigkeit eines Elements von ‘pathx[]’ wird durch ‘pthcntx[]’ angezeigt.

Beim Starten von Tochterprozessen werden die Standardpfade mit ihren Handles “vererbt”. Daher kann ein Handle mehrmals vergeben sein. DarĂŒber wird mit 'pthcntx[]’ (Tab. 1) Buch gefĂŒhrt. FĂŒr jedes Pfad-Handle steht hier die Anzahl der Prozesse, denen der jeweilige Standardpfad bekannt ist. Null zeigt an, daß das Pfad-Handle unbenutzt ist. Dann ist das zugehörige ‘pathx[]’ Undefiniert (es muß nicht unbedingt NIL sein).

Nun muß GEMDOS noch wissen, welche Standardpfade jeder Prozeß hat. Dazu findet sich im Prozeßdescriptor (PD) das Feld ‘char p_drvx[16]’, in dem fĂŒr jedes der möglichen 16 Laufwerke das Handle des Standardpfades steht (Null falls noch kein Standardpfad vergeben wurde, d.h. das Laufwerk noch nicht angesprochen wurde).

Abb. 3: Baum der Directory Descriptoren (Beispiel)

Vererbung von Pfaden

Bei ‘Pexec’ werden alle Pfad-Handles aus ‘p_drvx[]’ des Parent-PDs in den Child-PD kopiert, wobei ‘pthcntx[j’ inkrementiert wird. Kurioserweise geschieht dies auch bei Null-Handles, die eigentlich anzeigen, daß kein Standardpfad existiert. Dies fĂŒhrt dazu, daß ‘pthcntx[0]’ unsinnige Werte enthĂ€lt. Dadurch wird erreicht, daß der Child-Prozeß die Pfade des Parents erhĂ€lt, ohne daß fĂŒr jeden Pfad ein neues Handle vergeben werden muß. Erst wenn das Child einen Pfad umsetzt, wird ein neues Handle fĂ€llig. Da sich an den Pfad-Handles des Parents nichts Ă€ndert, bleiben dessen Pfade von allen Aktionen des Childs unberĂŒhrt.

Trotzdem sind 39 Pfad-Handles ein wenig knapp kalkuliert. Bei Verwendung wirklich aller 16 Laufwerke und Programme, die Pfade vieler Laufwerke neu setzen (z.B. Command Line Interpreter, Desktop) kann die Zahl gleichzeitig im Speicher haltbarer Prozesse schon auf 2-3 beschrÀnkt sein.

Beim Terminieren eines Prozesses mit einer der ‘Pterm’-Funktionen wird 'pthcntx[]' bei allen gĂŒltigen Handles dekrementiert. Hier werden Null-Handles extra abgefangen. Es wird jedoch nicht berĂŒcksichtigt, ob ‘pthcntx[]’ auch wirklich echt grĂ¶ĂŸer als Null ist. Dies dĂŒrfte eigentlich auch nicht Vorkommen, aber auf Grund zweier Fehler (siehe weiter unten) kann dieser Fall doch eintreten, und schon hat man negative Werte.

Mit Abb. 6 ist ein Beispiel fĂŒr die Pfadverwaltung mit zwei Prozessen gegeben. Prozeß 1 ist der Parent und hat Pfade fĂŒr die Laufwerke A: und B: definiert. Prozeß 2 ist das Child und hat zunĂ€chst beide Pfade geerbt. Dann hat er einen Pfad fĂŒr C: festgelegt und den Pfad fĂŒr B: umdefiniert. Vergleichen Sie auch mit "Dset-path’.

So weit, so gut. Probleme tauchen aber auf, wenn man Programme mit ‘Pexec’ einmal laden, resident im Speicher halten und mehrmals starten möchte. Denn das Vererben der Pfade geschieht bei allen Pexec’-Modi außer 4 (“nur starten”). Die Übertragung der Pfade geschieht also nur ?eim ersten Start eines Programms und das auch noch zum Zeitpunkt des Ladens. Deshalb wĂ€re es sinnvoller, dies mit den Modi 0 und 4 (“laden und starten” und “nur starten”) zu verknĂŒpfen.

Abb. 4: Child und Parent Directory

Ähnliches gilt ĂŒbrigens auch fĂŒr die I/O-Umleitung. Beim ‘Pterm’ wĂ€re es besser, wenn der PD wieder in den Zustand vor dem ‘Pexec’ gebracht, d.h. ‘p_drvx[]’ gelöscht wĂŒrde. WĂ€hrend letzteres noch “von Hand” gemacht werden könnte, ist ersteres Problem nicht lösbar - halbwegs saubere Programmierung vorausgesetzt. Hier sei an die Atari-Programmierer appelliert, in Zukunft fĂŒr Besserung zu sorgen.

Anmeldung von Laufwerken

Nach der Initialisierung des GEMDOS beim Systemstart sind keinerlei Pfad-Handles belegt (alle ‘p_drvx[]’ sind gelöscht), d.h. es gibt fĂŒr GEMDOS noch keine Standardpfade. FĂŒr den Anwender gilt jedoch das Root Directory als Standardpfad, solange kein anderer mit ‘Dsetpath’ ausgewĂ€hlt wird. Daher sorgt GEMDOS dafĂŒr, daß bei einem Zugriff auf ein Laufwerk, das noch keinen Standardpfad hat, automatisch das Root Directory als Standardpfad gesetzt wird. Dies geschieht in der Routine ‘d_chkdrv’, die bei allen Laufwerks- und Pfadoperationen aufgerufen wird, bevor mit Pfad-Handies usw. gearbeitet wird.

‘d_chkdrv’ wurde schon in der April-Ausgabe unter “Anmeldung von Laufwerken” beschrieben, jedoch folgt hier noch einmal das wesentliche, um den Zusammenhang zur Pfadverwaltung besser herauszustellen.

Abb. 5: Anwenderdatei und Directory

GEMDOS merkt sich, welche Laufwerke ihm bekannt sind, d.h. schon einmal angesprochen wurden. Bei bisher noch nicht bekanntem Laufwerk wird ĂŒberprĂŒft, ob es ĂŒberhaupt ansprechbar ist (mit ‘Getbpb’), wobei alle elementaren Strukturen fĂŒr das Laufwerk (DMD, FD fĂŒr FAT und Root Directory, DD des Root Directory) initialisiert werden. Existiert das Laufwerk nicht, bricht ‘d_chkdrv’ hier ab. Ansonsten ist das Laufwerk auf jeden Fall bekannt.

Ein Null-Pfad-Handle in 'p_drvx[]' zeigt an, daß noch kein Standardpfad festgelegt wurde. In diesem Fall wird ein freies Pfad-Handle ermittelt, der DD des Root Directory, der zu diesem Zeitpunkt auf jeden Fall vorhanden ist, in ‘pathx[]' eingetragen und ‘pthcntx[]’ auf Eins gesetzt. Damit ist das Root Directory als Standardpfad definiert. Das gleiche passiert auch, wenn das Pfad-Handle in ‘p_drvx[]’ zwar gĂŒltig ist, aber ‘pathx[] ein NIL-Zeiger ist. Dieser Fall soll der besonderen Behandlung von Medienwechseln dienen.

DD *pathx[40]; /* $564C/ $564c/ $7dee: Standardpfade */ char pthcntx[40]; /* $58c4/ $58c4/ $8066: Zahl der Prozesse */ long inv_pos /* $17b06/$fdlcl6/$fd3042: Konstante */ =-1L; char dirown[] /* $17b0a/$fdlcla/$fd3046: Directory-Eintrag */ = [ '.',0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20, 0,0,0,0,'0,0,0,0,0,0,0]; char dirpar[] /* $17b20/$fdlc30/$fd305c: Directory-Eintrag */ = [ '.','.',0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20 , 0,0,0,0,0,0,0,0,0,0,0];

Tabelle 1: Globale GEMDOS-Variablen zur Directory-Verwaltung

Diskettenwechsel - lieber nicht

Wie innerhalb dieser Serie schon des öfteren erwĂ€hnt, mĂŒssen bei Medienwechseln, die beim ST i.allg. auf Disketten beschrĂ€nkt sind, alle das Laufwerk betreffenden internen Datenstrukturen freigegeben und gegebenenfalls neu initialisiert werden. Dies gilt selbstverstĂ€ndlich auch fĂŒr die Pfadverwaltung. Deshalb gibt der “Media change”-Handler sĂ€mtliche DDs und FDs frei. Außerdem werden alle Pfade des betroffenen Laufwerks ungĂŒltig gemacht, indem ‘pthcntx[]’ auf Null gesetzt wird. Außerdem wird ‘pathx[]’ auf NIL gesetzt, um die oben erwĂ€hnte Sonderbehandlung durch ‘d_chkdrv' zu erzwingen, die aber nicht die rechte Wirkung zeigt. Das Problem besteht darin, daß die in den PDs gespeicherten Pfad-Handles von nun an ungĂŒltig gemachte Pfade bezeichnen.

Im Falle, daß der Pfad vorher nur vom eigenen Prozeß benutzt wurde, wird das Pfad-Handle bei der nĂ€chsten Gelegenheit neu vergeben, womöglich fĂŒr den Standardpfad eines anderen Laufwerks. Dies hat dann zur Folge, daß dieser Pfad auch fĂŒr das andere Laufwerk gilt. Und schon herrscht Chaos und der Programmierer wundert sich.

Abgesehen davon ist natĂŒrlich die ZĂ€hlung mit ‘pthcntx[]- durcheinandergeraten, was weitere “SpĂ€tfolgen” (z.B. negative Werte in ‘pthcntx [] ’ nach der Prozeß-Terminierung!) nach sich ziehen kann. Der Fehler ließe sich beseitigen, indem nur ‘pathx[]’ gelöscht wĂŒrde, ‘pthcntxf]’ aber unverĂ€ndert bliebe. Die Handles blieben dann belegt und die ‘p_drvx[] -Felder wĂŒrden weiterhin gĂŒltige Handles enthalten.

In den meisten FĂ€llen wird das Laufwerk mit einem neuen Medium ansprechbar sein, so daß dann die Pfad-Handles wieder gĂŒltig werden sollten. Die Routine ‘d_chkdrv\ die vor jeder Benutzung der Pfade zum Einsatz kommt, fragt diesen Fall (Laufwerk bekannt, handle gĂŒltig, aber ‘pathx[]‘ NIL) sogar schon extra ab, reagiert aber falsch darauf. Bisher vergibt sie einfach ein neues Handle, dem das Root Directory als Pfad zugeordnet wird, und setzt es auch noch in ‘p_drvx[]\ Dies wĂŒrde dann funktionieren, wenn dabei ‘ pthcntx []’ des alten Handles dekrementiert wĂŒrde, da es nun einem Prozeß weniger bekannt ist. Dies fehlt vermutlich deshalb, weil auf Grund des ersten Fehlers ‘pthcntx[]’ schon vom “Media change”-Handler gelöscht wird.

GĂŒnstiger wĂ€re es noch, gar kein neues Handle zu vergeben, sondern einfach ‘pathx[]' auf das Root Directory zu setzen. Damit wĂ€re nach einem Mediumwechsel der alte Pfad einfach gegen das Root Directory ausgetauscht worden.

Directory-Suchoperationen

Alle GEMDOS-Funktionen, die einen beliebigen GEMDOS-Dateinamen (möglicher Pfad + Name) als Parameter haben, rufen eine wichtige Routine auf, die aus dem Dateipfad den DD des zugehörigen Directories und den ĂŒbrig bleibenden eigentlichen Dateinamen ermittelt. Diese Routine heißt bei mir ‘d_getdir’.

Eine weitere wichtige Funktion ist ‘ d_srcname ’, die in einem schon ermittelten Directory nach bestimmten Dateien sucht. Beide Routinen werden in Teil 2 ausfĂŒhrlich erlĂ€utert. Entscheidend ist hier nur, daß bei beiden Funktionen alle Directories, die "auf dem Weg” liegen, automatisch in den DD-Baum eingefĂŒgt werden. Wenn ĂŒbergeordnete Routinen mit Directories und Pfaden arbeiten, können sie sich darauf verlassen, daß alle benötigten Strukturen hiernach definiert sind.

Globale GEMDOS-Variablen

Eine Zusammenstellung findet sich in Tab. 1. Die Adressen sind wie ĂŒblich TOS-abhĂ€ngig und beziehen sich auf das RAM-TOS vom 6.2.1986, das ROMTOS vom 6.2.1986 und das ROM-TOS vom 22.4.1987 (von links nach rechts), ‘dirown’ und ‘dirpar’ werden bei ‘Dcreate’ erlĂ€utert.

GEMDOS-Funktionen (TRAP #1)

Funktion $47 Dgetpath

long Dgetpath(char *buf, int drive)

Der aktuelle Standardpfad vom Laufwerk ‘drive’ (0 fĂŒr Standard-Laufwerk, 1 fĂŒr A:, 2 fĂŒr B: usw.) wird ermittelt und in ‘buf’ abgelegt. Der String beinhaltet nicht die Laufwerkskennung, beginnt und endet nicht mit ‘' und ist nullterminiert. Der Standardpfad ‘A:\ORDNERl\ORDNER2' wird also als ‘ORDNER1\ORDNER2' geliefert.

Es gibt keine GrĂ¶ĂŸenbeschrĂ€nkung von Seiten des GEMDOS; ‘buf muß groß genug bereitgestellt werden. Eine GrĂ¶ĂŸe von 128 Zeichen dĂŒrfte im allgemeinen ausreichen.

RĂŒckgabewerte:

-46L (EDRIVE) Laufwerk existiert nicht, interner Fehler (kein interner Speicher, keine Pfad-Handles mehr)
0L alles ok

Arbeitsweise

Zuerst wird eine e ventuelle Angabe des Standardlaufwerks ausgewertet, d.h.. das gewĂŒnschte Laufwerk wird ermittelt. Wie ĂŒblich erfolgt der Aufruf von ‘d_chkdrv' Beginnend beim DD des Directories am Pfadende (erreicht durch ‘p_drvx[]’ und ‘pathx[]' wird sich mittels der ‘dd_pardd’-Zeiger rekursiv bis zum Root Directory hochgearbeitet. Beim RĂŒcklauf der Rekursion werden die Directory-Namen (‘dd_name’) zum Pfad zusammengesetzt.

Stack-Überlauf-Probleme dĂŒrfte es hier nicht geben, da der fĂŒr die AusfĂŒhrung von GEMDOS-Funktionen eigens bereitgestellte Stack 3 kB groß ist. Dies ist eine richtig hĂŒbsche Routine; bei aller Kritik muß man die GEM-DOS-Programmierer auch einmal loben.

Funktion $3B Dsetpath

long Dsetpath(char *path)

Der Standardpfad fĂŒr das in ‘path' mit angegebene Laufwerk wird neu gesetzt. Er gilt nur fĂŒr den eigenen Prozeß und wird von diesem an Tochterprozesse vererbt, ‘path’ endet nicht mit ‘V. Da Accessories immer unter dem PD der Haupt-Applikation laufen, können sich hier Konflikte ergeben. Wenn Accessories Pfade umsetzen. findet die Applikation eventuell ihre Dateien nicht mehr. Daher sollten Accessories ‘Dsetpath’ (und auch ‘Dsetdrv’) nicht benutzen, sondern jeweils komplette Pfade angeben.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-1L (ERROR) Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles)
-34L (EPTHNF) Pfad nicht gefunden, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
OL alles ok.

Arbeitsweise

Zuerst wird das Laufwerk, bei dem der Standardpfad geÀndert werden soll, ermittelt. Wenn kein Laufwerk explizit im Pfad angegeben ist, wird das aktuelle genommen.

Auch hier wird zunĂ€chst ‘d_chkdrv’ aufgerufen. Anschließend wird der alte Pfad ungĂŒltig gemacht, indem ‘pthcntx[]’ um eins erniedrigt wird. Wenn der Pfad von mehreren Prozessen benutzt wird, bleibt das Pfad-Handle also noch belegt.

Nun wird ein freies Pfad-Handle gesucht (erkannt an einer Null in ‘pthcntx[]’), woraufhin mit ‘d_getdir’ der DD des gewĂŒnschten Directories (also des neuen Pfadendes) ermittelt wird.

Zuletzt wird ein Zeiger auf diesen DD in ‘pathx[]’ mit dem vorher gefundenen Pfad-Handle gespeichert, das zugehörige ‘pthcntx[]’ inkrementiert (hier also auf Eins gesetzt) und das Pfad-Handle in ‘p_drvx[]’ gemerkt.

Hier hat der Fehlerteufel mal wieder zugeschlagen:

Die Angabe des Laufwerks im Pfadnamen wird so gut wie ignoriert. Der Pfad bezieht sich immer auf das Standardlaufwerk. Beim zuerst ausgefĂŒhrten ‘d_chkdrv’ wird noch das korrekte Laufwerk benutzt. ‘d_getdir’ wird aber ein Pfad ohne Kennung ĂŒbergeben, so daß es auf dem Standardlaufwerk operiert. Dagegen verblaßt der andere Fehler geradezu.

Abb. 6: Verwaltung der Standardpfade (Beispiel)

Wenn kein Pfad-Handle mehr verfĂŒgbar ist (tritt praktisch nicht auf) oder der Pfad nicht gefunden wurde (durchaus möglich!), wird mit EPTHNF abgebrochen. Dabei ist aber der alte Pfad schon freigegeben (‘pthcntx[]’ dekrementiert), und die Referenz auf ihn ĂŒber das Handle aus ‘p_drvx[]’ ist noch gĂŒltig. Dieser Fehler wirkt sich ganz Ă€hnlich wie der unter “Diskettenwechsel” beschriebene aus.

Funktion $39 Dcreate

long Dcreate(char *path)

Ein neues Directory ‘path’ wird geschaffen. Eine Datei oder ein Directory gleichen Namens darf nicht schon existieren.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-34L (EPTHNF) Pfad nicht gefunden, Laufwerk existiert nicht, illegaler Dateiname (‘.’ oder ‘..’), interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) keine Datei-Handies mehr (intern wird ein Datei-Handle benötigt)
-36L (EACCDN) Diskette voll, Root Directory voll, Name existiert schon
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
0L alles ok.

Arbeitsweise

‘Dcreate’ ist im Prinzip ein erweitertes ‘ Fcreate'. Daher wird zuerst das interne ‘Fcreate’ mit dem Dateiattribut $10 fĂŒr Subdirectories aufgerufen. Zur Erinnerung sei hier noch einmal ein Teil der Beschreibung von ‘Fcreate’ wiederholt (s. letzte Folge).

Falls noch nicht vorhanden, wird fĂŒr das Parent Directory des neu zu erzeugenden Directories ein FD angelegt. Die meisten Daten des FD korrespondieren mit entsprechenden des DD und werden direkt ĂŒbernommen. Die DateilĂ€nge (‘fd_len’) wird auf $7FFFFFFF gesetzt, da die LĂ€nge von Subdirectories nur durch die KapazitĂ€t des Mediums begrenzt ist. ‘fd_link’ und ‘fd_mode’ werden nicht benutzt.

Falls das Subdirectory schon existiert, wird abgebrochen. Nun wird im Parent Directory ein freier Eintrag gesucht und mit Dateiname, Attribut und timestamp initialisiert. Die DateilĂ€nge und der Start-Cluster werden auf Null (letzteres zum Zeichen dafĂŒr, daß noch kein Cluster vergeben wurde) gesetzt. Außerdem wird dafĂŒr gesorgt, daß alle GEMDOS-Sektorpuffer zurĂŒckgeschrieben werden.

Jetzt wird die Datei mit einer der internen Open-Routinen, die auch das Handle generiert, richtig eröffnet (s. ‘Fopen’, letzte Folge). Hier werden Subdirectories wie jede andere Datei auch behandelt, d.h. das Subdirectory bekommt einen ganz normalen Eintrag im Parent Directory.

Nach dem ‘Fcreate' wird fĂŒr das neue Subdirectory ein DD eingerichtet und in die Child-Liste des Parent Directories eingehĂ€ngt. Außerdem wird auch gleich ein FD fĂŒr das Subdirectory angelegt. Wenn fĂŒr eine dieser Operationen nicht genĂŒgend interner Speicher vorhanden ist, wird zwar mit ENSMEM abgebrochen, doch die Subdirectory-Datei bleibt bestehen und geöffnet, insbesondere bleibt das Handle belegt und der Eintrag im Parent Directory bleibt erhalten.

Als nÀchstes ermittelt die FAT-Verwaltung einen freien Cluster und macht ihn zum ersten Cluster des Subdirectories. Bei vollem Speichennedium wird die Directory-Datei korrekt geschlossen, das Subdirectory im Parent Directory gelöscht und es erfolgt eine Fehlermeldung (EACCDN).

Nun werden die ersten zwei EintrĂ€ge im Subdirectory (‘.’ und generiert. Die RoheintrĂ€ge sind in den globalen GEM-DOS-Variablen zu finden (Tab. 1). Einziger Fehler hierbei: Der timestamp in diesen EintrĂ€gen wird nicht ins Intel-Format konvertiert. Wenn das Parent Directory das Root Directory ist, wird als Start-Cluster von Null eingesetzt (an Stelle der GEMDOS-eigenen negativen Cluster-Nummer).

Zuletzt wird die Subdirectory-Datei korrekt geschlossen, wobei alle Sektorpuffer zurĂŒckgeschrieben werden.

Funktion $3A Ddelete

long Ddelete(char *path)

Das Subdirectory 'path' wird gelöscht. Es muß dabei leer sein (bis auf die '.' EintrĂ€ge und ‘..’).

RĂŒckgabewerte:

-l..-31BIOS-Fehlermeldung bei Diskzugriff
-34L (EPTHNF) Pfad nicht gefunden, Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-36L (EACCDN) Directory nicht leer
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-65L (EINTRN) interner Fehler (s. “Arbeitsweise”)
0L alles ok

Arbeitsweise

Zuerst wird mit ‘d_getdir’ der DD des zu löschenden Subdirectories ermittelt und gegebenenfalls ein FD angelegt. Nun wird ĂŒberprĂŒft, ob das Subdirectory auch tatsĂ€chlich leer ist. Ab dem dritten Eintrag des Subdirectories werden alle EintrĂ€ge, die mit $E5 beginnen (gelöschte Datei), ĂŒberlesen. Wenn danach das Directory-Ende, erkannt an einem mit $00 beginnenden Eintrag oder am Ende der Directory-Datei, erreicht wird, ist das Subdirectory leer. Als nĂ€chstes wird das Subdirectory in der Child-Liste seines Parents gesucht. Dies muß eigentlich immer klappen. Es gibt aber eine Sicherheitsabfrage, die EINTRN zurĂŒckgeben soll, wenn der DD des Subdirectory nicht auffindbar ist. Leider ist diese Abfrage falsch programmiert, so daß es stattdessen einen Bus Error (2 Bomben) gibt.

Noch zwei weitere FĂ€lle, die eigentlich nicht auftreten dĂŒrfen, werden abgefangen und als EINTRN gemeldet: Es gibt noch offene Dateien im leeren Subdirectory (‘dd_fdl’ ungleich NIL), oder das Subdirectory hat selbst noch Subdirectories (‘dd_chdd’ ungleich NIL). Jetzt wird der DD aus dem DD-Baum ausgehĂ€ngt, DD und FD werden der internen Speicherverwaltung zurĂŒckgegeben und das Subdirectory wird mit einer internen Version von ‘Fdelete’ wie eine normale Datei gelöscht. Haben Sie schon einmal versucht, das Root Directory zu löschen? Eigentlich ĂŒberflĂŒssig zu erwĂ€hnen, daß dies nicht abgefangen wird. ‘Ddelete’ bedankt sich beim Zugriff auf den Parent-DD mit zwei netten Bömbchen.

Funktion $56 Frename

**long Frename(int dummy, char oldpth, char newpth)

Die Datei ‘oldpth’ wird in ‘newpth’ umbenannt, ‘newpth’ muß das gleiche Laufwerk bezeichnen, darf jedoch ein anderes Directory spezifizieren. Die Datei kann also nicht zwischen zwei Laufwerken kopiert, aber innerhalb eines Laufwerks verschoben werden. Dabei werden nur Directory-Verweise geĂ€ndert, die Datensektoren der Datei bleiben an ihrem Platz, sodaß ‘Frename ’ wesentlich schneller als Kopieren und anschließendes Löschen der Datei ist. Darum ist es besonders schade, daß man dies vom Desktop aus nicht ausnutzen kann.

‘dummy’ ist unbenutzt und sollte Null sein, um KompatibilitĂ€t zu spĂ€teren GEMDOS-Versionen zu wahren.

RĂŒckgabewerte:

-1..-31 BIOS-Fehlermeldung bei Diskzugriff
-33L (EFILNF) Datei nicht gefunden, interner Fehler (zu wenig interner Speicher), illegaler Quelldateiname, Datei ist Subdirectory
-34L (EPTHNF) Quell- oder Zielpfad nicht gefunden, Laufwerk existiert nicht, interner Fehler (keine Pfad-Handles, zu wenig interner Speicher)
-35L (ENHNDL) kein Datei-Handle fĂŒr Datei im Quellpfad
-36L (EACCDN) Zieldatei existiert schon, Datei ist ‘read only’
-39L (ENSMEM) interner Fehler (zu wenig interner Speicher)
-48L (ENSAME) unterschiedliche Laufwerke in Quell- und Zielpfad
-65L (EINTRN) interner Fehler (s. “Arbeitsweise”)
OL alles ok.

Arbeitsweise

Zuerst wird mit ‘Fsfirst’/’Fsnext’ (s. Teil 2) ĂŒberprĂŒft, ob es eine Datei gleichen Namens wie ‘newpth’ schon gibt. Dann werden mit ‘d__getdir’ die DDs von Quell- und Zielpfad bestimmt. Daraufhin werden Quell- und Ziellaufwerk miteinander verglichen, um evtl, mit ENSAME abzubrechen. Die Datei wird mit ‘Fopen’ und Modus 2 (“lesen und schreiben”) eröffnet, daher erfolgt hier ein Abbruch bei ‘read only'-Dateien. Wenn Quell- und Zieldirectory gleich sind, wird direkt mit internem ‘Fwrite’ der alte Dateiname im Directory durch den neuen ersetzt.

Bei verschiedenen Directories wird zuerst die Datei im Quelldi-rectory gelöscht, indem $E5 ins erste Zeichen des Dateinamens geschrieben wird. Dann werden Dateiattribut, Start-Cluster, DateilĂ€nge und timestamp gemerkt. Jetzt erst wird mit ‘Fcreate’ und dem alten Dateiattribut ein neuer Dateieintrag im Zieldirectory geschaffen. Die gemerkten Daten werden ins Zieldirectory ĂŒbertragen und Zieldatei und Zieldirectory geschlossen, so daß alle Änderungen hier auf das Speichermedium ĂŒbertragen werden.

In beiden FĂ€llen wird zum Schluß die Quelldatei geschlossen. Nur wenn hierbei kein Fehler auftritt, wird die Quell-directory-Datei ebenfalls geschlossen. Beim Umbenennen zwischen Ordnern ist vor allem die Reihenfolge der Aktionen zu beachten. Wenn beim Anlegen des neuen Eintrags etwas schiefgeht (z.B. BIOS-Fehler beim Zugriff auf Diskette), ist die Datei im Quelldirectory schon gelöscht!

Schlimmer noch, daß beim ‘Fcreate’ fĂŒr die Zieldatei Fehler nicht erkannt werden, was zu Bombenhagel fĂŒhren muß. Und zu diesem Zeitpunkt ist die Datei ebenfalls schon gelöscht... Mögliche Fehler sind z.B. “Diskette voll", “Root Directory voll”, “keine Datei-Handles mehr”. Auch die Abfrage zu Beginn, ob der Zielname schon existiert, greift nicht immer. ‘Fsfirst’ wird nĂ€mlich mit dem Suchattribut Null aufgerufen. Deshalb werden “versteckte Dateien” (‘hidden-Bit' oder ‘system-Bit’ im Dateiattribut gesetzt) nicht bemerkt. Das spĂ€tere ‘Fcreate’ löscht nun rĂŒcksichtslos die versteckte Datei, um den neuen Eintrag fĂŒr die umzubenennende Datei zu schaffen.

NĂ€chsten Monat geht es dann weiter mit Teil 2 der Directory-Verwaltung.

Alex Esser