Kompatibilitätsproblem: ICD-Festplattenformat nach AHDI wandeln

Der ATARI Falcon erlaubt es, neben der internen AT-Bus- auch eine externe SCSI(II)-FestpIatte zu verwenden. Manch einer, der bisher einen „kleinen“ ATARI sein Eigen nannte, möchte nun selbstverständlich eine vorhandene SCSI-Platte weiterverwenden. Das stellt im Allgemeinen auch kein Problem dar, es sein denn, man hat eine Platte, die mit ICD-Host-Adapter angeschlossen und mit ICD-Software mit mehr als 4 Partionen versehen worden war.

In diesem Fall sind nämlich die Formate der Root-Sektoren der beiden Platten unterschiedlich, so daß bei Benutzung von AHDI (das beim Falcon zum Einsatz kommt) oder kompatibler Treiber nur die ersten 4 Partitionen der ICD-Harddisk erkannt werden. Im folgenden Artikel sollen nun die Möglichkeiten, dieses Problem zu lösen, diskutiert werden (dies gilt selbstverständlich nicht nur für den Falcon, auch in anderen Fällen kann es sinnvoll sein, eine mit ICD-Software partitionierte Platte für AHDI zugänglich zu machen).

Zunächst einmal bietet sich die leichteste Lösung an, einfach den ICD-Treiber auf der Falcon-internen Platte zu installieren, solange dessen Versionsnummer 6.0.2 oder höher ist. Denn dieser ist in der Lage, Geräte am IDE-Bus zu bedienen und kann auch AHDI-Root-Sektoren mit XGM-Verkettung korrekt verarbeiten. Diese Lösung ist allerdings nicht besonders zu empfehlen, da es sich immer deutlicher abzeichnet, daß Harddisk-orientierte Programme (wie z.B. Diskus) nur AHDI-konforme Platten unterstützen bzw. auf einen AHDI-kompatiblen Treiber angewiesen sind. Diese Forderung kann hiermit leider nicht erfüllt werden.

Die zweite Möglichkeit besteht darin, von der ICD-Platte ein komplettes Backup zu machen (partitions- oder dateiorientiert), die Festplatte mit HDX oder einem AHDI-kompatiblen Partitionierer neu aufzuteilen und danach die Daten wieder zurückzukopieren. Dies ist sicherlich die „sauberste“ Methode, dafür ist sie aber extrem zeitaufwendig. Außerdem darf natürlich beim Backup und/oder beim Restore kein Fehler auftreten, sonst wäre Datenverlust vorprogrammiert.

Die dritte und letzte Möglichkeit, um die es in diesem Artikel auch hauptsächlich gehen soll, besteht darin, die mit ICD partitionierte Platte durch geeignete Manipulationen AHDI-kompatibel zu machen. Ich weise an dieser Stelle darauf hin, daß für die folgenden Änderungen an der Festplatte eine fundierte Kenntnis über den Aufbau einer solchen vorhanden sein sollte, sonst kann es schnell zum Datenverlust kommen.

Die Root-Sektorformate

Zunächst sollte man wissen, warum die Formate von ICD und AHDI nicht zueinander kompatibel sind:

ATARI sieht im Root-Sektor (physikalischer Sektor 0) einer Festplatte nur Platz für vier Partitionsinformationseinträge vor. Da dies, je nach Festplattengröße, durchaus eine Einschränkung sein kann, entschloß sich ICD dazu, die Informationen für mehr als vier Partitionen in unbenutzten Feldern des Root-Sektors und, je nach Bedarf, auch in den folgenden Sektoren abzulegen. Dabei werden zunächst, wie auch bei AHDI, die Felder $1C6 bis $1F5 benutzt (Platz für vier Partionsinformationen). Danach, und das ist der Unterschied zu AHDI, werden die Felder $156 - $1B5 für acht weitere Partitionsinformationen benutzt. Sind mehr als zwölf Partitionen vorhanden, legt die ICD-Software einen XGM-Verweis auf den dem Root-Sektor folgenden Sektor an (als 12. Partition im Root-Sektor) und legt dort die Informationen über die restlichen Partitionen ab, nach dem gleichen Prinzip wie schon im Root-Sektor selbst. An dieser Stelle ein Hinweis: ICD hat das verwendete Root-Sektorformat nicht dokumentiert, die hier genannten Informationen basieren lediglich auf Analysen von ICD-Root-Sektoren Bei ATARI entschloß man sich bei Einführung von AHDI 3.0 jedoch dazu, alle weiteren Partitionen als verkettete Liste zu organisieren. Dabei werden sogenannte Hilfsrootsektoren benutzt (im Root-Sektor durch die Kennung XGM gekennzeichnet), die neben einer(!) weiteren Partionsinformation ihrerseits wieder einen Verweis auf einen Hilfsrootsektor enthalten können. Somit sind prinzipiell unendlich viele Partitionen möglich (auch wenn TOS bis heute maximal 14 Partionen erlaubt). Wer mit dem Format von AHDI-Root-Sektoren noch nicht ganz vertraut ist, sollte sich [1] zu Gemüte führen.

Das Umwandlungsprinzip

Daß diese beiden Formate ohne einschneidende Änderungen beim besten Willen nicht auf einen Nenner zu bringen sind, leuchtet leicht ein. Die im folgenden beschriebene „Umwandlungskur“ basiert darauf, daß man den ICD-Partitionen von 3 - n-1 am Ende einen Cluster (i.A. 2 Sektoren) „klaut“ und darin die Hilfs-Root-Sektoren installiert. Dadurch gehen zwar einige wenige Sektoren verloren, doch das dürfte zu verschmerzen sein (soviel sind es schließlich auch wieder nicht).

Als erstes sollte man sich von der Platte, die man umwandeln will, mit dem ICD-Utility CLEANUP eine Sicherheitskopie der Root- und Boot-Sektoren machen, damit man bei Bedarf wieder die Ausgangssituation herstellen kann. Auch ein Backup empfindlicher Daten ist sehr zu empfehlen, vor allem dann, wenn man die ganze Prozedur trotz vorangegangener Warnungen mit schweißnassen Händen durchführt.

Da wir den einzelnen Partitionen den letzten Cluster „klauen“ wollen, muß zunächst sichergestellt sein, daß dieser auch leer ist. Dazu dient Listing 1, daß als Kommandozeilen-Utility fungiert und bei allen als Parameter übergebenen Partionen (also z.B. E, F, usw.) überprüft, ob der letzte Datencluster benutzt ist. Sollte dies der Fall sein, ermittelt das Programm, zu welcher Datei der Cluster gehört und gibt dies aus. Diese Datei(en) sollte(n) dann auf Diskette kopiert und auf der/den Partition(en) gelöscht werden. (Es kann sich in ungünstigen Fällen natürlich auch um einen ganzen Ordner handeln (zu erkennen am vom Listing ausgegeben abschließenden Backslash), dann muß halt der komplette Ordner kopiert und anschließend entfernt werden.) Das Listing ist übrigens darauf angewiesen, daß die FAT in Ordnung ist, es handelt sich ja auch nicht um einen halben Diskdoktor. Im Zweifelsfall sollte vorher ein entsprechendes Programm (z.B. CLEANUP von ICD oder Diskus) zu Rate gezogen werden.

Danach sollte man das Programm HDPARTS von der ICD-Diskette aufrufen und sich die ausgegebenen Partionsdaten (Startsektoren und Größen) notieren oder die Ausgabe des Programms mit einer Shell auf den Drucker umlenken. .

Dies ist nötig, um später die von Listing 2 ausgegebenen Daten auf Korrektheit überprüfen zu können (wie bereits erwähnt, ist das Root-Sektorformat von ICD nicht dokumentiert).

Jetzt tritt Listing 2 in Aktion, das am besten auch von einer Shell aus gestartet wird. Es fragt zunächst nach der Gerätenummer der zu behandelnden Festplatte (0. .7: ACSI-Bus-Geräte 0 bis 7,8.. 15: SCSI-Bus-Geräte 0 bis 7, 16/17: IDE-Geräte 0/1, jeweils nur LUN 0). Danach liest es die Partitionsdaten aus dem Root-Sektor und gibt sie aus (sind weniger als fünf Partionen vorhanden oder wurde die Platte nicht mit ICD-Software partitioniert, wird das Programm mit einer entsprechenden Meldung abgebrochen). Diese Daten sollte man nun mit den von HDPARTS ermittelten vergleichen. Sind Unterschiede festzustellen, sollte das Programm bei einer der folgenden Abfragen verlassen werden, da ein Daten Verlust sonst nicht auszuschließen wäre.

Das Programm sorgt nun nach zwei Sicherheitsabfragen dafür, daß bei den Partitionen 3 - n-1 jeweils der letzte Daten-Cluster entfernt wird (dabei werden selbstverständlich auch der Boot-Sektor angepaßt und BGM-Partitionen richtig behandelt) und anschließend in die dadurch freigewordenen Sektoren die jeweiligen XGM-Hilfsrootsektoren geschrieben werden. Außerdem wird eine leere Bad-Sector-List erzeugt (sie ist bei SCSI-Platten unnötig). Erst zum Abschluß wird der neue Root-Sektor geschrieben, damit bei eventuellen vorher auftretenden Fehlern die alten Informationen beibehalten werden. Bei diesen Aktionen werden nur absolute Zugriffe auf die Festplatte vorgenommen, wozu die BIOS-Funktion Rwabs in ihrer erweiterten Form benutzt wird (hierzu muß ein AHDI-konformer Treiber aktiv sein (was bei Programmstart auch geprüft wird), da der ICD-Treiber diesen Aufruf zwar angeblich unterstützt, sich bei mir bei einigen Sektornummern jedoch mit Endlosfestplattenzugriff verabschiedete). Leider bringt die Benutzung von Rwabs den Nachteil, daß nur LUN 0 des Geräts angesprochen werden kann. Wer einen XHDI-kompatiblen Treiber besitzt und das Programm durchschaut hat, kann es sich ja mittels XHDI auch für die weiteren LUNs anpassen (der Aufwand dafür dürfte sich in Grenzen halten).

Ist der Vorgang abgeschlossen, sollte möglichst kein Zugriff auf die Festplatte mehr erfolgen. Man sollte von Diskette(!) ein Programm laden, das einen AHDI-kompatiblen Treiber (z.B. HINSTALL oder HARDBOOT) auf der Platte installiert (dieser Schritt entfällt selbstverständlich, wenn die behandelte Platte nicht das Bootgerät war). Jetzt mit AHDI-kompatiblem Treiber neubooten und sehen, ob alle Partitionen korrekt erkannt wurden (HDDRIVER von Uwe Seimet gibt schon während des Bootens aus, welche Partitionen er gefunden hat) und ob die Daten noch korrekt sind. Wenn dies nicht der Fall sein sollte, empfiehlt es sich, die vorher mit CLEANUP gesicherten ICD-Root- und Boot-Sektoren wieder aufzutragen und mit dem ICD-Treiber nachzuschauen, ob noch alle Daten vorhanden sind. Danach sollte man dann die oben beschriebene zweite Methode verwenden (komplettes Backup, Neupartitionieren und anschließendes Restore).

Hat alles geklappt, kann man sich an seiner nun AHDI-konformen Platte erfreuen, sollte aber nicht vergessen, daß man mit dieser Aktion trotzdem stark an der Platte herumgetrickst hat und die nächste große Datensicherung (die man eigentlich nicht häufig genug machen kann) dazu nutzen, die Festplatte „legal“ neu zu partitionieren (natürlich nicht mehr mit der ICD-Software). Dabei sollte man ruhig ein paar Partitionen weniger verwenden, da man bei zu vielen Partitionen schnell in Platznot geraten kann (meine eigene Erfahrung ...).

Thomas Binder

Literatur:

[1] Jankowski/Rabich/Reschke, ATARI Profibuch ST-STE-TT, SYBEX-Verlag GmbH, Düsseldorf, 1991 (besonders Teil I, Kapitel 1, Abschnitt 5: Treiber für Festplatten)

; Projektdatei zu CLCHECK.TTP 
; (c)1993 by MAXON-Computer 
; Autor: Thomas Binder 
CLCHECK.TTP 
.C []
.L []

=

PCSTART.O ; der Startupcode

CLCHECK.C ; der Quelltext

PCSTDLIB.LXB ; die Standardbibliothek
PCTOSLIB.LIB ; die TOS-Bibliothek

/**********************************************/
/* Modulname      : CLCHECK.C                 */
/* (c)1993 by MAXON-Computer                  */
/* Autor          : Thomas Binder             */
/**********************************************/

#include <stdio.h>
#include <tos.h>
#include <string.h>
#include <stdlib.h>
#include <portab.h>

/* Benötigte Strukturen */

typedef struct 
{
    char  dir_name[11];
    BYTE  dir_attr;
    BYTE  dir_dummy[10];
    DWORD dir_time;
    DWORD dir_date;
    DWORD dir_stcl;
    DLONG dir_flen;
} DIR; /* Format eines Directory-Eintrags */

/* Prototypen */

void err(void);
void correct,dir(DIR *dir, WORD entries); 
void work_dir(DIR *dir, WORD entries, char *path); 
void swap(UWORD *value); 
void lswap(ULONG *value);

/* Globale Variablen */

WORD  drive;     /* Momentanes Laufwerk */
UWORD *fat;      /* Die Fat von drive */
BPB   *bpb;      /* Der BPB von drive */
DIR   *rootdir;  /* Rootdirectory von drive */
char  file[129]; /* Pfad der gefundenen Datei */
char  found = 0; /* Flag, ob Datei gefunden */
char  quit = 0;  /* Abbruchflag */

int main(int argc, char **argv)
{
    WORD i, j,
         entries; 
    char path[257];

    if (argc == 1)
    {
        printf("Aufruf: clcheck <Laufwerk1> "
          "<Laufwerk2 > ...\n\n"); 
        return(0);
    }

    /* Alle übergebenen Laufwerke durchgehen */ 
    for (j = 1; j < argc; j++)
    {
        drive = (int)(*argv[j] & ~32) - 65;

        printf("Laufwerk %c... ", drive + 65);

        /* BIOS-Parameterblock ermitteln */ 
        if ((bpb = Getbpb(drive)) == 0L) 
            err();

        /* Test auf 16-Bit-Fat */ 
        if ((bpb->bflags & 1) != 1) 
            err();

        /* Platz für Wurzelverzeichnis anfordern */ 
        if ((rootdir = (DIR *)malloc((LONG)bpb->rdlen * (LONG)bpb->recsiz)) == 0L)
        {
            err();
        }

        /* Platz für FAT anfordern */ 
        if ((fat = (UWORD *)malloc((LONG)bpb->fsiz * (LONG)bpb->recsiz)) == 0L)
        {
            free((void *)rootdir); 
            err();
        }

        /* Wurzelverzeichnis einlesen */ 
        entries = (WORD)((LONG)bpb->rdlen * bpb->recsiz / 32);
        Rwabs(0, (void *)rootdir, bpb->rdlen, bpb->fatrec + bpb->fsiz, drive);

        /* FAT einlesen und nach Motorola wandeln */ 
        for (i = 0; i < bpb->numcl; fat[i++] = 0); 
        Rwabs(0, (void *)fat, bpb->fsiz, bpb->fatrec, drive);
        for (i =0; i < bpb->numcl; i++) 
            swap(&fat[i]);

        /* Ist letzter Datencluster unbenutzt? */ 
        if (fat[bpb->numcl - 1])
        {
            sprintf(path, "%c:\\", drive + 65);
            /* Nein, Datei suchen */ 
            work_dir(rootdir, entries, path);

            /* fehlerfrei? */ 
            if (!quit)
            {
                /* Datei gefunden? */ 
                if (found)
                {
                    printf("der letzte Datencluster gehört zu: %s\n", file);
                }
                else
                {
                    printf("der letzte Datencluster ist " 
                        "belegt/defekt, aber keiner Datei " 
                        "zugehörig!\n");
                }
            }
            else
            {
                printf("OK, letzter Datencluster "
                    "unbenutzt!\n", drive + 65);
            }

            free((void *)rootdir); 
            free((void *)fat);

            if (quit) 
                err();
    }
    return(0);
}

/* Das Verzeichnis dir mit entries Einträgen absuchen */
void work_dir(DIR *dir, WORD entries, char *path)
{
    WORD  i, j;
    UWORD cl;
    ULONG clusts;
    DIR   *subdir; 
    char  my_path[129];

    /* Intel-Einträge in Motorola-Format wandeln */ 
    correct_dir(dir, entries);

    /* Alle Einträge durchgehen */ 
    for (i = 0; i < entries; i++)
    {
        /* Eintrag nicht gelöscht und nicht Laufwerksname? */ 
        if ((dir(i].dir_name[0] != (char)0xe5) &&
           ((dir[i].dir_attr & 8) == 0))
        {
            cl = dir[i].dir_stcl;
            /* Ist Eintrag ein Ordner? */ 
            if (dir[i].dir_attr & 16)
            {
                if (dir[i].dir_name[0] != '.')
                {
                    strcpy(my_path, path); 
                    for (j = 0; j < 8; j++)
                    {
                        if (dir[i].dir_name[j] != 32)
                        {
                            my_path[strlen(my_path) + 1] = 0; 
                            my_path[strlen(my_path)] = dir[i].dir_name[j];
                        }
                    }
                    if (dir[i].dir_name[8] != 32)
                    {
                        strcat (my_path, "."); 
                        for (j = 8; j < 11; j++)
                        {
                            if (dir[i].dir_name[j] != 32)
                            {
                                my_path[strlen(my_path) + 1] = 0; 
                                my_path[strlen(my_path)] = dir[i].dir_name[j];
                            }
                        }
                    }
                    strcat(my_path, "\\");

                    if ((subdir = (DIR *)malloc((LONG) bpb->clsizb)) != 0L)
                    {
                        clusts = 0;
                        while (cl < 0x8000U && !guit && !found)
                        {
                            /* Ist Ordnercluster = letzter Cluster? */ 
                            if (cl == bpb->numcl - 1)
                            {
                                strcpy (file, my_path); 
                                found = 1;
                            }

                            Rwabs(0, (void *)((LONG)subdir + 
                                clusts * (LONG)bpb->clsizb), 
                                bpb->clsiz, bpb->datrec +  
                                (cl - 2) * bpb->clsiz, drive);

                            cl = fat[cl]; 
                            clusts++;

                            if (cl < 0x8000U)
                            {
                                subdir = (DIR *)realloc((void *) 
                                    subdir, (clusts + 1L) * 
                                    (LONG)bpb->clsizb); 
                                if (subdir == 0L) 
                                    quit = 1;
                            }
                        }
                        if (!quit && !found)
                        {
                            work_dir(subdir, (WORD)(clusts * 
                                (LONG)bpb->clsizb / 32L), 
                                my_path); 
                            free(subdir);
                        }
                    }
                    else
                        quit = 1;
                }
            }
            else /* Eintrag ist also normale Datei */
            {
                if (dir[i].dir_name[0])
                {
                    strcpy(my_path, path); 
                    for (j = 0; j < 8; j++)
                    {
                        if (dir[i].dir_name[j] != 32)
                        {
                            my_path[strlen(my_path) +1] = 0; 
                            my_path[strlen(my_path)] = dir[i].dir_name[j];
                        }
                    }
                    if (dir[i].dir_name[8] != 32)
                    {
                        strcat(my_path, "."); 
                        for (j = 8; j < 11; j ++ )
                        {
                            if (dir[i].dir_name[j] != 32)
                            {
                                my_path[strlen(my_path) +1] =0; 
                                my_path[strlen(my_path)] = dir[i].dir_name[j];
                            }
                        }
                    }

                    while (cl < 0x8000U && !found)
                    {
                        /* Ist Dateicluster = letzter Datencluster? */ 
                        if (cl == bpb->numcl - 1)
                        {
                            strcpy(file, my_path); 
                            found = 1;
                        }

                        cl = fat[cl];

                    }
                }
            }
            if (quit || found) 
                return;
        }
    }
}

/* Directory-Eintrage nach Motorola wandeln */ 
void correct_dir(DIR *dir, WORD entries)
{
    WORD i;

    for (i = 0; i < entries; i++)
    {
        swap(&dir[i].dir_time); 
        swap(&dir[i].dir_date); 
        swap(&dir[i].dir_stcl); 
        lswap(&dir[i].dir_flen) ;
    }
}

/* 16-bit-Integer swappen (Intel -> Motorola) */ 
void swap(UWORD *value)
{
    *value = ((*value & 255) << 8) + (*value >> 8);
}

/* 32-bit-Integer swappen (Intel -> Motorola) */ 
void lswap(ULONG *value)
{
    UWORD high, low;

    low  = (UWORD)(*value & 65535L); 
    high = (UWORD)(*value >> 16);

    swap(&low); 
    swap(ahigh);

    *value = ((LONG)low << 16) + (LONG)high;
}

/* Fehlermeldung */ 
void err(void)
{
    printf("Fehler aufgetreten!\n"); 
    exit(1);
}
; Projektdatei zu ICD2AHDI.TOS 
; Autor: Thomas Binder 
; (c)1993 by MAXON-Computer 
ICD2AHDI.TOS 
.C []
.L []
=

PCSTART.O       ; der Startupcode

ICD2AHDI.C      ; der Quelltext

PCSTDLIB.LIB    ; die Standardbibliothek
PCTOSLIB.LIB    ; die TOS-Bibliothek
/***********************************************/ 
/* Modulname      : ICD2AHDI.C                 */
/* (c)1993 by MAXON-Computer                   */
/* Autor          : Thomas Binder              */
/***********************************************/ 

#include <stdio.h>
#include <tos.h>
#include <stdlib.h>
#include <string.h>
#include <portab.h>

/* Benötigte Strukturen */

typedef struct
{
    UBYTE p_flag;   /* Flag, Bit 0: aktiv ja/nein, 
                      Bit 8: bootbar ja/nein */ 
    char  p_id[3];  /* Kennung, "GEM", "BGM" oder "XGM" */
    LONG  p_st,     /* Start der Partition relativ zum Rootsektor */ 
          p_siz;    /* Große der Partition in 512-Byte-Sektoren */
} PINFO; /* Partitionseintrag im Rootsektor */

typedef struct
{
    WORD  puns;
    BYTE  pun[16];
    LONG  part_start[16];
    LONG  P_cookie;
    LONG  *P_cookptr;
    UWORD P_version;
    UWORD P_max_sector;
    LONG  reserved[16];
} PUN_INFO; /* AHDI-Informationsstruktur, siehe auch Profibuch */

/* Erweitertes Rwabs für 32-bit-Sektornummer */ 
#define R_wabs(a,b,c,d,e,f) bios(4,a,b,c,d,e,f)

/* Fehlertypen */
#define OK 0 
#define RERR 1 
#define WERR 2 
#define MEMORY 3
#define NOICD 4 
#define NOTMTFOUR 5 
#define ILLEGAL 6 
#define NOBOOT 7 
#define NOBSL 8 
#define NOAHDI 9

/* Prototypen */

void get_parts(LONG Sektor); 
void err(WORD error); 
void remove_cluster(WORD part); 
void create_xgm(WORD part); 
void create_new_root(void);
WORD check_ahdi(void);

/* Globale Variablen */

WORD  target;
UBYTE *root;
PINFO *part_table = 0L;
WORD  parts;
LONG  first_xgm = -1L;

void main(void)
{
    WORD    i; 
    char    type[3];
            answer[3];

    printf("ICD-nach-AHDI-Rootsektor-Wandler\n"); 
    printf("von Thomas Binder\n\n");

    /* Speicher für 512-Byte-Sektor reservieren */ 
    if ((root = (UBYTE *)malloc(512L)) == 0L) 
        err(MEMORY);

    /* Speicher für 1 Partionsinfo reservieren */ 
    if ((part_table = (PINFO *)malloc( sizeof(PINFO))) == 0L)
    {
        err(MEMORY);
    }

    /* Auf AHDI 3.0-kompatiblen Treiber testen */ 
    if (check_ahdi()) 
        err(NOAHDI);

    /* Gerätenummer erfragen */ 
    do 
    {
        printf("Bitte Festplatte wählen! (0..7: " 
            "ACSI-Geräte 0-7, 8..15: SCSI-Geräte "
            "0-7,\n"); 
        printf(" 16/17: IDE-Geräte 0/1)\n");
        printf("Gerätenummer? "); 
        scanf("%d", &target); 
        printf("\n");

        if ((target < 0) || (target > 17))
            printf("Illegale Gerätenummer!\n\n");
    } while ((target < 0) || (target > 17));

    /* Rootsektor lesen und auf ICD checken */ 
    if (Rwabs(8, (void *)root, 1, 0, 2 + target)) 
        err(RERR);

    strncpy(type, ((PINFO *)&root[0x156])->p_id,3); 
    if ((strncmp(type, "GEM", 3)) && (strncmp(type, "BGM", 3)) && (strncmp(type, "XGM", 3)))
    {
        err(NOICD);
    }

    parts = 0; 
    get_parts(0);

    if (parts < 5) 
        err(NOTMTFOUR);

    printf("Folgende Partitionen auf Gerät %d " 
        "gefunden:\n\n", target); 
    printf(" #   Start   Größe  Typ  Aktiv\n");
    printf("------------------------------\n");
    for (i = 0; i < parts; i++)
    {
        printf("%2d%08ld%8ld  %c%c%c  %s\n", i + 1, 
            part_table[i].p_st, part_table[i].p_siz, 
            part_table[i].p_id[0], 
            part_table[i].p_id[1], 
            part_table[i].p_id[2],
            (part_table[i].p_flag & 1) ? " Ja" : "Nein");
    }

    do
    {
        printf("\nWurde für die Partitionen #3 bis " 
            "#%d CLCHECK befragt (j/n)? ", parts - 1);

        fgets(answer, 1, stdin); 
        answer[0] &= ~32;

    } while ((answer[0] != 'J') && (answer[0] != 'N'));

    if (answer[0] == 'N') 
        err(OK);

    do
    {
        printf("\nGerät %d in AHDI-Format wandeln " 
            "(j/n)? ", target);

        fgets(answer, 1, stdin); 
        answer[0] &= ~32;

    } while ((answer[0] != 'J') && (answer(0] != 'N')); 
    printf("\n");

    if (answer[0] == 'N') 
        err(OK);

    for (i = 2; i < (parts - 1); i++)
    {
        printf("Entferne letzten Datencluster von " 
            "Partition #%d... ", i + 1); 
        remove_cluster(i); 
        printf("\n");
    }
    printf("\n");

    for (i = 2; i < (parts - 1); i++)
    {
        printf("Erzeuge XGM-Sektor am Ende von " 
            "Partition #%d... ", i + 1); 
        create_xgm(i); 
        printf("\n");
    }

    printf("\nErzeuge leere Bad-Sector-List... "); 
    memset((void *)root, 0, 512L); 
    root[3] = 0xa5;
    if (R_wabs(9, (void *)root, 1, -1, 2 + target, first_xgm - 1))
    {
        err(WERR);
    }

    printf("\n\nErzeuge neuen Rootsektor... "); 
    create_new_root(); 
    err(OK);
}

/* Neuen 1. Rootsektor mit XGM-Eintrag und BSL-Zeiger erzeugen */ 
void create_new_root(void)
{
    WORD i;
    LONG size;

    /* Alten Rootsektor lesen */
    if (Rwabs(8, (void *)root, 1, 0, 2 + target)) 
        err(RERR);

    /* Größe aller mit XGM verketteten Partionen zusammenrechnen */ 
    size = 0;
    for (i = 3; i < parts; i++)
        size += part_table[i].p_siz + 1;

    /* XGM-Eintrag im Rootsektor erzeugen */ 
    ((PINFO *)&root[0x1EA])->p_flag = 1; 
    strncpy(((PINFO *)&root[0x1EA])->p_id,"XGM", 3);
    ((PINFO *)&root[0x1EA])->p_st  = first_xgm; 
    ((PINFO *)&root[0x1EA])->p_siz = size;

    /* Bad-Sector-List-Zeiger eintragen */
    *(LONG *)&root[0x1F6] = first_xgm - 1;
    *(LONG *)&root[0x1FA] = 1;

    /* Alte ICD-Partition #5 im Rootsektor loschen */
    *(LONG *)&root[0x156] = 0L;

    /* Neuen Rootsektor schreiben */ 
    if (Rwabs(9, (void *)root, 1, 0, 2 + target)) 
        err(WERR);
}

/* Am Ende von Partion Nummer part XGM-Rootsektor für part + 1 erzeugen */ 
void create_xgm(WORD part)
{
    LONG    sektor;

    /* Sektor für XGM-Rootsektor errechnen (eins vor part +1) */
    sektor = part_table[part + l].p_st - 1;

    /* Ersten XGM-Rootsektor merken wegen AHDI-Patent... */ 
    if (part == 2)
        first_xgm = sektor;

    /* Leeren Sektor erzeugen */ 
    memset((void *)root, 0, 512L);

    /* Zeiger auf part + 1 einrichten */
    ((PINFO *)&root[0x1C6])->p_flag = part_table[part + 1].p_flag; 
    strncpy)((PINFO *)&root[0x1C6])->p_id, part_table[part + 1].p_id, 3);
    ((PINFO *)&root[0x1C6])->p_st = 1;
    ((PINFO *)&root[0x1C6])->p_siz = part_table[part + 1].p_siz;

    /* Wenn noch nötig, nächste XGM-Verkettung einrichten */ 
    if (part != parts - 2)
    {
        ((PINFO *)&root[0x1D2])->p_flag = 1; 
        strncpy(((PINFO *)&root[0x1D2])->p_id,"XGM", 3);
        ((PINFO *)&root[0x1D2])->p_st = part_table[part + 2].p_st - 1 - first_xgm; 
        ((PINFO *)&root[0x1D2])->p_siz = part_table[part + 2].p_siz + 1;
    }

    /* XGM-Rootsektor schreiben */
    if (R_wabs(9, (void *)root, 1, -1, 2 + target, sektor))
    {
        err(WERR);
    }
}

/* Bei Partition Nummer part letzten Datencluster entfernen */ 
void remove_cluster(WORD part)
{
    UWORD bps, 
          spc, 
          nsects;

    /* Bootsektor von Partition part einlesen */ 
    if (R_wabs(8, (void *)root, 1, -1, 2 + target, part_table[part].p_st))
    {
        err(RERR);
    }

    /* bps-, spc- und nsects-Einträge des Bootsektors ermitteln */ 
    bps = (*((UBYTE *)&root[0x0c]) << 8) + *((UBYTE *)&root[0x0b]); 
    spc = (UWORD)*((UBYTE *)&root[0x0d]); 
    nsects = (*((UBYTE *)&root[0x14]) << 8) + *((UBYTE *)&root[0x13]);

    /* Abbruch, wenn unmögliche Werte */ 
    if ((bps % 512) || (bps < 512) || (spc == 0)) 
        err(NOBOOT);

    /* Abbruch, wenn kein Platz für Bad-Sector-List am Ende von Partition 3 */ 
    if ((part == 2) && ((bps * spc) < 1024)) 
        err(NOBSL);

    /* Letzten Datencluster "wegzaubern" */
    nsects -= spc;
    *((UBYTE *)&root[0x14]) = nsects >> 8;
    *((UBYTE *)&root[0x13]) = nsects & 255; 
    part_table[part].p_siz -= spc * (bps / 512);

    /* Manipulierten Bootsektor zurückschreiben */ 
    if (R_wabs(9, (void *)root, 1, -1, 2 + target, part_table[part].p_st))
    {
        err(WERR);
    }
}

/* Übergebenen Sektor lesen und Partitionsinfo auswerten */ 
void get_parts(LONG sektor)
{
    WORD pos,
         i, j;
    char type[3];

    /* Sektor einlesen */
    if (R_wabs(8, (void *)root, 1, -1, 2 + target, sektor))
    {
        err(RERR);
    9

    /* Erst Einträge 1 - 4 (j = 0), dann 5-12 (j = 1) auswerten (ICD-Rootsektorformat) */ 
    for (j = 0; j < 2; j++)
    {
        pos = (j == 0) ? 0x1C6 : 0x156;

        for (i = 0; i < ((j == 0) ? 4 : 8); i++)
        {
            /* Aufhören, wenn 1. Langwort des Eintrags = 0 */
            if (*(LONG *)&root[pos] == 0L) 
                return;

            /* Auf erlaubten Partitionstyp testen */ 
            strncpy(type, ((PINFO *)&root[pos])->p_id, 3);
            if ((strncmp(type, "GEM", 3)) &&
                (strncmp(type, "BGM", 3)) &&
                (strncmp(type, "XGM", 3)))
            {
                err(ILLEGAL);
            }

            if (strncmp(type, "XGM", 3))
            {
                /* Auswerten, wenn kein XGM-Verweis */ 
                strncpy(part_table[parts].p_id, type, 3); 
                part_table[parts].p_flag = ((PINFO *)&root[pos])->p_flag; 
                part_table[parts].p_st   = ((PINFO *)&root[pos])->p_st + sektor; 
                part_table[parts].p_siz  = ((PINFO *)&root[pos])->p_siz;

                if ((part_table = realloc((void *) 
                  part_table, (++parts + 1) * 
                  sizeof(PINFO))) == 0L)
                {
                    err(MEMORY);
                }
            }
            else
            {
                /* XGM-Verkettung folgen */ 
                if (first_xgm == -1L)
                {
                    first_xgm = ((PINFO *)&root[pos])->p_st; 
                    get_parts(((PINFO *)&root[pos])->p_st + sektor);
                }
                else
                {
                    get_parts(((PINFO *)&root[pos])->p_st + first_xgm);
                }
            }

            pos += 12;
        }
    }
}

/* Testen, ob AHDI 3.0-kompatibler Treiber aktiv ist */

WORD check_ahdi(void)
{
    PUN_INFO  *p;
    LONG      osp;

    /* PUN_INFO-Pointer auslesen */ 
    if ((osp = Super((void *)1L)) == 0L) 
        osp = Super(0L); 
    p = *((PUN_INFO **)(0x516L)); 
    if (osp != -1L)
        Super((void *)osp);

    /* Checken, ob AHDI-Treiber >=3.0 aktiv ist */ 
    if ((p) && (p->P_cookie == 'AHDI') && 
      (p->P_cookptr == &(p->P_cookie)) && 
      (p->P_version >= 0x300))
    {
        return(0);
    }
    return(1);
}

/* Fehlermeldung ausgeben und Speicher freigeben */ 
void err(WORD error)
{
    switch (error)
    {
        case RERR:
            printf("Lesefehler bei Gerät %d!\n", target); 
            break; 
        case WERR:
            printf("Schreibfehler bei Gerät %d!\n", target);
            break; 
        case MEMORY:
            printf("Kein Speicher mehr frei!\n"); 
            break; 
        case NOICD:
            printf("Festplatte nicht mit ICD " 
              "formatiert oder hat weniger als 5 " 
              "Partitionen!\n"); 
            break; 
        case NOTMTFOUR:
            printf("Festplatte hat weniger als 5 " 
              "Partitionen!\n"); 
            break; 
        case ILLEGAL:
            printf("Illegale Partitionskennung!\n"); 
            break; 
        case NOBOOT:
            printf("Bootsektor enthält unsinnige " 
              "Werte!\n"); 
            break; 
        case NOBSL:
            printf("Partition #4 muß mindestens 1024 " 
              "Bytes pro Cluster haben!\n"); 
            break; 
        case NOAHDI:
            printf("Es muß ein AHDI 3.0-kompatibler " 
              "Treiber aktiv sein!\n");
    }

    /* Speicher freigeben, falls belegt */ 
    if (root != 0L)
        free((void *)root); 
    if (part_table != 0L)
        free((void *)part_table);

    exit(error);
}


Aus: ST-Computer 12 / 1993, Seite 106

Links

Copyright-Bestimmungen: siehe Über diese Seite