Beiträge anzeigen

Diese Sektion erlaubt es dir alle Beiträge dieses Mitglieds zu sehen. Beachte, dass du nur solche Beiträge sehen kannst, zu denen du auch Zugriffsrechte hast.


Nachrichten - FreakyPenguin

Seiten: 1 ... 5 6 [7] 8 9 ... 15
121
OS-Design / Re: TeeJay's FAT12 Bootlader
« am: 12. February 2008, 21:03 »
Nein, das Tutorial ist dafür garnicht gut geeignet. ;-)

Ich und auch andere hier im Forum sind der Meinung, dass der Einstieg mit Hilfe von Grub wesentlich leichter geht. Wenn du unter Linux arbeitest gibts hier.

Ich habe vor einiger Zeit mal einen gut Dokumentierten Beispielkernel dafür geschrieben: Hier

Edit:
Jo der Bootloader kommt mit FAT12 zurecht. Da musst du einfach die Diskette Formatieren und danach den Bootloader in den ersten Sektor schreiben. Der ist so formatiert, dass dem Dateisystem nichts passiert.
122
tyndur / Re: Direkter Zugriff auf Laufwerke?
« am: 12. February 2008, 20:18 »
Freut uns das zu hören  :-D

Wo hast du denn das Programm so getestet? In qemu tut es garantiert. Und ich habe bei mir auch schon auf mehreren Rechnern getestet, ata funktioniert dort eigentlich auch.

Dein Ansatz ist schon der richtige. :)

Naja die stdlibc wird halt einfach vervollständigt, sobald man was braucht. Die wichtigsten Funktionen sind mittlerweile schon implementiert. Zumindest gcc und binutils lassen sich schon mal mit unserer Libc kompilieren und linken.
123
tyndur / Re: cdi.fs
« am: 12. February 2008, 18:10 »
Hier die überarbeitete Version. Die besprochenen Änderungen sind gemacht und ein paar kleine Dinge die mir noch so eingefallen sind, habe ich auch noch ergänzt:

#ifndef _CDI_FS_H_
#define _CDI_FS_H_

#include "types.h"
#include "cdi.h"
#include "cdi/lists.h"


struct cdi_fs_driver {
    struct cdi_driver drv;
}

struct cdi_fs_object;
/**
 * Diese Struktur wird fuer jedes eingebundene Dateisystem einmal erstellt.
 */
struct cdi_fs_filesystem {
    // Wurzelobjekt fuer die Baumstruktur
    struct cdi_fs_object*   root_object;
   
    // Falls ein gravierender Fehler auftritt, wird diese Fehlernummer gesetzt.
    // Wenn sie != 0 ist wird das Dateisystem fuer Schreibzugriffe gesperrt.
    int                     error;

    // Das Dateisystem darf nicht geschrieben werden. Damit schlaegt unter
    // anderem cdi_fs_write_data fehl.
    int                     read_only;

    // Hier sollte man wohl noch ein paar allgemeine Mount-Optionen oder
    // sonstige Flags die das ganze Dateisystem betreffen.
};


// XXX Bei den Fehlernummern weiss ich noch nicht wirklich, was da notwendig
// ist, deshalb lasse ich das mal so stehen.
typedef enum {
    CDI_FS_ERROR_NONE = 0,
    // Operation nicht unterstuetzt
    CDI_FS_ERROR_ONS,
    // Ressource nicht gefunden
    CDI_FS_ERROR_RNF,
    // Beim lesen einer Datei wurde das Ende erreicht
    CDI_FS_ERROR_EOF,
    // Interner Fehler
    CDI_FS_ERROR_INTERNAL,
    // Unbekannter Fehler
    CDI_FS_ERROR_UNKNOWN
} cdi_fs_error_t;

/**
 * Der Stream stellt die Verbindung zwischen Aufrufer und Ressource dar.
 */
struct cdi_fs_stream {
    // Betroffene Ressource
    struct cdi_fs_resource* res;

    // Fehlernummer
    cdi_fs_error_t          error;
};


/**
 * Metaeigenschaften, die Ressourcen haben koennen
 */
typedef enum {
    // R  Groesse der Datei auslesen
    CDI_FS_META_SIZE,
    // R  Anzahl der Benutzten Dateisystemblocks (Irgendwo muesste man dann
    //    auch auf diese Blockgroesse zurgreiffen koennen)
    CDI_FS_META_USEDBLOCKS,
    // R  Optimale Blockgroesse mit der man auf die Datei zugreiffen sollte
    CDI_FS_META_BESTBLOCKSZ,
    // R  Zeitpunkt an dem die Ressource erstellt wurde
    CDI_FS_META_CREATETIME,
    // RW Letzter Zugriff auf die Ressource, auch lesend
    CDI_FS_META_ACCESSTIME,
    // RW Letzte Veraenderung der Ressource
    CDI_FS_META_CHANGETIME
} cdi_fs_meta_t;


/**
 * Siese Struktur stellt die Moeglichkeiten, die an einer Ressource zur
 * Verfuegung stehen, dar.
 */
struct cdi_fs_res_flags {
    // Ressource loeschen
    int                 remove;
    // Ressource umbenennen
    int                 rename;
    // Ressource verschieben
    int                 move;
    // Lesender Zugriff gestattet
    int                 read;
    // Schreibender Zugriff gestattet
    int                 write;
    // Ausfuehren gestattet
    int                 execute;
    // Auflisten der Verzeichniseintraege gestattet
    int                 browse;
    // Aufloesen des Links
    int                 read_link;
    // Aendern des Links
    int                 write_link;
    // Anlegen eines Untereintrags
    int                 create_child;
};


struct cdi_fs_res_file;
struct cdi_fs_res_dir;
struct cdi_fs_res_link;
struct cdi_fs_res_special;

/**
 * Typ der eine Ressource, die zu der Klasse der Spezialdateien gehoert noch
 * genauer beschreibt
 */
typedef enum {
    CDI_FS_BLOCK,
    CDI_FS_CHAR,
    CDI_FS_FIFO,
    CDI_FS_SOCKET
} cdi_fs_res_type_t;

/**
 * Konstanten fuer die einzelnen Klassen, um sie beim Funktionsaufruf zum
 * zuweisen einer Klasse, identifizieren zu koennen.
 */
typedef enum {
    CDI_FS_CLASS_FILE,
    CDI_FS_CLASS_DIR,
    CDI_FS_CLASS_LINK,
    CDI_FS_CLASS_SPECIAL
} cdi_fs_res_class_t;

/**
 * Dieser Typ dient dazu Ressourcen ganz oder teilweise zu sperren
 */
typedef enum {
    CDI_FS_LOCK_NONE,
    CDI_FS_LOCK_WRITE,
    CDI_FS_LOCK_ALL
} cdi_fs_lock_t;

/**
 * Das Dateisystem wird hier nur mit abstrakten Strukturen vom Typ
 * cdi_fs_resource dargestellt. Diese können beispielsweise sowohl regulaere
 * Datei als auch Verzeichnis gleichzeitig darstellen.
 *
 * Weiter gilt, dass Ressourcen, die zu keiner Klasse gehoeren, nicht
 * persistent sind.
 */
struct cdi_fs_resource {
    // Name der Ressource
    char*                   name;
   
    // Lock fuer diese Ressource
    cdi_fs_lock_t           lock;

    // Verweis auf das Elternobjekt
    struct cdi_fs_object*   parent;
    // Liste mit allfaelligen Kindobjekten
    cdi_list_t              children;

    // Link-Pfad
    char*                   link_path;

    // ACL; siehe Unten
    cdi_list_t              acl;
    // Flags
    struct cdi_fs_obj_flags flags;

    // Einzelne Klassen, zu denen die Ressourcen gehoeren kann, oder Null falls
    // es zu einer Bestimmten Klasse nicht gehoert.
    struct cdi_fs_res_file* file;
    struct cdi_fs_res_dir*  dir;
    struct cdi_fs_res_link* link;
    struct cdi_fs_res_special special;

    // Falls die Ressource zu einer Spezialklasse gehoert, wird hier angegeben,
    // um welchen Typ von Spezialressource sie gehoert.
    cdi_fs_res_type_t       type;
};



/**
 * Diese Dateisystemobjekte werden in Klassen eingeteilt, die das eigentliche
 * "Verhalten" der Ressourcen steuern. Diese Klassen beinhalten die moeglichen
 * Operationen und auch die Eigenschaften, die fuer die Ressourcen gelten,
 * denen diese Klassen zugeordnet sind.
 * Das Definieren der einzelnen Klassen uebernehmen dann die einzelnen Treiber.
 *
 * Die Flags koennen von der Ressource ueberschrieben werden. Es koennen
 * allerdings nur Flags deaktiviert werden, die in der Klasse gesetzt sind un
 * nicht umgekehrt.
 * Das Selbe gilt auch fuer Klassen bei denen NULL-Pointer fuer Operationen
 * eingetragen sind. Wenn zum Beispiel fuer write NULL eingetragen wird, dann
 * bringt ein gesetztes write-Flag nichts.
 */

/**
 * Diese Klasse gilt unabhaengig von den andern, also egal welche anderen
 * Klassen angegeben sind, diese muss angegeben werden.
 */
struct cdi_fs_obj_obj {
    /**
     * Ressource entfernen
     *
     * @param stream Stream
     *
     * @return Falls die Ressource erfolgreich geloescht wurde 1, sonst 0
     */
    int (*remove)(struct cdi_fs_stream* stream);

    /**
     * Namen einer Ressource aendern. Der Parameter name ist nur der
     * Resourcennamen ohne Pfad. Zum verschieben wird move() benutzt.
     *
     * @param stream Stream
     * @param name Neuer Name
     *
     * @return Falls die Ressource erfolgreich umbenennt wurde 1, sonst 0
     */
    int (*rename)(struct cdi_fs_stream* stream, const char* name);

    /**
     * Ressource innerhalb des Dateisystems verschieben. Das Verschieben ueber
     * Dateisystemgrenzen hinweg wird per kopieren und loeschen durchgefuehrt.
     *
     * @param stream Stream
     * @param dest Pointer auf die Ressource, in die die Ressource verschoben
     *             werden soll
     *
     * @return Falls die Ressource erfolgreich verschoben wurde 1, sonst 0
     */
    int (*move)(struct cdi_fs_stream* stream, struct cdi_fs_resource* dest);

    /**
     * Neue Ressource in der Aktuellen erstellen. Diese wird erstmal noch
     * keiner Klasse zugewiesen. Diese Funktion wird mit einem NULL-Pointer als
     * Ressource im Stream aufgerufen. Dieser NULL-Pointer muss bei
     * Erfolgreichem Beenden durch einen Pointer auf die neue Ressource ersetzt
     * worden sein.
     *
     * @param stream Mit NULL-Pointer als Ressource
     * @param parent Ressource, der die neue Ressource als Kindressource
     *               zugeordnet werden soll.
     *
     * @return Falls die Ressource erfolgreich erstellt wurde 1, sonst 0
     */
    int (*create_child)(struct cdi_fs_stream* stream,
        struct cdi_fs_resource* parent);

    /**
     * Diese Ressource einer neuen Klasse zuweisen. Dieser Aufruf schlaegt
     * fehl, wenn die Ressource dieser Klasse schon zugewiesen ist.
     *
     * @param stream Stream
     * @param class Konstante fuer den Typ der klasse, der die Ressource
     *              zugewiesen werden soll.
     *
     * @return 1 falls die Ressource erfolgreich der Klasse zugewiesen wurde, 0
     *         sonst
     */
     int (*assign_class)(struct cdi_fs_stream* stream,
        cdi_fs_res_class_t class);

    /**
     * Metaeigenschaft lesen
     *
     * @param stream Stream
     * @param meta Konstante fuer die gewuenschte Metaeigenschaft
     *
     * @return Wert der Metaeigenschaft
     */
    int64_t (*meta_read)(struct cdi_fs_stream* stream, cdi_fs_meta_t meta);

    /**
     * Metaeigenschaft schreiben
     *
     * @param stream Stream
     * @param meta Konstante fuer die gewuenschte Metaeigenschaft
     * @param value Neuen Wert fuer die Metaeigenschaft
     *
     * @return Falls die Metaeigenschaft erfolgreich geaendert wurde 1, sonst 0
     */
    int64_t (*meta_write)(struct cdi_fs_stream* stream, cdi_fs_meta_t meta,
        int64_t value);

}

struct cdi_fs_obj_file {
    // XXX (Aber wie geht das, wenn eine Datei nicht lesbar, aber ausfuehrbar
    // sein soll?)
    int                     executable;
   
    /**
     * Daten aus dieser Datei lesen. Wird nur aufgerufen, wenn es durch die
     * Flags oder Berechtigungen nicht verhindert wird.
     *
     * Im Fehlerfall wird je nach Fehler die Fehlernummer im Handle und die im
     * Device gesetzt.
     *
     * @param stream Stream
     * @param start Position von der an gelesen werden soll
     * @param size Groesse der zu lesenden Daten
     * @param buffer Puffer in den die Daten gelsen werden sollen
     *
     * @return Gelesene Bytes, oder 0 im Fehlerfall
     */
    size_t (*read)(struct cdi_fs_stream* stream, uint64_t start, size_t size,
        void* buffer);

    /**
     * Daten in diese Datei schreiben. Wird nur aufgerufen, wenn es durch die
     * Flags oder Berechtigungen nicht verhindert wird.
     *
     * Im Fehlerfall wird je nach Fehler die Fehlernummer im Handle und die im
     * Device gesetzt.
     *
     * @param stream Stream
     * @param start Position an die geschrieben werden soll
     * @param size Groesse der zu schreibenden Daten
     * @param buffer Puffer aus dem die Daten gelesen werden sollen
     *
     * @return Geschriebene Bytes oder 0 im Fehlerfall
     */
    size_t (*write)(struct cdi_fs_stream* stream, uint64_t start, size_t size,
        void* buffer);
};

struct cdi_fs_obj_dir {
    // XXX Ein browsable Flag koennen wir vermutlich hier eindeutig welgassen,
    // waere ja irgenwie witzlos. ;-)
   
    /**
     * Diese Funktion gibt einen Pointer auf die Liste mit den Eintraegen
     * zurueck. Hier wird nicht einfach fix der Pointer in fs_obj genommen,
     * damit dort auch "versteckte" Eintraege vorhanden sein koennten. (Ich
     * meine hier nicht irgend ein versteckt-Flag dass die Dateien vor dem
     * Benutzer Verstecken soll, sondern eher von fuer den Internen gebrauch
     * angelegten Eintraegen.
     *
     * @param stream Stream
     *
     * @return Pointer auf eine Liste mit den Untereintraegen.
     */
     cdi_list_t* (*list)(struct cdi_fs_stream* stream);
};

struct cdi_fs_obj_link {
    // XXX Ich glaube auch hier brauchen wir kein resolvable-Flag

    /**
     * Diese Funktion liest den Pfad aus, auf den der Link zeigt
     *
     * @param stream Stream
     *
     * @return Pointer auf einen Ouffer der den Pfad beinhaltet. Dieses Puffer
     *         darf vom Aufrufer nicht veraendert werden.
     */
    const char* (*read_link)(struct cdi_fs_stream* stream);

    /**
     * Aendert den Pfad auf den der Link zeigt
     *
     * @param stream Stream
     * @param path Neuer Pfad
     *
     * XXX Hm ist diese Funktion so vielleicht noch ein bisschen zu
     * optimistisch?
     */
    void (*write_link)(struct cdi_fs_stream* stream);
};

struct cdi_fs_res_special {
    // XXX Reicht das so?

    /**
     * Geraeteadresse der Spezialdatei Lesen
     *
     * @param stream Stream
     * @param dev Pointer auf die Variable in der die Geraeteadresse
     *            gespeichert werden soll.
     *
     * @return Falls die Geraeteadresse erfolgreich gelesen wurde 1, sonst 0
     */
    int (*dev_read)(struct cdi_fs_stream* stream, dev_t* dev);

    /**
     * Geraeteadresse der Spezialdatei Aendern
     *
     * @param stream Stream
     * @param dev Die neue Geraeteadresse
     *
     * @return Falls die Geraeteadresse erfolgreich geaendert wurde 1, sonst 0
     */
    int (*dev_write)(struct cdi_fs_stream* stream, dev_t dev);
}



/**
 * Die Berechtigunen werden mit Access controll lists, kurz ACLs verwaltet.
 * Diese werden in Form von Listen gespeichert. Diese Listen enthalten
 * eintraege von verschiedenen Typen.
 */
/// Eine UID
#define  CDI_FS_ACL_USER_NUMERIC,
/// Ein Benutzername als String
#define CDI_FS_ACL_USER_STRING,
/// Eine GID
#define CDI_FS_ACL_GROUP_NUMERIC,
/// Ein Gruppenname als String
#define CDI_FS_ACL_GROUP_STRING

/**
 * Der Basiseintrag in einer ACL, von dem die anderen Typen der Eintraege
 * abgeleitet sind.
 */
struct cdi_fs_acl_entry {
    // Typ des Eintrages, eine der obigen Konstanten
    int                     type;
   
    // Flags
    struct cdi_fs_obj_flags flags;
};

/**
 * Eintraege fuer die einzelnen Typen
 */
struct cdi_fs_acl_entry_usr_num {
    struct cdi_fs_acl_entry entry;
   
    // Benutzer-ID
    uid_t                   user_id;
}

struct cdi_fs_acl_entry_usr_str {
    struct cdi_fs_acl_entry entry;
   
    // Benutzername
    char*                   user_name;
}

struct cdi_fs_acl_entry_grp_num {
    struct cdi_fs_acl_entry entry;
   
    // Gruppen-ID
    gid_t                   group_id;
}

struct cdi_fs_acl_entry_usr_str {
    struct cdi_fs_acl_entry entry;
   
    // Gruppenname
    char*                   group_name;
}



void cdi_fs_driver_init(struct cdi_fs_driver* driver);
void cdi_fs_driver_destroy(struct cdi_fs_driver* driver);
void cdi_fs_driver_register(struct cdi_fs_driver* driver);


/**
 * Quelldateien fuer ein Dateisystem lesen
 * XXX Brauchen wir hier auch noch irgendwas errno-Maessiges?
 *
 * @param device Pointer auf die Device-Struktur des Dateisystems
 * @param start Position von der an gelesen werden soll
 * @param size Groesse des zu lesenden Datenblocks
 * @param buffer Puffer in dem die Daten abgelegt werden sollen
 *
 * @return die Anzahl der gelesenen Bytes
 */
size_t cdi_fs_data_read(struct cdi_fs_device* device, uint64_t start,
    size_t size, void* buffer);

/**
 * Quellmedium eines Dateisystems beschreiben
 * XXX Brauchen wir hier auch noch irgendwas errno-Maessiges?
 *
 * @param device Pointer auf die Device-Struktur des Dateisystems
 * @param start Position an die geschrieben werden soll
 * @param size Groesse des zu schreibenden Datenblocks
 * @param buffer Puffer aus dem die Daten gelesen werden sollen
 *
 * @return die Anzahl der geschriebenen Bytes
 */
size_t cdi_fs_data_write(struct cdi_fs_device* device, uint64_t start,
    size_t size, void* buffer);


#endif
124
tyndur / Re: cdi.fs
« am: 12. February 2008, 13:54 »
Ok das übernehme ich so mal.

Wahrscheinlich das geschickteste, einfach ein type-Feld dazupacken. Ansonsten hat man dreimal (block/char/pipe) dasselbe unter jeweils verschiedenem Namen.
Du meinst so, dass eine Ressource nur einen solchen Spezialtyp habe kann?
125
Lowlevel-Coding / Re: Globale daten mit nasm?
« am: 12. February 2008, 13:35 »
Man könnte auch die Frage stellen, warum man in Assembler [name] braucht um auf den Inhalt der Variable zuzugreifen.

Deswegen nimmt man ja den Assembler ohne falschrum-Syntax. ;-)
Aber das tut ja hier eigentlich nichts zur Sache. ;-)
126
tyndur / Re: cdi.fs
« am: 12. February 2008, 13:32 »
Zitat
rename ist lokal, also kein verschieben. Ich habe die getrennt, da das ja auch aus sicht vom Treiber unterschiedliche Aktionen sind.
Lokal relativ wozu? Verzeichnis oder Dateisystem? Verschieben auf ein anderes FS ist sowieso keine eigenständige Aktion, sondern ein Kopieren und Löschen.
Relativ zum Verzeichnis. Verschieben ist relativ zum Dateisystem

Zitat
Wie sieht es denn mit dem neu erstellen aus? Das ist eigentlich das was ich gemeint, aber selbstveständlich nicht hingeschrieben habe. ;-)

Edit:
Vielleicht könnte man da vom eigentlichen Erstellen wegkommen und eher was in Richtung Klasse zuordnen oder so machen...
Hm, gute Frage. Das Erstellen würde dann nur eine Ressource ohne Klasse erstellen und hinterher ordnet man die Klassen einzeln zu? Könnte funktionieren.
Jo das wäre vermutlich die beste Variante... Das geht aber nur solange, wie der Treiber nicht wissen muss, zu welchen Klassen die Ressource gehört, um sie erstellen zu können. Ok ein Treiber der das Braucht kann ja die "nackte" Ressource auch nur einfach mal im Speicher liegen lassen.

Zitat
Die Spezialdateien haben keine Nutzdaten auf der Platte. Sie beinhalten (in linux) lediglich 2 Zahlen im Inode, damit sie vom Kernel identifiziert werden koennen, wenn darauf zugegriffen wird, aber mit dem eigentlichen Zugriff hat der Dateisystemtreiber nichts mehr zu tun. Das wird dann vom Kernel an irgendwelche Treiber delegiert.
Was auf der Platte steht, ist wurscht. Wir reden gerade über das Interface. Das kann, muß aber nicht dasselbe sein. Aber eine eigene Klasse dafür mit read/write_major/minor sollte es doch tun?
Es geht mir ja auch um das Interface... Wenn das so ausreicht mit den 4 Funktionen ist das ja kein Problem. Dann würdest du einfach eine klasse fuer die Spezialfunktion machen, egal ob FIFO oder Block Datei?
127
Lowlevel-Coding / Re: Globale daten mit nasm?
« am: 12. February 2008, 12:48 »
Nein, wenn du sowas wie
int a = 42;hast, dann ergibt das in Assembler vielleicht sowas (praktisch gesehen natürlich nicht genau, aber vom sinn her. ;-)):
a:
dd 42

Und mit a greifst du dann auf den Inhalt zu. Mit dem & Zeichen kommst du zu der eigentlichen Adresse des labels.
Es kommt dann natürlich auch noch darauf an, wie du das Label für den C-Compiler deklarierst. Hier wäre das jetzt ein extern int a;
128
tyndur / Re: cdi.fs
« am: 12. February 2008, 12:35 »
// XXX Dateigroesse (Fuer mich stellt sich noch die Frage, ob sie wirklich zu
// den Meta-Informationen gehoert, oder ob wir sie nicht doch besser in die
// file-Struktur packen)
#define CDI_FS_META_SIZE        0
Eigentlich stellt sich die Frage nicht, denn sowas haben nur Dateien, aber nicht Verzeichnisse, Links...
Hm, eigentlich hast du recht... ;-)

/**
 * XXX Der Name Device ist hier unpassend, was besseres ist mir aber nicht
 * eingefallen.
 */
struct cdi_fs_device {
Wie wäre es mit cdi_fs_filesystem?
Daran habe ich auch schon gedacht, aber das sah irgendwie auch 0xd00f aus. Aber vom Sinn her ist es wohl gescheiter.

    // Wurzelobjekt fuer die Baumstruktur
    struct cdi_fs_object*   root_object;
   
    // Dieses Error-Flag sperrt den ganzen Mount fuer (Schreib?)Zugriffe
    int                     error;

    // Das Dateisystem darf nicht geschrieben werden. Damit schlaegt unter
    // anderem cdi_fs_write_data fehl.
    int                     read_only;
Was ist jetzt der Unterschied zwischen error und read_only, außer daß ersteres durch einen Fehler erzeugt wird?

Die Überlegung dahinter war, dass man auch erkennen kann, warum das Dateisystem jetzt gesperrt ist. Das Dateisystem kann ja auch read_only sein, ohne dass ein Fehler aufgetreten ist. Flag ist da natürlich auch der Falsche Ausdruck. Da würde man wohl besser eine Fehlernummer nehmen.

create_child oder sowas fehlt noch. Ist rename lokal zum Verzeichnis, zum Dateisystem, oder global? (Letzteres macht keinen Sinn, da es noch move gibt)
Jo das fehlt noch. Ich wusste noch nicht, wie ich das am dümmsten anstellen könnte. ;-) Unter anderem auch im Zusammenhang mit den Spezialdateien, aber dazu kommen wir ja weiter unten gleich.
rename ist lokal, also kein verschieben. Ich habe die getrennt, da das ja auch aus sicht vom Treiber unterschiedliche Aktionen sind.

Zitat
Mir ist gerade eingefallen, dass das mit den Klassen ja beim Lesen so funktionieren kann... Aber wie sieht das beim schreiben aus? Woher soll der Treiber wissen, welche file-Klasse da benutzt werden soll?
Gehört nicht jede Operation eindeutig zu einer Klasse?
Wie sieht es denn mit dem neu erstellen aus? Das ist eigentlich das was ich gemeint, aber selbstveständlich nicht hingeschrieben habe. ;-)

Edit:
Vielleicht könnte man da vom eigentlichen Erstellen wegkommen und eher was in Richtung Klasse zuordnen oder so machen...

Zitat
Aber für mich stellt sich jetzt noch die Frage, wie mit den Spezialdateien umgegangen werden soll...
Ich bin vermutlich von LostIO verdorben, aber was ändert sich mit Spezialdateien?
Und ich soll davon nicht verdorben sein? ;-)

Die Spezialdateien haben keine Nutzdaten auf der Platte. Sie beinhalten (in linux) lediglich 2 Zahlen im Inode, damit sie vom Kernel identifiziert werden koennen, wenn darauf zugegriffen wird, aber mit dem eigentlichen Zugriff hat der Dateisystemtreiber nichts mehr zu tun. Das wird dann vom Kernel an irgendwelche Treiber delegiert.
129
tyndur / Re: cdi.fs
« am: 12. February 2008, 01:26 »
So, ich habe hier jetzt mal zusammengetragen, was mir so eingefallen ist. Ich hoffe ihr werdet davon nicht allzusehr erschlagen.
Mir ist bewusst, dass Funktionen zum Ändern der Berechtiungen noch fehlen. Aber dazu bin ich heute nicht mehr im Stande.  :roll:

#ifndef _CDI_FS_H_
#define _CDI_FS_H_

#include "types.h"
#include "cdi.h"
#include "cdi/lists.h"

// XXX Dateigroesse (Fuer mich stellt sich noch die Frage, ob sie wirklich zu
// den Meta-Informationen gehoert, oder ob wir sie nicht doch besser in die
// file-Struktur packen)
#define CDI_FS_META_SIZE        0
// Anzahl der Benutzten Dateisystemblocks (Irgendwo muesste man dann auch auf
// diese Blockgroesse zurgreiffen koennen)
#define CDI_FS_META_USEDBLOCKS  1
// Optimale Blockgroesse mit der man auf die Datei zugreiffen sollte
#define CDI_FS_META_BESTBLOCKSZ 2
// Zeitpunkt an dem das Objekt erstellt wurde
#define CDI_FS_META_CREATETIME  3
// Letzter Zugriff auf das Objekt, auch lesend
#define CDI_FS_META_ACCESSTIME  4
// Letzte Veraenderung des Objekts
#define CDI_FS_META_CHANGETIME  5


// XXX Bei den Fehlernummern weiss ich noch nicht wirklich, was da notwendig
// ist, deshalb lasse ich das mal so stehen.
#define CDI_FS_ERROR_NONE    0 /* No Error */
#define CDI_FS_ERROR_ONS     1 /* Operation not supported */
#define CDI_FS_ERROR_FNF     2 /* File not found */
#define CDI_FS_ERROR_EOF     3 /* End of file */
#define CDI_FS_ERROR_UNKNOWN 4 /* Unknown error */


struct cdi_fs_driver {
    struct cdi_driver drv;
}

struct cdi_fs_object;
/**
 * XXX Der Name Device ist hier unpassend, was besseres ist mir aber nicht
 * eingefallen.
 */
struct cdi_fs_device {
    // Wurzelobjekt fuer die Baumstruktur
    struct cdi_fs_object*   root_object;
   
    // Dieses Error-Flag sperrt den ganzen Mount fuer (Schreib?)Zugriffe
    int                     error;

    // Das Dateisystem darf nicht geschrieben werden. Damit schlaegt unter
    // anderem cdi_fs_write_data fehl.
    int                     read_only;

    // Hier sollte man wohl noch ein paar allgemeine Mount-Optionen oder
    // sonstige Flags die das ganze Dateisystem betreffen.
};

/**
 * Das Handle stellt die Verbindung zwischen Aufrufer und betroffenem
 * Dateisystemobjekt dar.
 */
struct cdi_fs_handle {
    // Betroffenes Dateisystemobjekt
    struct cdi_fs_object*   obj;

    // Fehlernummer
    int                     error;
};


/**
 * Diese Struktur hat eigentlich nicht viel mit Zugriffsrechten zu tun. Deshalb
 * ist der Name vielleicht etwas ungeschickt gewaehlt, aber was bessers ist mir
 * nicht eingefallen.
 * Sie stellt allgemein die Moeglichkeiten, die an einem Objekt zur verfuegung
 * stehen dar.
 */
struct cdi_fs_obj_permissions {
    // Objekt loeschen
    int                 remove;
    // Objekt umbenennen
    int                 rename;
    // Objekt verschieben
    int                 move;
    // Lesender Zugriff gestattet
    int                 read;
    // Schreibender Zugriff gestattet
    int                 write;
    // Ausfuehren gestattet
    int                 execute;
    // Auflisten der Verzeichniseintraege gestattet
    int                 browse;
    // Aufloesen des Links
    int                 read_link;
    // Aendern des Links
    int                 write_link;
};


struct cdi_fs_obj_file;
struct cdi_fs_obj_dir;
struct cdi_fs_obj_link;
/**
 * Das Dateisystem wird hier nur mit abstrakten Strukturen vom Typ
 * cdi_fs_object dargestellt. Diese können beispielsweise sowohl regulaere
 * Datei als auch Verzeichnis gleichzeitig darstellen.
 */
struct cdi_fs_object {
    // Name des Objektes
    char*                   name;

    // Verweis auf das Elternobjekt
    struct cdi_fs_object*   parent;
    // Liste mit allfaelligen Kindobjekten
    cdi_list_t              children;

    // Link-Pfad
    char*                   link_path;

    // ACL; siehe Unten
    cdi_list_t              acl;
    // Flags
    struct cdi_fs_obj_permissions flags;

    // Einzelne Klassen, zu denen das Objekt gehoeren kann, oder Null falls es
    // zu einer Bestimmten Klasse nicht gehoert.
    struct cdi_fs_obj_file* file;
    struct cdi_fs_obj_dir*  dir;
    struct cdi_fs_obj_link* link;
};



/**
 * Diese Dateisystemobjekte werden in Klassen eingeteilt, die das eigentliche
 * "Verhalten" der Objekte steuern. Diese Klassen beinhalten die moeglichen
 * Operationen und auch die Eigenschaften, die fuer die Objekte gelten, denen
 * diese Klassen zugeordnet sind.
 * Das Definieren der einzelnen Klassen uebernehmen dann die einzelnen Treiber.
 *
 * Die Flags koennen vom Objekt ueberschrieben werden. Es duerfen allerdings
 * nur Flags deaktiviert werden, die in der Klasse gesetzt sind und nicht
 * umgekehrt.
 */

/**
 * Diese Klasse gilt unabhaengig von den andern, also egal welche anderen
 * Klassen angegeben sind, diese muss angegeben werden.
 */
struct cdi_fs_obj_obj {
    /**
     * Objekt entfernen
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     *
     * @return Falls das Objekt erfolgreich geloescht wurde 1, sonst 0
     */
    int remove(struct cdi_fs_handle* handle);

    /**
     * Objekt umbenennen
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     * @param name Neuer Name
     *
     * @return Falls das Objekt erfolgreich umbenennt wurde 1, sonst 0
     */
    int rename(struct cdi_fs_handle* handle, const char* name);

    /**
     * Objekt verschieben
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     * @param dest Pointer auf das Objekt, in das verschoben werden soll
     *
     * @return Falls das Objekt erfolgreich verschoben wurde 1, sonst 0
     */
    int move(struct cdi_fs_handle* handle, struct cdi_fs_object* dest);
}

struct cdi_fs_obj_file {
    // XXX (Aber wie geht das, wenn eine Datei nicht lesbar, aber ausfuehrbar
    // sein soll?)
    int                     executable;
   
    /**
     * Daten aus dieser Datei lesen. Wird nur aufgerufen, wenn es durch die
     * Flags oder Berechtigungen nicht verhindert wird.
     *
     * Im Fehlerfall wird je nach Fehler die Fehlernummer im Handle und die im
     * Device gesetzt.
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     * @param start Position von der an gelesen werden soll
     * @param size Groesse der zu lesenden Daten
     * @param buffer Puffer in den die Daten gelsen werden sollen
     *
     * @return Gelesene Bytes, oder 0 im Fehlerfall
     */
    size_t read(struct cdi_fs_handle* handle, uint64_t start, size_t size,
        void* buffer);

    /**
     * Daten in diese Datei schreiben. Wird nur aufgerufen, wenn es durch die
     * Flags oder Berechtigungen nicht verhindert wird.
     *
     * Im Fehlerfall wird je nach Fehler die Fehlernummer im Handle und die im
     * Device gesetzt.
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     * @param start Position an die geschrieben werden soll
     * @param size Groesse der zu schreibenden Daten
     * @param buffer Puffer aus dem die Daten gelesen werden sollen
     *
     * @return Geschriebene Bytes oder 0 im Fehlerfall
     */
    size_t write(struct cdi_fs_handle* handle, uint64_t start, size_t size,
        void* buffer);
};

struct cdi_fs_obj_dir {
    // XXX Ein browsable Flag koennen wir vermutlich hier eindeutig welgassen,
    // waere ja irgenwie witzlos. ;-)
   
    /**
     * Diese Funktion gibt einen Pointer auf die Liste mit den Eintraegen
     * zurueck. Hier wird nicht einfach fix der Pointer in fs_obj genommen,
     * damit dort auch "versteckte" Eintraege vorhanden sein koennten. (Ich
     * meine hier nicht irgend ein versteckt-Flag dass die Dateien vor dem
     * Benutzer Verstecken soll, sondern eher von fuer den Internen gebrauch
     * angelegten Eintraegen.
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     *
     * @return Pointer auf eine Liste mit den Untereintraegen.
     */
     cdi_list_t* list(struct cdi_fs_handle* handle);
};

struct cdi_fs_obj_link {
    // XXX Ich glaube auch hier brauchen wir kein resolvable-Flag

    /**
     * Diese Funktion liest den Pfad aus, auf den der Link zeigt
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     *
     * @return Pointer auf einen Ouffer der den Pfad beinhaltet. Dieses Puffer
     *         darf vom Aufrufer nicht veraendert werden.
     */
    const char* read_link(struct cdi_fs_handle* handle);

    /**
     * Aendert den Pfad auf den der Link zeigt
     *
     * @param handle Handle fuer die Verbindung zwischen Aufrufer und Objekt
     * @param path Neuer Pfad
     *
     * XXX Hm ist diese Funktion so vielleicht noch ein bisschen zu
     * optimistisch?
     */
    void write_link(struct cdi_fs_handle* handle);
};


/**
 * Die Berechtigunen werden mit Access controll lists, kurz ACLs verwaltet.
 * Diese werden in Form von Listen gespeichert. Diese Listen enthalten
 * eintraege von verschiedenen Typen.
 */
/// Eine UID
#define  CDI_FS_ACL_USER_NUMERIC,
/// Ein Benutzername als String
#define CDI_FS_ACL_USER_STRING,
/// Eine GID
#define CDI_FS_ACL_GROUP_NUMERIC,
/// Ein Gruppenname als String
#define CDI_FS_ACL_GROUP_STRING

/**
 * Der Basiseintrag in einer ACL, von dem die anderen Typen der Eintraege
 * abgeleitet sind.
 */
struct cdi_fs_acl_entry {
    // Typ des Eintrages, eine der obigen Konstanten
    int                     type;
   
    // Flags
    struct cdi_fs_obj_permissions flags;
};

/**
 * Eintraege fuer die einzelnen Typen
 */
struct cdi_fs_acl_entry_usr_num {
    struct cdi_fs_acl_entry entry;
   
    // Benutzer-ID
    uid_t                   user_id;
}

struct cdi_fs_acl_entry_usr_str {
    struct cdi_fs_acl_entry entry;
   
    // Benutzername
    char*                   user_name;
}

struct cdi_fs_acl_entry_grp_num {
    struct cdi_fs_acl_entry entry;
   
    // Gruppen-ID
    gid_t                   group_id;
}

struct cdi_fs_acl_entry_usr_str {
    struct cdi_fs_acl_entry entry;
   
    // Gruppenname
    char*                   group_name;
}



void cdi_fs_driver_init(struct cdi_fs_driver* driver);
void cdi_fs_driver_destroy(struct cdi_fs_driver* driver);
void cdi_fs_driver_register(struct cdi_fs_driver* driver);


/**
 * Quelldateien fuer ein Dateisystem lesen
 * XXX Brauchen wir hier auch noch irgendwas errno-Maessiges?
 *
 * @param device Pointer auf die Device-Struktur des Dateisystems
 * @param start Position von der an gelesen werden soll
 * @param size Groesse des zu lesenden Datenblocks
 * @param buffer Puffer in dem die Daten abgelegt werden sollen
 *
 * @return die Anzahl der gelesenen Bytes
 */
size_t cdi_fs_data_read(struct cdi_fs_device* device, uint64_t start,
    size_t size, void* buffer);

/**
 * Quellmedium eines Dateisystems beschreiben
 * XXX Brauchen wir hier auch noch irgendwas errno-Maessiges?
 *
 * @param device Pointer auf die Device-Struktur des Dateisystems
 * @param start Position an die geschrieben werden soll
 * @param size Groesse des zu schreibenden Datenblocks
 * @param buffer Puffer aus dem die Daten gelesen werden sollen
 *
 * @return die Anzahl der geschriebenen Bytes
 */
size_t cdi_fs_data_write(struct cdi_fs_device* device, uint64_t start,
    size_t size, void* buffer);


#endif

Edit:
Mir ist gerade eingefallen, dass das mit den Klassen ja beim Lesen so funktionieren kann... Aber wie sieht das beim schreiben aus? Woher soll der Treiber wissen, welche file-Klasse da benutzt werden soll?
Der Gedanke war eigentlich, dass sich beispielsweise eine Blockdatei oder ein FIFO von aussen gesehen auch nicht anders verhalten als eine reguläre Datei. Aber der Dateisystemtreiber ist dafür wohl schon eine Schicht zu tief, um das zu verhalten. Aber für mich stellt sich jetzt noch die Frage, wie mit den Spezialdateien umgegangen werden soll...
130
tyndur / Re: cdi.fs
« am: 12. February 2008, 00:10 »
Jo das macht so eindeutig Sinn. *hust* ;-)

Vorhin ist mir auch noch eine Frage durch den Kopf gegangen:
Wie könnte man es ermöglichen, dass jemand, der zum Beispiel Gerätedateien in ext2 benutzen möchte, das tun kann?
131
tyndur / Re: cdi.fs
« am: 11. February 2008, 22:59 »
Hm, die standardmäßigen readable, writable, executable?

Stimmt, das wäre noch was.

Mir ist eben noch etwas eingefallen:
Wie könnten wir das mit dem Öffnen machen, damit man da zwischen den einzelnen Möglichkeiten, wie man auf ein Objek zugreifen kann, wechseln kann?
Irgendwie muss man ja angeben können, ob man jetzt den Pfad des Symlinks, die Untereinträge oder die Daten, die das Objekt als reguläre Datei beinhaltet haben will... Es macht ja auch nur bedingt sinn, diese alle gleich auszuwerten, denn die Verzeichniseinträge beispielsweise mit einem read() zu holen ist ja auch nicht speziell angenehm.
132
tyndur / Re: cdi.fs
« am: 11. February 2008, 22:18 »
Jo ist klar. Aber wenn ich einen definierbaren Trennstring vorgeschlagen hätte, wäre vielleicht noch jemand auf die Idee gekommen, das könnte genügen. ;-)

Edit:
Was fällt dir denn da als Flags im Stil von browsable noch so ein?
133
tyndur / Re: cdi.fs
« am: 11. February 2008, 22:15 »
Die Idee mit get_meta und set_meta finde ich gut. Allerdings müsste man sich da überlegen, wie man Eigenschaften umsetzen möchte, die nich Integer sind. Also zum Beispiel Dateisysteme bei denen die Benutzer- und Gruppennamen als String gespeichert wird. (Zum Beispiel ACLs bei ext2)

Weiter finde ich die CDI_...._NONE Makros etwas seltsam. Das macht doch eigentlich nur bei der Fehlernummer sinn. Bei den anderen Kann man ja 0 einfach als ersten Wert benutzen.

Für die Verwaltung der Dateien möchte ich noch ein bisschen was ergänzen, denn es macht meiner Meinung nach nicht Sinn, in jedem Treiber den ganzen Code für die Verwaltung von Dateien und Verzeichnissen und das auflösen der Pfade neu zu schreiben. Abgsesehen davon müsste man sich ja damit fast auf ein Trennzeichen bei den Pfaden festlegen, und da wollen ja nicht alle das selbe.

Ich versuche mal, dafür noch ein bisschen was hin zu spinnen. ;-)
134
Das Wiki / Re: Sinkende Aktivität
« am: 11. February 2008, 21:09 »
Ich weiß, du bist so nerdig, du bist sogar im Internet sozial vereinsamt. :-P
Genau, du hast es erfasst. ;-)


Noooooooooooos:
Ich glaube nicht, dass es sehr förderlich ist, wenn wir das selbe Tutorial einfach in alle Foren posten. ;-)
135
Das Wiki / Re: Sinkende Aktivität
« am: 11. February 2008, 20:46 »
Ich denke jeder hat halt so ein paar Foren wo er auch noch unterwegs ist, zT. auch von seiner Coder-Vergangenheit...
Also ich nicht. Zu meiner Zeit gab es noch kein Internet. ;)

Zu meiner Zeit gab es zwar schon Internet, sowas habe ich aber trotzdem nicht.  :-P

Und jo, das geben von Antworten, die etwas weniger entmutigend wirken, ist sicherlich eine gute Lösung. Aber das habe ich eigentlich eh als selbstverständlich angesehen. ;-)

Eine andere Möglichkeit wäre vermutlich auch das schreiben von Tutorials/Artikeln in Foren. Dadurch interessieren sich sicher einige dafür, die sonst nichtmal auf die Idee gekommen wären. Und ein Link mit weiteren Informationen kann man da ja auch diskret anfügen.  :-D

Termite:
Bist du in dem Forum dabei? Wenn ja, könntest du den betreffenden Moderator mal anschreiben?
136
OS-Design / Re: Graphische Oberfläche mit GRUB, C und VGA
« am: 11. February 2008, 15:55 »
Wie gesagt, mit den Informationen, die ich im Moment von dir habe, kann ich nicht gross weiterhelfen. Das objdump -d könnte da noch am ehsten weiterhelfen.

Als Compiler unter Windows würde ich dir den Crosscompiler aus dem Wiki empfehlen: http://lowlevel.brainsware.org/wiki/index.php/Crosscompiler_f%C3%BCr_Windows

Ich kann dir nur noch raten, deine INPUT-Zeile im Linkerskript genau anzuschauen, und zu probieren, den Unterstrich im ASM-Code wegzunehmen.
137
Das Wiki / Re: Sinkende Aktivität
« am: 11. February 2008, 15:32 »
Das glaube ich eher wengier, denn es scheint ja in den Foren immer wieder irgendwelche Threads gegeben, in denen nach dem Thema gefragt wird.

Die Frage ist eher, wie wir diese Leute zu uns kriegen. Denn wir können ja auch nicht ständig die ganzen Foren durchsuchen. ;-)
138
OS-Design / Re: Graphische Oberfläche mit GRUB, C und VGA
« am: 11. February 2008, 15:15 »
Naja nach der Fehlermeldung zu schliessen macht dein Kompiler da kein unterschied hin. Das müsstest du auch bestätigt kriegen, wenn du mal ein objdump -d von deiner ckernel.obj machst.
Die Fehlermeldung kann eigentlich nur davon kommen, dass der deine Funktion anders nennt, oder dass die Datei mit dem C-Kernel nicht mitgelinkt wird.
Oder benutzt du vielleicht den g++ zum kompilieren? Der kann zwar prinzipiell auch C kompilieren, aber spätestens beim Linken gibts da probleme.

Naja, für den Anfang ist die Konsole ja ganz praktisch. Das kannst du auch später noch rauswerfen.
139
OS-Design / Re: Graphische Oberfläche mit GRUB, C und VGA
« am: 11. February 2008, 14:04 »
Naja, mit Windows oder Linux hat das eigentlich nicht viel zu tun. Eher damit, wie der gcc konfiguriert wurde. Am besten hängst du bei deinen CFLAGS ein -fno-leading-underscore an, dann kannst du davon ausgehen, dass keine Unterstriche benutzt werden.

Und ja, das 0x4 ist die Adresse Instruktion, die für den Fehler verantwortlich ist. Ich glaube eher nicht, dass das die main() ist, sondern eher die Stelle im Assemblercode, wo die main angesprungen werden soll.

Am besten Zeigst du mal den C und den ASM-Code her und sagst uns, welche Flags du zum kompilieren benutzt.

Zum Windows-Bootloader kann ich keine Auskunft geben. Ich befürchte aber, der ist nicht dafür gemacht, flache Binärdateien zu laden.
Ich würde dir, dazu raten, einen Multiboot-Header in deinen Assemblercode zu packen, ELF als Ausgabeformat zu benutzen und danach GRUB als Bootloader hernehmen. Damit können wir dir hier am Besten helfen.


Wie das zusammenhängt kannst du eigentlich grösstenteils selbst bestimmen. Klar, die GUI hängt vom Kernel ab, aber da gibt es verschiedene Ansätze wie man die GUI ins System integrieren kann. Der erste wäre der, der von Windows benutzt wird, also die GUI als Teil des Kernels. Die andere Möglichkeit sieht man bei Linux. Der Grafikserver X ist ein eigenständiges Programm und braucht den Kernel nur, um mit der Hardware zu kommunizieren.

Ob eine GUI die Konsole ersetzen kann, hängt von deinen Zielen ab. Aber prinzipiell ist das möglich.
140
tyndur / Re: Status von LOST
« am: 11. February 2008, 00:52 »
Der letzte Statusbericht ist ja nun schon eine Weile her. Und einiges hat sich getan. Dazu gehören die Releases von 0.1, 0.1.1 und eine erste Alpha von 0.2.

Ich will hier nicht nochmal alle neuen Features auflisten, sondern nur die meiner Meinung nach wichtigsten. Dazu gehören Treiber für das Diskettenlauferk, ATA-Festplatten, das FAT-Dateiystem und das ext2-Dateisytem. Weiter, ein Bisschen weniger grundlegend, wären da Treiber für rtl8139 Netzwerkkarten und einen für tcp/ip.

Die aktuellen Baustellen sind das Common Driver Interface, kurz CDI, ein xen-Port für kernel2 und die Ports von binutils und gcc.

Mit dem CDI versuchen wir von LOST und ein paar andere Entwicklern mit eigenen Betriebssystemen (vorallem bluecode, janosch und Homix) eine gemeinsame Treiberschnittstelle zu Definieren, damit unsere Treiber auf allen Betriebssystemen die CDI unterstützeb benutzt werden können, und nicht jeder seine eigenen schreiben muss.

kernel2 ist ein Rewrite von kernel, das aber im Moment und auch in der näheren Zukunft noch nicht mit dem Userland zusammen funktioniert. Wir versuchen damit, die grössten Schwachstellen von kernel auszubügeln. kernel2 soll unter anderem auch portabel sein, und mindestens auf i386 und amd64 laufen. Weiter ist auch SMP-Unterstützung eingeplant.
Mit dem xen-Port passt taljeth kernel2 so an, dass er mit xen (einem Virtualisierungssystem) betrieben werden kann.

Die Ports von GNU-binutils (die unter anderem as den GNU Assembler und ld den Linker beinhalten) und dem C-Compiler gcc funktionieren schon grösstenteils. Im Moment ist es damit möglich, ein Hello World zu kompilieren, danach zu linken und selbstverständlich danach auch auszuführen. ;-) Ein paar Macken, die noch behoben werden müssen, hat der gcc-Port aber noch.


Hier wieder die üblichen Metriken (leider diesmal ohne Kommentaranteil):
Aktuelle SVN-Revision:                   703
Anzahl Code-Zeilen in kernel/src:       4092
Anzahl Code-Zeilen in modules:         24842
Anzahl Code-Zeilen gesamt:             35253
Seiten: 1 ... 5 6 [7] 8 9 ... 15

Einloggen