• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
new_fat_inode(struct fs_info * fs)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  */
get_fat_sector(struct fs_info * fs,sector_t sector)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 
get_next_cluster(struct fs_info * fs,uint32_t clust_num)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 
fat_next_extent(struct inode * inode,uint32_t lstart)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 
get_next_sector(struct fs_info * fs,uint32_t sector)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  */
get_the_right_sector(struct file * file)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  */
next_sector(struct file * file)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  */
vfat_mangle_name(char * dst,const char * src)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  */
mangle_dos_name(char * mangle_buf,const char * src)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  */
vfat_match_longname(const char * str,const uint16_t * match,int len)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  */
vfat_cvt_longname(char * entry_name,const uint16_t * long_name)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 
copy_long_chunk(uint16_t * buf,const struct fat_dir_entry * de)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 
get_checksum(const char * dir_name)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 */
first_sector(struct fs_info * fs,const struct fat_dir_entry * dir)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 
get_inode_mode(uint8_t attr)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 
vfat_find_entry(const char * dname,struct inode * dir)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 
vfat_iget_root(struct fs_info * fs)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 
vfat_iget(const char * dname,struct inode * parent)584 static struct inode *vfat_iget(const char *dname, struct inode *parent)
585 {
586     return vfat_find_entry(dname, parent);
587 }
588 
vfat_readdir(struct file * file,struct dirent * dirent)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 */
vfat_fs_init(struct fs_info * fs)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 
vfat_copy_superblock(void * buf)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)
vfat_fs_uuid(struct fs_info * fs)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