summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c')
-rw-r--r--contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c795
1 files changed, 0 insertions, 795 deletions
diff --git a/contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c b/contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c
deleted file mode 100644
index 5902f48..0000000
--- a/contrib/syslinux/syslinux-4.03/core/fs/fat/fat.c
+++ /dev/null
@@ -1,795 +0,0 @@
-#include <dprintf.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/dirent.h>
-#include <cache.h>
-#include <core.h>
-#include <disk.h>
-#include <fs.h>
-#include <ilog2.h>
-#include <klibc/compiler.h>
-#include "codepage.h"
-#include "fat_fs.h"
-
-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");
-
- return inode;
-}
-
-/*
- * Check for a particular sector in the FAT cache
- */
-static const void *get_fat_sector(struct fs_info *fs, sector_t sector)
-{
- return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector);
-}
-
-static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
-{
- uint32_t next_cluster = 0;
- sector_t fat_sector;
- uint32_t offset;
- uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
- const uint8_t *data;
-
- switch(FAT_SB(fs)->fat_type) {
- case FAT12:
- offset = clust_num + (clust_num >> 1);
- fat_sector = offset >> SECTOR_SHIFT(fs);
- offset &= sector_mask;
- data = get_fat_sector(fs, fat_sector);
- if (offset == sector_mask) {
- /*
- * 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.
- */
- next_cluster = data[offset];
- data = get_fat_sector(fs, fat_sector + 1);
- next_cluster += data[0] << 8;
- } else {
- next_cluster = *(const uint16_t *)(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);
- offset &= sector_mask;
- data = get_fat_sector(fs, fat_sector);
- next_cluster = *(const uint16_t *)(data + offset);
- break;
-
- case FAT32:
- offset = clust_num << 2;
- fat_sector = offset >> SECTOR_SHIFT(fs);
- offset &= sector_mask;
- data = get_fat_sector(fs, fat_sector);
- next_cluster = *(const uint32_t *)(data + offset);
- next_cluster &= 0x0fffffff;
- break;
- }
-
- return next_cluster;
-}
-
-static int fat_next_extent(struct inode *inode, uint32_t lstart)
-{
- struct fs_info *fs = inode->fs;
- struct fat_sb_info *sbi = FAT_SB(fs);
- uint32_t mcluster = lstart >> sbi->clust_shift;
- uint32_t lcluster;
- uint32_t pcluster;
- uint32_t tcluster;
- uint32_t xcluster;
- const uint32_t cluster_bytes = UINT32_C(1) << sbi->clust_byte_shift;
- const uint32_t cluster_secs = UINT32_C(1) << sbi->clust_shift;
- sector_t data_area = sbi->data;
-
- tcluster = (inode->size + cluster_bytes - 1) >> sbi->clust_byte_shift;
- if (mcluster >= tcluster)
- goto err; /* Requested cluster beyond end of file */
-
- lcluster = PVT(inode)->offset >> sbi->clust_shift;
- pcluster = ((PVT(inode)->here - data_area) >> sbi->clust_shift) + 2;
-
- if (lcluster > mcluster || PVT(inode)->here < data_area) {
- lcluster = 0;
- pcluster = PVT(inode)->start_cluster;
- }
-
- for (;;) {
- if (pcluster-2 >= sbi->clusters) {
- inode->size = lcluster << sbi->clust_shift;
- goto err;
- }
-
- if (lcluster >= mcluster)
- break;
-
- lcluster++;
- pcluster = get_next_cluster(fs, pcluster);
- }
-
- inode->next_extent.pstart =
- ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
- inode->next_extent.len = cluster_secs;
- xcluster = 0; /* Nonsense */
-
- while (++lcluster < tcluster) {
- xcluster = get_next_cluster(fs, pcluster);
- if (xcluster != ++pcluster)
- break; /* Not contiguous */
- inode->next_extent.len += cluster_secs;
- }
-
- /* Note: ->here is bogus if ->offset >= EOF, but that's okay */
- PVT(inode)->offset = lcluster << sbi->clust_shift;
- PVT(inode)->here = ((xcluster-2) << sbi->clust_shift) + data_area;
-
- return 0;
-
-err:
- return -1;
-}
-
-static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
-{
- struct fat_sb_info *sbi = FAT_SB(fs);
- sector_t data_area = sbi->data;
- sector_t data_sector;
- uint32_t cluster;
- int clust_shift = sbi->clust_shift;
-
- if (sector < data_area) {
- /* Root directory sector... */
- sector++;
- if (sector >= data_area)
- 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 */
-
- /* get a new cluster */
- cluster = data_sector >> clust_shift;
- cluster = get_next_cluster(fs, cluster + 2) - 2;
-
- if (cluster >= sbi->clusters)
- return 0;
-
- /* return the start of the new cluster */
- sector = (cluster << clust_shift) + data_area;
- return sector;
-}
-
-/*
- * The FAT is a single-linked list. We remember the last place we
- * were, so for a forward seek we can move forward from there, but
- * for a reverse seek we have to start over...
- */
-static sector_t get_the_right_sector(struct file *file)
-{
- struct inode *inode = file->inode;
- uint32_t sector_pos = file->offset >> SECTOR_SHIFT(file->fs);
- uint32_t where;
- sector_t sector;
-
- if (sector_pos < PVT(inode)->offset) {
- /* Reverse seek */
- where = 0;
- sector = PVT(inode)->start;
- } else {
- where = PVT(inode)->offset;
- sector = PVT(inode)->here;
- }
-
- while (where < sector_pos) {
- sector = get_next_sector(file->fs, sector);
- where++;
- }
-
- PVT(inode)->offset = sector_pos;
- PVT(inode)->here = sector;
-
- return sector;
-}
-
-/*
- * Get the next sector in sequence
- */
-static sector_t next_sector(struct file *file)
-{
- struct inode *inode = file->inode;
- sector_t sector = get_next_sector(file->fs, PVT(inode)->here);
- PVT(inode)->offset++;
- PVT(inode)->here = sector;
-
- return sector;
-}
-
-/*
- * Mangle a filename pointed to by src into a buffer pointed to by dst;
- * ends on encountering any whitespace.
- *
- */
-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.
- */
- while (not_whitespace(c = *src)) {
- if (c == '\\')
- c = '/';
-
- if (c == '/') {
- if (src[1] == '/' || src[1] == '\\') {
- src++;
- i--;
- continue;
- }
- }
- i--;
- *dst++ = *src++;
- }
-
- /* Strip terminal slashes or whitespace */
- while (1) {
- if (dst == p)
- break;
- if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */
- break;
- if ((*(dst-1) != '/') && (*(dst-1) != '.'))
- break;
-
- dst--;
- i++;
- }
-
- i++;
- for (; i > 0; i --)
- *dst++ = '\0';
-}
-
-/*
- * Mangle a normal style string to DOS style string.
- */
-static void mangle_dos_name(char *mangle_buf, const char *src)
-{
- int i;
- unsigned char c;
-
- if (src[0] == '.' && (!src[1] || (src[1] == '.' && !src[2]))) {
- /* . and .. mangle to their respective zero-padded version */
- i = stpcpy(mangle_buf, src) - mangle_buf;
- } else {
- i = 0;
- while (i < 11) {
- c = *src++;
-
- if ((c <= ' ') || (c == '/'))
- break;
-
- if (c == '.') {
- while (i < 8)
- mangle_buf[i++] = ' ';
- i = 8;
- continue;
- }
-
- 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)
- mangle_buf[i++] = ' ';
-
- mangle_buf[i] = '\0';
-}
-
-/*
- * Match a string name against a longname. "len" is the number of
- * codepoints in the input; including padding.
- *
- * Returns true on match.
- */
-static bool vfat_match_longname(const char *str, const uint16_t *match,
- int len)
-{
- unsigned char c = -1; /* Nonzero: we have not yet seen NUL */
- uint16_t cp;
-
- dprintf("Matching: %s\n", str);
-
- while (len) {
- cp = *match++;
- len--;
- if (!cp)
- break;
- c = *str++;
- if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
- return false; /* Also handles c == '\0' */
- }
-
- /* This should have been the end of the matching string */
- if (*str)
- return false;
-
- /* Any padding entries must be FFFF */
- while (len--)
- if (*match++ != 0xffff)
- return false;
-
- return true;
-}
-
-/*
- * Convert an UTF-16 longname to the system codepage; return
- * the length on success or -1 on failure.
- */
-static int vfat_cvt_longname(char *entry_name, const uint16_t *long_name)
-{
- struct unicache {
- uint16_t utf16;
- uint8_t cp;
- };
- static struct unicache unicache[256];
- struct unicache *uc;
- uint16_t cp;
- unsigned int c;
- char *p = entry_name;
-
- do {
- cp = *long_name++;
- uc = &unicache[cp % 256];
-
- if (__likely(uc->utf16 == cp)) {
- *p++ = uc->cp;
- } else {
- for (c = 0; c < 512; c++) {
- /* This is a bit hacky... */
- if (codepage.uni[0][c] == cp) {
- uc->utf16 = cp;
- *p++ = uc->cp = (uint8_t)c;
- goto found;
- }
- }
- return -1; /* Impossible character */
- found:
- ;
- }
- } while (cp);
-
- return (p-entry_name)-1;
-}
-
-static void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de)
-{
- const struct fat_long_name_entry *le =
- (const struct fat_long_name_entry *)de;
-
- memcpy(buf, le->name1, 5 * 2);
- memcpy(buf + 5, le->name2, 6 * 2);
- memcpy(buf + 11, le->name3, 2 * 2);
-}
-
-static uint8_t get_checksum(const char *dir_name)
-{
- int i;
- uint8_t sum = 0;
-
- for (i = 11; i; i--)
- sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++;
- return sum;
-}
-
-
-/* compute the first sector number of one dir where the data stores */
-static inline sector_t first_sector(struct fs_info *fs,
- const struct fat_dir_entry *dir)
-{
- 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;
- if (first_clust == 0)
- sector = sbi->root; /* first_clust == 0 means root directory */
- else
- sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
-
- return sector;
-}
-
-static inline enum dirent_type get_inode_mode(uint8_t attr)
-{
- return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
-}
-
-
-static struct inode *vfat_find_entry(const char *dname, struct inode *dir)
-{
- struct fs_info *fs = dir->fs;
- struct inode *inode;
- const struct fat_dir_entry *de;
- struct fat_long_name_entry *long_de;
-
- char mangled_name[12];
- uint16_t long_name[260]; /* == 20*13 */
- int long_len;
-
- sector_t dir_sector = PVT(dir)->start;
- uint8_t vfat_init, vfat_next, vfat_csum = 0;
- uint8_t id;
- int slots;
- int entries;
- int checksum;
- int long_match = 0;
-
- slots = (strlen(dname) + 12) / 13;
- if (slots > 20)
- return NULL; /* Name too long */
-
- 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);
-
- while (dir_sector) {
- de = get_cache(fs->fs_dev, dir_sector);
- 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.
- */
- long_de = (struct fat_long_name_entry *)de;
- id = long_de->id;
- if (id != vfat_next)
- goto not_match;
-
- if (id & 0x40) {
- /* get the initial checksum value */
- vfat_csum = long_de->checksum;
- id &= 0x3f;
- long_len = id * 13;
-
- /* ZERO the long_name buffer */
- memset(long_name, 0, sizeof long_name);
- } else {
- 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.
- */
- if (id == 0) {
- if (!vfat_match_longname(dname, long_name, long_len))
- goto not_match;
- long_match = 1;
- }
- de++;
- continue; /* Try the next entry */
- } else {
- /*
- * 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
- * match is only valid if the checksum matches.
- */
- checksum = get_checksum(de->name);
- if (checksum == vfat_csum)
- goto found; /* Got it */
- } else {
- if (!memcmp(mangled_name, de->name, 11))
- 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_cluster =
- (de->first_cluster_high << 16) + de->first_cluster_low;
- if (PVT(inode)->start_cluster == 0) {
- /* Root directory */
- int root_size = FAT_SB(fs)->root_size;
-
- PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
- inode->size = root_size ? root_size << fs->sector_shift : ~0;
- PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
- } else {
- PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
- }
- inode->mode = get_inode_mode(de->attr);
-
- return inode;
-}
-
-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.
- */
- PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
- inode->size = root_size ? root_size << fs->sector_shift : ~0;
- PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
- inode->mode = DT_DIR;
-
- return inode;
-}
-
-static struct inode *vfat_iget(const char *dname, struct inode *parent)
-{
- return vfat_find_entry(dname, parent);
-}
-
-static int vfat_readdir(struct file *file, struct dirent *dirent)
-{
- struct fs_info *fs = file->fs;
- const struct fat_dir_entry *de;
- const char *data;
- const struct fat_long_name_entry *long_de;
-
- sector_t sector = get_the_right_sector(file);
-
- uint16_t long_name[261]; /* == 20*13 + 1 (to guarantee null) */
- char filename[261];
- int name_len = 0;
-
- uint8_t vfat_init, vfat_next, vfat_csum;
- uint8_t id;
- int entries_left;
- bool long_entry = false;
- int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
-
- data = get_cache(fs->fs_dev, sector);
- de = (const struct fat_dir_entry *)(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 -1; /* End of directory */
- 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;
- id &= 0x3f;
- if (id >= 20)
- goto invalid; /* Too long! */
-
- vfat_init = id;
-
- /* ZERO the long_name buffer */
- memset(long_name, 0, sizeof long_name);
- } else {
- if (long_de->checksum != vfat_csum || id != vfat_next)
- goto invalid;
- }
-
- vfat_next = --id;
-
- /* got the long entry name */
- copy_long_chunk(long_name + id*13, de);
-
- if (id == 0) {
- name_len = vfat_cvt_longname(filename, long_name);
- if (name_len > 0 && name_len < sizeof(dirent->d_name))
- long_entry = true;
- }
-
- goto next;
- } else {
- /*
- * It's a short entry
- */
- if (de->attr & 0x08) /* ignore volume labels */
- goto invalid;
-
- if (long_entry && get_checksum(de->name) == vfat_csum) {
- /* Got a long entry */
- } else {
- /* Use the shortname */
- int i;
- uint8_t c;
- char *p = filename;
-
- for (i = 0; i < 8; i++) {
- c = de->name[i];
- if (c == ' ')
- break;
- if (de->lcase & LCASE_BASE)
- c = codepage.lower[c];
- *p++ = c;
- }
- if (de->name[8] != ' ') {
- *p++ = '.';
- for (i = 8; i < 11; i++) {
- c = de->name[i];
- if (c == ' ')
- break;
- if (de->lcase & LCASE_EXT)
- c = codepage.lower[c];
- *p++ = c;
- }
- }
- *p = '\0';
- name_len = p - filename;
- }
- goto got; /* Got something one way or the other */
- }
-
- invalid:
- long_entry = false;
- next:
- de++;
- file->offset += sizeof(struct fat_dir_entry);
- }
-
- /* Try with the next sector */
- sector = next_sector(file);
- if (!sector)
- return -1;
- de = get_cache(fs->fs_dev, sector);
- entries_left = 1 << (fs->sector_shift - 5);
- }
-
-got:
- name_len++; /* Include final null */
- dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16);
- dirent->d_off = file->offset;
- dirent->d_reclen = offsetof(struct dirent, d_name) + name_len;
- dirent->d_type = get_inode_mode(de->attr);
- memcpy(dirent->d_name, filename, name_len);
-
- file->offset += sizeof(*de); /* Update for next reading */
-
- return 0;
-}
-
-/* init. the fs meta data, return the block size in bits */
-static int vfat_fs_init(struct fs_info *fs)
-{
- struct fat_bpb fat;
- struct fat_sb_info *sbi;
- struct disk *disk = fs->fs_dev->disk;
- 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->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 = ilog2(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;
- } else if (clusters <= 0xfff4) {
- sbi->fat_type = FAT16;
- } else {
- sbi->fat_type = FAT32;
-
- if (clusters > 0x0ffffff4)
- clusters = 0x0ffffff4; /* Maximum possible */
-
- if (fat.fat32.extended_flags & 0x80) {
- /* Non-mirrored FATs, we need to read the active one */
- sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat;
- }
-
- /* FAT32: root directory is a cluster chain */
- sbi->root = sbi->data
- + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
- }
- sbi->clusters = clusters;
-
- /* Initialize the cache */
- cache_init(fs->fs_dev, fs->block_shift);
-
- return fs->block_shift;
-}
-
-const struct fs_ops vfat_fs_ops = {
- .fs_name = "vfat",
- .fs_flags = FS_USEMEM | FS_THISIND,
- .fs_init = vfat_fs_init,
- .searchdir = NULL,
- .getfssec = generic_getfssec,
- .close_file = generic_close_file,
- .mangle_name = vfat_mangle_name,
- .load_config = generic_load_config,
- .readdir = vfat_readdir,
- .iget_root = vfat_iget_root,
- .iget = vfat_iget,
- .next_extent = fat_next_extent,
-};