source: bootcd/isolinux/syslinux-6.03/core/fs/fat/fat.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 20.5 KB
Line 
1#include <dprintf.h>
2#include <stdio.h>
3#include <ctype.h>
4#include <string.h>
5#include <sys/dirent.h>
6#include <cache.h>
7#include <core.h>
8#include <disk.h>
9#include <fs.h>
10#include <ilog2.h>
11#include <klibc/compiler.h>
12#include "codepage.h"
13#include "fat_fs.h"
14
15static struct inode * new_fat_inode(struct fs_info *fs)
16{
17    struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
18    if (!inode)
19        malloc_error("inode structure");
20
21    return inode;
22}
23
24/*
25 * Check for a particular sector in the FAT cache
26 */
27static const void *get_fat_sector(struct fs_info *fs, sector_t sector)
28{
29    return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector);
30}
31
32static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
33{
34    uint32_t next_cluster = 0;
35    sector_t fat_sector;
36    uint32_t offset;
37    uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
38    const uint8_t *data;
39
40    switch(FAT_SB(fs)->fat_type) {
41    case FAT12:
42        offset = clust_num + (clust_num >> 1);
43        fat_sector = offset >> SECTOR_SHIFT(fs);
44        offset &= sector_mask;
45        data = get_fat_sector(fs, fat_sector);
46        if (offset == sector_mask) {
47            /*
48             * we got the end of the one fat sector,
49             * but we have just one byte and we need two,
50             * so store the low part, then read the next fat
51             * sector, read the high part, then combine it.
52             */
53            next_cluster = data[offset];
54            data = get_fat_sector(fs, fat_sector + 1);
55            next_cluster += data[0] << 8;
56        } else {
57            next_cluster = *(const uint16_t *)(data + offset);
58        }
59
60        if (clust_num & 0x0001)
61            next_cluster >>= 4;         /* cluster number is ODD */
62        else
63            next_cluster &= 0x0fff;     /* cluster number is EVEN */
64        break;
65
66    case FAT16:
67        offset = clust_num << 1;
68        fat_sector = offset >> SECTOR_SHIFT(fs);
69        offset &= sector_mask;
70        data = get_fat_sector(fs, fat_sector);
71        next_cluster = *(const uint16_t *)(data + offset);
72        break;
73
74    case FAT32:
75        offset = clust_num << 2;
76        fat_sector = offset >> SECTOR_SHIFT(fs);
77        offset &= sector_mask;
78        data = get_fat_sector(fs, fat_sector);
79        next_cluster = *(const uint32_t *)(data + offset);
80        next_cluster &= 0x0fffffff;
81        break;
82    }
83
84    return next_cluster;
85}
86
87static int fat_next_extent(struct inode *inode, uint32_t lstart)
88{
89    struct fs_info *fs = inode->fs;
90    struct fat_sb_info *sbi = FAT_SB(fs);
91    uint32_t mcluster = lstart >> sbi->clust_shift;
92    uint32_t lcluster;
93    uint32_t pcluster;
94    uint32_t tcluster;
95    uint32_t xcluster;
96    const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
97    const uint32_t cluster_secs  = UINT32_C(1) << sbi->clust_shift;
98    sector_t data_area = sbi->data;
99
100    tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
101    if (mcluster >= tcluster)
102        goto err;               /* Requested cluster beyond end of file */
103
104    lcluster = PVT(inode)->offset >> sbi->clust_shift;
105    pcluster = ((PVT(inode)->here - data_area) >> sbi->clust_shift) + 2;
106
107    if (lcluster > mcluster || PVT(inode)->here < data_area) {
108        lcluster = 0;
109        pcluster = PVT(inode)->start_cluster;
110    }
111
112    for (;;) {
113        if (pcluster-2 >= sbi->clusters) {
114            inode->size = lcluster << sbi->clust_shift;
115            goto err;
116        }
117
118        if (lcluster >= mcluster)
119            break;
120
121        lcluster++;
122        pcluster = get_next_cluster(fs, pcluster);
123    }
124
125    inode->next_extent.pstart =
126        ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
127    inode->next_extent.len = cluster_secs;
128    xcluster = 0;               /* Nonsense */
129
130    while (++lcluster < tcluster) {
131        xcluster = get_next_cluster(fs, pcluster);
132        if (xcluster != ++pcluster)
133            break;              /* Not contiguous */
134        inode->next_extent.len += cluster_secs;
135    }
136
137    /* Note: ->here is bogus if ->offset >= EOF, but that's okay */
138    PVT(inode)->offset = lcluster << sbi->clust_shift;
139    PVT(inode)->here   = ((xcluster-2) << sbi->clust_shift) + data_area;
140
141    return 0;
142
143err:
144    dprintf("fat_next_extent: return error\n");
145    return -1;
146}
147
148static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
149{
150    struct fat_sb_info *sbi = FAT_SB(fs);
151    sector_t data_area = sbi->data;
152    sector_t data_sector;
153    uint32_t cluster;
154    int clust_shift = sbi->clust_shift;
155
156    if (sector < data_area) {
157        /* Root directory sector... */
158        sector++;
159        if (sector >= data_area)
160            sector = 0; /* Ran out of root directory, return EOF */
161        return sector;
162    }
163
164    data_sector = sector - data_area;
165    if ((data_sector + 1) & sbi->clust_mask)  /* Still in the same cluster */
166        return sector + 1;                    /* Next sector inside cluster */
167
168    /* get a new cluster */
169    cluster = data_sector >> clust_shift;
170    cluster = get_next_cluster(fs, cluster + 2) - 2;
171
172    if (cluster >= sbi->clusters)
173        return 0;
174
175    /* return the start of the new cluster */
176    sector = (cluster << clust_shift) + data_area;
177    return sector;
178}
179
180/*
181 * The FAT is a single-linked list.  We remember the last place we
182 * were, so for a forward seek we can move forward from there, but
183 * for a reverse seek we have to start over...
184 */
185static sector_t get_the_right_sector(struct file *file)
186{
187    struct inode *inode = file->inode;
188    uint32_t sector_pos  = file->offset >> SECTOR_SHIFT(file->fs);
189    uint32_t where;
190    sector_t sector;
191
192    if (sector_pos < PVT(inode)->offset) {
193        /* Reverse seek */
194        where = 0;
195        sector = PVT(inode)->start;
196    } else {
197        where = PVT(inode)->offset;
198        sector = PVT(inode)->here;
199    }
200
201    while (where < sector_pos) {
202        sector = get_next_sector(file->fs, sector);
203        where++;
204    }
205
206    PVT(inode)->offset = sector_pos;
207    PVT(inode)->here   = sector;
208
209    return sector;
210}
211
212/*
213 * Get the next sector in sequence
214 */
215static sector_t next_sector(struct file *file)
216{
217    struct inode *inode = file->inode;
218    sector_t sector = get_next_sector(file->fs, PVT(inode)->here);
219    PVT(inode)->offset++;
220    PVT(inode)->here = sector;
221
222    return sector;
223}
224
225/**
226 * mangle_name:
227 *
228 * Mangle a filename pointed to by src into a buffer pointed
229 * to by dst; ends on encountering any whitespace.
230 * dst is preserved.
231 *
232 * This verifies that a filename is < FILENAME_MAX characters,
233 * doesn't contain whitespace, zero-pads the output buffer,
234 * and removes redundant slashes.
235 *
236 * Unlike the generic version, this also converts backslashes to
237 * forward slashes.
238 *
239 */
240static void vfat_mangle_name(char *dst, const char *src)
241{
242    char *p = dst;
243    int i = FILENAME_MAX-1;
244    char c;
245
246    while (not_whitespace(c = *src)) {
247        if (c == '\\')
248            c = '/';
249
250        if (c == '/') {
251            if (src[1] == '/' || src[1] == '\\') {
252                src++;
253                i--;
254                continue;
255            }
256        }
257        i--;
258        *dst++ = *src++;
259    }
260
261    while (1) {
262        if (dst == p)
263            break;
264        if (dst[-1] != '/')
265            break;
266        if ((dst[-1] == '/') && ((dst - 1) == p))
267            break;
268
269        dst--;
270        i++;
271    }
272
273    i++;
274    for (; i > 0; i --)
275        *dst++ = '\0';
276}
277
278/*
279 * Mangle a normal style string to DOS style string.
280 */
281static void mangle_dos_name(char *mangle_buf, const char *src)
282{
283    int i;
284    unsigned char c;
285
286    if (src[0] == '.' && (!src[1] || (src[1] == '.' && !src[2]))) {
287        /* . and .. mangle to their respective zero-padded version */
288        i = stpcpy(mangle_buf, src) - mangle_buf;
289    } else {
290        i = 0;
291        while (i < 11) {
292            c = *src++;
293           
294        if ((c <= ' ') || (c == '/'))
295            break;
296       
297        if (c == '.') {
298            while (i < 8)
299                mangle_buf[i++] = ' ';
300            i = 8;
301            continue;
302        }
303       
304        c = codepage.upper[c];
305        if (i == 0 && c == 0xe5)
306            c = 0x05;           /* Special hack for the first byte only! */
307       
308        mangle_buf[i++] = c;
309        }
310    }
311
312    while (i < 11)
313        mangle_buf[i++] = ' ';
314
315    mangle_buf[i] = '\0';
316}
317
318/*
319 * Match a string name against a longname.  "len" is the number of
320 * codepoints in the input; including padding.
321 *
322 * Returns true on match.
323 */
324static bool vfat_match_longname(const char *str, const uint16_t *match,
325                                int len)
326{
327    unsigned char c = -1;       /* Nonzero: we have not yet seen NUL */
328    uint16_t cp;
329
330    dprintf("Matching: %s len %d\n", str, len);
331
332    while (len) {
333        cp = *match++;
334        len--;
335        if (!cp)
336            break;
337        c = *str++;
338        if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
339            return false;       /* Also handles c == '\0' */
340    }
341
342    /* This should have been the end of the matching string */
343    if (*str)
344        return false;
345
346    /* Any padding entries must be FFFF */
347    while (len--)
348        if (*match++ != 0xffff)
349            return false;
350
351    return true;
352}
353
354/*
355 * Convert an UTF-16 longname to the system codepage; return
356 * the length on success or -1 on failure.
357 */
358static int vfat_cvt_longname(char *entry_name, const uint16_t *long_name)
359{
360    struct unicache {
361        uint16_t utf16;
362        uint8_t cp;
363    };
364    static struct unicache unicache[256];
365    struct unicache *uc;
366    uint16_t cp;
367    unsigned int c;
368    char *p = entry_name;
369
370    do {
371        cp = *long_name++;
372        uc = &unicache[cp % 256];
373
374        if (__likely(uc->utf16 == cp)) {
375            *p++ = uc->cp;
376        } else {
377            for (c = 0; c < 512; c++) {
378                /* This is a bit hacky... */
379                if (codepage.uni[0][c] == cp) {
380                    uc->utf16 = cp;
381                    *p++ = uc->cp = (uint8_t)c;
382                    goto found;
383                }
384            }
385            return -1;          /* Impossible character */
386        found:
387            ;
388        }
389    } while (cp);
390
391    return (p-entry_name)-1;
392}
393
394static void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de)
395{
396    const struct fat_long_name_entry *le =
397        (const struct fat_long_name_entry *)de;
398
399    memcpy(buf,      le->name1, 5 * 2);
400    memcpy(buf + 5,  le->name2, 6 * 2);
401    memcpy(buf + 11, le->name3, 2 * 2);
402}
403
404static uint8_t get_checksum(const char *dir_name)
405{
406    int  i;
407    uint8_t sum = 0;
408
409    for (i = 11; i; i--)
410        sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++;
411    return sum;
412}
413
414
415/* compute the first sector number of one dir where the data stores */
416static inline sector_t first_sector(struct fs_info *fs,
417                                    const struct fat_dir_entry *dir)
418{
419    const struct fat_sb_info *sbi = FAT_SB(fs);
420    sector_t first_clust;
421    sector_t sector;
422
423    first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
424    if (first_clust == 0)
425        sector = sbi->root;     /* first_clust == 0 means root directory */
426    else
427        sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
428
429    return sector;
430}
431
432static inline enum dirent_type get_inode_mode(uint8_t attr)
433{
434    return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
435}
436
437
438static struct inode *vfat_find_entry(const char *dname, struct inode *dir)
439{
440    struct fs_info *fs = dir->fs;
441    struct inode *inode;
442    const struct fat_dir_entry *de;
443    struct fat_long_name_entry *long_de;
444
445    char mangled_name[12];
446    uint16_t long_name[260];    /* == 20*13 */
447    int long_len;
448
449    sector_t dir_sector = PVT(dir)->start;
450    uint8_t vfat_init, vfat_next, vfat_csum = 0;
451    uint8_t id;
452    int slots;
453    int entries;
454    int checksum;
455    int long_match = 0;
456
457    slots = (strlen(dname) + 12) / 13;
458    if (slots > 20)
459        return NULL;            /* Name too long */
460
461    slots |= 0x40;
462    vfat_init = vfat_next = slots;
463    long_len = slots*13;
464
465    /* Produce the shortname version, in case we need it. */
466    mangle_dos_name(mangled_name, dname);
467
468    while (dir_sector) {
469        de = get_cache(fs->fs_dev, dir_sector);
470        entries = 1 << (fs->sector_shift - 5);
471
472        while (entries--) {
473            if (de->name[0] == 0)
474                return NULL;
475
476            if (de->attr == 0x0f) {
477                /*
478                 * It's a long name entry.
479                 */
480                long_de = (struct fat_long_name_entry *)de;
481                id = long_de->id;
482                if (id != vfat_next)
483                    goto not_match;
484
485                if (id & 0x40) {
486                    /* get the initial checksum value */
487                    vfat_csum = long_de->checksum;
488                    id &= 0x3f;
489                    long_len = id * 13;
490
491                    /* ZERO the long_name buffer */
492                    memset(long_name, 0, sizeof long_name);
493                } else {
494                    if (long_de->checksum != vfat_csum)
495                        goto not_match;
496                }
497
498                vfat_next = --id;
499
500                /* got the long entry name */
501                copy_long_chunk(long_name + id*13, de);
502
503                /*
504                 * If we got the last entry, check it.
505                 * Or, go on with the next entry.
506                 */
507                if (id == 0) {
508                    if (!vfat_match_longname(dname, long_name, long_len))
509                        goto not_match;
510                    long_match = 1;
511                }
512                de++;
513                continue;     /* Try the next entry */
514            } else {
515                /*
516                 * It's a short entry
517                 */
518                if (de->attr & 0x08) /* ignore volume labels */
519                    goto not_match;
520
521                if (long_match) {
522                    /*
523                     * We already have a VFAT long name match. However, the
524                     * match is only valid if the checksum matches.
525                     */
526                    checksum = get_checksum(de->name);
527                    if (checksum == vfat_csum)
528                        goto found;  /* Got it */
529                } else {
530                    if (!memcmp(mangled_name, de->name, 11))
531                        goto found;
532                }
533            }
534
535        not_match:
536            vfat_next = vfat_init;
537            long_match = 0;
538
539            de++;
540        }
541
542        /* Try with the next sector */
543        dir_sector = get_next_sector(fs, dir_sector);
544    }
545    return NULL;                /* Nothing found... */
546
547found:
548    inode = new_fat_inode(fs);
549    inode->size = de->file_size;
550    PVT(inode)->start_cluster =
551        (de->first_cluster_high << 16) + de->first_cluster_low;
552    if (PVT(inode)->start_cluster == 0) {
553        /* Root directory */
554        int root_size = FAT_SB(fs)->root_size;
555
556        PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
557        inode->size = root_size ? root_size << fs->sector_shift : ~0;
558        PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
559    } else {
560        PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
561    }
562    inode->mode = get_inode_mode(de->attr);
563
564    return inode;
565}
566
567static struct inode *vfat_iget_root(struct fs_info *fs)
568{
569    struct inode *inode = new_fat_inode(fs);
570    int root_size = FAT_SB(fs)->root_size;
571
572    /*
573     * For FAT32, the only way to get the root directory size is to
574     * follow the entire FAT chain to the end... which seems pointless.
575     */
576    PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
577    inode->size = root_size ? root_size << fs->sector_shift : ~0;
578    PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
579    inode->mode = DT_DIR;
580
581    return inode;
582}
583
584static struct inode *vfat_iget(const char *dname, struct inode *parent)
585{
586    return vfat_find_entry(dname, parent);
587}
588
589static int vfat_readdir(struct file *file, struct dirent *dirent)
590{
591    struct fs_info *fs = file->fs;
592    const struct fat_dir_entry *de;
593    const char *data;
594    const struct fat_long_name_entry *long_de;
595
596    sector_t sector = get_the_right_sector(file);
597
598    uint16_t long_name[261];    /* == 20*13 + 1 (to guarantee null) */
599    char filename[261];
600    int name_len = 0;
601
602    uint8_t vfat_next, vfat_csum;
603    uint8_t id;
604    int entries_left;
605    bool long_entry = false;
606    int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
607
608    data = get_cache(fs->fs_dev, sector);
609    de = (const struct fat_dir_entry *)(data + sec_off);
610    entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
611
612    vfat_next = vfat_csum = 0xff;
613
614    while (1) {
615        while (entries_left--) {
616            if (de->name[0] == 0)
617                return -1;      /* End of directory */
618            if ((uint8_t)de->name[0] == 0xe5)
619                goto invalid;
620
621            if (de->attr == 0x0f) {
622                /*
623                 * It's a long name entry.
624                 */
625                long_de = (struct fat_long_name_entry *)de;
626                id = long_de->id;
627
628                if (id & 0x40) {
629                    /* init vfat_csum */
630                    vfat_csum = long_de->checksum;
631                    id &= 0x3f;
632                    if (id >= 20)
633                        goto invalid; /* Too long! */
634
635                    /* ZERO the long_name buffer */
636                    memset(long_name, 0, sizeof long_name);
637                } else {
638                    if (long_de->checksum != vfat_csum || id != vfat_next)
639                        goto invalid;
640                }
641
642                vfat_next = --id;
643
644                /* got the long entry name */
645                copy_long_chunk(long_name + id*13, de);
646
647                if (id == 0) {
648                    name_len = vfat_cvt_longname(filename, long_name);
649                    if (name_len > 0 && name_len < sizeof(dirent->d_name))
650                        long_entry = true;
651                }
652
653                goto next;
654            } else {
655                /*
656                 * It's a short entry
657                 */
658                if (de->attr & 0x08) /* ignore volume labels */
659                    goto invalid;
660
661                if (long_entry && get_checksum(de->name) == vfat_csum) {
662                   /* Got a long entry */
663                } else {
664                    /* Use the shortname */
665                    int i;
666                    uint8_t c;
667                    char *p = filename;
668
669                    for (i = 0; i < 8; i++) {
670                        c = de->name[i];
671                        if (c == ' ')
672                            break;
673                        if (de->lcase & LCASE_BASE)
674                            c = codepage.lower[c];
675                        *p++ = c;
676                    }
677                    if (de->name[8] != ' ') {
678                        *p++ = '.';
679                        for (i = 8; i < 11; i++) {
680                            c = de->name[i];
681                            if (c == ' ')
682                                break;
683                            if (de->lcase & LCASE_EXT)
684                                c = codepage.lower[c];
685                            *p++ = c;
686                        }
687                    }
688                    *p = '\0';
689                    name_len = p - filename;
690                }
691                goto got;       /* Got something one way or the other */
692            }
693
694        invalid:
695            long_entry = false;
696        next:
697            de++;
698            file->offset += sizeof(struct fat_dir_entry);
699        }
700
701        /* Try with the next sector */
702        sector = next_sector(file);
703        if (!sector)
704            return -1;
705        de = get_cache(fs->fs_dev, sector);
706        entries_left = 1 << (fs->sector_shift - 5);
707    }
708
709got:
710    name_len++;                 /* Include final null */
711    dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16);
712    dirent->d_off = file->offset;
713    dirent->d_reclen = offsetof(struct dirent, d_name) + name_len;
714    dirent->d_type = get_inode_mode(de->attr);
715    memcpy(dirent->d_name, filename, name_len);
716
717    file->offset += sizeof(*de);  /* Update for next reading */
718
719    return 0;
720}
721
722/* init. the fs meta data, return the block size in bits */
723static int vfat_fs_init(struct fs_info *fs)
724{
725    struct fat_bpb fat;
726    struct fat_sb_info *sbi;
727    struct disk *disk = fs->fs_dev->disk;
728    int sectors_per_fat;
729    uint32_t clusters;
730    sector_t total_sectors;
731
732    fs->sector_shift = fs->block_shift = disk->sector_shift;
733    fs->sector_size  = 1 << fs->sector_shift;
734    fs->block_size   = 1 << fs->block_shift;
735
736    disk->rdwr_sectors(disk, &fat, 0, 1, 0);
737
738    /* XXX: Find better sanity checks... */
739    if (!fat.bxResSectors || !fat.bxFATs)
740        return -1;
741    sbi = malloc(sizeof(*sbi));
742    if (!sbi)
743        malloc_error("fat_sb_info structure");
744    fs->fs_info = sbi;
745
746    sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
747    total_sectors   = fat.bxSectors ? : fat.bsHugeSectors;
748
749    sbi->fat       = fat.bxResSectors;
750    sbi->root      = sbi->fat + sectors_per_fat * fat.bxFATs;
751    sbi->root_size = root_dir_size(fs, &fat);
752    sbi->data      = sbi->root + sbi->root_size;
753
754    sbi->clust_shift      = ilog2(fat.bxSecPerClust);
755    sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
756    sbi->clust_mask       = fat.bxSecPerClust - 1;
757    sbi->clust_size       = fat.bxSecPerClust << fs->sector_shift;
758
759    clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
760    if (clusters <= 0xff4) {
761        sbi->fat_type = FAT12;
762    } else if (clusters <= 0xfff4) {
763        sbi->fat_type = FAT16;
764    } else {
765        sbi->fat_type = FAT32;
766
767        if (clusters > 0x0ffffff4)
768            clusters = 0x0ffffff4; /* Maximum possible */
769
770        if (fat.fat32.extended_flags & 0x80) {
771            /* Non-mirrored FATs, we need to read the active one */
772            sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat;
773        }
774
775        /* FAT32: root directory is a cluster chain */
776        sbi->root = sbi->data
777            + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
778    }
779    sbi->clusters = clusters;
780
781    /* fs UUID - serial number */
782    if (FAT32 == sbi->fat_type)
783        sbi->uuid = fat.fat32.num_serial;
784    else
785        sbi->uuid = fat.fat12_16.num_serial;
786
787    /* Initialize the cache */
788    cache_init(fs->fs_dev, fs->block_shift);
789
790    return fs->block_shift;
791}
792
793static int vfat_copy_superblock(void *buf)
794{
795        struct fat_bpb fat;
796        struct disk *disk;
797        size_t sb_off;
798        void *dst;
799        int sb_len;
800
801        disk = this_fs->fs_dev->disk;
802        disk->rdwr_sectors(disk, &fat, 0, 1, 0);
803
804        /* XXX: Find better sanity checks... */
805        if (!fat.bxResSectors || !fat.bxFATs)
806                return -1;
807
808        sb_off = offsetof(struct fat_bpb, sector_size);
809        sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \
810                + sizeof(fat.fat12_16);
811
812        /*
813         * Only copy fields of the superblock we actually care about.
814         */
815        dst = buf + sb_off;
816        memcpy(dst, (void *)&fat + sb_off, sb_len);
817
818        return 0;
819}
820
821#define FAT_UUID_LEN (4 + 1 + 4 + 1)
822static char *vfat_fs_uuid(struct fs_info *fs)
823{
824    char *uuid = NULL;
825    char *ptr;
826
827    uuid = malloc(FAT_UUID_LEN);
828    if (!uuid)
829        return NULL;
830
831    if (snprintf(uuid, FAT_UUID_LEN, "%04x-%04x",
832                  (uint16_t)(FAT_SB(fs)->uuid >> 16),
833                  (uint16_t)FAT_SB(fs)->uuid) < 0) {
834        free(uuid);
835        return NULL;
836    }
837
838    for (ptr = uuid; ptr && *ptr; ptr++)
839        *ptr = toupper(*ptr);
840
841    return uuid;
842}
843
844const struct fs_ops vfat_fs_ops = {
845    .fs_name       = "vfat",
846    .fs_flags      = FS_USEMEM | FS_THISIND,
847    .fs_init       = vfat_fs_init,
848    .searchdir     = NULL,
849    .getfssec      = generic_getfssec,
850    .close_file    = generic_close_file,
851    .mangle_name   = vfat_mangle_name,
852    .chdir_start   = generic_chdir_start,
853    .open_config   = generic_open_config,
854    .readdir       = vfat_readdir,
855    .iget_root     = vfat_iget_root,
856    .iget          = vfat_iget,
857    .next_extent   = fat_next_extent,
858    .copy_super    = vfat_copy_superblock,
859    .fs_uuid       = vfat_fs_uuid,
860};
Note: See TracBrowser for help on using the repository browser.