3 #include <sys/dirent.h>
11 static struct inode * new_fat_inode(void)
13 struct inode *inode = malloc(sizeof(*inode));
15 malloc_error("inode structure");
16 memset(inode, 0, sizeof(*inode));
19 * We just need allocate one uint32_t data to store the
20 * first cluster number.
22 inode->data = malloc(sizeof(uint32_t));
24 malloc_error("inode->data");
30 static void vfat_close_file(struct file *file)
34 free_inode(file->inode);
40 * Check for a particular sector in the FAT cache
42 static struct cache_struct * get_fat_sector(struct fs_info *fs, sector_t sector)
44 return get_cache_block(fs->fs_dev, FAT_SB(fs)->fat + sector);
47 static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
49 uint32_t next_cluster;
53 struct cache_struct *cs;
55 switch(FAT_SB(fs)->fat_type) {
57 fat_sector = (clust_num + clust_num / 2) >> SECTOR_SHIFT;
58 cs = get_fat_sector(fs, fat_sector);
59 offset = (clust_num * 3 / 2) & ((1 << SECTOR_SHIFT) - 1);
60 if (offset == 0x1ff) {
62 * we got the end of the one fat sector,
63 * but we don't got we have(just one byte, we need two),
64 * so store the low part, then read the next fat
65 * sector, read the high part, then combine it.
67 lo = *(uint8_t *)(cs->data + offset);
68 cs = get_fat_sector(fs, fat_sector + 1);
69 hi = *(uint8_t *)cs->data;
70 next_cluster = (hi << 8) + lo;
72 next_cluster = *(uint16_t *)(cs->data + offset);
75 if (clust_num & 0x0001)
76 next_cluster >>= 4; /* cluster number is ODD */
78 next_cluster &= 0x0fff; /* cluster number is EVEN */
79 if (next_cluster > 0x0ff0)
84 fat_sector = clust_num >> (SECTOR_SHIFT - 1);
85 offset = clust_num & ((1 << (SECTOR_SHIFT-1)) -1);
86 cs = get_fat_sector(fs, fat_sector);
87 next_cluster = ((uint16_t *)cs->data)[offset];
88 if (next_cluster > 0xfff0)
93 fat_sector = clust_num >> (SECTOR_SHIFT - 2);
94 offset = clust_num & ((1 << (SECTOR_SHIFT-2)) -1);
95 cs = get_fat_sector(fs, fat_sector);
96 next_cluster = ((uint32_t *)cs->data)[offset] & 0x0fffffff;
97 if (next_cluster > 0x0ffffff0)
105 /* got an unexcepted cluster number, so return ZERO */
110 static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
112 sector_t data_area = FAT_SB(fs)->data;
113 sector_t data_sector;
116 if (sector < data_area) {
118 /* if we reached the end of root area */
119 if (sector == data_area)
120 sector = 0; /* return 0 */
124 data_sector = sector - data_area;
125 if ((data_sector + 1) & FAT_SB(fs)->clust_mask) /* in a cluster */
128 /* get a new cluster */
129 cluster = get_next_cluster(fs, (data_sector >> FAT_SB(fs)->clust_shift) + 2);
133 /* return the start of the new cluster */
134 sector = ((cluster - 2) << FAT_SB(fs)->clust_shift) + data_area;
139 * Here comes the place I don't like VFAT fs most; if we need seek
140 * the file to the right place, we need get the right sector address
141 * from begining everytime! Since it's a kind a signle link list, we
142 * need to traver from the head-node to find the right node in that list.
144 * What a waste of time!
146 static sector_t get_the_right_sector(struct file *file)
149 int sector_pos = file->offset >> SECTOR_SHIFT;
150 sector_t sector = *file->inode->data;
152 for (; i < sector_pos; i++)
153 sector = get_next_sector(file->fs, sector);
161 * get multiple sectors from a file
163 * This routine makes sure the subransfers do not cross a 64K boundary
164 * and will correct the situation if it does, UNLESS *sectos* cross
168 static void __getfssec(struct fs_info *fs, char *buf,
169 struct file *file, uint32_t sectors)
171 sector_t curr_sector = get_the_right_sector(file);
172 sector_t frag_start , next_sector;
173 uint32_t con_sec_cnt;
174 struct disk *disk = fs->fs_dev->disk;
179 frag_start = curr_sector;
182 /* get consective sector count */
188 next_sector = get_next_sector(fs, curr_sector);
191 }while(next_sector == (++curr_sector));
194 printf("You are reading data stored at sector --0x%x--0x%x\n",
195 frag_start, frag_start + con_sec_cnt -1);
199 disk->rdwr_sectors(disk, buf, frag_start, con_sec_cnt, 0);
200 buf += con_sec_cnt << SECTOR_SHIFT;/* adjust buffer pointer */
205 curr_sector = next_sector;
213 * get multiple sectors from a file
215 * @param: buf, the buffer to store the read data
216 * @param: file, the file structure pointer
217 * @param: sectors, number of sectors wanna read
218 * @param: have_more, set one if has more
220 * @return: number of bytes read
223 static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors,
226 struct fs_info *fs = file->fs;
227 uint32_t bytes_left = file->inode->size - file->offset;
228 uint32_t bytes_read = sectors << fs->sector_shift;
231 sector_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> fs->sector_shift;
232 if (sectors > sector_left)
233 sectors = sector_left;
235 __getfssec(fs, buf, file, sectors);
237 if (bytes_read >= bytes_left) {
238 bytes_read = bytes_left;
243 file->offset += bytes_read;
249 * Mangle a filename pointed to by src into a buffer pointed to by dst;
250 * ends on encountering any whitespace.
253 static void vfat_mangle_name(char *dst, const char *src)
257 int i = FILENAME_MAX -1;
260 * Copy the filename, converting backslash to slash and
261 * collapsing duplicate separators.
263 while (not_whitespace(c = *src)) {
268 if (src[1] == '/' || src[1] == '\\') {
278 /* Strip terminal slashes or whitespace */
282 if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */
284 if ((*(dst-1) != '/') && (*(dst-1) != '.'))
297 * Mangle a normal style string to DOS style string.
299 static void mangle_dos_name(char *mangle_buf, char *src)
301 char *dst = mangle_buf;
308 for (i = 0; i < 11; i++) {
311 if ((c <= ' ') || (c == '/'))
315 dst = &mangle_buf[8];
320 if (c >= 'a' && c <= 'z')
322 if ((c == 0xe5) && (i == 11))
327 mangle_buf[11] = '\0';
331 /* try with the biggest long name */
332 static char long_name[0x40 * 13];
333 static char entry_name[14];
335 static void unicode_to_ascii(char *entry_name, uint16_t *unicode_buf)
339 for (; i < 13; i++) {
340 if (unicode_buf[i] == 0xffff) {
341 entry_name[i] = '\0';
344 entry_name[i] = (char)unicode_buf[i];
349 * get the long entry name
352 static void long_entry_name(struct fat_long_name_entry *dir)
354 uint16_t unicode_buf[13];
356 memcpy(unicode_buf, dir->name1, 5 * 2);
357 memcpy(unicode_buf + 5, dir->name2, 6 * 2);
358 memcpy(unicode_buf + 11, dir->name3, 2 * 2);
360 unicode_to_ascii(entry_name, unicode_buf);
364 static uint8_t get_checksum(char *dir_name)
370 sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++;
375 /* compute the first sector number of one dir where the data stores */
376 static inline sector_t first_sector(struct fat_dir_entry *dir)
378 struct fat_sb_info *sbi = FAT_SB(this_fs);
379 uint32_t first_clust;
382 first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
383 sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
388 static inline int get_inode_mode(uint8_t attr)
390 if (attr == FAT_ATTR_DIRECTORY)
397 static struct inode *vfat_find_entry(char *dname, struct inode *dir)
399 struct inode *inode = new_fat_inode();
400 struct fat_dir_entry *de;
401 struct fat_long_name_entry *long_de;
402 struct cache_struct *cs;
404 char mangled_name[12] = {0, };
405 sector_t dir_sector = *dir->data;
407 uint8_t vfat_init, vfat_next, vfat_csum = 0;
414 slots = (strlen(dname) + 12) / 13 ;
416 vfat_init = vfat_next = slots;
419 cs = get_cache_block(this_fs->fs_dev, dir_sector);
420 de = (struct fat_dir_entry *)cs->data;
421 entries = 1 << (this_fs->sector_shift - 5);
424 if (de->name[0] == 0)
427 if (de->attr == 0x0f) {
429 * It's a long name entry.
431 long_de = (struct fat_long_name_entry *)de;
437 /* get the initial checksum value */
438 vfat_csum = long_de->checksum;
441 /* ZERO the long_name buffer */
442 memset(long_name, 0, sizeof long_name);
444 if (long_de->checksum != vfat_csum)
450 /* got the long entry name */
451 long_entry_name(long_de);
452 memcpy(long_name + id * 13, entry_name, 13);
455 * If we got the last entry, check it.
456 * Or, go on with the next entry.
459 if (strcmp(long_name, dname))
465 continue; /* Try the next entry */
470 if (de->attr & 0x08) /* ignore volume labels */
473 if (long_match == 1) {
475 * We already have a VFAT long name match. However, the
476 * match is only valid if the checksum matches.
478 * Well, let's trun the long_match flag off first.
481 checksum = get_checksum(de->name);
482 if (checksum == vfat_csum)
483 goto found; /* Got it */
485 if (mangled_name[0] == 0) {
486 /* We haven't mangled it, mangle it first. */
487 mangle_dos_name(mangled_name, dname);
490 if (!strncmp(mangled_name, de->name, 11))
496 vfat_next = vfat_init;
501 /* Try with the next sector */
502 dir_sector = get_next_sector(this_fs, dir_sector);
508 inode->size = de->file_size;
509 *inode->data = first_sector(de);
510 inode->mode = get_inode_mode(de->attr);
515 static struct inode *vfat_iget_root(void)
517 struct inode *inode = new_fat_inode();
518 int root_size = FAT_SB(this_fs)->root_size;
520 inode->size = root_size << this_fs->sector_shift;
521 *inode->data = FAT_SB(this_fs)->root;
527 static struct inode *vfat_iget(char *dname, struct inode *parent)
529 return vfat_find_entry(dname, parent);
533 * The open dir function, just call the searchdir function directly.
534 * I don't think we need call the mangle_name function first
536 void vfat_opendir(com32sys_t *regs)
538 char *src = MK_PTR(regs->es, regs->esi.w[0]);
539 char *dst = MK_PTR(regs->ds, regs->edi.w[0]);
545 /* Load the config file, return 1 if failed, or 0 */
546 static int vfat_load_config(void)
548 static const char syslinux_cfg1[] = "/boot/syslinux/syslinux.cfg";
549 static const char syslinux_cfg2[] = "/syslinux/syslinux.cfg";
550 static const char syslinux_cfg3[] = "/syslinux.cfg";
551 const char * const syslinux_cfg[] =
552 { syslinux_cfg1, syslinux_cfg2, syslinux_cfg3 };
558 * we use the ConfigName to pass the config path because
559 * it is under the address 0xffff
561 memset(®s, 0, sizeof regs);
562 regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
564 strcpy(ConfigName, syslinux_cfg[i]);
565 call16(core_open, ®s, ®s);
567 /* if zf flag set, then failed; try another */
568 if (! (regs.eflags.l & EFLAGS_ZF))
572 printf("no config file found\n");
573 return 1; /* no config file */
576 strcpy(ConfigName, "syslinux.cfg");
577 strcpy(CurrentDirName, syslinux_cfg[i]);
578 p = strrchr(CurrentDirName, '/');
584 static inline __constfunc uint32_t bsr(uint32_t num)
586 asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
590 /* init. the fs meta data, return the block size in bits */
591 static int vfat_fs_init(struct fs_info *fs)
594 struct fat_sb_info *sbi;
595 struct disk *disk = fs->fs_dev->disk;
598 sector_t total_sectors;
600 fs->sector_shift = fs->block_shift = disk->sector_shift;
601 disk->rdwr_sectors(disk, &fat, 0, 1, 0);
603 sbi = malloc(sizeof(*sbi));
605 malloc_error("fat_sb_info structure");
609 sectors_per_fat = fat.bxFATsecs ? : fat.u.fat32.bxFATsecs_32;
610 total_sectors = fat.bxSectors ? : fat.bsHugeSectors;
612 sbi->fat = fat.bxResSectors;
613 sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs;
614 sbi->root_size = root_dir_size(&fat);
615 sbi->data = sbi->root + sbi->root_size;
617 sbi->clust_shift = bsr(fat.bxSecPerClust);
618 sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
619 sbi->clust_mask = fat.bxSecPerClust - 1;
620 sbi->clust_size = fat.bxSecPerClust << fs->sector_shift;
622 clust_num = (total_sectors - sbi->data) >> sbi->clust_shift;
623 if (clust_num < 4085)
624 sbi->fat_type = FAT12;
625 else if (clust_num < 65525)
626 sbi->fat_type = FAT16;
628 sbi->fat_type = FAT32;
630 /* for SYSLINUX, the cache is based on sector size */
631 return fs->sector_shift;
634 const struct fs_ops vfat_fs_ops = {
636 .fs_flags = FS_USEMEM | FS_THISIND,
637 .fs_init = vfat_fs_init,
639 .getfssec = vfat_getfssec,
640 .close_file = vfat_close_file,
641 .mangle_name = vfat_mangle_name,
642 .unmangle_name = generic_unmangle_name,
643 .load_config = vfat_load_config,
644 .opendir = vfat_opendir,
646 .iget_root = vfat_iget_root,
647 .iget_current = NULL,