Home | History | Annotate | Download | only in fat
      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 
     15 static 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  */
     27 static 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 
     32 static 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 
     87 static 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 
    143 err:
    144     dprintf("fat_next_extent: return error\n");
    145     return -1;
    146 }
    147 
    148 static 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  */
    185 static 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  */
    215 static 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  */
    240 static 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  */
    281 static 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  */
    324 static 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  */
    358 static 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 
    394 static 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 
    404 static 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 */
    416 static 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 
    432 static inline enum dirent_type get_inode_mode(uint8_t attr)
    433 {
    434     return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
    435 }
    436 
    437 
    438 static 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 
    547 found:
    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 
    567 static 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 
    584 static struct inode *vfat_iget(const char *dname, struct inode *parent)
    585 {
    586     return vfat_find_entry(dname, parent);
    587 }
    588 
    589 static 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 
    709 got:
    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 */
    723 static 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 
    793 static 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)
    822 static 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 
    844 const 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 };
    861