843c90c82e08c5e2293f1b47ac5d03c2c53fbaeb
[profile/ivi/syslinux.git] / core / fs / fat / fat.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <sys/dirent.h>
4 #include <cache.h>
5 #include <core.h>
6 #include <disk.h>
7 #include <fs.h>
8 #include "fat_fs.h"
9
10
11 static struct inode * new_fat_inode(void)
12 {
13     struct inode *inode = malloc(sizeof(*inode));
14     if (!inode) 
15         malloc_error("inode structure");                
16     memset(inode, 0, sizeof(*inode));
17     
18     /* 
19      * We just need allocate one uint32_t data to store the 
20      * first cluster number.
21      */
22     inode->data = malloc(sizeof(uint32_t));
23     if (!inode->data) 
24         malloc_error("inode->data");
25     
26     return inode;
27 }
28
29
30 static void vfat_close_file(struct file *file)
31 {
32     if (file->inode) {
33         file->offset = 0;
34         free_inode(file->inode);
35     }
36 }
37
38
39 /*
40  * Check for a particular sector in the FAT cache
41  */
42 static struct cache_struct * get_fat_sector(struct fs_info *fs, sector_t sector)
43 {
44     return get_cache_block(fs->fs_dev, FAT_SB(fs)->fat + sector);
45 }
46
47 static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
48 {
49     uint32_t next_cluster;
50     sector_t fat_sector;
51     uint32_t offset;
52     int lo, hi;
53     struct cache_struct *cs;
54     
55     switch(FAT_SB(fs)->fat_type) {
56     case FAT12:
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) {
61             /* 
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.
66              */
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;
71         } else {
72             next_cluster = *(uint16_t *)(cs->data + offset);
73         }
74         
75         if (clust_num & 0x0001)
76             next_cluster >>= 4;         /* cluster number is ODD */
77         else
78             next_cluster &= 0x0fff;     /* cluster number is EVEN */
79         if (next_cluster > 0x0ff0)
80             goto fail;
81         break;
82         
83     case FAT16:
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)
89             goto fail;
90         break;
91         
92     case FAT32:
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)
98             goto fail;
99         break;
100     }
101     
102     return next_cluster;
103     
104 fail:  
105     /* got an unexcepted cluster number, so return ZERO */
106     return 0;
107 }
108
109
110 static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
111 {
112     sector_t data_area = FAT_SB(fs)->data;
113     sector_t data_sector;
114     uint32_t cluster;
115     
116     if (sector < data_area) {
117         sector++;
118         /* if we reached the end of root area */
119         if (sector == data_area)
120             sector = 0; /* return 0 */
121         return sector;
122     }
123     
124     data_sector = sector - data_area;
125     if ((data_sector + 1) & FAT_SB(fs)->clust_mask)  /* in a cluster */
126         return ++sector;
127     
128     /* get a new cluster */
129     cluster = get_next_cluster(fs, (data_sector >> FAT_SB(fs)->clust_shift) + 2);
130     if (!cluster ) 
131         return 0;
132     
133     /* return the start of the new cluster */
134     sector = ((cluster - 2) << FAT_SB(fs)->clust_shift) + data_area;
135     return sector;
136 }
137
138 /*
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.
143  *
144  * What a waste of time!
145  */
146 static sector_t get_the_right_sector(struct file *file)
147 {
148     int i = 0;
149     int sector_pos  = file->offset >> SECTOR_SHIFT;
150     sector_t sector = *file->inode->data;
151     
152     for (; i < sector_pos; i++) 
153         sector = get_next_sector(file->fs, sector);
154     
155     return sector;
156 }
157
158 /**
159  * __getfssec:
160  *
161  * get multiple sectors from a file
162  *
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
165  * 64K boundaries.
166  *
167  */
168 static void __getfssec(struct fs_info *fs, char *buf, 
169                        struct file *file, uint32_t sectors)
170 {
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;
175     
176     while (sectors) {
177         /* get fragment */
178         con_sec_cnt = 0;
179         frag_start = curr_sector;
180         
181         do {
182             /* get consective sector  count */
183             con_sec_cnt ++;
184             sectors --;
185             if (sectors == 0)
186                 break;
187             
188             next_sector = get_next_sector(fs, curr_sector);
189             if (!next_sector)
190                 break;                        
191         }while(next_sector == (++curr_sector));
192         
193 #if 0   
194         printf("You are reading data stored at sector --0x%x--0x%x\n", 
195                frag_start, frag_start + con_sec_cnt -1);
196 #endif 
197                         
198         /* do read */
199         disk->rdwr_sectors(disk, buf, frag_start, con_sec_cnt, 0);
200         buf += con_sec_cnt << SECTOR_SHIFT;/* adjust buffer pointer */
201         
202         if (!sectors)
203             break;
204         
205         curr_sector = next_sector;
206     }
207     
208 }
209
210
211
212 /**
213  * get multiple sectors from a file 
214  *
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
219  *
220  * @return: number of bytes read
221  *
222  */
223 static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors,
224                               bool *have_more)
225 {
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;
229     int sector_left;
230         
231     sector_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> fs->sector_shift;
232     if (sectors > sector_left)
233         sectors = sector_left;
234     
235     __getfssec(fs, buf, file, sectors);
236     
237     if (bytes_read >= bytes_left) {
238         bytes_read = bytes_left;
239         *have_more = 0;
240     } else {
241         *have_more = 1;
242     }    
243     file->offset += bytes_read;
244     
245     return bytes_read;
246 }
247
248 /*
249  * Mangle a filename pointed to by src into a buffer pointed to by dst; 
250  * ends on encountering any whitespace.
251  *
252  */
253 static void vfat_mangle_name(char *dst, const char *src)
254 {
255     char *p = dst;
256     char c;
257     int i = FILENAME_MAX -1;
258     
259     /*
260      * Copy the filename, converting backslash to slash and
261      * collapsing duplicate separators.
262      */
263     while (not_whitespace(c = *src)) {
264         if (c == '\\')
265             c = '/';
266         
267         if (c == '/') {
268             if (src[1] == '/' || src[1] == '\\') {
269                 src++;
270                 i--;
271                 continue;
272             }
273         }        
274         i--;
275         *dst++ = *src++;
276     }
277
278     /* Strip terminal slashes or whitespace */
279     while (1) {
280         if (dst == p)
281             break;
282                 if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */
283                         break;
284         if ((*(dst-1) != '/') && (*(dst-1) != '.'))
285             break;
286         
287         dst--;
288         i++;
289     }
290
291     i++;
292     for (; i > 0; i --)
293         *dst++ = '\0';
294 }
295
296 /*
297  * Mangle a normal style string to DOS style string.
298  */
299 static void mangle_dos_name(char *mangle_buf, char *src)
300 {       
301     char *dst = mangle_buf;
302     int i = 0;
303     unsigned char c;        
304     
305     for (; i < 11; i ++)
306         mangle_buf[i] = ' ';
307     
308     for (i = 0; i < 11; i++) {
309         c = *src ++;
310         
311         if ((c <= ' ') || (c == '/')) 
312             break;
313         
314         if (c == '.') {
315             dst = &mangle_buf[8];
316             i = 7;
317             continue;
318         }
319         
320         if (c >= 'a' && c <= 'z')
321             c -= 32;
322         if ((c == 0xe5) && (i == 11))
323             c = 0x05;
324         
325         *dst++ = c;
326     }
327     mangle_buf[11] = '\0';
328 }
329
330
331 /* try with the biggest long name */
332 static char long_name[0x40 * 13];
333 static char entry_name[14];
334
335 static void unicode_to_ascii(char *entry_name, uint16_t *unicode_buf)
336 {
337     int i = 0;
338     
339     for (; i < 13; i++) {
340         if (unicode_buf[i] == 0xffff) {
341             entry_name[i] = '\0';
342             return;
343         }
344         entry_name[i] = (char)unicode_buf[i];
345     }
346 }
347
348 /*
349  * get the long entry name
350  *
351  */
352 static void long_entry_name(struct fat_long_name_entry *dir)
353 {
354     uint16_t unicode_buf[13];
355     
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);
359     
360     unicode_to_ascii(entry_name, unicode_buf);    
361 }
362
363
364 static uint8_t get_checksum(char *dir_name)
365 {
366     int  i;
367     uint8_t sum = 0;
368     
369     for (i = 11; i; i--)
370         sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++;
371     return sum;
372 }
373
374
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)
377 {
378     struct fat_sb_info *sbi = FAT_SB(this_fs);
379     uint32_t first_clust;
380     sector_t sector;
381     
382     first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
383     sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
384     
385     return sector;
386 }
387
388 static inline int get_inode_mode(uint8_t attr)
389 {
390     if (attr == FAT_ATTR_DIRECTORY)
391         return I_DIR;
392     else
393         return I_FILE;
394 }
395
396  
397 static struct inode *vfat_find_entry(char *dname, struct inode *dir)
398 {
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;
403     
404     char mangled_name[12] = {0, };
405     sector_t dir_sector = *dir->data;
406     
407     uint8_t vfat_init, vfat_next, vfat_csum = 0;
408     uint8_t id;
409     int slots;
410     int entries;
411     int checksum;
412     int long_match = 0;
413     
414     slots = (strlen(dname) + 12) / 13 ;
415     slots |= 0x40;
416     vfat_init = vfat_next = slots;
417     
418     while (1) {
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);
422         
423         while(entries--) {
424             if (de->name[0] == 0)
425                 return NULL;
426             
427             if (de->attr == 0x0f) {
428                 /*
429                  * It's a long name entry.
430                  */
431                 long_de = (struct fat_long_name_entry *)de;
432                 id = long_de->id;
433                 if (id != vfat_next)
434                     goto not_match;
435                 
436                 if (id & 0x40) {
437                     /* get the initial checksum value */
438                     vfat_csum = long_de->checksum;
439                     id &= 0x3f;
440
441                     /* ZERO the long_name buffer */
442                     memset(long_name, 0, sizeof long_name);
443                 } else {
444                     if (long_de->checksum != vfat_csum)
445                         goto not_match;
446                 }
447                 
448                 vfat_next = --id;
449                 
450                 /* got the long entry name */
451                 long_entry_name(long_de);
452                 memcpy(long_name + id * 13, entry_name, 13);
453                                 
454                 /* 
455                  * If we got the last entry, check it.
456                  * Or, go on with the next entry.
457                  */
458                 if (id == 0) {
459                     if (strcmp(long_name, dname))
460                         goto not_match;
461                     long_match = 1;
462                 }
463                 
464                 de++;
465                 continue;     /* Try the next entry */
466             } else {
467                 /*
468                  * It's a short entry 
469                  */
470                 if (de->attr & 0x08) /* ignore volume labels */
471                     goto not_match;
472                 
473                 if (long_match == 1) {
474                     /* 
475                      * We already have a VFAT long name match. However, the 
476                      * match is only valid if the checksum matches.
477                      *
478                      * Well, let's trun the long_match flag off first.
479                      */
480                      long_match = 0;
481                     checksum = get_checksum(de->name);
482                     if (checksum == vfat_csum)
483                         goto found;  /* Got it */
484                 } else {
485                     if (mangled_name[0] == 0) {
486                         /* We haven't mangled it, mangle it first. */
487                         mangle_dos_name(mangled_name, dname);
488                     }
489                     
490                     if (!strncmp(mangled_name, de->name, 11))
491                         goto found;
492                 }
493             }
494             
495         not_match:
496             vfat_next = vfat_init;
497             
498             de++;
499         }
500         
501         /* Try with the next sector */
502         dir_sector = get_next_sector(this_fs, dir_sector);
503         if (!dir_sector)
504             return NULL;
505     }
506     
507 found:
508     inode->size = de->file_size;
509     *inode->data = first_sector(de);
510     inode->mode = get_inode_mode(de->attr);
511     
512     return inode;
513 }
514
515 static struct inode *vfat_iget_root(void)
516 {
517     struct inode *inode = new_fat_inode();
518     int root_size = FAT_SB(this_fs)->root_size;
519     
520     inode->size = root_size << this_fs->sector_shift;
521     *inode->data = FAT_SB(this_fs)->root;
522     inode->mode = I_DIR;
523     
524     return inode;
525 }
526
527 static struct inode *vfat_iget(char *dname, struct inode *parent)
528 {
529     return vfat_find_entry(dname, parent);
530 }
531
532 /*
533  * The open dir function, just call the searchdir function  directly. 
534  * I don't think we need call the mangle_name function first 
535  */
536 void vfat_opendir(com32sys_t *regs)
537 {
538     char *src = MK_PTR(regs->es, regs->esi.w[0]);
539     char *dst = MK_PTR(regs->ds, regs->edi.w[0]);
540     strcpy(dst, src);
541     searchdir(regs);    
542 }
543
544
545 /* Load the config file, return 1 if failed, or 0 */
546 static int vfat_load_config(void)
547 {
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 };
553     com32sys_t regs;
554     char *p;
555     int i = 0;
556
557     /* 
558      * we use the ConfigName to pass the config path because
559      * it is under the address 0xffff
560      */
561     memset(&regs, 0, sizeof regs);
562     regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
563     for (; i < 3; i++) {
564         strcpy(ConfigName, syslinux_cfg[i]);
565         call16(core_open, &regs, &regs);
566
567         /* if zf flag set, then failed; try another */
568         if (! (regs.eflags.l & EFLAGS_ZF))
569             break;
570     }
571     if (i == 3) {
572         printf("no config file found\n");
573         return 1;  /* no config file */
574     }
575     
576     strcpy(ConfigName, "syslinux.cfg");
577     strcpy(CurrentDirName, syslinux_cfg[i]);
578     p = strrchr(CurrentDirName, '/');
579     *p = '\0';
580     
581     return 0;
582 }
583  
584 static inline __constfunc uint32_t bsr(uint32_t num)
585 {
586     asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
587     return num;
588 }
589
590 /* init. the fs meta data, return the block size in bits */
591 static int vfat_fs_init(struct fs_info *fs)
592 {
593     struct fat_bpb fat;
594     struct fat_sb_info *sbi;
595     struct disk *disk = fs->fs_dev->disk;
596     int sectors_per_fat;
597     uint32_t clust_num;
598     sector_t total_sectors;
599     
600     fs->sector_shift = fs->block_shift = disk->sector_shift;
601     disk->rdwr_sectors(disk, &fat, 0, 1, 0);
602     
603     sbi = malloc(sizeof(*sbi));
604     if (!sbi)
605         malloc_error("fat_sb_info structure");
606     fs->fs_info = sbi;
607     this_fs = fs;
608     
609     sectors_per_fat = fat.bxFATsecs ? : fat.u.fat32.bxFATsecs_32;
610     total_sectors   = fat.bxSectors ? : fat.bsHugeSectors;
611     
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;
616     
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;
621     
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;
627     else
628         sbi->fat_type = FAT32;
629     
630     /* for SYSLINUX, the cache is based on sector size */
631     return fs->sector_shift;
632 }
633         
634 const struct fs_ops vfat_fs_ops = {
635     .fs_name       = "vfat",
636     .fs_flags      = FS_USEMEM | FS_THISIND,
637     .fs_init       = vfat_fs_init,
638     .searchdir     = NULL,
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,
645     .readdir       = NULL,
646     .iget_root     = vfat_iget_root,
647     .iget_current  = NULL,
648     .iget          = vfat_iget,
649 };