1/*
2 * Copyright (c) 2009, 2011, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <string.h>
11#include <barrelfish/types.h>
12#include <barrelfish/barrelfish.h>
13#include <barrelfish/nameservice_client.h>
14#include <vfs/vfs_path.h>
15#include <errors/errno.h>
16#include <dev/fat_bpb_dev.h>
17#include <dev/fat16_ebpb_dev.h>
18#include <dev/fat32_ebpb_dev.h>
19#include <dev/fat_direntry_dev.h>
20#include <if/ata_rw28_defs.h>
21#include <if/ata_rw28_ahci_defs.h>
22#include <if/ata_rw28_defs.h>
23#include <ahci/ahci.h>
24#include "vfs_fat_conv.h"
25#include "vfs_backends.h"
26#include "vfs_ops.h"
27#include "vfs_cache.h"
28
29//#define FAT_DEBUG 1
30#if defined(FAT_DEBUG) || defined(VFS_DEBUG) || defined(GLOBAL_DEBUG)
31#   define FAT_DEBUG_ENABLED
32
33#   ifdef FAT_DEBUG
34#       undef FAT_DEBUG
35#   endif
36
37#   define FAT_DEBUG_P() printf("fat:%s: ", __func__)
38#   define FAT_DEBUG(s) \
39        printf("fat:%s: " s "\n", __func__)
40#   define FAT_DEBUG_F(s, x...) \
41        printf("fat:%s: " s "\n", __func__, x)
42#   define TRACE_ENTER \
43        printf("fat: entering %s\n", __func__)
44#   define TRACE_ENTER_F(s, x...) \
45        printf("fat: entering %s: " s "\n", __func__, x)
46
47#else
48
49#   ifdef FAT_DEBUG_ENABLED
50#       undef FAT_DEBUG_ENABLED
51#   endif
52
53#   define FAT_DEBUG_P() ((void)0)
54#   define FAT_DEBUG(s) ((void)0)
55#   define FAT_DEBUG_F(s, x...) ((void)0)
56#   define TRACE_ENTER ((void)0)
57#   define TRACE_ENTER_F(s, x...) ((void)0)
58
59#endif
60
61#define DUMP_DEV(dev_t, dev_p, buf_s) do { \
62    char dump_dev_buf__[(buf_s)]; \
63    dev_t ## _pr(dump_dev_buf__, (buf_s)-1, (dev_p)); \
64    dump_dev_buf__[(buf_s)-1] = 0; \
65    printf("%s\n", dump_dev_buf__); \
66} while(0)
67
68#ifndef CEIL_DIV
69#define CEIL_DIV(x, d) (((x) + ((d)-1)) / (d))
70#endif
71
72#define fat_direntry_size 32
73
74#define cluster_for_offset(offset, mount) \
75    ((offset) / ((mount)->block_size * (mount)->cluster_size))
76
77#define offset_from_cluster(offset, mount) \
78    ((offset) % ((mount)->block_size * (mount)->cluster_size))
79
80#define cluster_to_block(cluster, mount) \
81    ((mount)->clusters_start + (((cluster) - 2) * (mount)->cluster_size))
82
83#define cluster_entry_size(mount) \
84    ((mount)->fat_type == FAT_TYPE_FAT16 ? sizeof(uint16_t) : sizeof(uint32_t))
85
86#define fat_block_for_cluster(cluster, mount) \
87    ((cluster) * cluster_entry_size(mount) / (mount)->block_size)
88
89#define fat_offset_for_cluster(cluster, mount) \
90    ((cluster) * cluster_entry_size(mount) % (mount)->block_size)
91
92// NOTE: specification says max 255 chars, but format in principle seems to
93// allow 260, so be on the safe side
94#define LFN_CHAR_COUNT 260
95#define LFN_MAX_LENGTH 255
96#define DOSFN_MAX_LEN_UTF8 40
97
98#define ASCII_MAX 127
99
100struct fat_dirsearch {
101    size_t index;
102    fat_direntry_t *parent_direntry;
103    uint8_t *data;
104    size_t data_index;
105    size_t data_key;
106};
107
108struct fat_handle_common {
109    struct vfs_handle common;
110    uint8_t dirent_data[fat_direntry_size];
111    fat_direntry_t dirent;
112};
113
114struct fat_handle {
115    struct fat_handle_common h;
116    size_t offset;
117};
118
119struct fat_dirhandle {
120    struct fat_handle_common h;
121    struct fat_dirsearch search;
122};
123
124enum {
125    FAT_TYPE_FAT16 = 16,
126    FAT_TYPE_FAT32 = 32,
127};
128
129struct fat_mount {
130    struct ata_rw28_binding *ata_rw28_binding;
131    struct ahci_binding *ahci_binding;
132    errval_t bind_err;
133
134    int fat_type;
135    size_t startblock;
136    uint8_t bootsec_data[512];
137    fat_bpb_t bpb;
138    union {
139        fat16_ebpb_t f16;
140        fat32_ebpb_t f32;
141    } ebpb;
142
143    struct fs_cache *block_cache;
144    struct fs_cache *cluster_cache;
145
146    size_t block_count;
147    size_t block_size; // bytes
148    size_t cluster_size; // blocks
149    size_t fat_start; // blocks
150    size_t fat_size; // blocks
151    size_t rootdir_start; // blocks. 0 implies rootdir in clusters
152    size_t rootdir_cluster;
153    size_t clusters_start; // blocks
154    uint32_t last_cluster_start; // cluster
155};
156
157static errval_t
158acquire_or_read(struct fat_mount *mount, struct fs_cache *cache,
159        uint32_t idx, uint8_t **data, size_t block, size_t size)
160{
161    TRACE_ENTER_F("idx=%"PRIu32", block=%zu, size=%zu", idx, block, size);
162
163    uint8_t *data_;
164    errval_t err;
165
166    err = fs_cache_acquire(cache, idx, (void**)&data_);
167    if (err_is_fail(err) && err != FS_CACHE_NOTPRESENT) {
168        return err;
169    }
170    else if (err == FS_CACHE_NOTPRESENT) {
171        size_t read_size;
172        data_ = malloc(size);
173        err = mount->ata_rw28_binding->rpc_tx_vtbl.read_dma(mount->ata_rw28_binding,
174                size, block, data_, &read_size);
175        if (err_is_fail(err)) {
176            return err;
177        }
178        assert(size == read_size);
179
180        err = fs_cache_put(cache, idx, data_);
181        if (err_is_fail(err)) {
182            return err;
183        }
184    }
185
186    *data = data_;
187    return SYS_ERR_OK;
188}
189
190static errval_t
191acquire_block(struct fat_mount *mount, size_t block, uint8_t **data)
192{
193    return acquire_or_read(mount, mount->block_cache,
194            block, data, block, mount->block_size);
195}
196
197static errval_t
198release_block(struct fat_mount *mount, size_t block)
199{
200    return fs_cache_release(mount->block_cache, block);
201}
202
203static errval_t
204acquire_cluster(struct fat_mount *mount, uint32_t cluster, uint8_t **data)
205{
206    return acquire_or_read(mount, mount->cluster_cache, cluster, data,
207            cluster_to_block(cluster, mount),
208            mount->block_size * mount->cluster_size);
209}
210
211static errval_t
212release_cluster(struct fat_mount *mount, uint32_t cluster)
213{
214    return fs_cache_release(mount->cluster_cache, cluster);
215}
216
217static void
218dirsearch_initialize(struct fat_dirsearch *search, fat_direntry_t *parent)
219{
220    memset(search, 0, sizeof(*search));
221    search->parent_direntry = parent;
222}
223
224static void
225dirsearch_wipe(struct fat_dirsearch *search, struct fat_mount *mount)
226{
227    errval_t err;
228    if (search->data) {
229        if (!search->parent_direntry && mount->fat_type == FAT_TYPE_FAT16) {
230            // the special rootdir area of FAT16 is handled with the block cache
231            err = release_block(mount, search->data_key);
232        }
233        else {
234            err = release_cluster(mount, search->data_key);
235        }
236        if (err_is_fail(err)) {
237            // this should not happen
238            USER_PANIC_ERR(err, "could not release search data cache");
239        }
240    }
241    memset(search, 0, sizeof(*search));
242}
243
244static uint32_t
245next_cluster(struct fat_mount *mount, uint32_t cluster, uint32_t *rescluster)
246{
247    TRACE_ENTER;
248    errval_t err;
249
250    // calculate block and offset
251    size_t cluster_fat_block = fat_block_for_cluster(cluster, mount)+mount->fat_start;
252    size_t cluster_fat_offset = fat_offset_for_cluster(cluster, mount);
253    FAT_DEBUG_F("cluster %"PRIu32" is at fat block %zu + %zu",
254            cluster, cluster_fat_block, cluster_fat_offset);
255
256    // fetch block data
257    uint8_t *data;
258    err = acquire_block(mount, cluster_fat_block, &data);
259    if (err_is_fail(err)) {
260        return err;
261    }
262
263    // lookup cluster in found block
264    uint32_t result;
265    if (mount->fat_type == FAT_TYPE_FAT16) {
266        result = *(uint16_t*)(data+cluster_fat_offset);
267    }
268    else {
269        result = *(uint32_t*)(data+cluster_fat_offset);
270    }
271
272    // release cache ref
273    err = release_block(mount, cluster_fat_block);
274    if (err_is_fail(err)) {
275        return err;
276    }
277
278    FAT_DEBUG_F("next cluster is %"PRIu32, result);
279    *rescluster = result;
280    return SYS_ERR_OK;
281}
282
283static void
284update_lfn(const uint8_t *entry_data, fat_direntry_t *entry,
285        uint16_t lfn_data[LFN_CHAR_COUNT])
286{
287    TRACE_ENTER;
288    uint8_t seq_nr = entry_data[0];
289    FAT_DEBUG_F("updating lfn data from entry with seq_nr 0x%x", (int)seq_nr);
290    if (seq_nr & 0x40) {
291        // first entry, reset lfn_data
292        FAT_DEBUG("first entry, resetting lfn_data");
293        memset(lfn_data, 0, LFN_CHAR_COUNT*sizeof(*lfn_data));
294    }
295    /*
296    if (seq_nr & 0x80) {
297        // entry is deleted
298        printf("entry is deleted");
299        return;
300    }
301    */
302
303    // remove flag bits from sequence number, make 0-based
304    seq_nr = (seq_nr & 0x1f) - 1;
305    static const size_t chars_per_lfn_entry = 13;
306
307    // chars 0-4 are 16-bit words from offset 0x1
308    for (size_t i = 0; i < 5; i++) {
309        uint16_t c = *(uint16_t*)(entry_data + 0x1 + i*2);
310        lfn_data[seq_nr*chars_per_lfn_entry+0+i] = c;
311    }
312    // chars 5-10 are 16-bit words from offset 0xe
313    for (size_t i = 0; i < 6; i++) {
314        uint16_t c = *(uint16_t*)(entry_data + 0xe + i*2);
315        lfn_data[seq_nr*chars_per_lfn_entry+5+i] = c;
316    }
317    // chars 11-12 are 16-bit words from offset 0x1c
318    for (size_t i = 0; i < 2; i++) {
319        uint16_t c = *(uint16_t*)(entry_data + 0x1c + i*2);
320        lfn_data[seq_nr*chars_per_lfn_entry+11+i] = c;
321    }
322}
323
324static errval_t
325next_f16_rootdir_block(struct fat_mount *mount, struct fat_dirsearch *search,
326        uint8_t **data)
327{
328    TRACE_ENTER;
329    errval_t err = SYS_ERR_OK;
330    assert(!search->parent_direntry);
331    assert(mount->fat_type == FAT_TYPE_FAT16);
332
333    // check that index is within number of valid root entries
334    size_t search_end = fat_bpb_rtc_rd(&mount->bpb);
335    if (search->index >= search_end) {
336        return FS_ERR_INDEX_BOUNDS;
337    }
338
339    // calculate block index
340    size_t bytes_offset = search->index * fat_direntry_size;
341    size_t block_index = bytes_offset / mount->block_size;
342    size_t block_offset = bytes_offset % mount->block_size;
343    FAT_DEBUG_F("search index %zu at block %zu + %zu",
344            search->index, block_index, block_offset);
345
346    if (!search->data || search->data_index != block_index) {
347        FAT_DEBUG("need new block data");
348
349        // determine block
350        size_t block = mount->rootdir_start + block_index;
351        FAT_DEBUG_F("file block %zu is block %zu", block_index, block);
352
353        // fetch new block data
354        uint8_t *new_data;
355        err = acquire_block(mount, block, &new_data);
356        if (err_is_fail(err)) {
357            return err;
358        }
359
360        // free old block data
361        if (search->data) {
362            err = release_block(mount, search->data_key);
363            if (err_is_fail(err)) {
364                release_block(mount, block);
365                return err;
366            }
367        }
368
369        search->data = new_data;
370        search->data_index = block_index;
371        search->data_key = block;
372    }
373
374    *data = search->data + block_offset;
375    return SYS_ERR_OK;
376}
377
378static errval_t
379next_subdir_block(struct fat_mount *mount, struct fat_dirsearch *search,
380        uint8_t **data)
381{
382    TRACE_ENTER;
383    errval_t err = SYS_ERR_OK;
384    assert(search->parent_direntry || mount->fat_type == FAT_TYPE_FAT32);
385
386    // calculate block index
387    size_t bytes_offset = search->index * fat_direntry_size;
388    size_t block_index = bytes_offset / mount->block_size;
389    size_t cluster_index = block_index / mount->cluster_size;
390    size_t cluster_offset = bytes_offset %
391        (mount->block_size * mount->cluster_size);
392    FAT_DEBUG_F("search index %zu at cluster %zu + %zu",
393            search->index, cluster_index, cluster_offset);
394
395    if (!search->data || search->data_index != cluster_index) {
396        FAT_DEBUG("need new block data");
397
398        // get start cluster of direntry data
399        uint32_t cluster = 0;
400        if (search->parent_direntry) {
401            if (mount->fat_type == FAT_TYPE_FAT32) {
402                cluster = (uint32_t)fat_direntry_starth_rd(search->parent_direntry) << 16;
403            }
404            cluster += fat_direntry_start_rd(search->parent_direntry);
405        }
406        else {
407            cluster = mount->rootdir_cluster;
408        }
409
410        // determine cluster corresponding to cluster_index
411        for (size_t clsidx = cluster_index;
412            cluster < mount->last_cluster_start && clsidx > 0;
413            clsidx--)
414        {
415            err = next_cluster(mount, cluster, &cluster);
416            if (err_is_fail(err)) {
417                return err;
418            }
419        }
420        FAT_DEBUG_F("dir cluster %zu is cluster %"PRIu32, cluster_index, cluster);
421        if (cluster >= mount->last_cluster_start) {
422            return FS_ERR_INDEX_BOUNDS;
423        }
424
425        // read new cluster data
426        uint8_t *new_data;
427        err = acquire_cluster(mount, cluster, &new_data);
428        if (err_is_fail(err)) {
429            return err;
430        }
431
432        // free old data
433        if (search->data) {
434            err = release_cluster(mount, search->data_key);
435            if (err_is_fail(err)) {
436                USER_PANIC_ERR(err, "could not release dir data cache");
437            }
438        }
439
440        search->data = new_data;
441        search->data_index = cluster_index;
442        search->data_key = cluster;
443    }
444
445    *data = search->data + cluster_offset;
446    return SYS_ERR_OK;
447}
448
449static errval_t
450read_next_direntry(struct fat_mount *mount, struct fat_dirsearch *search,
451        char dosfn[12], uint16_t lfn_data[LFN_CHAR_COUNT], bool *has_lfn,
452        uint8_t direntry_data[fat_direntry_size])
453{
454    TRACE_ENTER;
455    errval_t err = SYS_ERR_OK;
456    bool has_lfn_ = false;
457
458    for ( ; ; search->index++) {
459        FAT_DEBUG_F("checking search index %zu", search->index);
460
461        uint8_t *entry_data = NULL;
462        bool special_rootdir = !search->parent_direntry &&
463            mount->fat_type == FAT_TYPE_FAT16;
464        if (special_rootdir) {
465            err = next_f16_rootdir_block(mount, search, &entry_data);
466        }
467        else {
468            err = next_subdir_block(mount, search, &entry_data);
469        }
470        if (err_is_fail(err)) {
471            return err;
472        }
473
474        fat_direntry_t entry;
475        fat_direntry_initialize(&entry, (char*)entry_data);
476
477        // check for various kinds of non-regular entries
478        if (fat_direntry_fn_rd(&entry, 0) == 0xe5) {
479            // 0xe5 implies deleted, skip
480            FAT_DEBUG("found 0xe5 direntry");
481            has_lfn_ = false;
482            continue;
483        }
484        else if (fat_direntry_attr_rd(&entry) == 0xf) {
485            // (attr == ro | hidden | system | volume) implies long filename
486            FAT_DEBUG("found lfn direntry");
487            if (entry_data[0] & 0x40 && !(entry_data[0] & 0x80)) {
488                // first lfn entry and not deleted implies next real entry has a lfn
489                has_lfn_ = true;
490            }
491            update_lfn(entry_data, &entry, lfn_data);
492            continue;
493        }
494        else if (!entry_data[0]) {
495            // entries with names starting at 0 have never been allocated, so
496            // later entries are unused too
497            FAT_DEBUG("found clean direntry");
498            return FS_ERR_INDEX_BOUNDS;
499        }
500        else if (fat_direntry_start_rd(&entry) == 0 &&
501                !fat_direntry_attr_dir_rdf(&entry)) {
502            // entries with starting cluster 0 should only occur in directory
503            // and lfn entries.  lfn entries are already handled, so something
504            // weird is going on if we get here.
505            FAT_DEBUG("found non-lfn/dir direntry with start cluster 0");
506            continue;
507        }
508
509#ifdef FAT_DEBUG_ENABLED
510        FAT_DEBUG_P();
511        // dump found names
512        printf("8.3 entry: \"");
513        for (uint8_t *p = entry_data; p < entry_data+11; p++) {
514            putchar(*p);
515        }
516        printf("\", lfn entry: ");
517        if (has_lfn_) {
518            putchar('"');
519            for (uint16_t *p = lfn_data; *p && p < lfn_data+LFN_CHAR_COUNT; p++) {
520                putchar(*p > ASCII_MAX ? '?' : (char)*p);
521            }
522            putchar('"');
523        }
524        else {
525            printf("(none)");
526        }
527        putchar('\n');
528#endif
529
530        // at this point, we've found a real entry
531        memcpy(dosfn, entry_data, 11);
532        dosfn[11] = 0;
533        *has_lfn = has_lfn_;
534        memcpy(direntry_data, entry_data, fat_direntry_size);
535        search->index++;
536        return SYS_ERR_OK;
537    }
538    return FS_ERR_INDEX_BOUNDS;
539}
540
541static errval_t
542find_path(struct fat_mount *mount, const char *path,
543        uint8_t direntry[fat_direntry_size])
544{
545    TRACE_ENTER_F("\"%s\"", path);
546    errval_t err = SYS_ERR_OK;
547    const char *part_begin = path, *part_end;
548
549    fat_direntry_t parent_entry;
550    fat_direntry_t *parent = NULL;
551
552    do {
553        if (*part_begin == '/') {
554            part_begin++;
555        }
556        for (part_end = part_begin+1; *part_end && *part_end != '/'; part_end++) ;
557
558        size_t part_len = part_end - part_begin;
559#ifdef FAT_DEBUG_ENABLED
560        {
561            char part[part_len+1];
562            strncpy(part, part_begin, part_len);
563            part[part_len]=0;
564            FAT_DEBUG_F("part \"%s\"", part);
565        }
566#endif
567
568        struct fat_dirsearch search;
569        dirsearch_initialize(&search, parent);
570
571        char dosfn[12];
572        uint16_t lfn_data[LFN_CHAR_COUNT];
573        bool has_lfn;
574        char buf[LFN_CHAR_COUNT + 1];
575
576        do {
577
578            err = read_next_direntry(mount, &search, dosfn, lfn_data,
579                    &has_lfn, direntry);
580            if (err_is_fail(err)) {
581                dirsearch_wipe(&search, mount);
582                if (err == FS_ERR_INDEX_BOUNDS) {
583                    err = FS_ERR_NOTFOUND;
584                }
585                return err;
586            }
587
588            if (has_lfn) {
589                // In theory, FAT long file names allow a maximum of 255
590                // UTF-16 characters, but as Barrelfish does not have a clear
591                // notion of any UTF encoding, we do not support opening files
592                // with file names that are not pure ASCII.
593                // Additionally, when reading directory entries we replace any
594                // non-ASCII characters in file names with question marks (cf.
595                // dir_read_next()).
596                size_t len;
597                for (len = 0; len < LFN_CHAR_COUNT; len++) {
598                    if (!lfn_data[len] || lfn_data[len] > ASCII_MAX) {
599                        break;
600                    }
601                }
602                // have non-ASCII char in LFN, ignore file when looking up
603                // path for opening.
604                if (len < LFN_CHAR_COUNT && lfn_data[len] > ASCII_MAX) {
605                    continue;
606                }
607                // here: LFN is pure ASCII, and thus we can cast the
608                // UTF-16-encoded UTF characters to ASCII chars without having
609                // to worry about them not fitting. We also properly
610                // zero-terminate the new ASCII LFN.
611                assert(len <= LFN_CHAR_COUNT);
612                for (size_t i = 0; i < len; ++i) {
613                    buf[i] = (char)lfn_data[i];
614                }
615                buf[len] = 0;
616            }
617            else {
618                if (dos2unixfn((const unsigned char*)dosfn, (unsigned char*)buf, LFN_CHAR_COUNT)) {
619                    // TODO: handle error
620                }
621            }
622
623            FAT_DEBUG_F("comparing part to %s", buf);
624            bool match = strncmp(part_begin, buf, part_len) == 0 && buf[part_len] == 0;
625            if (match) {
626                break;
627            }
628
629        } while (1);
630
631        parent = &parent_entry;
632        fat_direntry_initialize(parent, (char*)direntry);
633
634        dirsearch_wipe(&search, mount);
635        part_begin = part_end;
636    } while(*part_begin);
637
638    return SYS_ERR_OK;
639}
640
641static errval_t
642openhandle(struct fat_mount *mount, const char *path, struct fat_handle_common *handle)
643{
644    TRACE_ENTER;
645    errval_t err = SYS_ERR_OK;
646
647    err = find_path(mount, path, handle->dirent_data);
648    if (err_is_fail(err)) {
649        return err;
650    }
651
652    fat_direntry_initialize(&handle->dirent, (char*)handle->dirent_data);
653
654#ifdef FAT_DEBUG_ENABLED
655    /*DUMP_DEV(fat_direntry, &handle->dirent, 4096);*/
656#endif
657
658    return SYS_ERR_OK;
659}
660
661static errval_t
662open(void *st, const char *path, vfs_handle_t *fhandle)
663{
664    TRACE_ENTER_F("\"%s\"", path);
665    errval_t err = SYS_ERR_OK;
666    struct fat_handle *handle = NULL;
667    struct fat_mount *mount = st;
668
669    handle = calloc(1, sizeof(*handle));
670    if (!handle) {
671        err = LIB_ERR_MALLOC_FAIL;
672        goto error;
673    }
674
675    err = openhandle(mount, path, &handle->h);
676    if (err_is_fail(err)) {
677        goto error;
678    }
679
680    if (fat_direntry_attr_dir_rdf(&handle->h.dirent)) {
681        err = FS_ERR_NOTFILE;
682        goto error;
683    }
684
685    *fhandle = handle;
686    goto end;
687
688error:
689    free(handle);
690
691end:
692    return err;
693}
694
695static errval_t
696create(void *st, const char *path, vfs_handle_t *handle)
697{
698    TRACE_ENTER;
699    return LIB_ERR_NOT_IMPLEMENTED;
700}
701
702static errval_t
703fat_remove(void *st, const char *path)
704{
705    TRACE_ENTER;
706    return LIB_ERR_NOT_IMPLEMENTED;
707}
708
709static errval_t
710read(void *st, vfs_handle_t fhandle, void *buffer, size_t bytes, size_t *bytes_read)
711{
712    TRACE_ENTER;
713    errval_t err;
714    struct fat_handle *handle = fhandle;
715    struct fat_mount *mount = st;
716    size_t file_size = fat_direntry_size_rd(&handle->h.dirent);
717    size_t offset = handle->offset;
718
719    assert(bytes_read);
720    *bytes_read = 0;
721
722    // limited requested bytes to remaining file size
723    size_t file_remainder = file_size - handle->offset;
724    if (bytes > file_remainder) {
725        bytes = file_remainder;
726    }
727    if (bytes == 0) {
728        return SYS_ERR_OK;
729    }
730    FAT_DEBUG_F("reading %zu bytes", bytes);
731
732    fat_direntry_t *dirent = &handle->h.dirent;
733    int isdir = fat_direntry_attr_dir_rdf(dirent);
734    if (isdir) {
735        return FS_ERR_NOTFILE;
736    }
737
738    size_t remaining = bytes;
739    do {
740        // split read offset into cluster index and offset within cluster
741        size_t cluster_index = cluster_for_offset(offset, mount);
742        size_t cluster_offset = offset_from_cluster(offset, mount);
743        FAT_DEBUG_F("reading from file cluster %zu + %zu",
744                cluster_index, cluster_offset);
745
746        // determin read size, only read single cluster and not beyond end of file
747        size_t read_size = remaining;
748        size_t cluster_remainder = mount->cluster_size * mount->block_size - cluster_offset;
749        if (cluster_remainder < read_size) {
750            read_size = cluster_remainder;
751        }
752        file_remainder = fat_direntry_size_rd(dirent) - offset;
753        if (file_remainder < read_size) {
754            read_size = file_remainder;
755        }
756        FAT_DEBUG_F("reading %zu from cluster (clus_rem=%zu, f_rem=%zu)",
757                read_size, cluster_remainder, file_remainder);
758
759        // determine cluster corresponding to cluster_index
760        uint32_t cluster = fat_direntry_start_rd(dirent);
761        if (mount->fat_type == FAT_TYPE_FAT32) {
762            cluster += (uint32_t)fat_direntry_starth_rd(dirent) << 16;
763        }
764        for (size_t clsidx = cluster_index; clsidx > 0; clsidx--) {
765            err = next_cluster(mount, cluster, &cluster);
766            if (err_is_fail(err)) {
767                return err;
768            }
769        }
770        FAT_DEBUG_F("file cluster %zu is cluster %"PRIu32, cluster_index, cluster);
771        assert(cluster < mount->last_cluster_start);
772
773        // fetch data and copy into buffer
774        uint8_t *data;
775        err = acquire_cluster(mount, cluster, &data);
776        if (err_is_fail(err)) {
777            return err;
778        }
779        memcpy(buffer, data+cluster_offset, read_size);
780        err = release_cluster(mount, cluster);
781        if (err_is_fail(err)) {
782            // should not happen
783            USER_PANIC_ERR(err, "could not release file cluster cache");
784        }
785
786        // update variables for successful read
787        buffer = (char *)buffer + read_size;
788        remaining -= read_size;
789        *bytes_read += read_size;
790        offset += read_size;
791
792        FAT_DEBUG_F("read of cluster %"PRIu32" completed", cluster);
793    } while (remaining);
794
795    // read completed, update handle's offset
796    FAT_DEBUG_F("read of %zu bytes completed", bytes);
797    handle->offset = offset;
798    return SYS_ERR_OK;
799}
800
801static errval_t
802write(void *st, vfs_handle_t handle, const void *buffer, size_t bytes,
803        size_t *bytes_written)
804{
805    TRACE_ENTER;
806    return LIB_ERR_NOT_IMPLEMENTED;
807}
808
809static errval_t
810fat_truncate(void *st, vfs_handle_t handle, size_t bytes)
811{
812    TRACE_ENTER;
813    return LIB_ERR_NOT_IMPLEMENTED;
814}
815
816static errval_t
817seek(void *st, vfs_handle_t fhandle, enum vfs_seekpos whence, off_t offset)
818{
819    TRACE_ENTER;
820
821    struct fat_handle *handle = fhandle;
822    size_t size = fat_direntry_size_rd(&handle->h.dirent);
823    size_t base = 0;
824
825    switch (whence) {
826    case VFS_SEEK_SET: base = 0; break;
827    case VFS_SEEK_CUR: base = handle->offset; break;
828    case VFS_SEEK_END: base = size; break;
829    default: USER_PANIC("invalid whence argument to fat seek"); break;
830    }
831
832    if (offset < 0 && (-offset) > base) {
833        handle->offset = 0;
834    }
835    else if (base + offset > size) {
836        handle->offset = size;
837    }
838    else {
839        handle->offset = base + offset;
840    }
841
842    return SYS_ERR_OK;
843}
844
845static errval_t
846tell(void *st, vfs_handle_t fhandle, size_t *pos)
847{
848    TRACE_ENTER;
849
850    struct fat_handle *handle = fhandle;
851    *pos = handle->offset;
852
853    return SYS_ERR_OK;
854}
855
856static errval_t
857stat(void *st, vfs_handle_t handle, struct vfs_fileinfo *info)
858{
859    TRACE_ENTER;
860
861    struct fat_handle_common *fhc = handle;
862
863    if (fat_direntry_attr_dir_rdf(&fhc->dirent)) {
864        info->type = VFS_DIRECTORY;
865        info->size = 0; // TODO: something more useful?
866    }
867    else {
868        info->type = VFS_FILE;
869        info->size = fat_direntry_size_rd(&fhc->dirent);
870    }
871
872    return SYS_ERR_OK;;
873}
874
875static errval_t
876close(void *st, vfs_handle_t fhandle)
877{
878    TRACE_ENTER;
879    struct fat_handle *handle = fhandle;
880
881    free(handle);
882    return SYS_ERR_OK;
883}
884
885static errval_t
886mkdir(void *st, const char *path)
887{
888    TRACE_ENTER_F("\"%s\"", path);
889    // fail if already present
890    return LIB_ERR_NOT_IMPLEMENTED;
891}
892
893static errval_t
894rmdir(void *st, const char *path)
895{
896    TRACE_ENTER_F("\"%s\"", path);
897    // fail if not empty
898    return LIB_ERR_NOT_IMPLEMENTED;
899}
900
901static errval_t
902opendir(void *st, const char *path, vfs_handle_t *dhandle)
903{
904    TRACE_ENTER_F("\"%s\"", path);
905    errval_t err = SYS_ERR_OK;
906    struct fat_dirhandle *new_handle = NULL;
907    struct fat_mount *mount = st;
908
909    new_handle = calloc(1, sizeof(*new_handle));
910    if (!new_handle) {
911        err = LIB_ERR_MALLOC_FAIL;
912        goto error;
913    }
914
915    if (!path[0] || (path[0] == '/' && path[1] == '\0')) {
916        FAT_DEBUG("got opendir for root");
917        *dhandle = new_handle;
918        goto end;
919    }
920
921    err = openhandle(mount, path, &new_handle->h);
922    if (err_is_fail(err)) {
923        goto error;
924    }
925
926    if (!fat_direntry_attr_dir_rdf(&new_handle->h.dirent)) {
927        err = FS_ERR_NOTDIR;
928        goto error;
929    }
930    FAT_DEBUG_F("directory %s found", path);
931
932    dirsearch_initialize(&new_handle->search, &new_handle->h.dirent);
933
934    *dhandle = new_handle;
935    goto end;
936
937error:
938    free(new_handle);
939
940end:
941    return err;
942}
943
944static errval_t
945dir_read_next(void *st, vfs_handle_t dhandle, char **name, struct vfs_fileinfo *info)
946{
947    TRACE_ENTER;
948    errval_t err = SYS_ERR_OK;
949
950    struct fat_dirhandle *handle = dhandle;
951    struct fat_mount *mount = st;
952
953    FAT_DEBUG_F("handle->search.index = %zu", handle->search.index);
954
955    char dosfn[12];
956    uint16_t lfn_data[LFN_CHAR_COUNT];
957    bool has_lfn;
958    uint8_t dirent_data[fat_direntry_size];
959    fat_direntry_t dirent;
960
961    // read next direntry
962    err = read_next_direntry(mount, &handle->search, dosfn, lfn_data,
963            &has_lfn, dirent_data);
964    if (err_is_fail(err)) {
965        return err;
966    }
967    fat_direntry_initialize(&dirent, (char*)dirent_data);
968
969    if (has_lfn) {
970        // In theory, FAT long file names allow a maximum of 255 UTF-16
971        // characters, but as Barrelfish does not have a clear notion of any
972        // UTF encoding, we replace non-ASCII characters with question marks
973        // when reading directory entries for a directory listing.
974        // Additionally we do not support opening files with names that
975        // contain non-ASCII characters (cf. find_path()).
976        size_t len;
977        for (len = 0; len < LFN_CHAR_COUNT; len++) {
978            if (!lfn_data[len]) {
979                break;
980            }
981        }
982        char *buf = malloc(len+1);
983        if (!buf) {
984            return LIB_ERR_MALLOC_FAIL;
985        }
986        for (size_t i = 0; i < len; ++i) {
987            if (lfn_data[i] > ASCII_MAX) {
988                buf[i] = '?';
989            }
990            else {
991                buf[i] = (char)lfn_data[i];
992            }
993        }
994        buf[len] = 0;
995        *name = buf;
996    }
997    else {
998        char *buf = malloc(DOSFN_MAX_LEN_UTF8);
999        dos2unixfn((const unsigned char*)dosfn, (unsigned char*)buf, DOSFN_MAX_LEN_UTF8);
1000        *name = buf;
1001    }
1002
1003#ifdef FAT_DEBUG_ENABLED
1004    /*DUMP_DEV(fat_direntry, &dirent, 4096);*/
1005#endif
1006
1007    bool isdir = fat_direntry_attr_dir_rdf(&dirent);
1008    info->type = isdir ? VFS_DIRECTORY : VFS_FILE;
1009    info->size = fat_direntry_size_rd(&dirent);
1010    return SYS_ERR_OK;
1011}
1012
1013static errval_t
1014closedir(void *st, vfs_handle_t dhandle)
1015{
1016    TRACE_ENTER;
1017    errval_t err = SYS_ERR_OK;
1018    struct fat_mount *mount = st;
1019    struct fat_dirhandle *handle = dhandle;
1020
1021    dirsearch_wipe(&handle->search, mount);
1022    free(handle);
1023
1024    return err;
1025}
1026
1027struct vfs_ops fat_ops = {
1028    .open = open,
1029    .create = create,
1030    .remove = fat_remove,
1031    .read = read,
1032    .write = write,
1033    .truncate = fat_truncate,
1034    .seek = seek,
1035    .tell = tell,
1036    .stat = stat,
1037    .close = close,
1038    .opendir = opendir,
1039    .dir_read_next = dir_read_next,
1040    .closedir = closedir,
1041    .mkdir = mkdir,
1042    .rmdir = rmdir,
1043};
1044
1045#if defined(__x86_64__) || defined(__i386__)
1046static void
1047ahci_init_cb(void *st, errval_t err, struct ahci_binding *b)
1048{
1049    TRACE_ENTER;
1050    struct fat_mount *mount = st;
1051
1052    if (err_is_fail(err)) {
1053        mount->bind_err = err;
1054        return;
1055    }
1056
1057    mount->ahci_binding = b;
1058}
1059
1060static void
1061ahci_close_cb(void *arg)
1062{
1063    *(bool *)arg = true;
1064}
1065
1066#elif defined(__pandaboard__)
1067
1068static void
1069bind_cb(void *st, errval_t err, struct ata_rw28_binding *b)
1070{
1071    printf("%s:%d\n", __FUNCTION__, __LINE__);
1072
1073    if (err_is_fail(err)) {
1074        USER_PANIC_ERR(err, "bind failed");
1075    }
1076
1077    struct fat_mount *mount = (struct fat_mount*) st;
1078
1079    ata_rw28_binding_init(b);
1080    mount->ata_rw28_binding = b;
1081}
1082#endif
1083
1084errval_t
1085vfs_fat_mount(const char *uri, void **retst, struct vfs_ops **retops)
1086{
1087    TRACE_ENTER;
1088    errval_t err;
1089    // format: scheme://port[+offset]
1090
1091    int type;
1092    if (strncmp(uri, "fat16://", 8) == 0) {
1093        type = FAT_TYPE_FAT16;
1094    }
1095    else if (strncmp(uri, "fat32://", 8) == 0) {
1096        type = FAT_TYPE_FAT32;
1097    }
1098    else {
1099        return VFS_ERR_BAD_URI;
1100    }
1101    // skip scheme
1102    const char *puri = uri + 8;
1103
1104    // parse port
1105    if (*puri < '0' || *puri > '9') {
1106        return VFS_ERR_BAD_URI;
1107    }
1108    size_t port = 0, startblock = 0;
1109    while (*puri >= '0' && *puri <= '9') {
1110        port = port*10 + (*puri++ - '0');
1111    }
1112    if (*puri == '+') {
1113        // parse offset
1114        puri++;
1115        if (*puri < '0' || *puri > '9') {
1116            return VFS_ERR_BAD_URI;
1117        }
1118        while (*puri >= '0' && *puri <= '9') {
1119            startblock = startblock*10 + (*puri++ - '0');
1120        }
1121    }
1122    if (*puri != 0) {
1123        return VFS_ERR_BAD_URI;
1124    }
1125
1126    FAT_DEBUG_F("got mount for port %zu, offset %zu", port, startblock);
1127
1128    struct fat_mount *mount = calloc(1, sizeof(struct fat_mount));
1129    if (!mount) {
1130        return LIB_ERR_MALLOC_FAIL;
1131    }
1132    mount->fat_type = type;
1133    mount->startblock = startblock;
1134
1135    // TODO(gz): We should probably decouple the FAT implementation from
1136    // all ATA related stuff to avoid these preprocessor hacks
1137#if defined(__x86_64__) || defined(__i386__)
1138    err = ahci_init(port, ahci_init_cb, mount, get_default_waitset());
1139    if (err_is_fail(err)) {
1140        goto ahci_init_failed;
1141    }
1142
1143    while (!mount->ahci_binding && err_is_ok(mount->bind_err)) {
1144        messages_wait_and_handle_next();
1145    }
1146    if (err_is_fail(mount->bind_err)) {
1147        err = mount->bind_err;
1148        goto ahci_init_failed;
1149    }
1150
1151    FAT_DEBUG("ahci_init completed");
1152
1153    struct ahci_ata_rw28_binding *ahci_ata_rw28_binding;
1154    ahci_ata_rw28_binding = calloc(1, sizeof(struct ahci_ata_rw28_binding));
1155    err = ahci_ata_rw28_init(ahci_ata_rw28_binding,
1156            get_default_waitset(), mount->ahci_binding);
1157    if (err_is_fail(err)) {
1158        goto ata_rw28_init_failed;
1159    }
1160    FAT_DEBUG("ahci_ata_rw28_init completed");
1161    mount->ata_rw28_binding = (struct ata_rw28_binding*)ahci_ata_rw28_binding;
1162    ata_rw28_rpc_client_init(mount->ata_rw28_binding);
1163    FAT_DEBUG("ata_rw28_binding_init completed");
1164#elif defined(__pandaboard__)
1165    FAT_DEBUG("wait for mmchs service\n");
1166    iref_t iref;
1167    err = nameservice_blocking_lookup("mmchs", &iref);
1168    if (err_is_fail(err)) {
1169        USER_PANIC_ERR(err, "nameservice_blocking_lookup failed");
1170    }
1171    err = ata_rw28_bind(iref,
1172                     bind_cb,
1173                     mount,
1174                     get_default_waitset(),
1175                     IDC_BIND_FLAGS_DEFAULT);
1176    if (err_is_fail(err)) {
1177        USER_PANIC_ERR(err, "bind failed");
1178    }
1179
1180    while(mount->ata_rw28_binding == NULL) {
1181        event_dispatch(get_default_waitset());
1182    }
1183
1184    FAT_DEBUG("ata_rw28 initialized.\n");
1185#endif
1186    size_t size;
1187    // read data from fat boot sector
1188    uint8_t *data = malloc(ata_rw28__read_dma_block_response_buffer_MAX_ARGUMENT_SIZE);
1189    err = mount->ata_rw28_binding->rpc_tx_vtbl.read_dma_block(mount->ata_rw28_binding,
1190            mount->startblock, mount->bootsec_data, &size);
1191    if (err_is_fail(err)) {
1192        goto bootsec_read_failed;
1193    }
1194    assert(size == 512);
1195#ifdef FAT_DEBUG_ENABLED
1196    FAT_DEBUG("dumping sector 0 raw");
1197    for (size_t i = 0; i < size; i+=16) {
1198        printf("%02x %02x %02x %02x  %02x %02x %02x %02x    "
1199                "%02x %02x %02x %02x  %02x %02x %02x %02x\n",
1200                data[i+0], data[i+1], data[i+2], data[i+3],
1201                data[i+4], data[i+5], data[i+6], data[i+7],
1202                data[i+8], data[i+9], data[i+10], data[i+11],
1203                data[i+12], data[i+13], data[i+14], data[i+15]
1204                );
1205    }
1206    FAT_DEBUG("end sector 0 dump");
1207#endif
1208
1209    data = NULL;
1210
1211    if (memcmp(mount->bootsec_data+0x1FE, "\x55\xAA", 2) != 0) {
1212        FAT_DEBUG_F("boot sector check bytes do not match, expected 0x55 0xAA,"
1213                " got 0x%02x 0x%02x", mount->bootsec_data[0x1FE],
1214                mount->bootsec_data[0x1FF]);
1215        goto fs_check_failed;
1216    }
1217
1218    fat_bpb_initialize(&mount->bpb, (mackerel_addr_t)&mount->bootsec_data);
1219    mount->block_size = fat_bpb_bps_rd(&mount->bpb);
1220    mount->cluster_size = fat_bpb_spc_rd(&mount->bpb);
1221    mount->fat_start = mount->startblock + fat_bpb_rsvs_rd(&mount->bpb);
1222    size_t ssc = fat_bpb_ssc_rd(&mount->bpb), lsc = fat_bpb_lsc_rd(&mount->bpb);
1223    if (ssc && lsc) {
1224        FAT_DEBUG_F("both small and large sector count are nonzero:"
1225                " ssc=%zu, lsc=%zu", ssc, lsc);
1226        goto fs_check_failed;
1227    }
1228    else if (ssc) {
1229        mount->block_count = ssc;
1230    }
1231    else if (lsc) {
1232        mount->block_count = lsc;
1233    }
1234    else {
1235        FAT_DEBUG("both small and large sector count are zero");
1236        goto fs_check_failed;
1237    }
1238#ifdef FAT_DEBUG_ENABLED
1239    DUMP_DEV(fat_bpb, &mount->bpb, 4096);
1240#endif
1241
1242    if (mount->fat_type == FAT_TYPE_FAT16) {
1243        fat16_ebpb_initialize(&mount->ebpb.f16, (char*)&mount->bootsec_data);
1244#ifdef FAT_DEBUG_ENABLED
1245        DUMP_DEV(fat16_ebpb, &mount->ebpb.f16, 4096);
1246#endif
1247        uint8_t signature = fat16_ebpb_ebs_rd(&mount->ebpb.f16);
1248        if (signature == 0x28) {
1249            FAT_DEBUG("FAT16 EBPB signature 0x28 indicates unsupported FAT type");
1250            goto fs_check_failed;
1251        }
1252        if (signature != 0x29) {
1253            FAT_DEBUG_F("FAT16 EBPB signature does not match, expected 0x29,"
1254                    " got 0x%02x", signature);
1255            goto fs_check_failed;
1256        }
1257        mount->fat_size = fat_bpb_spf_rd(&mount->bpb);
1258        mount->rootdir_start = mount->fat_start +
1259            fat_bpb_fatc_rd(&mount->bpb) * mount->fat_size;
1260        mount->clusters_start = mount->rootdir_start +
1261            CEIL_DIV(fat_bpb_rtc_rd(&mount->bpb) * fat_direntry_size,
1262                    fat_bpb_bps_rd(&mount->bpb));
1263        mount->last_cluster_start = 0xfff8;
1264    }
1265    else {
1266        fat32_ebpb_initialize(&mount->ebpb.f32, (char*)&mount->bootsec_data);
1267#ifdef FAT_DEBUG_ENABLED
1268        DUMP_DEV(fat32_ebpb, &mount->ebpb.f32, 4096);
1269#endif
1270        uint8_t signature = fat32_ebpb_ebs_rd(&mount->ebpb.f32);
1271        if (signature != 0x29) {
1272            FAT_DEBUG_F("FAT32 EBPB signature does not match, expected 0x29,"
1273                    " got 0x%02x", signature);
1274            goto fs_check_failed;
1275        }
1276        mount->fat_size = fat32_ebpb_spf_rd(&mount->ebpb.f32);
1277        mount->rootdir_cluster = fat32_ebpb_rtst_rd(&mount->ebpb.f32);
1278        mount->clusters_start = mount->fat_start +
1279            fat_bpb_fatc_rd(&mount->bpb) * mount->fat_size;
1280        mount->last_cluster_start = 0xfffffff8;
1281
1282        size_t fs_info_sector = fat32_ebpb_fsis_rd(&mount->ebpb.f32);
1283        if (fs_info_sector <= 0 || fs_info_sector >= mount->block_count) {
1284            FAT_DEBUG_F("File System Information Sector out of range,"
1285                    " block=%zu, block_count=%zu", fs_info_sector,
1286                    mount->block_count);
1287            goto fs_check_failed;
1288        }
1289        struct ata_rw28_read_dma_block_response__rx_args reply;
1290        mount->ata_rw28_binding->rpc_tx_vtbl.read_dma_block(mount->ata_rw28_binding,
1291                mount->startblock + fs_info_sector, reply.buffer ,
1292                &reply.buffer_size);
1293        if (memcmp(reply.buffer+0, "RRaA", 4) != 0 ||
1294            memcmp(reply.buffer+0x1e4, "rrAa", 4) != 0)
1295        {
1296            FAT_DEBUG_F("File System Information Sector signatures do not match,"
1297                    " %"PRIx32", %"PRIx32, *(uint32_t*)(reply.buffer+0),
1298                    *(uint32_t*)(reply.buffer+0x1e4));
1299            goto fs_check_failed;
1300        }
1301        if (memcmp(reply.buffer+0x1fe, "\x55\xAA", 2) != 0) {
1302            FAT_DEBUG("File System Information Sector check bytes do not match");
1303            goto fs_check_failed;
1304        }
1305#ifdef FAT_DEBUG_ENABLED
1306        FAT_DEBUG("dumping FSIS");
1307        printf("nr of free clusters: %"PRIu32"\n", *(uint32_t*)(reply.buffer+0x1e8));
1308        printf("most recently allocated cluster: %"PRIu32"\n",
1309                *(uint32_t*)(reply.buffer+0x1ec));
1310        printf("----------------\n");
1311#endif
1312        data = NULL;
1313    }
1314
1315#ifdef FAT_DEBUG_ENABLED
1316    FAT_DEBUG("dumping mount variables");
1317#define D(v) printf("%-18s %zu\n", #v ":", mount-> v )
1318    D(block_size);
1319    D(cluster_size);
1320    D(fat_start);
1321    D(fat_size);
1322    D(rootdir_start);
1323    D(rootdir_cluster);
1324    D(clusters_start);
1325#undef D
1326    printf("----------------\n");
1327#endif
1328
1329    err = fs_cache_init(1<<7, 1<<8, &mount->block_cache);
1330    if (err_is_fail(err)) {
1331        goto cache_init_failed;
1332    }
1333    err = fs_cache_init(1<<7, 1<<8, &mount->cluster_cache);
1334    if (err_is_fail(err)) {
1335        goto cache_init_failed;
1336    }
1337
1338    *retops = &fat_ops;
1339    *retst = mount;
1340    return SYS_ERR_OK;
1341
1342cache_init_failed:
1343    if (mount->block_cache) {
1344        fs_cache_free(mount->block_cache);
1345    }
1346    if (mount->cluster_cache) {
1347        fs_cache_free(mount->cluster_cache);
1348    }
1349
1350    goto bootsec_read_failed;
1351
1352fs_check_failed:
1353    err = FAT_ERR_BAD_FS;
1354
1355bootsec_read_failed:
1356    if (data) {
1357        free(data);
1358    }
1359    data = NULL;
1360
1361#if defined(__x86_64__) || defined(__i386__)
1362ata_rw28_init_failed:
1363    free(ahci_ata_rw28_binding);
1364    bool closed = false;
1365    errval_t err2 = ahci_close(mount->ahci_binding, MKCLOSURE(ahci_close_cb, &closed));
1366    if (err_is_ok(err2)) {
1367        while (!closed) {
1368            event_dispatch(mount->ahci_binding->waitset);
1369        }
1370    }
1371    // TODO: bindings leak? how to close flounder connection?
1372
1373ahci_init_failed:
1374    free(mount);
1375#endif
1376
1377    return err;
1378
1379}
1380