4 #include <sys/dirent.h>
10 #include <klibc/compiler.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
));
18 malloc_error("inode structure");
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;
36 uint32_t sector_mask
= SECTOR_SIZE(fs
) - 1;
39 switch(FAT_SB(fs
)->fat_type
) {
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;
56 next_cluster
= *(const uint16_t *)(data
+ offset
);
59 if (clust_num
& 0x0001)
60 next_cluster
>>= 4; /* cluster number is ODD */
62 next_cluster
&= 0x0fff; /* cluster number is EVEN */
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
);
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;
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
;
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
) {
108 pcluster
= PVT(inode
)->start_cluster
;
112 if (pcluster
-2 >= sbi
->clusters
) {
113 inode
->size
= lcluster
<< sbi
->clust_shift
;
117 if (lcluster
>= mcluster
)
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
;
143 dprintf("fat_next_extent: return error\n");
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
;
153 int clust_shift
= sbi
->clust_shift
;
155 if (sector
< data_area
) {
156 /* Root directory sector... */
158 if (sector
>= data_area
)
159 sector
= 0; /* Ran out of root directory, return EOF */
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
)
174 /* return the start of the new cluster */
175 sector
= (cluster
<< clust_shift
) + data_area
;
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
);
191 if (sector_pos
< PVT(inode
)->offset
) {
194 sector
= PVT(inode
)->start
;
196 where
= PVT(inode
)->offset
;
197 sector
= PVT(inode
)->here
;
200 while (where
< sector_pos
) {
201 sector
= get_next_sector(file
->fs
, sector
);
205 PVT(inode
)->offset
= sector_pos
;
206 PVT(inode
)->here
= 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
;
227 * Mangle a filename pointed to by src into a buffer pointed
228 * to by dst; ends on encountering any whitespace.
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
239 static void vfat_mangle_name(char *dst
, const char *src
)
242 int i
= FILENAME_MAX
-1;
245 while (not_whitespace(c
= *src
)) {
250 if (src
[1] == '/' || src
[1] == '\\') {
265 if ((dst
[-1] == '/') && ((dst
- 1) == p
))
278 * Mangle a normal style string to DOS style string.
280 static void mangle_dos_name(char *mangle_buf
, const char *src
)
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
;
293 if ((c
<= ' ') || (c
== '/'))
298 mangle_buf
[i
++] = ' ';
303 c
= codepage
.upper
[c
];
304 if (i
== 0 && c
== 0xe5)
305 c
= 0x05; /* Special hack for the first byte only! */
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
,
326 unsigned char c
= -1; /* Nonzero: we have not yet seen NUL */
329 dprintf("Matching: %s len %d\n", str
, len
);
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 */
345 /* Any padding entries must be FFFF */
347 if (*match
++ != 0xffff)
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
)
363 static struct unicache unicache
[256];
367 char *p
= entry_name
;
371 uc
= &unicache
[cp
% 256];
373 if (__likely(uc
->utf16
== cp
)) {
376 for (c
= 0; c
< 512; c
++) {
377 /* This is a bit hacky... */
378 if (codepage
.uni
[0][c
] == cp
) {
380 *p
++ = uc
->cp
= (uint8_t)c
;
384 return -1; /* Impossible character */
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
)
409 sum
= ((sum
& 1) << 7) + (sum
>> 1) + (uint8_t)*dir_name
++;
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
;
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 */
426 sector
= ((first_clust
- 2) << sbi
->clust_shift
) + sbi
->data
;
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
;
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 */
448 sector_t dir_sector
= PVT(dir
)->start
;
449 uint8_t vfat_init
, vfat_next
, vfat_csum
= 0;
456 slots
= (strlen(dname
) + 12) / 13;
458 return NULL
; /* Name too long */
461 vfat_init
= vfat_next
= slots
;
464 /* Produce the shortname version, in case we need it. */
465 mangle_dos_name(mangled_name
, dname
);
468 de
= get_cache(fs
->fs_dev
, dir_sector
);
469 entries
= 1 << (fs
->sector_shift
- 5);
472 if (de
->name
[0] == 0)
475 if (de
->attr
== 0x0f) {
477 * It's a long name entry.
479 long_de
= (struct fat_long_name_entry
*)de
;
485 /* get the initial checksum value */
486 vfat_csum
= long_de
->checksum
;
490 /* ZERO the long_name buffer */
491 memset(long_name
, 0, sizeof long_name
);
493 if (long_de
->checksum
!= vfat_csum
)
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.
507 if (!vfat_match_longname(dname
, long_name
, long_len
))
512 continue; /* Try the next entry */
517 if (de
->attr
& 0x08) /* ignore volume labels */
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 */
529 if (!memcmp(mangled_name
, de
->name
, 11))
535 vfat_next
= vfat_init
;
541 /* Try with the next sector */
542 dir_sector
= get_next_sector(fs
, dir_sector
);
544 return NULL
; /* Nothing 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) {
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
;
559 PVT(inode
)->start
= PVT(inode
)->here
= first_sector(fs
, de
);
561 inode
->mode
= get_inode_mode(de
->attr
);
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
;
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
;
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) */
601 uint8_t vfat_next
, vfat_csum
;
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;
614 while (entries_left
--) {
615 if (de
->name
[0] == 0)
616 return -1; /* End of directory */
617 if ((uint8_t)de
->name
[0] == 0xe5)
620 if (de
->attr
== 0x0f) {
622 * It's a long name entry.
624 long_de
= (struct fat_long_name_entry
*)de
;
629 vfat_csum
= long_de
->checksum
;
632 goto invalid
; /* Too long! */
634 /* ZERO the long_name buffer */
635 memset(long_name
, 0, sizeof long_name
);
637 if (long_de
->checksum
!= vfat_csum
|| id
!= vfat_next
)
643 /* got the long entry name */
644 copy_long_chunk(long_name
+ id
*13, de
);
647 name_len
= vfat_cvt_longname(filename
, long_name
);
648 if (name_len
> 0 && name_len
< sizeof(dirent
->d_name
))
657 if (de
->attr
& 0x08) /* ignore volume labels */
660 if (long_entry
&& get_checksum(de
->name
) == vfat_csum
) {
661 /* Got a long entry */
663 /* Use the shortname */
668 for (i
= 0; i
< 8; i
++) {
672 if (de
->lcase
& LCASE_BASE
)
673 c
= codepage
.lower
[c
];
676 if (de
->name
[8] != ' ') {
678 for (i
= 8; i
< 11; i
++) {
682 if (de
->lcase
& LCASE_EXT
)
683 c
= codepage
.lower
[c
];
688 name_len
= p
- filename
;
690 goto got
; /* Got something one way or the other */
697 file
->offset
+= sizeof(struct fat_dir_entry
);
700 /* Try with the next sector */
701 sector
= next_sector(file
);
704 de
= get_cache(fs
->fs_dev
, sector
);
705 entries_left
= 1 << (fs
->sector_shift
- 5);
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 */
721 /* init. the fs meta data, return the block size in bits */
722 static int vfat_fs_init(struct fs_info
*fs
)
725 struct fat_sb_info
*sbi
;
726 struct disk
*disk
= fs
->fs_dev
->disk
;
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
)
740 sbi
= malloc(sizeof(*sbi
));
742 malloc_error("fat_sb_info structure");
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
;
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
)
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
)
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.
809 memcpy(dst
, (void *)&fat
+ sb_off
, sb_len
);
814 const struct fs_ops vfat_fs_ops
= {
816 .fs_flags
= FS_USEMEM
| FS_THISIND
,
817 .fs_init
= vfat_fs_init
,
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
,
827 .next_extent
= fat_next_extent
,
828 .copy_super
= vfat_copy_superblock
,