[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [cdi-devel] [RFC][PATCH] fat: Beginnings of a FAT file system driver



On 30/07/2010, at 7:10 AM, Kevin Wolf <kevin@xxxxxxxxxx> wrote:

> This adds a read-only FAT driver for FAT12/16/32. Long file names are
> not yet supported.
> 
> Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
> ---
> fat/include              |    1 +
> fat/libfat/dir.c         |  245 +++++++++++++++++++++++++++++++
> fat/libfat/file.c        |  360 ++++++++++++++++++++++++++++++++++++++++++++++
> fat/libfat/fs.c          |   80 ++++++++++
> fat/libfat/include/fat.h |  273 +++++++++++++++++++++++++++++++++++
> fat/main.c               |  334 ++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 1293 insertions(+), 0 deletions(-)
> create mode 120000 fat/include
> create mode 100644 fat/libfat/dir.c
> create mode 100644 fat/libfat/file.c
> create mode 100644 fat/libfat/fs.c
> create mode 100644 fat/libfat/include/fat.h
> create mode 100644 fat/main.c
> 
> diff --git a/fat/include b/fat/include
> new file mode 120000
> index 0000000..6f62628
> --- /dev/null
> +++ b/fat/include
> @@ -0,0 +1 @@
> +libfat/include/
> \ No newline at end of file
> diff --git a/fat/libfat/dir.c b/fat/libfat/dir.c
> new file mode 100644
> index 0000000..bbc7b42
> --- /dev/null
> +++ b/fat/libfat/dir.c
> @@ -0,0 +1,245 @@
> +#include <stdint.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <ctype.h>
> +#include "fat.h"
> +
> +static int fat_rootdir_open(struct fat_fs* fs, struct fat_dir* dir)
> +{
> +    int64_t rootdir_offset = fs->bpb.reserved_sectors +
> +        (fs->bpb.num_fats * fs->bpb.fat_sectors);
> +
> +    uint32_t rootdir_sectors =
> +        (fs->bpb.rootdir_length * sizeof(struct fat_disk_dir_entry) + 511)
> +        / 512;
> +
> +    void* buf = malloc(512 * rootdir_sectors);
> +
> +    // Wurzelverzeichnis einlesen
> +    if (!fs->dev_read(rootdir_offset * 512, rootdir_sectors * 512, buf,
> +        fs->dev_private))
> +    {
> +        goto fail;
> +    }
> +
> +    // Rueckgabestruktur befuellen
> +    dir->entries = buf;
> +    dir->fs = fs;
> +    dir->i = 0;
> +    dir->num = rootdir_sectors * 512 / sizeof(struct fat_disk_dir_entry);
> +
> +    return 0;
> +
> +fail:
> +    free(buf);
> +    return -1;
> +}
> +
> +static int fat_rootdir32_open(struct fat_fs* fs, struct fat_dir* dir)
> +{
> +    struct fat_file file;
> +    struct fat32_extended_bpb* ext = &fs->bpb.extended.fat32;
> +    struct fat_dir_entry dentry = {
> +        .size = 0,
> +        .first_cluster = ext->rootdir_cluster,
> +    };
> +    int ret;
> +    void* buf;
> +
> +    ret = fat_file_open_by_dir_entry(fs, &dentry, &file);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    buf = malloc(file.size);
> +
> +    ret = fat_file_read(&file, buf, 0, file.size);
> +    fat_file_close(&file);
> +    if (ret < 0) {
> +        free(buf);
> +        return ret;
> +    }
> +
> +    /* Rueckgabestruktur befuellen */
> +    dir->entries = buf;
> +    dir->fs = fs;
> +    dir->i = 0;
> +    dir->num = file.size / sizeof(struct fat_disk_dir_entry);
> +
> +    return 0;
> +}
> +
> +int fat_subdir_open(struct fat_fs* fs, struct fat_dir* dir, const char* path,
> +    struct fat_dir* subdir)
> +{
> +    struct fat_dir_entry dentry;
> +    struct fat_file file;
> +    void* buf;
> +    int ret;
> +
> +    /* Vaterverzeichnis nach Unterverzeichnis durchsuchen */
> +    do {
> +        ret = fat_dir_read(dir, &dentry);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +
> +        if ((ret >= 0) && !strcmp(dentry.name, path)) {
> +            goto found;
> +        }
> +    } while (ret >= 0);
> +
> +    return -FAT_ENOENT;
> +
> +    /* Verzeichnis als Datei oeffnen und Eintraege laden */
> +found:
> +    ret = fat_file_open_by_dir_entry(fs, &dentry, &file);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    buf = malloc(file.size);
> +
> +    ret = fat_file_read(&file, buf, 0, file.size);
> +    fat_file_close(&file);
> +    if (ret < 0) {
> +        free(buf);
> +        return ret;
> +    }
> +
> +    /* Rueckgabestruktur befuellen */
> +    subdir->entries = buf;
> +    subdir->fs = fs;
> +    subdir->i = 0;
> +    subdir->num = file.size / sizeof(struct fat_disk_dir_entry);
> +
> +    return 0;
> +}
> +
> +int fat_dir_open(struct fat_fs* fs, const char* _path, struct fat_dir* dir)
> +{
> +    int ret;
> +    char* path = strdup(_path);
> +    char* p;
> +    struct fat_dir subdir;
> +
> +    if (fs->type == FS_TYPE_FAT32) {
> +        ret = fat_rootdir32_open(fs, dir);
> +    } else {
> +        ret = fat_rootdir_open(fs, dir);
> +    }
> +
> +    if (ret < 0) {
> +        goto out;
> +    }
> +
> +    /* Pfade muessen mit einem Slash anfangen */
> +    if (*path != '/') {
> +        ret = -FAT_EINVAL;
> +        goto out;
> +    }
> +
> +    /* Jetzt die Unterverzeichnisse parsen */
> +    p = strtok(path + 1, "/");
> +    while (p != NULL) {
> +        ret = fat_subdir_open(fs, dir, p, &subdir);
> +        fat_dir_close(dir);
> +        if (ret < 0) {
> +            goto out;
> +        }
> +        *dir = subdir;
> +        p = strtok(NULL, "/");
> +    }
> +
> +    ret = 0;
> +out:
> +    free(path);
> +    return ret;
> +}
> +
> +static void short_fn_to_string(struct fat_disk_dir_entry* entry, char* buf)
> +{
> +    int i;
> +    int pos = -1;
> +    int ext_start;
> +
> +    // 8 Zeichen Name in Kleinbuchstaben
> +    for (i = 0; i < 8; i++) {
> +        buf[i] = tolower(entry->name[i]);
> +        if (!isspace(buf[i])) {
> +            pos = i;
> +        }
> +    }
> +
> +    // Punkt
> +    buf[++pos] = '.';
> +
> +    // 3 Zeichen Erweiterung in Kleinbuchstaben
> +    ext_start = pos + 1;
> +    for (i = 0; i < 3; i++) {
> +        buf[ext_start + i] = tolower(entry->ext[i]);
> +        if (!isspace(buf[ext_start + i])) {
> +            pos = ext_start + i + 1;
> +        }
> +    }
> +
> +    // Nullterminieren
> +    buf[pos] = '\0';
> +}
> +
> +static int is_valid_entry(struct fat_disk_dir_entry* entry)
> +{
> +    // Die Datei existiert nicht
> +    if (entry->name[0] == '\0') {
> +        return 0;
> +    }
> +
> +    // Die Datei ist geloescht
> +    if (entry->name[0] == (char) 0xE5) {
> +        return 0;
> +    }
> +
> +    // Die Datentraegerbezeichnung interessiert uns nicht
> +    if (entry->attrib & FAT_ATTRIB_VOLUME) {
> +        return 0;
> +    }
> +
> +    // . und .. werden ignoriert
> +    if (entry->name[0] == '.') {
> +        return 0;
> +    }
> +
> +    return 1;
> +}
> +
> +int fat_dir_read(struct fat_dir* dir, struct fat_dir_entry* entry)
> +{
> +    if (dir->i < 0 || dir->i >= dir->num) {
> +        return -1;
> +    }
> +
> +    // Ungueltige Eintraege ueberspringen
> +    while (dir->i < dir->num && !is_valid_entry(&dir->entries[dir->i])) {
> +        dir->i++;
> +    }
> +
> +    // Fertig?
> +    if (dir->i >= dir->num) {
> +        return -1;
> +    }
> +
> +    short_fn_to_string(&dir->entries[dir->i], entry->name);
> +
> +    entry->first_cluster = dir->entries[dir->i].first_cluster;
> +    entry->size = dir->entries[dir->i].size;
> +    entry->attrib = dir->entries[dir->i].attrib;
> +
> +    dir->i++;
> +    return 0;
> +}
> +
> +int fat_dir_close(struct fat_dir* dir)
> +{
> +    free(dir->entries);
> +    return 0;
> +}
> diff --git a/fat/libfat/file.c b/fat/libfat/file.c
> new file mode 100644
> index 0000000..677fb2d
> --- /dev/null
> +++ b/fat/libfat/file.c
> @@ -0,0 +1,360 @@
> +#include <string.h>
> +#include <assert.h>
> +
> +#include "fat.h"
> +
> +#define NUM_CLUSTERS(x, cluster_size) \
> +    (((x) + (cluster_size) - 1) / (cluster_size))
> +#define SECTOR_BITS (512 * 8)
> +
> +#define MIN(a,b) ((a) < (b) ? (a) : (b))
> +
> +// FIXME Sektorgroesse != 512
> +
> +static uint8_t* get_fat_buffer(struct fat_fs* fs, uint32_t sector)
> +{
> +    static uint8_t buf[1024];
> +    static struct fat_fs* old_fs = NULL;
> +    static uint32_t old_sector = 0;
> +
> +    if (fs == old_fs && sector == old_sector) {
> +        return buf;
> +    }
> +
> +    /* TODO Evtl. reicht memcpy und lesen nur eines Sektors */
> +    if (!fs->dev_read(sector * 512, 1024, buf, fs->dev_private)) {
> +        old_fs = NULL;
> +        return NULL;
> +    }
> +
> +    old_fs = fs;
> +    old_sector = sector;
> +
> +    return buf;
> +}
> +
> +static uint32_t get_next_cluster_fat12(struct fat_fs* fs, uint32_t cluster)
> +{
> +    uint8_t* buf;
> +    uint32_t fat_bit_offset = (cluster * 12ULL);
> +    uint32_t fat_sector = fat_bit_offset / SECTOR_BITS;
> +    uint32_t value;
> +
> +    fat_sector += fs->bpb.reserved_sectors;
> +    fat_bit_offset %= SECTOR_BITS;
> +
> +    buf = get_fat_buffer(fs, fat_sector);
> +    if (buf == NULL) {
> +        return 0;
> +    }
> +
> +    value = *((uint16_t*) &buf[fat_bit_offset / 8]);
> +    if ((fat_bit_offset & 7) == 0) {
> +        value &= 0xFFF;
> +    } else {
> +        value >>= 4;
> +    }
> +
> +    if (value > 0xff5) {
> +        value |= 0xfffff000;
> +    }
> +
> +    return value;
> +}
> +
> +static uint32_t get_next_cluster_fat16(struct fat_fs* fs, uint32_t cluster)
> +{
> +    uint8_t* buf;
> +    uint32_t fat_sector = (cluster * sizeof(uint16_t)) / 512;
> +    uint32_t fat_offset = (cluster * sizeof(uint16_t)) % 512;
> +    uint32_t value;
> +
> +    fat_sector += fs->bpb.reserved_sectors;
> +
> +    buf = get_fat_buffer(fs, fat_sector);
> +    if (buf == NULL) {
> +        return 0;
> +    }
> +
> +    value = *((uint16_t*) &buf[fat_offset]);
> +
> +    if (value > 0xfff5) {
> +        value |= 0xffff0000;
> +    }
> +
> +    return value;
> +}
> +
> +static uint32_t get_next_cluster_fat32(struct fat_fs* fs, uint32_t cluster)
> +{
> +    uint8_t* buf;
> +    uint32_t fat_sector = (cluster * sizeof(uint32_t)) / 512;
> +    uint32_t fat_offset = (cluster * sizeof(uint32_t)) % 512;
> +    uint32_t value;
> +
> +    fat_sector += fs->bpb.reserved_sectors;
> +
> +    buf = get_fat_buffer(fs, fat_sector);
> +    if (buf == NULL) {
> +        return 0;
> +    }
> +
> +    value = *((uint32_t*) &buf[fat_offset]);
> +
> +    if (value > 0xffffff5) {
> +        value |= 0xf0000000;
> +    }
> +
> +    return value;
> +}
> +
> +int fat_file_open_by_dir_entry(struct fat_fs* fs, struct fat_dir_entry* entry,
> +    struct fat_file* file)
> +{
> +    uint32_t cluster_size = fs->bpb.cluster_sectors * 512;
> +    uint32_t cluster;
> +    uint32_t i;
> +
> +    /* Passende Clusterlesefunktion aussuchen */
> +    uint32_t (*get_next_cluster)(struct fat_fs*, uint32_t);
> +    switch (fs->type) {
> +        case FS_TYPE_FAT12:
> +            get_next_cluster = get_next_cluster_fat12;
> +            break;
> +
> +        case FS_TYPE_FAT16:
> +            get_next_cluster = get_next_cluster_fat16;
> +            break;
> +
> +        case FS_TYPE_FAT32:
> +            get_next_cluster = get_next_cluster_fat32;
> +            break;
> +
> +        default:
> +            return -FAT_EINVAL;
> +    }
> +
> +    /* Dateistruktur anlegen */
> +    file->fs = fs;
> +    file->size = entry->size;
> +
> +    /*
> +     * Blockliste anlegen und einlesen. Bei Groesse 0 (z.B. Verzeichnisse gehen
> +     * wir von unbekannter Dateigroesse aus und lassen die Blockliste dynamisch
> +     * wachsen.
> +     */
> +    if (file->size > 0) {
> +        file->blocklist_size = NUM_CLUSTERS(file->size, cluster_size);
> +    } else {
> +        file->blocklist_size = 1;
> +    }
> +
> +    file->blocklist = malloc(file->blocklist_size * sizeof(uint32_t));
> +    file->blocklist[0] = entry->first_cluster;
> +
> +    cluster = 0;
> +    i = 1;
> +    while (1) {
> +        cluster = get_next_cluster(fs, file->blocklist[i - 1]);
> +
> +        /* Fehler und Dateiende */
> +        if (cluster == 0) {
> +            goto fail;
> +        } else if (cluster >= 0xfffffff8) {
> +            break;
> +        }
> +
> +        /* Blocklist vergroessern, falls noetig */
> +        if (file->blocklist_size <= i) {
> +            file->blocklist_size *= 2;
> +            file->blocklist = realloc(file->blocklist,
> +                file->blocklist_size * sizeof(uint32_t));
> +        }
> +
> +        /* In die Blockliste eintragen */
> +        file->blocklist[i] = cluster;
> +
> +        i++;
> +    }
> +
> +    /* Dateigroesse anpassen, wenn sie unbekannt war */
> +    if (file->size == 0) {
> +        file->size = i * cluster_size;
> +    }
> +
> +    return 0;
> +
> +fail:
> +    free(file->blocklist);
> +    return -FAT_EIO;
> +}
> +
> +int fat_file_open(struct fat_fs* fs, const char* path, struct fat_file* file)
> +{
> +    struct fat_dir dir;
> +    struct fat_dir_entry dentry;
> +    int ret;
> +    char* p;
> +
> +    /* Letzten Pfadtrenner suchen */
> +    p = strrchr(path, '/');
> +    if (p == NULL) {
> +        return -FAT_EINVAL;
> +    }
> +
> +    /* Verzeichnis oeffnen */
> +    int len = p - path + 1;
> +    char dirname[len + 1];
> +
> +    memcpy(dirname, path, len);
> +    dirname[len] = '\0';
> +
> +    ret = fat_dir_open(fs, dirname, &dir);
> +    if (ret< 0) {
> +        return ret;
> +    }
> +
> +    /* Alles vor dem Dateinamen wegwerfen */
> +    path = p + 1;
> +
> +    /* Verzeichnis nach Dateinamen durchsuchen */
> +    do {
> +        ret = fat_dir_read(&dir, &dentry);
> +        if (ret >= 0 && !strcmp(dentry.name, path)) {
> +            break;
> +        }
> +    } while (ret >= 0);
> +
> +    /* Datei oeffnen oder -ENOENT zurueckgeben, wenn sie nicht existiert */
> +    if (ret < 0) {
> +        return -FAT_ENOENT;
> +    }
> +
> +    /* Verzeichnis wieder schliessen */
> +    ret = fat_dir_close(&dir);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    return fat_file_open_by_dir_entry(fs, &dentry, file);
> +}
> +
> +static uint32_t file_get_cluster(struct fat_file* file, uint32_t file_cluster)
> +{
> +    if (file_cluster >= file->blocklist_size) {
> +        return 0;
> +    }
> +
> +    return file->blocklist[file_cluster];
> +}
> +
> +static int64_t get_data_cluster_offset(struct fat_fs* fs, uint32_t cluster_idx)
> +{
> +    uint32_t sector = fs->bpb.cluster_sectors * (cluster_idx - 2);
> +
> +    return (fs->first_data_cluster + sector) * 512;
> +}
> +
> +static int read_cluster(struct fat_fs* fs, void* buf, uint32_t cluster_idx)
> +{
> +    uint32_t cluster_size = fs->bpb.cluster_sectors * 512;
> +    uint32_t cluster_offset = get_data_cluster_offset(fs, cluster_idx);
> +
> +    if (!fs->dev_read(cluster_offset, cluster_size, buf, fs->dev_private)) {
> +        return -FAT_EIO;
> +    }
> +
> +    return 0;
> +}
> +
> +static int read_cluster_part(struct fat_fs* fs, void* buf,
> +    uint32_t cluster_idx,  size_t offset, size_t len)
> +{
> +    uint32_t cluster_size = fs->bpb.cluster_sectors * 512;
> +    uint8_t cluster[cluster_size];
> +    int ret;
> +
> +    assert (offset + len <= cluster_size);
> +
> +    ret = read_cluster(fs, cluster, cluster_idx);
> +    if (ret < 0) {
> +        return ret;
> +    }
> +
> +    memcpy(buf, cluster + offset, len);
> +    return 0;
> +}
> +
> +int32_t fat_file_read(struct fat_file* file, void* buf, uint32_t offset,
> +    size_t len)
> +{
> +    struct fat_fs* fs = file->fs;
> +    uint32_t cluster_size = fs->bpb.cluster_sectors * 512;
> +    uint8_t* p = buf;
> +    size_t n = len;
> +    int ret;
> +    uint32_t file_cluster = offset / cluster_size;
> +    uint32_t disk_cluster;
> +    uint32_t offset_in_cluster;
> +
> +    /* Bei Ueberlaenge Fehler zurueckgeben */
> +    if (offset + len > file->size) {
> +        return -FAT_EIO;
> +    }
> +
> +    /* Angefangener Cluster am Anfang */
> +    offset_in_cluster = offset & (cluster_size - 1);
> +    if (offset_in_cluster) {
> +        uint32_t count = MIN(n, cluster_size - offset_in_cluster);
> +
> +        disk_cluster = file_get_cluster(file, file_cluster++);
> +        if (disk_cluster == 0) {
> +            return -FAT_EIO;
> +        }
> +
> +        ret = read_cluster_part(fs, p, disk_cluster, offset_in_cluster, count);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +
> +        p += count;
> +        n -= count;
> +    }
> +
> +    /* Eine beliebige Anzahl von ganzen Clustern in der Mitte */
> +    while (n >= cluster_size) {
> +        disk_cluster = file_get_cluster(file, file_cluster++);
> +        if (disk_cluster == 0) {
> +            return -FAT_EIO;
> +        }
> +
> +        ret = read_cluster(fs, p, disk_cluster);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +
> +        p += cluster_size;
> +        n -= cluster_size;
> +    }
> +
> +    /* Und wieder ein angefangener Cluster am Ende */
> +    if (n) {
> +        disk_cluster = file_get_cluster(file, file_cluster);
> +        if (disk_cluster == 0) {
> +            return -FAT_EIO;
> +        }
> +
> +        ret = read_cluster_part(fs, p, disk_cluster, 0, n);
> +        if (ret < 0) {
> +            return ret;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +int fat_file_close(struct fat_file* file)
> +{
> +    free(file->blocklist);
> +    return 0;
> +}
> diff --git a/fat/libfat/fs.c b/fat/libfat/fs.c
> new file mode 100644
> index 0000000..5fcf583
> --- /dev/null
> +++ b/fat/libfat/fs.c
> @@ -0,0 +1,80 @@
> +#include <stdio.h>
> +#include "fat.h"
> +
> +/**
> + * fat-Dateisystem einbinden. Dafuer muessen dev_read, dev_write und
> + * dev_private (falls notwendig) initialisiert sein.
> + *
> + * @return 1 bei Erfolg, im Fehlerfall 0
> + */
> +int fat_fs_mount(struct fat_fs* fs)
> +{
> +    // BPB einlesen
> +    if (!fs->dev_read(0, 512, &fs->bpb, fs->dev_private)) {
> +        return 0;
> +    }
> +
> +    // Magic pruefen
> +    if (fs->bpb.magic != 0xaa55) {
> +        return 0;
> +    }
> +
> +    // FAT-Variante bestimmen
> +    uint32_t total_sectors;
> +    uint32_t rootdir_sectors;
> +    uint32_t data_sectors;
> +    uint32_t data_clusters;
> +
> +    if (fs->bpb.total_sectors != 0) {
> +        total_sectors = fs->bpb.total_sectors;
> +    } else {
> +        // TODO Hier ist auf jeden Fall FAT32, egal wie viele Cluster
> +        total_sectors = fs->bpb.total_sectors_large;
> +    }
> +
> +    rootdir_sectors =
> +        (fs->bpb.rootdir_length * sizeof(struct fat_disk_dir_entry) + 511)
> +        / 512;
> +
> +    // TODO fat_sectors ungueltig fuer FAT32
> +    data_sectors = total_sectors
> +        - fs->bpb.reserved_sectors
> +        - (fs->bpb.num_fats * fs->bpb.fat_sectors)
> +        - rootdir_sectors;
> +    data_clusters = data_sectors / fs->bpb.cluster_sectors;
> +
> +    if (data_clusters < 4085) {
> +        fs->type = FS_TYPE_FAT12;
> +    } else if (data_clusters < 65525) {
> +        fs->type = FS_TYPE_FAT16;
> +    } else {
> +        fs->type = FS_TYPE_FAT32;
> +        data_sectors -=
> +            (fs->bpb.num_fats * fs->bpb.extended.fat32.fat_sectors);
> +    }
> +
> +
> +    fs->total_sectors = total_sectors;
> +    fs->first_data_cluster = total_sectors - data_sectors;
> +
> +    return 1;
> +}
> +
> +/**
> + * fat-Dateisystem aushaengen. Dabei werden saemtliche gecachten Daten auf die
> + * Platte geschrieben. Die Member sb und cache_handle in der fs-Struktur sind
> + * danach NULL.
> + *
> + * @return 1 bei Erfolg, im Fehlerfall 0
> + */
> +int fat_fs_unmount(struct fat_fs* fs)
> +{
> +    return 0;
> +}
> +
> +/**
> + * Saetmliche gecachten Daten auf die Platte schreiben
> + */
> +void fat_fs_sync(struct fat_fs* fs)
> +{
> +}
> diff --git a/fat/libfat/include/fat.h b/fat/libfat/include/fat.h
> new file mode 100644
> index 0000000..6acac1e
> --- /dev/null
> +++ b/fat/libfat/include/fat.h
> @@ -0,0 +1,273 @@
> +#ifndef _FAT_H_
> +#define _FAT_H_
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#ifdef CDI_HAS_ERRNO
> +
> +#include <errno.h>
> +#define FAT_ENOENT  ENOENT
> +#define FAT_EIO     EIO
> +#define FAT_EINVAL  EINVAL
> +
> +#else
> +
> +#define FAT_ENOENT  1
> +#define FAT_EIO     2
> +#define FAT_EINVAL  3
> +
> +#endif
> +
> +// TODO Nur fuer Debugging - spaeter entfernen
> +#include <stdio.h>
> +
> +struct fat32_extended_bpb {
> +    uint32_t    fat_sectors;
> +    uint16_t    flags;
> +    uint16_t    version;
> +    uint32_t    rootdir_cluster;
> +} __attribute__((packed));
> +
> +/**
> + * BIOS Parameter Block
> + */
> +struct fat_bpb {
> +    uint8_t     jmp_code[3];
> +    char        os_name[8];
> +    uint16_t    sector_size;
> +    uint8_t     cluster_sectors;
> +    uint16_t    reserved_sectors;
> +    uint8_t     num_fats;
> +    uint16_t    rootdir_length;
> +    uint16_t    total_sectors;
> +    uint8_t     media_type;
> +
> +    /* Nur fuer FAT12/16; FAT32 benutzt fat32_extended_bpb.fat_sectors */
> +    uint16_t    fat_sectors;
> +
> +    uint16_t    sectors_per_track;
> +    uint16_t    heads;
> +    uint32_t    hidden_sectors;
> +    uint32_t    total_sectors_large;
> +    union {
> +        struct fat32_extended_bpb   fat32;
> +        uint8_t                     bytes[474];
> +    } extended;
> +    uint16_t    magic;
> +} __attribute__((packed));
> +
> +enum fat_type {
> +    FS_TYPE_FAT12,
> +    FS_TYPE_FAT16,
> +    FS_TYPE_FAT32,
> +};
> +
> +/**
> + * Zentrale Struktur zur Verwaltung eines eingebundenen Dateisystemes
> + */
> +struct fat_fs {
> +    /**
> +     * Funktionspointer der von Aufrufer gesetzt werden muss. Diese Funktion
> +     * liest Daten vom Datentraeger ein, auf dem sich das Dateisystem befindet.
> +     *
> +     * @param start Position von der an gelesen werden soll
> +     * @param size  Anzahl der zu lesenden Bytes
> +     * @param dest  Pointer auf den Puffer in dem die Daten abgelegt werden
> +     *              sollen
> +     * @param prv   Private Daten zum Zugriff auf den Datentraeger (z.B.
> +     *              Dateideskriptor), aus dev_private zu entnehmen
> +     *
> +     * @return 1 wenn das Lesen geklappt hat, 0 sonst
> +     */
> +    int (*dev_read)(uint64_t start, size_t size, void* dest, void* prv);
> +
> +    /**
> +     * Funktionspointer der von Aufrufer gesetzt werden muss. Diese Funktion
> +     * schreibt Daten auf den Datentraeger, auf dem sich das Dateisystem
> +     * befindet.
> +     *
> +     * @param start Position von der an gelesen werden soll
> +     * @param size  Anzahl der zu lesenden Bytes
> +     * @param dest  Pointer auf den Puffer, aus dem die Daten gelesen werden
> +     *              sollen.
> +     * @param prv   Private Daten zum Zugriff auf den Datentraeger (z.B.
> +     *              Dateideskriptor), aus dev_private zu entnehmen
> +     *
> +     * @return 1 wenn das Schreiben geklappt hat, 0 sonst
> +     */
> +    int (*dev_write)(uint64_t start, size_t size, const void* source,
> +        void* prv);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * erstellt einen neuen Cache und gibt ein Handle darauf zurueck.
> +     *
> +     * @param fs            Pointer auf das Dateisystem fuer das der Cache
> +     *                      erstellt werden soll.
> +     * @param block_size    Blockgroesse im Cache
> +     *
> +     * @return Handle oder NULL im Fehlerfall
> +     */
> +    void* (*cache_create)(struct fat_fs* fs, size_t block_size);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * zerstoert einen mit cache_create erstellten Cache
> +     *
> +     * @param cache Handle
> +     */
> +    void (*cache_destroy)(void* handle);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * schreibt alle veraenderten Cache-Blocks auf die Platte
> +     *
> +     * @param cache Handle
> +     */
> +    void (*cache_sync)(void* handle);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * holt einen Block aus dem Cache und gibt einen Pointer auf ein
> +     * Block-Handle zrueck. Dieser Pointer ist mindestens solange gueltig, wie
> +     * das Handle nicht freigegeben wird.
> +     *
> +     * @param cache     Cache-Handle
> +     * @param block     Blocknummer
> +     * @param noread    Wenn dieser Parameter != 0 ist, wird der Block nicht
> +     *                  eingelesen. Das kann benutzt werden, wenn er eh
> +     *                  vollstaendig ueberschrieben wird.
> +     *
> +     * @return Block-Handle
> +     */
> +    struct fat_cache_block* (*cache_block)(void* cache, uint64_t block,
> +        int noread);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * markiert den angegebenen Block als veraendert, damit er geschrieben wird,
> +     * bevor er aus dem Cache entfernt wird.
> +     *
> +     * @param handle Block-Handle
> +     */
> +    void (*cache_block_dirty)(struct fat_cache_block* handle);
> +
> +    /**
> +     * Funktionspointer der vom Aufrufer gesetzt werden muss. Diese Funktion
> +     * gibt einen mit cache_block alloziierten Block wieder frei.
> +     *
> +     * @param handle    Block-Handle
> +     * @param dirty     Wenn != 0 wird der Block auch als Dirty markiert
> +     */
> +    void (*cache_block_free)(struct fat_cache_block* handle, int dirty);
> +
> +
> +    /// Private Daten zum Zugriff auf den Datentraeger
> +    void* dev_private;
> +
> +    /// Handle fuer Blockcache
> +    void* cache_handle;
> +
> +    /// Darf vom Aufrufer benutzt werden
> +    void* opaque;
> +
> +    /// BIOS Parameter Block
> +    struct fat_bpb bpb;
> +
> +    enum fat_type type;
> +    uint32_t total_sectors;
> +    uint32_t first_data_cluster;
> +};
> +
> +/**
> + * Cache-Block-Handle
> + */
> +struct fat_cache_block {
> +    /// Blocknummer
> +    uint64_t number;
> +
> +    /// Daten
> +    void* data;
> +
> +    /// Cache-Handle
> +    void* cache;
> +
> +    /// Daten fuer die Implementierung
> +    void* opaque;
> +};
> +
> +/**
> + * fat-Dateisystem einbinden. Dafuer muessen dev_read, dev_write und
> + * dev_private (falls notwendig) initialisiert sein.
> + *
> + * @return 1 bei Erfolg, im Fehlerfall 0
> + */
> +int fat_fs_mount(struct fat_fs* fs);
> +
> +/**
> + * fat-Dateisystem aushaengen. Dabei werden saemtliche gecachten Daten auf die
> + * Platte geschrieben. Die Member sb und cache_handle in der fs-Struktur sind
> + * danach NULL.
> + *
> + * @return 1 bei Erfolg, im Fehlerfall 0
> + */
> +int fat_fs_unmount(struct fat_fs* fs);
> +
> +/**
> + * Saetmliche gecachten Daten auf die Platte schreiben
> + */
> +void fat_fs_sync(struct fat_fs* fs);
> +
> +#define FAT_ATTRIB_READONLY 0x01
> +#define FAT_ATTRIB_HIDDEN   0x02
> +#define FAT_ATTRIB_SYSTEM   0x04
> +#define FAT_ATTRIB_VOLUME   0x08
> +#define FAT_ATTRIB_DIR      0x10
> +#define FAT_ATTRIB_ARCHIVE  0x20
> +
> +struct fat_disk_dir_entry {
> +    char name[8];
> +    char ext[3];
> +    uint8_t attrib;
> +    uint8_t data[14];
> +    uint16_t first_cluster;
> +    uint32_t size;
> +} __attribute__((packed));
> +
> +
> +struct fat_dir_entry {
> +    char name[13]; /* 8.3 plus \0 */
> +    int first_cluster;
> +    int attrib;
> +    uint32_t size;
> +};
> +
> +struct fat_file;
> +
> +struct fat_dir {
> +    struct fat_fs* fs;
> +    struct fat_disk_dir_entry* entries;
> +    int i;
> +    int num;
> +};
> +
> +int fat_dir_open(struct fat_fs* fs, const char* path, struct fat_dir* dir);
> +int fat_dir_read(struct fat_dir* dir, struct fat_dir_entry* entry);
> +int fat_dir_close(struct fat_dir* dir);
> +
> +struct fat_file {
> +    struct fat_fs* fs;
> +    uint32_t size;
> +    size_t blocklist_size;
> +    uint32_t* blocklist;
> +};
> +
> +int fat_file_open(struct fat_fs* fs, const char* path, struct fat_file* file);
> +int fat_file_open_by_dir_entry(struct fat_fs* fs, struct fat_dir_entry* entry,
> +    struct fat_file* file);
> +int32_t fat_file_read(struct fat_file* file, void *buf, uint32_t offset,
> +    size_t len);
> +int fat_file_close(struct fat_file* file);
> +
> +#endif
> diff --git a/fat/main.c b/fat/main.c
> new file mode 100644
> index 0000000..8290c41
> --- /dev/null
> +++ b/fat/main.c
> @@ -0,0 +1,334 @@
> +/*
> + * Copyright (c) 2010 The tyndur Project. All rights reserved.
> + *
> + * This code is derived from software contributed to the tyndur Project
> + * by Kevin Wolf.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> + * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
> + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <cdi.h>
> +#include <cdi/fs.h>
> +
> +#include "fat.h"
> +
> +#define DRIVER_NAME "fat"
> +
> +struct fat_res {
> +    struct cdi_fs_res       cdi;
> +    char*                   path; /* TODO Remove this */
> +    struct fat_dir_entry    dir_entry;
> +    struct fat_file         file;
> +};
> +
> +static int fat_res_load(struct cdi_fs_stream* s);
> +static int64_t fat_res_meta_read(struct cdi_fs_stream* s, cdi_fs_meta_t meta);
> +static cdi_list_t fat_dir_list(struct cdi_fs_stream* s);
> +static size_t fat_file_op_read(struct cdi_fs_stream* s, uint64_t start,
> +    size_t size, void* buffer);
> +
> +static struct cdi_fs_driver fat_driver;
> +
> +static struct cdi_fs_res_res fat_res_ops = {
> +    .load = fat_res_load,
> +    .meta_read = fat_res_meta_read,
> +};
> +
> +static struct cdi_fs_res_dir fat_dir_ops = {
> +    .list = fat_dir_list,
> +};
> +
> +static struct cdi_fs_res_file fat_file_ops = {
> +    .read = fat_file_op_read,
> +};
> +
> +static int fat_data_read(uint64_t start, size_t size, void* dest, void* prv)
> +{
> +    int ret;
> +    ret = cdi_fs_data_read(prv, start, size, dest);
> +    return ret == size;
> +}
> +
> +static int fat_data_write(uint64_t start, size_t size, const void* source,
> +    void* prv)
> +{
> +    int ret;
> +    ret = cdi_fs_data_write(prv, start, size, source);
> +    return ret == size;
> +}
> +
> +/**
> + * Initialisiert die Datenstrukturen fuer den fat-Treiber
> + */
> +static int fat_driver_init(void)
> +{
> +    cdi_fs_driver_init(&fat_driver);
> +    return 0;
> +}
> +
> +/**
> + * Deinitialisiert die Datenstrukturen fuer den fat-Treiber
> + */
> +static int fat_driver_destroy(void)
> +{
> +    cdi_fs_driver_destroy(&fat_driver);
> +    return 0;
> +}
> +
> +/**
> + * Neues Dateisystem initialisieren; Diese Funktion muss das root_object in
> + * der Dateisystemstruktur eintragen.
> + *
> + * @return Wenn das Dateisystem erfolgreich initialisiert wurde 1, sonst
> + *         0. Falls ein Fehler auftritt, muss das error-Feld in der
> + *         Dateisystemstruktur gesetzt werden.
> + */
> +static int fat_fs_init(struct cdi_fs_filesystem* fs)
> +{
> +    struct fat_res* root;
> +    struct fat_fs* fat_fs;
> +
> +    /* Initialize root node */
> +    root = malloc(sizeof(*root));
> +    *root = (struct fat_res) {
> +        .cdi = {
> +            .name = "/",
> +            .res = &fat_res_ops,
> +        },
> +        .path = "/",
> +        .dir_entry = {
> +            .attrib = FAT_ATTRIB_DIR,
> +        },
> +    };
> +
> +    /* Mount file system */
> +    fat_fs = malloc(sizeof(*fat_fs));
> +    fat_fs->dev_read    = fat_data_read;
> +    fat_fs->dev_write   = fat_data_write;
> +    fat_fs->dev_private = fs;
> +
> +    if (!fat_fs_mount(fat_fs)) {
> +        goto fail;
> +    }
> +
> +    /* Initialize fs structure */
> +    fs->root_res = &root->cdi;
> +    fs->opaque = fat_fs;
> +
> +    return 1;
> +
> +fail:
> +    free(root);
> +    free(fat_fs);
> +    return 0;
> +}
> +
> +/**
> + * Dateisystem deinitialisieren
> + *
> + * @return Wenn das Dateisystem erfolgreich deinitialisiert wurde 1, 0
> + *         sonst. Falls ein Fehler auftritt, muss das error-Feld in der
> + *         Dateisystemstruktur gesetzt werden.
> + */
> +static int fat_fs_destroy(struct cdi_fs_filesystem* fs)
> +{
> +    return 1;
> +}
> +
> +static int fat_res_load_dir(struct fat_fs* fs, struct fat_res* res)
> +{
> +    struct fat_dir dir;
> +    struct fat_dir_entry dentry;
> +    struct fat_res* child;
> +    int ret;
> +
> +    res->cdi.children = cdi_list_create();
> +
> +    ret = fat_dir_open(fs, res->path, &dir);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +
> +    while(1) {
> +        ret = fat_dir_read(&dir, &dentry);
> +        if (ret < 0) {
> +            break;
> +        }
> +
> +        child = calloc(1, sizeof(*child));
> +        child->cdi.res = &fat_res_ops;
> +        child->cdi.name = strdup(dentry.name);
> +        asprintf(&child->path, "%s/%s", res->path, dentry.name);
> +        child->dir_entry = dentry;
> +
> +        cdi_list_push(res->cdi.children, child);
> +    }
> +
> +    fat_dir_close(&dir);
> +
> +    res->cdi.dir = &fat_dir_ops;
> +    res->cdi.flags.browse = 1;
> +
> +    return 1;
> +
> +fail:
> +    cdi_list_destroy(res->cdi.children);
> +    return 0;
> +}
> +
> +static int fat_res_load_file(struct fat_fs* fs, struct fat_res* res)
> +{
> +    int ret;
> +
> +    ret  = fat_file_open_by_dir_entry(fs, &res->dir_entry, &res->file);
> +    if (ret < 0) {
> +        return 0;
> +    }
> +
> +    res->cdi.file = &fat_file_ops;
> +    res->cdi.flags.read = 1;
> +
> +    return 1;
> +}
> +
> +/**
> + * Ressource laden
> + *
> + * @param stream Stream
> + *
> + * @return Falls die Ressource erfolgreich geladen wurde 1, sonst 0
> + */
> +static int fat_res_load(struct cdi_fs_stream* s)
> +{
> +    struct fat_res* res = (struct fat_res*) s->res;
> +    int ret;
> +
> +    if (res->dir_entry.attrib & FAT_ATTRIB_DIR) {
> +        ret = fat_res_load_dir(s->fs->opaque, res);
> +    } else {
> +        ret = fat_res_load_file(s->fs->opaque, res);
> +    }
> +
> +    if (!ret) {
> +        goto fail;
> +    }
> +
> +    s->res->loaded = 1;
> +
> +    return 1;
> +
> +fail:
> +    return 0;
> +}
> +
> +/**
> + * Metaeigenschaft lesen
> + *
> + * @param stream Stream
> + * @param meta Konstante fuer die gewuenschte Metaeigenschaft
> + *
> + * @return Wert der Metaeigenschaft
> + */
> +static int64_t fat_res_meta_read(struct cdi_fs_stream* s, cdi_fs_meta_t meta)
> +{
> +    struct fat_res* res = (struct fat_res*) s->res;
> +    struct fat_fs* fs = s->fs->opaque;
> +
> +    switch (meta) {
> +        case CDI_FS_META_SIZE:
> +            return res->dir_entry.size;
> +
> +        case CDI_FS_META_BLOCKSZ:
> +        case CDI_FS_META_BESTBLOCKSZ:
> +            return fs->bpb.sector_size * fs->bpb.cluster_sectors;
> +
> +        default:
> +            return -1;
> +    }
> +}
> +
> +/**
> + * Diese Funktion gibt einen Pointer auf die Liste mit den Eintraegen
> + * zurueck. Hier wird nicht einfach fix der Pointer in fs_res 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.
> + * Diese Liste muss nicht vom Aufrufer freigegeben werden, da einige
> + * Treiber hier direkt children aus fs_res benutzen, und andere dafuer eine
> + * eigene Liste erstellen, die sie intern abspeichern.
> + *
> + * @param stream Stream
> + *
> + * @return Pointer auf eine Liste mit den Untereintraegen vom Typ
> + * cdi_fs_res.
> + */
> +static cdi_list_t fat_dir_list(struct cdi_fs_stream* s)
> +{
> +    return s->res->children;
> +}
> +
> +/**
> + * 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
> + */
> +static size_t fat_file_op_read(struct cdi_fs_stream* s, uint64_t start,
> +    size_t size, void* buffer)
> +{
> +    struct fat_res* res = (struct fat_res*) s->res;
> +    int32_t ret;
> +
> +    ret = fat_file_read(&res->file, buffer, start, size);
> +    if (ret < 0) {
> +        /* TODO Set error */
> +        return 0;
> +    } else {
> +        return size;
> +    }
> +}
> +
> +
> +static struct cdi_fs_driver fat_driver = {
> +    .drv = {
> +        .type           = CDI_FILESYSTEM,
> +        .name           = DRIVER_NAME,
> +        .init           = fat_driver_init,
> +        .destroy        = fat_driver_destroy,
> +    },
> +    .fs_init            = fat_fs_init,
> +    .fs_destroy         = fat_fs_destroy,
> +};
> +
> +CDI_DRIVER(DRIVER_NAME, fat_driver)
> -- 
> 1.6.0.2
> 
> _______________________________________________
> cdi-devel mailing list
> cdi-devel@xxxxxxxxxx
> http://list.tyndur.org/cgi-bin/mailman/listinfo/cdi-devel

Looks good.

Acked-By: Matthew Iselin <matthew@xxxxxxxxxxxxxx>