aboutsummaryrefslogtreecommitdiffstats
path: root/core/fs/fat
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2010-02-08 10:54:31 +0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-10 16:04:14 -0800
commitddd58320f422651a418731d6f8bd75f61df43293 (patch)
treec63f8150ecc665beaa386f9f3eb72023c1ee7d0c /core/fs/fat
parent7c503d3286ce0b562ef075480029338556b306d7 (diff)
downloadsyslinux-ddd58320f422651a418731d6f8bd75f61df43293.tar.gz
syslinux-ddd58320f422651a418731d6f8bd75f61df43293.tar.xz
syslinux-ddd58320f422651a418731d6f8bd75f61df43293.zip
pathbased: Add FAT support to extlinux and let FAT be "true" pathbased
Now extlinux can install FAT partition now, and FAT honors patched path area. Signed-off-by: Alek Du <alek.du@intel.com> Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'core/fs/fat')
-rw-r--r--core/fs/fat/fat.c194
1 files changed, 102 insertions, 92 deletions
diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c
index 13cf674d..d723f288 100644
--- a/core/fs/fat/fat.c
+++ b/core/fs/fat/fat.c
@@ -13,7 +13,7 @@ static struct inode * new_fat_inode(struct fs_info *fs)
{
struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
if (!inode)
- malloc_error("inode structure");
+ malloc_error("inode structure");
return inode;
}
@@ -44,7 +44,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
int lo, hi;
struct cache_struct *cs;
uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
-
+
switch(FAT_SB(fs)->fat_type) {
case FAT12:
offset = clust_num + (clust_num >> 1);
@@ -52,8 +52,8 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
offset &= sector_mask;
cs = get_fat_sector(fs, fat_sector);
if (offset == sector_mask) {
- /*
- * we got the end of the one fat sector,
+ /*
+ * we got the end of the one fat sector,
* but we have just one byte and we need two,
* so store the low part, then read the next fat
* sector, read the high part, then combine it.
@@ -65,13 +65,13 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
} else {
next_cluster = *(uint16_t *)(cs->data + offset);
}
-
+
if (clust_num & 0x0001)
next_cluster >>= 4; /* cluster number is ODD */
else
next_cluster &= 0x0fff; /* cluster number is EVEN */
break;
-
+
case FAT16:
offset = clust_num << 1;
fat_sector = offset >> SECTOR_SHIFT(fs);
@@ -79,7 +79,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
cs = get_fat_sector(fs, fat_sector);
next_cluster = *(uint16_t *)(cs->data + offset);
break;
-
+
case FAT32:
offset = clust_num << 2;
fat_sector = offset >> SECTOR_SHIFT(fs);
@@ -89,7 +89,7 @@ static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
next_cluster &= 0x0fffffff;
break;
}
-
+
return next_cluster;
}
@@ -101,7 +101,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
sector_t data_sector;
uint32_t cluster;
int clust_shift = sbi->clust_shift;
-
+
if (sector < data_area) {
/* Root directory sector... */
sector++;
@@ -109,7 +109,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
sector = 0; /* Ran out of root directory, return EOF */
return sector;
}
-
+
data_sector = sector - data_area;
if ((data_sector + 1) & sbi->clust_mask) /* Still in the same cluster */
return sector + 1; /* Next sector inside cluster */
@@ -120,7 +120,7 @@ static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
if (cluster >= sbi->clusters)
return 0;
-
+
/* return the start of the new cluster */
sector = (cluster << clust_shift) + data_area;
return sector;
@@ -157,7 +157,7 @@ static sector_t get_the_right_sector(struct file *file)
PVT(inode)->offset = sector_pos;
PVT(inode)->here = sector;
-
+
return sector;
}
@@ -184,19 +184,19 @@ static sector_t next_sector(struct file *file)
* 64K boundaries.
*
*/
-static void __getfssec(struct fs_info *fs, char *buf,
+static void __getfssec(struct fs_info *fs, char *buf,
struct file *file, uint32_t sectors)
{
sector_t curr_sector = get_the_right_sector(file);
sector_t frag_start , next_sector;
uint32_t con_sec_cnt;
struct disk *disk = fs->fs_dev->disk;
-
+
while (sectors) {
/* get fragment */
con_sec_cnt = 0;
frag_start = curr_sector;
-
+
do {
/* get consective sector count */
con_sec_cnt++;
@@ -204,14 +204,14 @@ static void __getfssec(struct fs_info *fs, char *buf,
next_sector = get_next_sector(fs, curr_sector);
curr_sector++;
} while (sectors && next_sector == curr_sector);
-
+
PVT(file->inode)->offset += con_sec_cnt;
PVT(file->inode)->here = next_sector;
-
+
/* do read */
disk->rdwr_sectors(disk, buf, frag_start, con_sec_cnt, 0);
buf += con_sec_cnt << SECTOR_SHIFT(fs);/* adjust buffer pointer */
-
+
curr_sector = next_sector;
}
}
@@ -219,7 +219,7 @@ static void __getfssec(struct fs_info *fs, char *buf,
/**
- * get multiple sectors from a file
+ * get multiple sectors from a file
*
* @param: buf, the buffer to store the read data
* @param: file, the file structure pointer
@@ -236,26 +236,26 @@ static uint32_t vfat_getfssec(struct file *file, char *buf, int sectors,
uint32_t bytes_left = file->inode->size - file->offset;
uint32_t bytes_read = sectors << fs->sector_shift;
int sector_left;
-
+
sector_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> fs->sector_shift;
if (sectors > sector_left)
sectors = sector_left;
-
+
__getfssec(fs, buf, file, sectors);
-
+
if (bytes_read >= bytes_left) {
bytes_read = bytes_left;
*have_more = 0;
} else {
*have_more = 1;
- }
+ }
file->offset += bytes_read;
-
+
return bytes_read;
}
/*
- * Mangle a filename pointed to by src into a buffer pointed to by dst;
+ * Mangle a filename pointed to by src into a buffer pointed to by dst;
* ends on encountering any whitespace.
*
*/
@@ -264,7 +264,7 @@ static void vfat_mangle_name(char *dst, const char *src)
char *p = dst;
char c;
int i = FILENAME_MAX -1;
-
+
/*
* Copy the filename, converting backslash to slash and
* collapsing duplicate separators.
@@ -272,14 +272,14 @@ static void vfat_mangle_name(char *dst, const char *src)
while (not_whitespace(c = *src)) {
if (c == '\\')
c = '/';
-
+
if (c == '/') {
if (src[1] == '/' || src[1] == '\\') {
src++;
i--;
continue;
}
- }
+ }
i--;
*dst++ = *src++;
}
@@ -292,7 +292,7 @@ static void vfat_mangle_name(char *dst, const char *src)
break;
if ((*(dst-1) != '/') && (*(dst-1) != '.'))
break;
-
+
dst--;
i++;
}
@@ -306,17 +306,17 @@ static void vfat_mangle_name(char *dst, const char *src)
* Mangle a normal style string to DOS style string.
*/
static void mangle_dos_name(char *mangle_buf, char *src)
-{
+{
int i;
- unsigned char c;
-
+ unsigned char c;
+
i = 0;
while (i < 11) {
c = *src++;
-
- if ((c <= ' ') || (c == '/'))
+
+ if ((c <= ' ') || (c == '/'))
break;
-
+
if (c == '.') {
while (i < 8)
mangle_buf[i++] = ' ';
@@ -327,7 +327,7 @@ static void mangle_dos_name(char *mangle_buf, char *src)
c = codepage.upper[c];
if (i == 0 && c == 0xe5)
c = 0x05; /* Special hack for the first byte only! */
-
+
mangle_buf[i++] = c;
}
while (i < 11)
@@ -422,7 +422,7 @@ static uint8_t get_checksum(char *dir_name)
{
int i;
uint8_t sum = 0;
-
+
for (i = 11; i; i--)
sum = ((sum & 1) << 7) + (sum >> 1) + *dir_name++;
return sum;
@@ -436,7 +436,7 @@ static inline sector_t first_sector(struct fs_info *fs,
const struct fat_sb_info *sbi = FAT_SB(fs);
sector_t first_clust;
sector_t sector;
-
+
first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
@@ -451,7 +451,7 @@ static inline int get_inode_mode(uint8_t attr)
return I_FILE;
}
-
+
static struct inode *vfat_find_entry(char *dname, struct inode *dir)
{
struct fs_info *fs = dir->fs;
@@ -459,7 +459,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
struct fat_dir_entry *de;
struct fat_long_name_entry *long_de;
struct cache_struct *cs;
-
+
char mangled_name[12];
uint16_t long_name[260]; /* == 20*13 */
int long_len;
@@ -471,7 +471,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
int entries;
int checksum;
int long_match = 0;
-
+
slots = (strlen(dname) + 12) / 13;
if (slots > 20)
return NULL; /* Name too long */
@@ -479,7 +479,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
slots |= 0x40;
vfat_init = vfat_next = slots;
long_len = slots*13;
-
+
/* Produce the shortname version, in case we need it. */
mangle_dos_name(mangled_name, dname);
@@ -487,11 +487,11 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
cs = get_cache_block(fs->fs_dev, dir_sector);
de = (struct fat_dir_entry *)cs->data;
entries = 1 << (fs->sector_shift - 5);
-
+
while (entries--) {
if (de->name[0] == 0)
return NULL;
-
+
if (de->attr == 0x0f) {
/*
* It's a long name entry.
@@ -500,7 +500,7 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
id = long_de->id;
if (id != vfat_next)
goto not_match;
-
+
if (id & 0x40) {
/* get the initial checksum value */
vfat_csum = long_de->checksum;
@@ -513,13 +513,13 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
if (long_de->checksum != vfat_csum)
goto not_match;
}
-
+
vfat_next = --id;
-
+
/* got the long entry name */
copy_long_chunk(long_name + id*13, de);
-
- /*
+
+ /*
* If we got the last entry, check it.
* Or, go on with the next entry.
*/
@@ -532,14 +532,14 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
continue; /* Try the next entry */
} else {
/*
- * It's a short entry
+ * It's a short entry
*/
if (de->attr & 0x08) /* ignore volume labels */
goto not_match;
-
+
if (long_match) {
- /*
- * We already have a VFAT long name match. However, the
+ /*
+ * We already have a VFAT long name match. However, the
* match is only valid if the checksum matches.
*/
checksum = get_checksum(de->name);
@@ -550,25 +550,25 @@ static struct inode *vfat_find_entry(char *dname, struct inode *dir)
goto found;
}
}
-
+
not_match:
vfat_next = vfat_init;
long_match = 0;
-
+
de++;
}
-
+
/* Try with the next sector */
dir_sector = get_next_sector(fs, dir_sector);
}
return NULL; /* Nothing found... */
-
+
found:
inode = new_fat_inode(fs);
inode->size = de->file_size;
PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
inode->mode = get_inode_mode(de->attr);
-
+
return inode;
}
@@ -576,15 +576,15 @@ static struct inode *vfat_iget_root(struct fs_info *fs)
{
struct inode *inode = new_fat_inode(fs);
int root_size = FAT_SB(fs)->root_size;
-
- /*
+
+ /*
* For FAT32, the only way to get the root directory size is to
* follow the entire FAT chain to the end... which seems pointless.
*/
inode->size = root_size ? root_size << fs->sector_shift : ~0;
PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
inode->mode = I_DIR;
-
+
return inode;
}
@@ -600,9 +600,9 @@ static struct dirent * vfat_readdir(struct file *file)
struct fat_dir_entry *de;
struct fat_long_name_entry *long_de;
struct cache_struct *cs;
-
+
sector_t sector = get_the_right_sector(file);
-
+
uint16_t long_name[261]; /* == 20*13 + 1 (to guarantee null) */
char filename[261];
@@ -612,27 +612,27 @@ static struct dirent * vfat_readdir(struct file *file)
int checksum;
int long_entry = 0;
int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
-
+
cs = get_cache_block(fs->fs_dev, sector);
de = (struct fat_dir_entry *)(cs->data + sec_off);
entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
vfat_next = vfat_csum = 0xff;
-
+
while (1) {
while(entries_left--) {
if (de->name[0] == 0)
return NULL;
if ((uint8_t)de->name[0] == 0xe5)
goto invalid;
-
+
if (de->attr == 0x0f) {
/*
* It's a long name entry.
*/
long_de = (struct fat_long_name_entry *)de;
id = long_de->id;
-
+
if (id & 0x40) {
/* init vfat_csum and vfat_init */
vfat_csum = long_de->checksum;
@@ -641,7 +641,7 @@ static struct dirent * vfat_readdir(struct file *file)
goto invalid; /* Too long! */
vfat_init = id;
-
+
/* ZERO the long_name buffer */
memset(long_name, 0, sizeof long_name);
} else {
@@ -649,9 +649,9 @@ static struct dirent * vfat_readdir(struct file *file)
id != vfat_next)
goto invalid;
}
-
+
vfat_next = --id;
-
+
/* got the long entry name */
copy_long_chunk(long_name + id*13, de);
@@ -661,17 +661,17 @@ static struct dirent * vfat_readdir(struct file *file)
if (longlen > 0 && longlen < sizeof(dirent->d_name))
long_entry = 1;
}
-
+
de++;
file->offset += sizeof(struct fat_dir_entry);
continue; /* Try the next entry */
} else {
/*
- * It's a short entry
+ * It's a short entry
*/
if (de->attr & 0x08) /* ignore volume labels */
goto invalid;
-
+
if (long_entry == 1) {
/* Got a long entry */
checksum = get_checksum(de->name);
@@ -682,7 +682,7 @@ static struct dirent * vfat_readdir(struct file *file)
int i;
uint8_t c;
char *p = filename;
-
+
for (i = 0; i < 8; i++) {
c = de->name[i];
if (c == ' ')
@@ -703,16 +703,16 @@ static struct dirent * vfat_readdir(struct file *file)
}
}
*p = '\0';
-
+
goto got;
}
}
-
+
invalid:
de++;
file->offset += sizeof(struct fat_dir_entry);
}
-
+
/* Try with the next sector */
sector = next_sector(file);
if (!sector)
@@ -721,7 +721,7 @@ static struct dirent * vfat_readdir(struct file *file)
de = (struct fat_dir_entry *)cs->data;
entries_left = 1 << (fs->sector_shift - 5);
}
-
+
got:
if (!(dirent = malloc(sizeof(*dirent)))) {
malloc_error("dirent structure in vfat_readdir");
@@ -734,7 +734,7 @@ got:
strcpy(dirent->d_name, filename);
file->offset += sizeof(*de); /* Update for next reading */
-
+
return dirent;
}
@@ -750,12 +750,19 @@ static int vfat_load_config(void)
char *p;
int i = 0;
- /*
+ /*
* we use the ConfigName to pass the config path because
* it is under the address 0xffff
*/
memset(&regs, 0, sizeof regs);
regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+ if (*CurrentDirName) { /* installed by extlinux not syslinux */
+ sprintf(ConfigName, "%s/extlinux.conf", CurrentDirName);
+ call16(core_open, &regs, &regs);
+ strcpy(ConfigName, "extlinux.conf");
+ return regs.eflags.l & EFLAGS_ZF;
+ }
+ /* installed by syslinux */
for (; i < 3; i++) {
strcpy(ConfigName, syslinux_cfg[i]);
call16(core_open, &regs, &regs);
@@ -768,15 +775,15 @@ static int vfat_load_config(void)
printf("no config file found\n");
return 1; /* no config file */
}
-
+
strcpy(ConfigName, "syslinux.cfg");
strcpy(CurrentDirName, syslinux_cfg[i]);
p = strrchr(CurrentDirName, '/');
*(p + 1) = '\0'; /* In case we met '/syslinux.cfg' */
-
+
return 0;
}
-
+
static inline __constfunc uint32_t bsr(uint32_t num)
{
asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
@@ -792,31 +799,34 @@ static int vfat_fs_init(struct fs_info *fs)
int sectors_per_fat;
uint32_t clusters;
sector_t total_sectors;
-
+
fs->sector_shift = fs->block_shift = disk->sector_shift;
fs->sector_size = 1 << fs->sector_shift;
fs->block_size = 1 << fs->block_shift;
disk->rdwr_sectors(disk, &fat, 0, 1, 0);
-
+
+ /* XXX: Find better sanity checks... */
+ if (!fat.bxResSectors || !fat.bxFATs)
+ return -1;
sbi = malloc(sizeof(*sbi));
if (!sbi)
malloc_error("fat_sb_info structure");
fs->fs_info = sbi;
-
+
sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
total_sectors = fat.bxSectors ? : fat.bsHugeSectors;
-
- sbi->fat = fat.bxResSectors;
+
+ sbi->fat = fat.bxResSectors;
sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs;
sbi->root_size = root_dir_size(fs, &fat);
sbi->data = sbi->root + sbi->root_size;
-
+
sbi->clust_shift = bsr(fat.bxSecPerClust);
sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
sbi->clust_mask = fat.bxSecPerClust - 1;
sbi->clust_size = fat.bxSecPerClust << fs->sector_shift;
-
+
clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
if (clusters <= 0xff4) {
sbi->fat_type = FAT12;
@@ -838,11 +848,11 @@ static int vfat_fs_init(struct fs_info *fs)
+ ((fat.fat32.root_cluster-2) << sbi->clust_shift);
}
sbi->clusters = clusters;
-
+
/* for SYSLINUX, the cache is based on sector size */
return fs->sector_shift;
}
-
+
const struct fs_ops vfat_fs_ops = {
.fs_name = "vfat",
.fs_flags = FS_USEMEM | FS_THISIND,