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