Add memtest support.
[syslinux-debian/hramrach.git] / core / fs / fat / fat.c
blobd7346ae1343b4f6a06bc61edfe4ad0d5a7cd9091
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/dirent.h>
5 #include <cache.h>
6 #include <core.h>
7 #include <disk.h>
8 #include <fs.h>
9 #include <ilog2.h>
10 #include <klibc/compiler.h>
11 #include "codepage.h"
12 #include "fat_fs.h"
14 static struct inode * new_fat_inode(struct fs_info *fs)
16 struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
17 if (!inode)
18 malloc_error("inode structure");
20 return inode;
24 * Check for a particular sector in the FAT cache
26 static const void *get_fat_sector(struct fs_info *fs, sector_t sector)
28 return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector);
31 static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
33 uint32_t next_cluster = 0;
34 sector_t fat_sector;
35 uint32_t offset;
36 uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
37 const uint8_t *data;
39 switch(FAT_SB(fs)->fat_type) {
40 case FAT12:
41 offset = clust_num + (clust_num >> 1);
42 fat_sector = offset >> SECTOR_SHIFT(fs);
43 offset &= sector_mask;
44 data = get_fat_sector(fs, fat_sector);
45 if (offset == sector_mask) {
47 * we got the end of the one fat sector,
48 * but we have just one byte and we need two,
49 * so store the low part, then read the next fat
50 * sector, read the high part, then combine it.
52 next_cluster = data[offset];
53 data = get_fat_sector(fs, fat_sector + 1);
54 next_cluster += data[0] << 8;
55 } else {
56 next_cluster = *(const uint16_t *)(data + offset);
59 if (clust_num & 0x0001)
60 next_cluster >>= 4; /* cluster number is ODD */
61 else
62 next_cluster &= 0x0fff; /* cluster number is EVEN */
63 break;
65 case FAT16:
66 offset = clust_num << 1;
67 fat_sector = offset >> SECTOR_SHIFT(fs);
68 offset &= sector_mask;
69 data = get_fat_sector(fs, fat_sector);
70 next_cluster = *(const uint16_t *)(data + offset);
71 break;
73 case FAT32:
74 offset = clust_num << 2;
75 fat_sector = offset >> SECTOR_SHIFT(fs);
76 offset &= sector_mask;
77 data = get_fat_sector(fs, fat_sector);
78 next_cluster = *(const uint32_t *)(data + offset);
79 next_cluster &= 0x0fffffff;
80 break;
83 return next_cluster;
86 static int fat_next_extent(struct inode *inode, uint32_t lstart)
88 struct fs_info *fs = inode->fs;
89 struct fat_sb_info *sbi = FAT_SB(fs);
90 uint32_t mcluster = lstart >> sbi->clust_shift;
91 uint32_t lcluster;
92 uint32_t pcluster;
93 uint32_t tcluster;
94 uint32_t xcluster;
95 const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
96 const uint32_t cluster_secs = UINT32_C(1) << sbi->clust_shift;
97 sector_t data_area = sbi->data;
99 tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
100 if (mcluster >= tcluster)
101 goto err; /* Requested cluster beyond end of file */
103 lcluster = PVT(inode)->offset >> sbi->clust_shift;
104 pcluster = ((PVT(inode)->here - data_area) >> sbi->clust_shift) + 2;
106 if (lcluster > mcluster || PVT(inode)->here < data_area) {
107 lcluster = 0;
108 pcluster = PVT(inode)->start_cluster;
111 for (;;) {
112 if (pcluster-2 >= sbi->clusters) {
113 inode->size = lcluster << sbi->clust_shift;
114 goto err;
117 if (lcluster >= mcluster)
118 break;
120 lcluster++;
121 pcluster = get_next_cluster(fs, pcluster);
124 inode->next_extent.pstart =
125 ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
126 inode->next_extent.len = cluster_secs;
127 xcluster = 0; /* Nonsense */
129 while (++lcluster < tcluster) {
130 xcluster = get_next_cluster(fs, pcluster);
131 if (xcluster != ++pcluster)
132 break; /* Not contiguous */
133 inode->next_extent.len += cluster_secs;
136 /* Note: ->here is bogus if ->offset >= EOF, but that's okay */
137 PVT(inode)->offset = lcluster << sbi->clust_shift;
138 PVT(inode)->here = ((xcluster-2) << sbi->clust_shift) + data_area;
140 return 0;
142 err:
143 dprintf("fat_next_extent: return error\n");
144 return -1;
147 static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
149 struct fat_sb_info *sbi = FAT_SB(fs);
150 sector_t data_area = sbi->data;
151 sector_t data_sector;
152 uint32_t cluster;
153 int clust_shift = sbi->clust_shift;
155 if (sector < data_area) {
156 /* Root directory sector... */
157 sector++;
158 if (sector >= data_area)
159 sector = 0; /* Ran out of root directory, return EOF */
160 return sector;
163 data_sector = sector - data_area;
164 if ((data_sector + 1) & sbi->clust_mask) /* Still in the same cluster */
165 return sector + 1; /* Next sector inside cluster */
167 /* get a new cluster */
168 cluster = data_sector >> clust_shift;
169 cluster = get_next_cluster(fs, cluster + 2) - 2;
171 if (cluster >= sbi->clusters)
172 return 0;
174 /* return the start of the new cluster */
175 sector = (cluster << clust_shift) + data_area;
176 return sector;
180 * The FAT is a single-linked list. We remember the last place we
181 * were, so for a forward seek we can move forward from there, but
182 * for a reverse seek we have to start over...
184 static sector_t get_the_right_sector(struct file *file)
186 struct inode *inode = file->inode;
187 uint32_t sector_pos = file->offset >> SECTOR_SHIFT(file->fs);
188 uint32_t where;
189 sector_t sector;
191 if (sector_pos < PVT(inode)->offset) {
192 /* Reverse seek */
193 where = 0;
194 sector = PVT(inode)->start;
195 } else {
196 where = PVT(inode)->offset;
197 sector = PVT(inode)->here;
200 while (where < sector_pos) {
201 sector = get_next_sector(file->fs, sector);
202 where++;
205 PVT(inode)->offset = sector_pos;
206 PVT(inode)->here = sector;
208 return sector;
212 * Get the next sector in sequence
214 static sector_t next_sector(struct file *file)
216 struct inode *inode = file->inode;
217 sector_t sector = get_next_sector(file->fs, PVT(inode)->here);
218 PVT(inode)->offset++;
219 PVT(inode)->here = sector;
221 return sector;
225 * mangle_name:
227 * Mangle a filename pointed to by src into a buffer pointed
228 * to by dst; ends on encountering any whitespace.
229 * dst is preserved.
231 * This verifies that a filename is < FILENAME_MAX characters,
232 * doesn't contain whitespace, zero-pads the output buffer,
233 * and removes redundant slashes.
235 * Unlike the generic version, this also converts backslashes to
236 * forward slashes.
239 static void vfat_mangle_name(char *dst, const char *src)
241 char *p = dst;
242 int i = FILENAME_MAX-1;
243 char c;
245 while (not_whitespace(c = *src)) {
246 if (c == '\\')
247 c = '/';
249 if (c == '/') {
250 if (src[1] == '/' || src[1] == '\\') {
251 src++;
252 i--;
253 continue;
256 i--;
257 *dst++ = *src++;
260 while (1) {
261 if (dst == p)
262 break;
263 if (dst[-1] != '/')
264 break;
265 if ((dst[-1] == '/') && ((dst - 1) == p))
266 break;
268 dst--;
269 i++;
272 i++;
273 for (; i > 0; i --)
274 *dst++ = '\0';
278 * Mangle a normal style string to DOS style string.
280 static void mangle_dos_name(char *mangle_buf, const char *src)
282 int i;
283 unsigned char c;
285 if (src[0] == '.' && (!src[1] || (src[1] == '.' && !src[2]))) {
286 /* . and .. mangle to their respective zero-padded version */
287 i = stpcpy(mangle_buf, src) - mangle_buf;
288 } else {
289 i = 0;
290 while (i < 11) {
291 c = *src++;
293 if ((c <= ' ') || (c == '/'))
294 break;
296 if (c == '.') {
297 while (i < 8)
298 mangle_buf[i++] = ' ';
299 i = 8;
300 continue;
303 c = codepage.upper[c];
304 if (i == 0 && c == 0xe5)
305 c = 0x05; /* Special hack for the first byte only! */
307 mangle_buf[i++] = c;
311 while (i < 11)
312 mangle_buf[i++] = ' ';
314 mangle_buf[i] = '\0';
318 * Match a string name against a longname. "len" is the number of
319 * codepoints in the input; including padding.
321 * Returns true on match.
323 static bool vfat_match_longname(const char *str, const uint16_t *match,
324 int len)
326 unsigned char c = -1; /* Nonzero: we have not yet seen NUL */
327 uint16_t cp;
329 dprintf("Matching: %s len %d\n", str, len);
331 while (len) {
332 cp = *match++;
333 len--;
334 if (!cp)
335 break;
336 c = *str++;
337 if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
338 return false; /* Also handles c == '\0' */
341 /* This should have been the end of the matching string */
342 if (*str)
343 return false;
345 /* Any padding entries must be FFFF */
346 while (len--)
347 if (*match++ != 0xffff)
348 return false;
350 return true;
354 * Convert an UTF-16 longname to the system codepage; return
355 * the length on success or -1 on failure.
357 static int vfat_cvt_longname(char *entry_name, const uint16_t *long_name)
359 struct unicache {
360 uint16_t utf16;
361 uint8_t cp;
363 static struct unicache unicache[256];
364 struct unicache *uc;
365 uint16_t cp;
366 unsigned int c;
367 char *p = entry_name;
369 do {
370 cp = *long_name++;
371 uc = &unicache[cp % 256];
373 if (__likely(uc->utf16 == cp)) {
374 *p++ = uc->cp;
375 } else {
376 for (c = 0; c < 512; c++) {
377 /* This is a bit hacky... */
378 if (codepage.uni[0][c] == cp) {
379 uc->utf16 = cp;
380 *p++ = uc->cp = (uint8_t)c;
381 goto found;
384 return -1; /* Impossible character */
385 found:
388 } while (cp);
390 return (p-entry_name)-1;
393 static void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de)
395 const struct fat_long_name_entry *le =
396 (const struct fat_long_name_entry *)de;
398 memcpy(buf, le->name1, 5 * 2);
399 memcpy(buf + 5, le->name2, 6 * 2);
400 memcpy(buf + 11, le->name3, 2 * 2);
403 static uint8_t get_checksum(const char *dir_name)
405 int i;
406 uint8_t sum = 0;
408 for (i = 11; i; i--)
409 sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++;
410 return sum;
414 /* compute the first sector number of one dir where the data stores */
415 static inline sector_t first_sector(struct fs_info *fs,
416 const struct fat_dir_entry *dir)
418 const struct fat_sb_info *sbi = FAT_SB(fs);
419 sector_t first_clust;
420 sector_t sector;
422 first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
423 if (first_clust == 0)
424 sector = sbi->root; /* first_clust == 0 means root directory */
425 else
426 sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
428 return sector;
431 static inline enum dirent_type get_inode_mode(uint8_t attr)
433 return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
437 static struct inode *vfat_find_entry(const char *dname, struct inode *dir)
439 struct fs_info *fs = dir->fs;
440 struct inode *inode;
441 const struct fat_dir_entry *de;
442 struct fat_long_name_entry *long_de;
444 char mangled_name[12];
445 uint16_t long_name[260]; /* == 20*13 */
446 int long_len;
448 sector_t dir_sector = PVT(dir)->start;
449 uint8_t vfat_init, vfat_next, vfat_csum = 0;
450 uint8_t id;
451 int slots;
452 int entries;
453 int checksum;
454 int long_match = 0;
456 slots = (strlen(dname) + 12) / 13;
457 if (slots > 20)
458 return NULL; /* Name too long */
460 slots |= 0x40;
461 vfat_init = vfat_next = slots;
462 long_len = slots*13;
464 /* Produce the shortname version, in case we need it. */
465 mangle_dos_name(mangled_name, dname);
467 while (dir_sector) {
468 de = get_cache(fs->fs_dev, dir_sector);
469 entries = 1 << (fs->sector_shift - 5);
471 while (entries--) {
472 if (de->name[0] == 0)
473 return NULL;
475 if (de->attr == 0x0f) {
477 * It's a long name entry.
479 long_de = (struct fat_long_name_entry *)de;
480 id = long_de->id;
481 if (id != vfat_next)
482 goto not_match;
484 if (id & 0x40) {
485 /* get the initial checksum value */
486 vfat_csum = long_de->checksum;
487 id &= 0x3f;
488 long_len = id * 13;
490 /* ZERO the long_name buffer */
491 memset(long_name, 0, sizeof long_name);
492 } else {
493 if (long_de->checksum != vfat_csum)
494 goto not_match;
497 vfat_next = --id;
499 /* got the long entry name */
500 copy_long_chunk(long_name + id*13, de);
503 * If we got the last entry, check it.
504 * Or, go on with the next entry.
506 if (id == 0) {
507 if (!vfat_match_longname(dname, long_name, long_len))
508 goto not_match;
509 long_match = 1;
511 de++;
512 continue; /* Try the next entry */
513 } else {
515 * It's a short entry
517 if (de->attr & 0x08) /* ignore volume labels */
518 goto not_match;
520 if (long_match) {
522 * We already have a VFAT long name match. However, the
523 * match is only valid if the checksum matches.
525 checksum = get_checksum(de->name);
526 if (checksum == vfat_csum)
527 goto found; /* Got it */
528 } else {
529 if (!memcmp(mangled_name, de->name, 11))
530 goto found;
534 not_match:
535 vfat_next = vfat_init;
536 long_match = 0;
538 de++;
541 /* Try with the next sector */
542 dir_sector = get_next_sector(fs, dir_sector);
544 return NULL; /* Nothing found... */
546 found:
547 inode = new_fat_inode(fs);
548 inode->size = de->file_size;
549 PVT(inode)->start_cluster =
550 (de->first_cluster_high << 16) + de->first_cluster_low;
551 if (PVT(inode)->start_cluster == 0) {
552 /* Root directory */
553 int root_size = FAT_SB(fs)->root_size;
555 PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
556 inode->size = root_size ? root_size << fs->sector_shift : ~0;
557 PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
558 } else {
559 PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
561 inode->mode = get_inode_mode(de->attr);
563 return inode;
566 static struct inode *vfat_iget_root(struct fs_info *fs)
568 struct inode *inode = new_fat_inode(fs);
569 int root_size = FAT_SB(fs)->root_size;
572 * For FAT32, the only way to get the root directory size is to
573 * follow the entire FAT chain to the end... which seems pointless.
575 PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
576 inode->size = root_size ? root_size << fs->sector_shift : ~0;
577 PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
578 inode->mode = DT_DIR;
580 return inode;
583 static struct inode *vfat_iget(const char *dname, struct inode *parent)
585 return vfat_find_entry(dname, parent);
588 static int vfat_readdir(struct file *file, struct dirent *dirent)
590 struct fs_info *fs = file->fs;
591 const struct fat_dir_entry *de;
592 const char *data;
593 const struct fat_long_name_entry *long_de;
595 sector_t sector = get_the_right_sector(file);
597 uint16_t long_name[261]; /* == 20*13 + 1 (to guarantee null) */
598 char filename[261];
599 int name_len = 0;
601 uint8_t vfat_next, vfat_csum;
602 uint8_t id;
603 int entries_left;
604 bool long_entry = false;
605 int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
607 data = get_cache(fs->fs_dev, sector);
608 de = (const struct fat_dir_entry *)(data + sec_off);
609 entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
611 vfat_next = vfat_csum = 0xff;
613 while (1) {
614 while (entries_left--) {
615 if (de->name[0] == 0)
616 return -1; /* End of directory */
617 if ((uint8_t)de->name[0] == 0xe5)
618 goto invalid;
620 if (de->attr == 0x0f) {
622 * It's a long name entry.
624 long_de = (struct fat_long_name_entry *)de;
625 id = long_de->id;
627 if (id & 0x40) {
628 /* init vfat_csum */
629 vfat_csum = long_de->checksum;
630 id &= 0x3f;
631 if (id >= 20)
632 goto invalid; /* Too long! */
634 /* ZERO the long_name buffer */
635 memset(long_name, 0, sizeof long_name);
636 } else {
637 if (long_de->checksum != vfat_csum || id != vfat_next)
638 goto invalid;
641 vfat_next = --id;
643 /* got the long entry name */
644 copy_long_chunk(long_name + id*13, de);
646 if (id == 0) {
647 name_len = vfat_cvt_longname(filename, long_name);
648 if (name_len > 0 && name_len < sizeof(dirent->d_name))
649 long_entry = true;
652 goto next;
653 } else {
655 * It's a short entry
657 if (de->attr & 0x08) /* ignore volume labels */
658 goto invalid;
660 if (long_entry && get_checksum(de->name) == vfat_csum) {
661 /* Got a long entry */
662 } else {
663 /* Use the shortname */
664 int i;
665 uint8_t c;
666 char *p = filename;
668 for (i = 0; i < 8; i++) {
669 c = de->name[i];
670 if (c == ' ')
671 break;
672 if (de->lcase & LCASE_BASE)
673 c = codepage.lower[c];
674 *p++ = c;
676 if (de->name[8] != ' ') {
677 *p++ = '.';
678 for (i = 8; i < 11; i++) {
679 c = de->name[i];
680 if (c == ' ')
681 break;
682 if (de->lcase & LCASE_EXT)
683 c = codepage.lower[c];
684 *p++ = c;
687 *p = '\0';
688 name_len = p - filename;
690 goto got; /* Got something one way or the other */
693 invalid:
694 long_entry = false;
695 next:
696 de++;
697 file->offset += sizeof(struct fat_dir_entry);
700 /* Try with the next sector */
701 sector = next_sector(file);
702 if (!sector)
703 return -1;
704 de = get_cache(fs->fs_dev, sector);
705 entries_left = 1 << (fs->sector_shift - 5);
708 got:
709 name_len++; /* Include final null */
710 dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16);
711 dirent->d_off = file->offset;
712 dirent->d_reclen = offsetof(struct dirent, d_name) + name_len;
713 dirent->d_type = get_inode_mode(de->attr);
714 memcpy(dirent->d_name, filename, name_len);
716 file->offset += sizeof(*de); /* Update for next reading */
718 return 0;
721 /* init. the fs meta data, return the block size in bits */
722 static int vfat_fs_init(struct fs_info *fs)
724 struct fat_bpb fat;
725 struct fat_sb_info *sbi;
726 struct disk *disk = fs->fs_dev->disk;
727 int sectors_per_fat;
728 uint32_t clusters;
729 sector_t total_sectors;
731 fs->sector_shift = fs->block_shift = disk->sector_shift;
732 fs->sector_size = 1 << fs->sector_shift;
733 fs->block_size = 1 << fs->block_shift;
735 disk->rdwr_sectors(disk, &fat, 0, 1, 0);
737 /* XXX: Find better sanity checks... */
738 if (!fat.bxResSectors || !fat.bxFATs)
739 return -1;
740 sbi = malloc(sizeof(*sbi));
741 if (!sbi)
742 malloc_error("fat_sb_info structure");
743 fs->fs_info = sbi;
745 sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
746 total_sectors = fat.bxSectors ? : fat.bsHugeSectors;
748 sbi->fat = fat.bxResSectors;
749 sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs;
750 sbi->root_size = root_dir_size(fs, &fat);
751 sbi->data = sbi->root + sbi->root_size;
753 sbi->clust_shift = ilog2(fat.bxSecPerClust);
754 sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
755 sbi->clust_mask = fat.bxSecPerClust - 1;
756 sbi->clust_size = fat.bxSecPerClust << fs->sector_shift;
758 clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
759 if (clusters <= 0xff4) {
760 sbi->fat_type = FAT12;
761 } else if (clusters <= 0xfff4) {
762 sbi->fat_type = FAT16;
763 } else {
764 sbi->fat_type = FAT32;
766 if (clusters > 0x0ffffff4)
767 clusters = 0x0ffffff4; /* Maximum possible */
769 if (fat.fat32.extended_flags & 0x80) {
770 /* Non-mirrored FATs, we need to read the active one */
771 sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat;
774 /* FAT32: root directory is a cluster chain */
775 sbi->root = sbi->data
776 + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
778 sbi->clusters = clusters;
780 /* Initialize the cache */
781 cache_init(fs->fs_dev, fs->block_shift);
783 return fs->block_shift;
786 static int vfat_copy_superblock(void *buf)
788 struct fat_bpb fat;
789 struct disk *disk;
790 size_t sb_off;
791 void *dst;
792 int sb_len;
794 disk = this_fs->fs_dev->disk;
795 disk->rdwr_sectors(disk, &fat, 0, 1, 0);
797 /* XXX: Find better sanity checks... */
798 if (!fat.bxResSectors || !fat.bxFATs)
799 return -1;
801 sb_off = offsetof(struct fat_bpb, sector_size);
802 sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \
803 + sizeof(fat.fat12_16);
806 * Only copy fields of the superblock we actually care about.
808 dst = buf + sb_off;
809 memcpy(dst, (void *)&fat + sb_off, sb_len);
811 return 0;
814 const struct fs_ops vfat_fs_ops = {
815 .fs_name = "vfat",
816 .fs_flags = FS_USEMEM | FS_THISIND,
817 .fs_init = vfat_fs_init,
818 .searchdir = NULL,
819 .getfssec = generic_getfssec,
820 .close_file = generic_close_file,
821 .mangle_name = vfat_mangle_name,
822 .chdir_start = generic_chdir_start,
823 .open_config = generic_open_config,
824 .readdir = vfat_readdir,
825 .iget_root = vfat_iget_root,
826 .iget = vfat_iget,
827 .next_extent = fat_next_extent,
828 .copy_super = vfat_copy_superblock,