summaryrefslogtreecommitdiffstats
path: root/fs/fat
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat')
-rw-r--r--fs/fat/Makefile15
-rw-r--r--fs/fat/buffer.c155
-rw-r--r--fs/fat/cache.c298
-rw-r--r--fs/fat/dir.c434
-rw-r--r--fs/fat/fatfs_syms.c58
-rw-r--r--fs/fat/file.c385
-rw-r--r--fs/fat/inode.c596
-rw-r--r--fs/fat/misc.c557
-rw-r--r--fs/fat/mmap.c113
-rw-r--r--fs/fat/msbuffer.h15
-rw-r--r--fs/fat/tables.c280
-rw-r--r--fs/fat/tables.h35
12 files changed, 2941 insertions, 0 deletions
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
new file mode 100644
index 000000000..d45ecfab5
--- /dev/null
+++ b/fs/fat/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux fat-filesystem support.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := fat.o
+O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
+OX_OBJS := fatfs_syms.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c
new file mode 100644
index 000000000..eebbf29b5
--- /dev/null
+++ b/fs/fat/buffer.c
@@ -0,0 +1,155 @@
+/*
+ * linux/fs/fat/buffer.c
+ *
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+
+struct buffer_head *fat_bread (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
+ if (sb->s_blocksize == 512) {
+ ret = bread (sb->s_dev,block,512);
+ } else {
+ struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+
+ if (real != NULL){
+ ret = (struct buffer_head *)
+ kmalloc (sizeof(struct buffer_head), GFP_KERNEL);
+ if (ret != NULL) {
+ /* #Specification: msdos / strategy / special device / dummy blocks
+ Many special device (Scsi optical disk for one) use
+ larger hardware sector size. This allows for higher
+ capacity.
+
+ Most of the time, the MsDOS file system that sit
+ on this device is totally unaligned. It use logically
+ 512 bytes sector size, with logical sector starting
+ in the middle of a hardware block. The bad news is
+ that a hardware sector may hold data own by two
+ different files. This means that the hardware sector
+ must be read, patch and written almost all the time.
+
+ Needless to say that it kills write performance
+ on all OS.
+
+ Internally the linux msdos fs is using 512 bytes
+ logical sector. When accessing such a device, we
+ allocate dummy buffer cache blocks, that we stuff
+ with the information of a real one (1k large).
+
+ This strategy is used to hide this difference to
+ the core of the msdos fs. The slowdown is not
+ hidden though!
+ */
+ /*
+ The memset is there only to catch errors. The msdos
+ fs is only using b_data
+ */
+ memset (ret,0,sizeof(*ret));
+ ret->b_data = real->b_data;
+ if (block & 1) ret->b_data += 512;
+ ret->b_next = real;
+ }else{
+ brelse (real);
+ }
+ }
+ }
+ return ret;
+}
+struct buffer_head *fat_getblk (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+ /* #Specification: msdos / special device / writing
+ A write is always preceded by a read of the complete block
+ (large hardware sector size). This defeat write performance.
+ There is a possibility to optimize this when writing large
+ chunk by making sure we are filling large block. Volunteer ?
+ */
+ ret = fat_bread (sb,block);
+ }
+ return ret;
+}
+
+void fat_brelse (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (bh != NULL){
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+ brelse (bh->b_next);
+ /* We can free the dummy because a new one is allocated at
+ each fat_getblk() and fat_bread().
+ */
+ kfree (bh);
+ }
+ }
+}
+
+void fat_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_dirty (bh,dirty_val);
+}
+
+void fat_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_uptodate(bh, val);
+}
+int fat_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ return buffer_uptodate(bh);
+}
+
+void fat_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+ struct buffer_head *tmp[32];
+ int i;
+ for (i=0; i<nbreq; i++){
+ tmp[i] = bh[i]->b_next;
+ }
+ ll_rw_block(opr,nbreq,tmp);
+ }
+}
+
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
new file mode 100644
index 000000000..af79ce25e
--- /dev/null
+++ b/fs/fat/cache.c
@@ -0,0 +1,298 @@
+/*
+ * linux/fs/fat/cache.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ */
+
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include "msbuffer.h"
+
+
+static struct fat_cache *fat_cache,cache[FAT_CACHE];
+
+/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
+ new_value is != -1, that FAT entry is replaced by it. */
+
+int fat_access(struct super_block *sb,int nr,int new_value)
+{
+ struct buffer_head *bh,*bh2,*c_bh,*c_bh2;
+ unsigned char *p_first,*p_last;
+ int first,last,next,copy,b;
+
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ first = last = nr*2;
+ } else {
+ first = nr*3/2;
+ last = first+1;
+ }
+ b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS);
+ if (!(bh = fat_bread(sb, b))) {
+ printk("bread in fat_access failed\n");
+ return 0;
+ }
+ if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) {
+ bh2 = bh;
+ } else {
+ if (!(bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, bh);
+ printk("2nd bread in fat_access failed\n");
+ return 0;
+ }
+ }
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = CF_LE_W(((unsigned short *) bh->b_data)[(first &
+ (SECTOR_SIZE-1)) >> 1]);
+ if (next >= 0xfff7) next = -1;
+ }
+ else {
+ p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
+ p_last = &((unsigned char *) bh2->b_data)[(first+1) &
+ (SECTOR_SIZE-1)];
+ if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+ else next = (*p_first+(*p_last << 8)) & 0xfff;
+ if (next >= 0xff7) next = -1;
+ }
+ if (new_value != -1) {
+ if (MSDOS_SB(sb)->fat_bits == 16)
+ ((unsigned short *) bh->b_data)[(first & (SECTOR_SIZE-1)) >>
+ 1] = CT_LE_W(new_value);
+ else {
+ if (nr & 1) {
+ *p_first = (*p_first & 0xf) | (new_value << 4);
+ *p_last = new_value >> 4;
+ }
+ else {
+ *p_first = new_value & 0xff;
+ *p_last = (*p_last & 0xf0) | (new_value >> 8);
+ }
+ fat_mark_buffer_dirty(sb, bh2, 1);
+ }
+ fat_mark_buffer_dirty(sb, bh, 1);
+ for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
+ b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS) +
+ MSDOS_SB(sb)->fat_length * copy;
+ if (!(c_bh = fat_bread(sb, b)))
+ break;
+ memcpy(c_bh->b_data,bh->b_data,SECTOR_SIZE);
+ fat_mark_buffer_dirty(sb, c_bh, 1);
+ if (bh != bh2) {
+ if (!(c_bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, c_bh);
+ break;
+ }
+ memcpy(c_bh2->b_data,bh2->b_data,SECTOR_SIZE);
+ fat_brelse(sb, c_bh2);
+ }
+ fat_brelse(sb, c_bh);
+ }
+ }
+ fat_brelse(sb, bh);
+ if (bh != bh2)
+ fat_brelse(sb, bh2);
+ return next;
+}
+
+
+void cache_init(void)
+{
+ static int initialized = 0;
+ int count;
+
+ if (initialized) return;
+ fat_cache = &cache[0];
+ for (count = 0; count < FAT_CACHE; count++) {
+ cache[count].device = 0;
+ cache[count].next = count == FAT_CACHE-1 ? NULL :
+ &cache[count+1];
+ }
+ initialized = 1;
+}
+
+
+void cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
+{
+ struct fat_cache *walk;
+
+#ifdef DEBUG
+printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
+ inode->i_ino, cluster, *f_clu, *d_clu);
+#endif
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (inode->i_dev == walk->device
+ && walk->ino == inode->i_ino
+ && walk->file_cluster <= cluster
+ && walk->file_cluster > *f_clu) {
+ *d_clu = walk->disk_cluster;
+#ifdef DEBUG
+printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
+#endif
+ if ((*f_clu = walk->file_cluster) == cluster) return;
+ }
+#ifdef DEBUG
+printk("cache miss\n");
+#endif
+}
+
+
+#ifdef DEBUG
+static void list_cache(void)
+{
+ struct fat_cache *walk;
+
+ for (walk = fat_cache; walk; walk = walk->next) {
+ if (walk->device)
+ printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
+ walk->ino, walk->file_cluster, walk->disk_cluster);
+ else printk("-- ");
+ }
+ printk("\n");
+}
+#endif
+
+
+void cache_add(struct inode *inode,int f_clu,int d_clu)
+{
+ struct fat_cache *walk,*last;
+
+#ifdef DEBUG
+printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
+ inode->i_ino, f_clu, d_clu);
+#endif
+ last = NULL;
+ for (walk = fat_cache; walk->next; walk = (last = walk)->next)
+ if (inode->i_dev == walk->device
+ && walk->ino == inode->i_ino
+ && walk->file_cluster == f_clu) {
+ if (walk->disk_cluster != d_clu) {
+ printk("FAT cache corruption");
+ fat_cache_inval_inode(inode);
+ return;
+ }
+ /* update LRU */
+ if (last == NULL) return;
+ last->next = walk->next;
+ walk->next = fat_cache;
+ fat_cache = walk;
+#ifdef DEBUG
+list_cache();
+#endif
+ return;
+ }
+ walk->device = inode->i_dev;
+ walk->ino = inode->i_ino;
+ walk->file_cluster = f_clu;
+ walk->disk_cluster = d_clu;
+ last->next = NULL;
+ walk->next = fat_cache;
+ fat_cache = walk;
+#ifdef DEBUG
+list_cache();
+#endif
+}
+
+
+/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
+ fixes itself after a while. */
+
+void fat_cache_inval_inode(struct inode *inode)
+{
+ struct fat_cache *walk;
+
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (walk->device == inode->i_dev
+ && walk->ino == inode->i_ino)
+ walk->device = 0;
+}
+
+
+void fat_cache_inval_dev(kdev_t device)
+{
+ struct fat_cache *walk;
+
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (walk->device == device)
+ walk->device = 0;
+}
+
+
+int get_cluster(struct inode *inode,int cluster)
+{
+ int nr,count;
+
+ if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+ if (!cluster) return nr;
+ count = 0;
+ for (cache_lookup(inode,cluster,&count,&nr); count < cluster;
+ count++) {
+ if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+ if (!nr) return 0;
+ }
+ cache_add(inode,cluster,nr);
+ return nr;
+}
+
+
+int fat_smap(struct inode *inode,int sector)
+{
+ struct msdos_sb_info *sb;
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
+ if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start)) {
+ if (sector >= sb->dir_entries >> MSDOS_DPS_BITS) return 0;
+ return sector+sb->dir_start;
+ }
+ cluster = sector/sb->cluster_size;
+ offset = sector % sb->cluster_size;
+ if (!(cluster = get_cluster(inode,cluster))) return 0;
+ return (cluster-2)*sb->cluster_size+sb->data_start+offset;
+}
+
+
+/* Free all clusters after the skip'th cluster. Doesn't use the cache,
+ because this way we get an additional sanity check. */
+
+int fat_free(struct inode *inode,int skip)
+{
+ int nr,last;
+
+ if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+ last = 0;
+ while (skip--) {
+ last = nr;
+ if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+ if (!nr) {
+ printk("fat_free: skipped EOF\n");
+ return -EIO;
+ }
+ }
+ if (last)
+ fat_access(inode->i_sb,last,MSDOS_SB(inode->i_sb)->fat_bits ==
+ 12 ? 0xff8 : 0xfff8);
+ else {
+ MSDOS_I(inode)->i_start = 0;
+ inode->i_dirt = 1;
+ }
+ lock_fat(inode->i_sb);
+ while (nr != -1) {
+ if (!(nr = fat_access(inode->i_sb,nr,0))) {
+ fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
+ break;
+ }
+ if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
+ MSDOS_SB(inode->i_sb)->free_clusters++;
+ inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
+ }
+ unlock_fat(inode->i_sb);
+ fat_cache_inval_inode(inode);
+ return 0;
+}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
new file mode 100644
index 000000000..6938b7b9e
--- /dev/null
+++ b/fs/fat/dir.c
@@ -0,0 +1,434 @@
+/*
+ * linux/fs/fat/dir.c
+ *
+ * directory handling functions for fat-based filesystems
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *
+ * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
+ * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+#include <linux/dirent.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+
+#define PRINTK(X)
+
+static long fat_dir_read(struct inode * inode,struct file * filp,
+ char * buf, unsigned long count)
+{
+ return -EISDIR;
+}
+
+struct file_operations fat_dir_operations = {
+ NULL, /* lseek - default */
+ fat_dir_read, /* read */
+ NULL, /* write - bad */
+ fat_readdir, /* readdir */
+ NULL, /* select - default */
+ fat_dir_ioctl, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* fsync */
+};
+
+/* Convert Unicode string to ASCII. If uni_xlate is enabled and we
+ * can't get a 1:1 conversion, use a colon as an escape character since
+ * it is normally invalid on the vfat filesystem. The following three
+ * characters are a sort of uuencoded 16 bit Unicode value. This lets
+ * us do a full dump and restore of Unicode filenames. We could get
+ * into some trouble with long Unicode names, but ignore that right now.
+ */
+static int
+uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
+{
+ unsigned char *ip, *op;
+ unsigned char page, pg_off;
+ unsigned char *uni_page;
+ unsigned short val;
+
+ ip = uni;
+ op = ascii;
+
+ while (*ip || ip[1]) {
+ pg_off = *ip++;
+ page = *ip++;
+
+ uni_page = fat_uni2asc_pg[page];
+ if (uni_page && uni_page[pg_off]) {
+ *op++ = uni_page[pg_off];
+ } else {
+ if (uni_xlate == 1) {
+ *op++ = ':';
+ val = (pg_off << 8) + page;
+ op[2] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ op[1] = fat_uni2code[val & 0x3f];
+ val >>= 6;
+ *op = fat_uni2code[val & 0x3f];
+ op += 3;
+ } else {
+ *op++ = '?';
+ }
+ }
+ }
+ *op = 0;
+ return (op - ascii);
+}
+
+int fat_readdirx(
+ struct inode *inode,
+ struct file *filp,
+ void *dirent,
+ fat_filldir_t fat_filldir,
+ filldir_t filldir,
+ int shortnames,
+ int longnames,
+ int both)
+{
+ struct super_block *sb = inode->i_sb;
+ int ino,i,i2,last;
+ char c;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ unsigned long oldpos = filp->f_pos;
+ unsigned long spos;
+ int is_long;
+ char longname[275];
+ unsigned char long_len = 0; /* Make compiler warning go away */
+ unsigned char alias_checksum = 0; /* Make compiler warning go away */
+ unsigned char long_slots = 0;
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ unsigned char *unicode = NULL;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+/* Fake . and .. for the root directory. */
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ while (oldpos < 2) {
+ if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
+ return 0;
+ oldpos++;
+ filp->f_pos++;
+ }
+ if (oldpos == 2)
+ filp->f_pos = 0;
+ }
+ if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
+ return -ENOENT;
+
+ bh = NULL;
+ longname[0] = longname[1] = 0;
+ is_long = 0;
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ while (ino > -1) {
+ /* Check for long filename entry */
+ if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
+ is_long = 0;
+ oldpos = filp->f_pos;
+ } else if (MSDOS_SB(sb)->options.isvfat && de->attr == ATTR_EXT) {
+ int get_new_entry;
+ struct msdos_dir_slot *ds;
+ int offset;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots = 0;
+
+ if (!unicode) {
+ unicode = (unsigned char *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode)
+ return -ENOMEM;
+ }
+
+ offset = 0;
+ ds = (struct msdos_dir_slot *) de;
+ id = ds->id;
+ if (id & 0x40) {
+ slots = id & ~0x40;
+ long_slots = slots;
+ is_long = 1;
+ alias_checksum = ds->alias_checksum;
+ }
+
+ get_new_entry = 1;
+ slot = slots;
+ while (slot > 0) {
+ PRINTK(("1. get_new_entry: %d\n", get_new_entry));
+ if (ds->attr != ATTR_EXT) {
+ is_long = 0;
+ get_new_entry = 0;
+ break;
+ }
+ if ((ds->id & ~0x40) != slot) {
+ is_long = 0;
+ break;
+ }
+ if (ds->alias_checksum != alias_checksum) {
+ is_long = 0;
+ break;
+ }
+ slot--;
+ offset = slot * 26;
+ PRINTK(("2. get_new_entry: %d\n", get_new_entry));
+ memcpy(&unicode[offset], ds->name0_4, 10);
+ offset += 10;
+ memcpy(&unicode[offset], ds->name5_10, 12);
+ offset += 12;
+ memcpy(&unicode[offset], ds->name11_12, 4);
+ offset += 4;
+
+ if (ds->id & 0x40) {
+ unicode[offset] = 0;
+ unicode[offset+1] = 0;
+ }
+ if (slot > 0) {
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ PRINTK(("4. get_new_entry: %d\n", get_new_entry));
+ if (ino == -1) {
+ is_long = 0;
+ get_new_entry = 0;
+ break;
+ }
+ ds = (struct msdos_dir_slot *) de;
+ }
+ PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+ }
+ } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+ char bufname[14];
+ char *ptname = bufname;
+ int dotoffset = 0;
+
+ if (is_long) {
+ unsigned char sum;
+ long_len = uni2ascii(unicode, longname, uni_xlate);
+ for (sum = 0, i = 0; i < 11; i++) {
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ }
+
+ if (sum != alias_checksum) {
+ PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
+ is_long = 0;
+ }
+ }
+
+ if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
+ bufname[0] = '.';
+ dotoffset = 1;
+ ptname = bufname+1;
+ }
+ for (i = 0, last = 0; i < 8; i++) {
+ if (!(c = de->name[i])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ /* see namei.c, msdos_format_name */
+ if (c == 0x05) c = 0xE5;
+ if (c != ' ')
+ last = i+1;
+ ptname[i] = c;
+ }
+ i = last;
+ ptname[i] = '.';
+ i++;
+ for (i2 = 0; i2 < 3; i2++) {
+ if (!(c = de->ext[i2])) break;
+ if (c >= 'A' && c <= 'Z') c += 32;
+ if (c != ' ')
+ last = i+1;
+ ptname[i] = c;
+ i++;
+ }
+ if ((i = last) != 0) {
+ if (!strcmp(de->name,MSDOS_DOT))
+ ino = inode->i_ino;
+ else if (!strcmp(de->name,MSDOS_DOTDOT))
+ ino = fat_parent_ino(inode,0);
+
+ if (shortnames || !is_long) {
+ dcache_add(inode, bufname, i+dotoffset, ino);
+ if (both) {
+ bufname[i+dotoffset] = '\0';
+ }
+ spos = oldpos;
+ if (is_long) {
+ spos = filp->f_pos - sizeof(struct msdos_dir_entry);
+ } else {
+ long_slots = 0;
+ }
+ if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
+ filp->f_pos = oldpos;
+ break;
+ }
+ }
+ if (is_long && longnames) {
+ dcache_add(inode, longname, long_len, ino);
+ if (both) {
+ memcpy(&longname[long_len+1], bufname, i+dotoffset);
+ long_len += i+dotoffset;
+ }
+ spos = filp->f_pos - sizeof(struct msdos_dir_entry);
+ if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
+ filp->f_pos = oldpos;
+ break;
+ }
+ }
+ oldpos = filp->f_pos;
+ }
+ is_long = 0;
+ } else {
+ is_long = 0;
+ oldpos = filp->f_pos;
+ }
+ ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
+ }
+ if (bh)
+ fat_brelse(sb, bh);
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
+ return 0;
+}
+
+static int fat_filldir(
+ filldir_t filldir,
+ void * buf,
+ const char * name,
+ int name_len,
+ int is_long,
+ off_t offset,
+ off_t short_offset,
+ int long_slots,
+ ino_t ino)
+{
+ return filldir(buf, name, name_len, offset, ino);
+}
+
+int fat_readdir(
+ struct inode *inode,
+ struct file *filp,
+ void *dirent,
+ filldir_t filldir)
+{
+ return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
+ 0, 1, 0);
+}
+
+static int vfat_ioctl_fill(
+ filldir_t filldir,
+ void * buf,
+ const char * name,
+ int name_len,
+ int is_long,
+ off_t offset,
+ off_t short_offset,
+ int long_slots,
+ ino_t ino)
+{
+ struct dirent *d1 = (struct dirent *)buf;
+ struct dirent *d2 = d1 + 1;
+ int len, slen;
+ int dotdir;
+
+ get_user(len, &d1->d_reclen);
+ if (len != 0) {
+ return -1;
+ }
+
+ if ((name_len == 1 && name[0] == '.') ||
+ (name_len == 2 && name[0] == '.' && name[1] == '.')) {
+ dotdir = 1;
+ len = name_len;
+ } else {
+ dotdir = 0;
+ len = strlen(name);
+ }
+ if (len != name_len) {
+ copy_to_user(d2->d_name, name, len);
+ put_user(0, d2->d_name + len);
+ put_user(len, &d2->d_reclen);
+ put_user(ino, &d2->d_ino);
+ put_user(offset, &d2->d_off);
+ slen = name_len - len;
+ copy_to_user(d1->d_name, name+len+1, slen);
+ put_user(0, d1->d_name+slen);
+ put_user(slen, &d1->d_reclen);
+ } else {
+ put_user(0, d2->d_name);
+ put_user(0, &d2->d_reclen);
+ copy_to_user(d1->d_name, name, len);
+ put_user(0, d1->d_name+len);
+ put_user(len, &d1->d_reclen);
+ }
+ PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
+ d1, d2, len, name_len));
+
+ return 0;
+}
+
+int fat_dir_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+ * Samba should use this ioctl instead of readdir() to
+ * get the information it needs.
+ */
+ switch (cmd) {
+ case VFAT_IOCTL_READDIR_BOTH: {
+ struct dirent *d1 = (struct dirent *)arg;
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
+ put_user(0, &d1->d_reclen);
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 0, 1, 1);
+ }
+ case VFAT_IOCTL_READDIR_SHORT: {
+ struct dirent *d1 = (struct dirent *)arg;
+ put_user(0, &d1->d_reclen);
+ err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ if (err)
+ return err;
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, NULL, 1, 0, 1);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
new file mode 100644
index 000000000..6318549cc
--- /dev/null
+++ b/fs/fat/fatfs_syms.c
@@ -0,0 +1,58 @@
+/*
+ * linux/fs/fat/fatfs_syms.c
+ *
+ * Exported kernel symbols for the low-level FAT-based fs support.
+ *
+ */
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+extern struct file_operations fat_dir_operations;
+
+static struct symbol_table fat_syms = {
+#include <linux/symtab_begin.h>
+ X(fat_a2alias),
+ X(fat_a2uni),
+ X(fat_add_cluster),
+ X(fat_bmap),
+ X(fat_brelse),
+ X(fat_cache_inval_inode),
+ X(fat_code2uni),
+ X(fat_date_unix2dos),
+ X(fat_dir_operations),
+ X(fat_file_read),
+ X(fat_file_write),
+ X(fat_fs_panic),
+ X(fat_get_entry),
+ X(fat_lock_creation),
+ X(fat_mark_buffer_dirty),
+ X(fat_mmap),
+ X(fat_notify_change),
+ X(fat_parent_ino),
+ X(fat_put_inode),
+ X(fat_put_super),
+ X(fat_read_inode),
+ X(fat_read_super),
+ X(fat_readdirx),
+ X(fat_readdir),
+ X(fat_scan),
+ X(fat_smap),
+ X(fat_statfs),
+ X(fat_truncate),
+ X(fat_uni2asc_pg),
+ X(fat_uni2code),
+ X(fat_unlock_creation),
+ X(fat_write_inode),
+#include <linux/symtab_end.h>
+};
+
+int init_fat_fs(void)
+{
+ return register_symtab(&fat_syms);
+}
+
diff --git a/fs/fat/file.c b/fs/fat/file.c
new file mode 100644
index 000000000..b9162f7d9
--- /dev/null
+++ b/fs/fat/file.c
@@ -0,0 +1,385 @@
+/*
+ * linux/fs/fat/file.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * regular file handling primitives for fat-based filesystems
+ */
+
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include "msbuffer.h"
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+static struct file_operations fat_file_operations = {
+ NULL, /* lseek - default */
+ fat_file_read, /* read */
+ fat_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ file_fsync /* fsync */
+};
+
+struct inode_operations fat_file_inode_operations = {
+ &fat_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ fat_bmap, /* bmap */
+ fat_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+/* #Specification: msdos / special devices / mmap
+ Mmapping does work because a special mmap is provide in that case.
+ Note that it is much less efficient than the generic_file_mmap normally
+ used since it allocate extra buffer. generic_file_mmap is used for
+ normal device (512 bytes hardware sectors).
+*/
+static struct file_operations fat_file_operations_1024 = {
+ NULL, /* lseek - default */
+ fat_file_read, /* read */
+ fat_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ fat_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ file_fsync /* fsync */
+};
+
+/* #Specification: msdos / special devices / swap file
+ Swap file can't work on special devices with a large sector
+ size (1024 bytes hard sector). Those devices have a weird
+ MsDOS filesystem layout. Generally a single hardware sector
+ may contain 2 unrelated logical sector. This mean that there is
+ no easy way to do a mapping between disk sector of a file and virtual
+ memory. So swap file is difficult (not available right now)
+ on those devices. Off course, Ext2 does not have this problem.
+*/
+struct inode_operations fat_file_inode_operations_1024 = {
+ &fat_file_operations_1024, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ fat_truncate, /* truncate */
+ NULL, /* permission */
+ NULL /* smap */
+};
+
+#define MSDOS_PREFETCH 32
+struct fat_pre {
+ int file_sector;/* Next sector to read in the prefetch table */
+ /* This is relative to the file, not the disk */
+ struct buffer_head *bhlist[MSDOS_PREFETCH]; /* All buffers needed */
+ int nblist; /* Number of buffers in bhlist */
+ int nolist; /* index in bhlist */
+};
+/*
+ Order the prefetch of more sectors.
+*/
+static void fat_prefetch (
+ struct inode *inode,
+ struct fat_pre *pre,
+ int nb) /* How many must we prefetch at once */
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bhreq[MSDOS_PREFETCH]; /* Buffers not */
+ /* already read */
+ int nbreq = 0; /* Number of buffers in bhreq */
+ int i;
+ for (i=0; i<nb; i++){
+ int sector = fat_smap(inode,pre->file_sector);
+ if (sector != 0){
+ struct buffer_head *bh;
+ PRINTK (("fsector2 %d -> %d\n",pre->file_sector-1,sector));
+ pre->file_sector++;
+ bh = fat_getblk(sb, sector);
+ if (bh == NULL) break;
+ pre->bhlist[pre->nblist++] = bh;
+ if (!fat_is_uptodate(sb,bh))
+ bhreq[nbreq++] = bh;
+ }else{
+ break;
+ }
+ }
+ if (nbreq > 0) fat_ll_rw_block (sb,READ,nbreq,bhreq);
+ for (i=pre->nblist; i<MSDOS_PREFETCH; i++) pre->bhlist[i] = NULL;
+}
+
+/*
+ Read a file into user space
+*/
+long fat_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ unsigned long count)
+{
+ struct super_block *sb = inode->i_sb;
+ char *start = buf;
+ char *end = buf + count;
+ int i;
+ int left_in_file;
+ struct fat_pre pre;
+
+
+ if (!inode) {
+ printk("fat_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ if (filp->f_pos >= inode->i_size || count == 0) return 0;
+ /*
+ Tell the buffer cache which block we expect to read in advance
+ Since we are limited with the stack, we preread only MSDOS_PREFETCH
+ because we have to keep the result into the local
+ arrays pre.bhlist and bhreq.
+
+ Each time we process one block in bhlist, we replace
+ it by a new prefetch block if needed.
+ */
+ PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,filp->f_pos,inode->i_size,count));
+ {
+ /*
+ We must prefetch complete block, so we must
+ take in account the offset in the first block.
+ */
+ int count_max = (filp->f_pos & (SECTOR_SIZE-1)) + count;
+ int to_reada; /* How many block to read all at once */
+ pre.file_sector = filp->f_pos >> SECTOR_BITS;
+ to_reada = count_max / SECTOR_SIZE;
+ if (count_max & (SECTOR_SIZE-1)) to_reada++;
+ if (filp->f_reada || !MSDOS_I(inode)->i_binary){
+ /* Doing a read ahead on ascii file make sure we always */
+ /* pre read enough, since we don't know how many blocks */
+ /* we really need */
+ int ahead = read_ahead[MAJOR(inode->i_dev)];
+ PRINTK (("to_reada %d ahead %d\n",to_reada,ahead));
+ if (ahead == 0) ahead = 8;
+ to_reada += ahead;
+ }
+ if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH;
+ pre.nblist = 0;
+ fat_prefetch (inode,&pre,to_reada);
+ }
+ pre.nolist = 0;
+ PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist));
+ while ((left_in_file = inode->i_size - filp->f_pos) > 0
+ && buf < end){
+ struct buffer_head *bh = pre.bhlist[pre.nolist];
+ char *data;
+ int size,offset;
+ if (bh == NULL) break;
+ pre.bhlist[pre.nolist] = NULL;
+ pre.nolist++;
+ if (pre.nolist == MSDOS_PREFETCH/2){
+ memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2
+ ,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0]));
+ pre.nblist -= MSDOS_PREFETCH/2;
+ fat_prefetch (inode,&pre,MSDOS_PREFETCH/2);
+ pre.nolist = 0;
+ }
+ PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count));
+ wait_on_buffer(bh);
+ if (!fat_is_uptodate(sb,bh)){
+ /* read error ? */
+ fat_brelse (sb, bh);
+ break;
+ }
+ offset = filp->f_pos & (SECTOR_SIZE-1);
+ data = bh->b_data + offset;
+ size = MIN(SECTOR_SIZE-offset,left_in_file);
+ if (MSDOS_I(inode)->i_binary) {
+ size = MIN(size,end-buf);
+ copy_to_user(buf,data,size);
+ buf += size;
+ filp->f_pos += size;
+ }else{
+ for (; size && buf < end; size--) {
+ char ch = *data++;
+ filp->f_pos++;
+ if (ch == 26){
+ filp->f_pos = inode->i_size;
+ break;
+ }else if (ch != '\r'){
+ put_user(ch,buf++);
+ }
+ }
+ }
+ fat_brelse(sb, bh);
+ }
+ PRINTK (("--- %d -> %d\n",count,(int)(buf-start)));
+ for (i=0; i<pre.nblist; i++)
+ fat_brelse (sb, pre.bhlist[i]);
+ if (start == buf)
+ return -EIO;
+ if (!IS_RDONLY(inode))
+ inode->i_atime = CURRENT_TIME;
+ filp->f_reada = 1; /* Will be reset if a lseek is done */
+ return buf-start;
+}
+
+/*
+ Write to a file either from user space
+*/
+long fat_file_write(
+ struct inode *inode,
+ struct file *filp,
+ const char *buf,
+ unsigned long count)
+{
+ struct super_block *sb = inode->i_sb;
+ int sector,offset,size,left,written;
+ int error,carry;
+ const char *start;
+ char *to,ch;
+ struct buffer_head *bh;
+ int binary_mode = MSDOS_I(inode)->i_binary;
+
+ if (!inode) {
+ printk("fat_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ /* system files may be immutable */
+ if (IS_IMMUTABLE(inode))
+ return -EPERM;
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ filp->f_pos = inode->i_size;
+ if (count == 0)
+ return 0;
+ error = carry = 0;
+ for (start = buf; count || carry; count -= size) {
+ while (!(sector = fat_smap(inode,filp->f_pos >> SECTOR_BITS)))
+ if ((error = fat_add_cluster(inode)) < 0) break;
+ if (error) {
+ fat_truncate(inode);
+ break;
+ }
+ offset = filp->f_pos & (SECTOR_SIZE-1);
+ size = MIN(SECTOR_SIZE-offset,MAX(carry,count));
+ if (binary_mode
+ && offset == 0
+ && (size == SECTOR_SIZE
+ || filp->f_pos + size >= inode->i_size)){
+ /* No need to read the block first since we will */
+ /* completely overwrite it */
+ /* or at least write past the end of file */
+ if (!(bh = fat_getblk(sb,sector))){
+ error = -EIO;
+ break;
+ }
+ } else if (!(bh = fat_bread(sb,sector))) {
+ error = -EIO;
+ break;
+ }
+ if (binary_mode) {
+ copy_from_user(bh->b_data+offset,buf,written = size);
+ buf += size;
+ } else {
+ written = left = SECTOR_SIZE-offset;
+ to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
+ if (carry) {
+ *to++ = '\n';
+ left--;
+ carry = 0;
+ }
+ for (size = 0; size < count && left; size++) {
+ get_user(ch, buf++);
+ if (ch == '\n') {
+ *to++ = '\r';
+ left--;
+ }
+ if (!left) carry = 1;
+ else {
+ *to++ = ch;
+ left--;
+ }
+ }
+ written -= left;
+ }
+ update_vm_cache(inode, filp->f_pos, bh->b_data + (filp->f_pos & (SECTOR_SIZE-1)), written);
+ filp->f_pos += written;
+ if (filp->f_pos > inode->i_size) {
+ inode->i_size = filp->f_pos;
+ inode->i_dirt = 1;
+ }
+ fat_set_uptodate(sb, bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 0);
+ fat_brelse(sb, bh);
+ }
+ if (start == buf)
+ return error;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+ inode->i_dirt = 1;
+ return buf-start;
+}
+
+void fat_truncate(struct inode *inode)
+{
+ int cluster;
+
+ /* Why no return value? Surely the disk could fail... */
+ if (IS_IMMUTABLE(inode))
+ return /* -EPERM */;
+ cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
+ (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
+ MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+ inode->i_dirt = 1;
+}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
new file mode 100644
index 000000000..55d91ba2b
--- /dev/null
+++ b/fs/fat/inode.c
@@ -0,0 +1,596 @@
+/*
+ * linux/fs/fat/inode.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ *
+ * 3 May 1996 Fixed alignment problems for RISC architectures.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include "msbuffer.h"
+#include "tables.h"
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+
+
+void fat_put_inode(struct inode *inode)
+{
+ struct inode *depend, *linked;
+ struct super_block *sb;
+
+ depend = MSDOS_I(inode)->i_depend;
+ linked = MSDOS_I(inode)->i_linked;
+ sb = inode->i_sb;
+ if (inode->i_nlink) {
+ if (depend) {
+ iput(depend);
+ }
+ if (linked) {
+ iput(linked);
+ MSDOS_I(inode)->i_linked = NULL;
+ }
+ if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
+ return;
+ }
+ inode->i_size = 0;
+ fat_truncate(inode);
+ if (depend) {
+ if (MSDOS_I(depend)->i_old != inode) {
+ printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+ depend, inode, MSDOS_I(depend)->i_old);
+ fat_fs_panic(sb,"...");
+ goto done;
+ }
+ MSDOS_I(depend)->i_old = NULL;
+ iput(depend);
+ }
+ if (linked) {
+ if (MSDOS_I(linked)->i_oldlink != inode) {
+ printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+ linked, inode, MSDOS_I(linked)->i_oldlink);
+ fat_fs_panic(sb,"...");
+ goto done;
+ }
+ MSDOS_I(linked)->i_oldlink = NULL;
+ iput(linked);
+ }
+done:
+ clear_inode(inode);
+}
+
+
+void fat_put_super(struct super_block *sb)
+{
+ fat_cache_inval_dev(sb->s_dev);
+ set_blocksize (sb->s_dev,BLOCK_SIZE);
+ lock_super(sb);
+ sb->s_dev = 0;
+ unlock_super(sb);
+ MOD_DEC_USE_COUNT;
+ return;
+}
+
+
+static int parse_options(char *options,int *fat, int *blksize, int *debug,
+ struct fat_mount_options *opts)
+{
+ char *this_char,*value;
+
+ opts->name_check = 'n';
+ opts->conversion = 'b';
+ opts->fs_uid = current->uid;
+ opts->fs_gid = current->gid;
+ opts->fs_umask = current->fs->umask;
+ opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = opts->isvfat = 0;
+ *debug = *fat = 0;
+
+ if (!options) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"check") && value) {
+ if (value[0] && !value[1] && strchr("rns",*value))
+ opts->name_check = *value;
+ else if (!strcmp(value,"relaxed")) opts->name_check = 'r';
+ else if (!strcmp(value,"normal")) opts->name_check = 'n';
+ else if (!strcmp(value,"strict")) opts->name_check = 's';
+ else return 0;
+ }
+ else if (!strcmp(this_char,"conv") && value) {
+ if (value[0] && !value[1] && strchr("bta",*value))
+ opts->conversion = *value;
+ else if (!strcmp(value,"binary")) opts->conversion = 'b';
+ else if (!strcmp(value,"text")) opts->conversion = 't';
+ else if (!strcmp(value,"auto")) opts->conversion = 'a';
+ else return 0;
+ }
+ else if (!strcmp(this_char,"dots")) {
+ opts->dotsOK = 1;
+ }
+ else if (!strcmp(this_char,"nodots")) {
+ opts->dotsOK = 0;
+ }
+ else if (!strcmp(this_char,"showexec")) {
+ opts->showexec = 1;
+ }
+ else if (!strcmp(this_char,"dotsOK") && value) {
+ if (!strcmp(value,"yes")) opts->dotsOK = 1;
+ else if (!strcmp(value,"no")) opts->dotsOK = 0;
+ else return 0;
+ }
+ else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 0;
+ opts->fs_uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 0;
+ opts->fs_gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"umask")) {
+ if (!value || !*value)
+ return 0;
+ opts->fs_umask = simple_strtoul(value,&value,8);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"debug")) {
+ if (value) return 0;
+ *debug = 1;
+ }
+ else if (!strcmp(this_char,"fat")) {
+ if (!value || !*value)
+ return 0;
+ *fat = simple_strtoul(value,&value,0);
+ if (*value || (*fat != 12 && *fat != 16))
+ return 0;
+ }
+ else if (!strcmp(this_char,"quiet")) {
+ if (value) return 0;
+ opts->quiet = 1;
+ }
+ else if (!strcmp(this_char,"blocksize")) {
+ *blksize = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ if (*blksize != 512 && *blksize != 1024){
+ printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
+ }
+ }
+ else if (!strcmp(this_char,"sys_immutable")) {
+ if (value)
+ return 0;
+ opts->sys_immutable = 1;
+ }
+ }
+ return 1;
+}
+
+
+/* Read the super block of an MS-DOS FS. */
+
+struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
+{
+ struct buffer_head *bh;
+ struct msdos_boot_sector *b;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int debug,error,fat;
+ int blksize = 512;
+ struct fat_mount_options opts;
+
+ MOD_INC_USE_COUNT;
+ if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
+ blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)];
+ if (blksize != 512){
+ printk ("MSDOS: Hardware sector size is %d\n",blksize);
+ }
+ }
+ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
+ || (blksize != 512 && blksize != 1024)) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ cache_init();
+ lock_super(sb);
+ /* The first read is always 1024 bytes */
+ sb->s_blocksize = 1024;
+ set_blocksize(sb->s_dev, 1024);
+ bh = fat_bread(sb, 0);
+ unlock_super(sb);
+ if (bh == NULL || !fat_is_uptodate(sb,bh)) {
+ fat_brelse (sb, bh);
+ sb->s_dev = 0;
+ printk("FAT bread failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ b = (struct msdos_boot_sector *) bh->b_data;
+ set_blocksize(sb->s_dev, blksize);
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ *
+ * DOS 3 partition managers got around this problem by faking a
+ * larger sector size, ie treating multiple physical sectors as
+ * a single logical sector.
+ *
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+ /* don't divide by zero */
+
+ logical_sector_size = b->sector_size[0] | (b->sector_size[1] << 8);
+ logical_sector_size =
+ CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
+ sector_mult = logical_sector_size >> SECTOR_BITS;
+ MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
+ MSDOS_SB(sb)->fats = b->fats;
+ MSDOS_SB(sb)->fat_start = CF_LE_W(b->reserved)*sector_mult;
+ MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
+ MSDOS_SB(sb)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W(
+ b->fat_length))*sector_mult;
+ MSDOS_SB(sb)->dir_entries =
+ CF_LE_W(get_unaligned((unsigned short *) &b->dir_entries));
+ MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
+ MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
+ sector_mult);
+ data_sectors = ((b->sectors[0] | (b->sectors[1] << 8)) ?
+ (b->sectors[0] | (b->sectors[1] << 8)) :
+ CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(sb)->data_start;
+ data_sectors = CF_LE_W(get_unaligned((unsigned short *) &b->sectors));
+ if (!data_sectors) {
+ data_sectors = CF_LE_L(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
+ error = !b->cluster_size || !sector_mult;
+ if (!error) {
+ MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
+ b->cluster_size/sector_mult : 0;
+ MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
+ MSDOS_FAT12 ? 16 : 12;
+ fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+ MSDOS_SB(sb)->fat_bits;
+ error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
+ (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+ MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+ || !b->secs_track || !b->heads;
+ }
+ fat_brelse(sb, bh);
+ /*
+ This must be done after the brelse because the bh is a dummy
+ allocated by fat_bread (see buffer.c)
+ */
+ sb->s_blocksize = blksize; /* Using this small block size solves */
+ /* the misfit with buffer cache and cluster */
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+ "uid=%d,gid=%d,umask=%03o%s]\n",
+ MSDOS_SB(sb)->fat_bits,opts.name_check,
+ opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
+ MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : "");
+ printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
+ "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
+ MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
+ MSDOS_SB(sb)->data_start,
+ (b->sectors[0] | (b->sectors[1] << 8)),
+ (unsigned long)b->total_sect,logical_sector_size);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+ if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev "
+ "%s.\n", kdevname(sb->s_dev));
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ sb->s_magic = MSDOS_SUPER_MAGIC;
+ /* set up enough so that it can read an inode */
+ MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
+ MSDOS_SB(sb)->fat_wait = NULL;
+ MSDOS_SB(sb)->fat_lock = 0;
+ MSDOS_SB(sb)->prev_free = 0;
+ memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
+ if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
+ sb->s_dev = 0;
+ printk("get root inode failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ return sb;
+}
+
+
+void fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
+{
+ int free,nr;
+ struct statfs tmp;
+
+ lock_fat(sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ free = MSDOS_SB(sb)->free_clusters;
+ else {
+ free = 0;
+ for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
+ if (!fat_access(sb,nr,-1)) free++;
+ MSDOS_SB(sb)->free_clusters = free;
+ }
+ unlock_fat(sb);
+ tmp.f_type = sb->s_magic;
+ tmp.f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
+ tmp.f_blocks = MSDOS_SB(sb)->clusters;
+ tmp.f_bfree = free;
+ tmp.f_bavail = free;
+ tmp.f_files = 0;
+ tmp.f_ffree = 0;
+ tmp.f_namelen = 12;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+
+int fat_bmap(struct inode *inode,int block)
+{
+ struct msdos_sb_info *sb;
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ return sb->dir_start + block;
+ }
+ cluster = block/sb->cluster_size;
+ offset = block % sb->cluster_size;
+ if (!(cluster = get_cluster(inode,cluster))) return 0;
+ return (cluster-2)*sb->cluster_size+sb->data_start+offset;
+}
+
+static int is_exec(char *extension)
+{
+ char *exe_extensions = "EXECOMBAT", *walk;
+
+ for (walk = exe_extensions; *walk; walk += 3)
+ if (!strncmp(extension, walk, 3))
+ return 1;
+ return 0;
+}
+
+void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *raw_entry;
+ int nr;
+
+ MSDOS_I(inode)->i_busy = 0;
+ MSDOS_I(inode)->i_depend = MSDOS_I(inode)->i_old = NULL;
+ MSDOS_I(inode)->i_linked = MSDOS_I(inode)->i_oldlink = NULL;
+ MSDOS_I(inode)->i_binary = 1;
+ inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
+ inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
+ inode->i_version = ++event;
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) |
+ S_IFDIR;
+ inode->i_op = fs_dir_inode_ops;
+ inode->i_nlink = fat_subdirs(inode)+2;
+ /* subdirs (neither . nor ..) plus . and "self" */
+ inode->i_size = MSDOS_SB(sb)->dir_entries*
+ sizeof(struct msdos_dir_entry);
+ inode->i_blksize = MSDOS_SB(sb)->cluster_size*
+ SECTOR_SIZE;
+ inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+ inode->i_blksize*MSDOS_SB(sb)->cluster_size;
+ MSDOS_I(inode)->i_start = 0;
+ MSDOS_I(inode)->i_attrs = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ return;
+ }
+ if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
+ printk("dev = %s, ino = %ld\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ panic("fat_read_inode: unable to read i-node block");
+ }
+ raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+ [inode->i_ino & (MSDOS_DPB-1)];
+ if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
+ inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
+ ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
+ inode->i_op = fs_dir_inode_ops;
+
+ MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ inode->i_nlink = fat_subdirs(inode);
+ /* includes .., compensating for "self" */
+#ifdef DEBUG
+ if (!inode->i_nlink) {
+ printk("directory %d: i_nlink == 0\n",inode->i_ino);
+ inode->i_nlink = 1;
+ }
+#endif
+ inode->i_size = 0;
+ if ((nr = CF_LE_W(raw_entry->start)) != 0)
+ while (nr != -1) {
+ inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->
+ i_sb)->cluster_size;
+ if (!(nr = fat_access(sb,nr,-1))) {
+ printk("Directory %ld: bad FAT\n",
+ inode->i_ino);
+ break;
+ }
+ }
+ } else { /* not a directory */
+ inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
+ ((IS_NOEXEC(inode) ||
+ (MSDOS_SB(sb)->options.showexec &&
+ !is_exec(raw_entry->ext)))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
+ inode->i_op = (sb->s_blocksize == 1024)
+ ? &fat_file_inode_operations_1024
+ : &fat_file_inode_operations;
+ MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ inode->i_nlink = 1;
+ inode->i_size = CF_LE_L(raw_entry->size);
+ }
+ if(raw_entry->attr & ATTR_SYS)
+ if (MSDOS_SB(sb)->options.sys_immutable)
+ inode->i_flags |= S_IMMUTABLE;
+ MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(sb)->options.conversion,
+ raw_entry->ext);
+ MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
+ /* this is as close to the truth as we can get ... */
+ inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
+ inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+ inode->i_blksize*MSDOS_SB(sb)->cluster_size;
+ inode->i_mtime = inode->i_atime =
+ date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
+ inode->i_ctime =
+ MSDOS_SB(sb)->options.isvfat
+ ? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
+ : inode->i_mtime;
+ fat_brelse(sb, bh);
+}
+
+
+void fat_write_inode(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *raw_entry;
+ struct inode *linked;
+
+ linked = MSDOS_I(inode)->i_linked;
+ if (linked) {
+ if (MSDOS_I(linked)->i_oldlink != inode) {
+ printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n",
+ linked, inode, MSDOS_I(linked)->i_oldlink);
+ fat_fs_panic(sb,"...");
+ return;
+ }
+ linked->i_version = ++event;
+ linked->i_mode = inode->i_mode;
+ linked->i_uid = inode->i_uid;
+ linked->i_gid = inode->i_gid;
+ linked->i_size = inode->i_size;
+ linked->i_atime = inode->i_atime;
+ linked->i_mtime = inode->i_mtime;
+ linked->i_ctime = inode->i_ctime;
+ linked->i_blocks = inode->i_blocks;
+ linked->i_atime = inode->i_atime;
+ MSDOS_I(linked)->i_attrs = MSDOS_I(inode)->i_attrs;
+ linked->i_dirt = 1;
+ }
+
+ inode->i_dirt = 0;
+ if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
+ if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
+ printk("dev = %s, ino = %ld\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ panic("msdos_write_inode: unable to read i-node block");
+ }
+ raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+ [inode->i_ino & (MSDOS_DPB-1)];
+ if (S_ISDIR(inode->i_mode)) {
+ raw_entry->attr = ATTR_DIR;
+ raw_entry->size = 0;
+ }
+ else {
+ raw_entry->attr = ATTR_NONE;
+ raw_entry->size = CT_LE_L(inode->i_size);
+ }
+ raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
+ MSDOS_I(inode)->i_attrs;
+ raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_start);
+ fat_date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
+ raw_entry->time = CT_LE_W(raw_entry->time);
+ raw_entry->date = CT_LE_W(raw_entry->date);
+ if (MSDOS_SB(sb)->options.isvfat) {
+ fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
+ raw_entry->ctime = CT_LE_W(raw_entry->ctime);
+ raw_entry->cdate = CT_LE_W(raw_entry->cdate);
+ }
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
+}
+
+
+int fat_notify_change(struct inode * inode,struct iattr * attr)
+{
+ struct super_block *sb = inode->i_sb;
+ int error;
+
+ error = inode_change_ok(inode, attr);
+ if (error)
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != MSDOS_SB(sb)->options.fs_uid)) ||
+ ((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != MSDOS_SB(sb)->options.fs_gid)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode & ~MSDOS_VALID_MODE)))
+ error = -EPERM;
+
+ if (error)
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
+
+ inode_setattr(inode, attr);
+
+ if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode))
+ inode->i_mode &= S_IFMT | S_IRUGO | S_IWUGO;
+ else
+ inode->i_mode |= S_IXUGO;
+
+ inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
+ ~MSDOS_SB(sb)->options.fs_umask;
+ return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ return init_fat_fs();
+}
+
+
+void cleanup_module(void)
+{
+ /* Nothing to be done, really! */
+ return;
+}
+#endif
+
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
new file mode 100644
index 000000000..120c522e7
--- /dev/null
+++ b/fs/fat/misc.c
@@ -0,0 +1,557 @@
+/*
+ * linux/fs/fat/misc.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#include "msbuffer.h"
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+/* Well-known binary file extensions - of course there are many more */
+
+static char bin_extensions[] =
+ "EXE" "COM" "BIN" "APP" "SYS" "DRV" "OVL" "OVR" "OBJ" "LIB" "DLL" "PIF" /* program code */
+ "ARC" "ZIP" "LHA" "LZH" "ZOO" "TAR" "Z " "ARJ" /* common archivers */
+ "TZ " "TAZ" "TZP" "TPZ" /* abbreviations of tar.Z and tar.zip */
+ "GZ " "TGZ" "DEB" /* .gz, .tar.gz and Debian packages */
+ "GIF" "BMP" "TIF" "GL " "JPG" "PCX" /* graphics */
+ "TFM" "VF " "GF " "PK " "PXL" "DVI"; /* TeX */
+
+
+/*
+ * fat_fs_panic reports a severe file system problem and sets the file system
+ * read-only. The file system can be made writable again by remounting it.
+ */
+
+void fat_fs_panic(struct super_block *s,const char *msg)
+{
+ int not_ro;
+
+ not_ro = !(s->s_flags & MS_RDONLY);
+ if (not_ro) s->s_flags |= MS_RDONLY;
+ printk("Filesystem panic (dev %s, ", kdevname(s->s_dev));
+ printk("mounted on %s:%ld)\n %s\n", /* note: kdevname returns & static char[] */
+ kdevname(s->s_covered->i_dev), s->s_covered->i_ino, msg);
+ if (not_ro)
+ printk(" File system has been set read-only\n");
+}
+
+
+/*
+ * is_binary selects optional text conversion based on the conversion mode and
+ * the extension part of the file name.
+ */
+
+int is_binary(char conversion,char *extension)
+{
+ char *walk;
+
+ switch (conversion) {
+ case 'b':
+ return 1;
+ case 't':
+ return 0;
+ case 'a':
+ for (walk = bin_extensions; *walk; walk += 3)
+ if (!strncmp(extension,walk,3)) return 1;
+ return 0;
+ default:
+ printk("Invalid conversion mode - defaulting to "
+ "binary.\n");
+ return 1;
+ }
+}
+
+
+/* File creation lock. This is system-wide to avoid deadlocks in rename. */
+/* (rename might deadlock before detecting cross-FS moves.) */
+
+static struct wait_queue *creation_wait = NULL;
+static creation_lock = 0;
+
+
+void fat_lock_creation(void)
+{
+ while (creation_lock) sleep_on(&creation_wait);
+ creation_lock = 1;
+}
+
+
+void fat_unlock_creation(void)
+{
+ creation_lock = 0;
+ wake_up(&creation_wait);
+}
+
+
+void lock_fat(struct super_block *sb)
+{
+ while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait);
+ MSDOS_SB(sb)->fat_lock = 1;
+}
+
+
+void unlock_fat(struct super_block *sb)
+{
+ MSDOS_SB(sb)->fat_lock = 0;
+ wake_up(&MSDOS_SB(sb)->fat_wait);
+}
+
+
+/*
+ * fat_add_cluster tries to allocate a new cluster and adds it to the file
+ * represented by inode. The cluster is zero-initialized.
+ */
+
+int fat_add_cluster(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int count,nr,limit,last,curr,sector,last_sector,file_cluster;
+ struct buffer_head *bh;
+ int cluster_size = MSDOS_SB(sb)->cluster_size;
+
+ if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+ if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC;
+ lock_fat(sb);
+ limit = MSDOS_SB(sb)->clusters;
+ nr = limit; /* to keep GCC happy */
+ for (count = 0; count < limit; count++) {
+ nr = ((count+MSDOS_SB(sb)->prev_free) % limit)+2;
+ if (fat_access(sb,nr,-1) == 0) break;
+ }
+ PRINTK (("cnt = %d --",count));
+#ifdef DEBUG
+printk("free cluster: %d\n",nr);
+#endif
+ MSDOS_SB(sb)->prev_free = (count+MSDOS_SB(sb)->prev_free+1) % limit;
+ if (count >= limit) {
+ MSDOS_SB(sb)->free_clusters = 0;
+ unlock_fat(sb);
+ return -ENOSPC;
+ }
+ fat_access(sb,nr,MSDOS_SB(sb)->fat_bits == 12 ?
+ 0xff8 : 0xfff8);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ MSDOS_SB(sb)->free_clusters--;
+ unlock_fat(sb);
+#ifdef DEBUG
+printk("set to %x\n",fat_access(sb,nr,-1));
+#endif
+ last = 0;
+ /* We must locate the last cluster of the file to add this
+ new one (nr) to the end of the link list (the FAT).
+
+ Here file_cluster will be the number of the last cluster of the
+ file (before we add nr).
+
+ last is the corresponding cluster number on the disk. We will
+ use last to plug the nr cluster. We will use file_cluster to
+ update the cache.
+ */
+ file_cluster = 0;
+ if ((curr = MSDOS_I(inode)->i_start) != 0) {
+ cache_lookup(inode,INT_MAX,&last,&curr);
+ file_cluster = last;
+ while (curr && curr != -1){
+ PRINTK (("."));
+ file_cluster++;
+ if (!(curr = fat_access(sb,
+ last = curr,-1))) {
+ fat_fs_panic(sb,"File without EOF");
+ return -ENOSPC;
+ }
+ }
+ PRINTK ((" -- "));
+ }
+#ifdef DEBUG
+printk("last = %d\n",last);
+#endif
+ if (last) fat_access(sb,last,nr);
+ else {
+ MSDOS_I(inode)->i_start = nr;
+ inode->i_dirt = 1;
+ }
+#ifdef DEBUG
+if (last) printk("next set to %d\n",fat_access(sb,last,-1));
+#endif
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
+ #endif
+ if (!(bh = fat_getblk(sb, sector)))
+ printk("getblk failed\n");
+ else {
+ memset(bh->b_data,0,SECTOR_SIZE);
+ fat_set_uptodate(sb, bh, 1);
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
+ }
+ }
+ if (file_cluster != inode->i_blocks/cluster_size){
+ printk ("file_cluster badly computed!!! %d <> %ld\n"
+ ,file_cluster,inode->i_blocks/cluster_size);
+ }else{
+ cache_add(inode,file_cluster,nr);
+ }
+ inode->i_blocks += cluster_size;
+ if (S_ISDIR(inode->i_mode)) {
+ if (inode->i_size & (SECTOR_SIZE-1)) {
+ fat_fs_panic(sb,"Odd directory size");
+ inode->i_size = (inode->i_size+SECTOR_SIZE) &
+ ~(SECTOR_SIZE-1);
+ }
+ inode->i_size += SECTOR_SIZE*cluster_size;
+#ifdef DEBUG
+printk("size is %d now (%x)\n",inode->i_size,inode);
+#endif
+ inode->i_dirt = 1;
+ }
+ return 0;
+}
+
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+int date_dos2unix(unsigned short time,unsigned short date)
+{
+ int month,year,secs;
+
+ month = ((date >> 5) & 15)-1;
+ year = date >> 9;
+ secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+ ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+ month < 2 ? 1 : 0)+3653);
+ /* days since 1.1.70 plus 80's leap day */
+ secs += sys_tz.tz_minuteswest*60;
+ if (sys_tz.tz_dsttime) {
+ secs -= 3600;
+ }
+ return secs;
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+void fat_date_unix2dos(int unix_date,unsigned short *time,
+ unsigned short *date)
+{
+ int day,year,nl_day,month;
+
+ unix_date -= sys_tz.tz_minuteswest*60;
+ *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+ (((unix_date/3600) % 24) << 11);
+ day = unix_date/86400-3652;
+ year = day/365;
+ if ((year+3)/4+365*year > day) year--;
+ day -= (year+3)/4+365*year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ }
+ else {
+ nl_day = (year & 3) || day <= 59 ? day : day-1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day) break;
+ }
+ *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
+
+
+/* Returns the inode number of the directory entry at offset pos. If bh is
+ non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
+ returned in bh. */
+
+int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+ struct msdos_dir_entry **de)
+{
+ struct super_block *sb = dir->i_sb;
+ int sector, offset;
+
+ while (1) {
+ offset = *pos;
+ PRINTK (("get_entry offset %d\n",offset));
+ if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1)
+ return -1;
+ PRINTK (("get_entry sector %d %p\n",sector,*bh));
+ if (!sector)
+ return -1; /* beyond EOF */
+ *pos += sizeof(struct msdos_dir_entry);
+ if (*bh)
+ fat_brelse(sb, *bh);
+ PRINTK (("get_entry sector apres brelse\n"));
+ if (!(*bh = fat_bread(sb, sector))) {
+ printk("Directory sread (sector %d) failed\n",sector);
+ continue;
+ }
+ PRINTK (("get_entry apres sread\n"));
+ *de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
+ (SECTOR_SIZE-1)));
+ return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
+ MSDOS_DIR_BITS);
+ }
+}
+
+
+/*
+ * Now an ugly part: this set of directory scan routines works on clusters
+ * rather than on inodes and sectors. They are necessary to locate the '..'
+ * directory "inode". raw_scan_sector operates in four modes:
+ *
+ * name number ino action
+ * -------- -------- -------- -------------------------------------------------
+ * non-NULL - X Find an entry with that name
+ * NULL non-NULL non-NULL Find an entry whose data starts at *number
+ * NULL non-NULL NULL Count subdirectories in *number. (*)
+ * NULL NULL non-NULL Find an empty entry
+ *
+ * (*) The return code should be ignored. It DOES NOT indicate success or
+ * failure. *number has to be initialized to zero.
+ *
+ * - = not used, X = a value is returned unless NULL
+ *
+ * If res_bh is non-NULL, the buffer is not deallocated but returned to the
+ * caller on success. res_de is set accordingly.
+ *
+ * If cont is non-zero, raw_found continues with the entry after the one
+ * res_bh/res_de point to.
+ */
+
+
+#define RSS_NAME /* search for name */ \
+ done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
+ !(data[entry].attr & ATTR_VOLUME);
+
+#define RSS_START /* search for start cluster */ \
+ done = !IS_FREE(data[entry].name) && CF_LE_W(data[entry].start) == *number;
+
+#define RSS_FREE /* search for free entry */ \
+ { \
+ done = IS_FREE(data[entry].name); \
+ if (done) { \
+ inode = iget(sb,sector*MSDOS_DPS+entry); \
+ if (inode) { \
+ /* Directory slots of busy deleted files aren't available yet. */ \
+ done = !MSDOS_I(inode)->i_busy; \
+ iput(inode); \
+ } \
+ } \
+ }
+
+#define RSS_COUNT /* count subdirectories */ \
+ { \
+ done = 0; \
+ if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
+ (*number)++; \
+ }
+
+static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
+ int *number,int *ino,struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de,char scantype)
+{
+ struct buffer_head *bh;
+ struct msdos_dir_entry *data;
+ struct inode *inode;
+ int entry,start,done;
+
+ if (!(bh = fat_bread(sb,sector)))
+ return -EIO;
+ data = (struct msdos_dir_entry *) bh->b_data;
+ for (entry = 0; entry < MSDOS_DPS; entry++) {
+/* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
+ if (name) {
+ RSS_NAME
+ if (done && scantype) { /* scantype != SCAN_ANY */
+ done = (data[entry].attr & ATTR_HIDDEN)
+ ? (scantype==SCAN_HID)
+ : (scantype==SCAN_NOTHID);
+ }
+ } else {
+ if (!ino) RSS_COUNT
+ else {
+ if (number) RSS_START
+ else RSS_FREE
+ }
+ }
+ if (done) {
+ if (ino) *ino = sector*MSDOS_DPS+entry;
+ start = CF_LE_W(data[entry].start);
+ if (!res_bh)
+ fat_brelse(sb, bh);
+ else {
+ *res_bh = bh;
+ *res_de = &data[entry];
+ }
+ return start;
+ }
+ }
+ fat_brelse(sb, bh);
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan_root performs raw_scan_sector on the root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
+ struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
+{
+ int count,cluster;
+
+ for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
+ if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
+ name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
+ }
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
+ int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
+ **res_de,char scantype)
+{
+ int count,cluster;
+
+#ifdef DEBUG
+ printk("raw_scan_nonroot: start=%d\n",start);
+#endif
+ do {
+ for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
+ if ((cluster = raw_scan_sector(sb,(start-2)*
+ MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
+ count,name,number,ino,res_bh,res_de,scantype)) >= 0)
+ return cluster;
+ }
+ if (!(start = fat_access(sb,start,-1))) {
+ fat_fs_panic(sb,"FAT error");
+ break;
+ }
+#ifdef DEBUG
+ printk("next start: %d\n",start);
+#endif
+ }
+ while (start != -1);
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan performs raw_scan_sector on any sector.
+ *
+ * NOTE: raw_scan must not be used on a directory that is is the process of
+ * being created.
+ */
+
+static int raw_scan(struct super_block *sb, int start, const char *name,
+ int *number, int *ino, struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de, char scantype)
+{
+ if (start) return raw_scan_nonroot
+ (sb,start,name,number,ino,res_bh,res_de,scantype);
+ else return raw_scan_root
+ (sb,name,number,ino,res_bh,res_de,scantype);
+}
+
+
+/*
+ * fat_parent_ino returns the inode number of the parent directory of dir.
+ * File creation has to be deferred while fat_parent_ino is running to
+ * prevent renames.
+ */
+
+int fat_parent_ino(struct inode *dir,int locked)
+{
+ static int zero = 0;
+ int error,curr,prev,nr;
+
+ if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
+ if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
+ if (!locked) fat_lock_creation(); /* prevent renames */
+ if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
+ &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
+ return curr;
+ }
+ if (!curr) nr = MSDOS_ROOT_INO;
+ else {
+ if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
+ NULL,NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
+ return prev;
+ }
+ if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
+ NULL,SCAN_ANY)) < 0) {
+ if (!locked) fat_unlock_creation();
+ return error;
+ }
+ }
+ if (!locked) fat_unlock_creation();
+ return nr;
+}
+
+
+/*
+ * fat_subdirs counts the number of sub-directories of dir. It can be run
+ * on directories being created.
+ */
+
+int fat_subdirs(struct inode *dir)
+{
+ int count;
+
+ count = 0;
+ if (dir->i_ino == MSDOS_ROOT_INO)
+ (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+ else {
+ if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
+ else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
+ NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+ }
+ return count;
+}
+
+
+/*
+ * Scans a directory for a given file (name points to its formatted name) or
+ * for an empty directory slot (name is NULL). Returns an error code or zero.
+ */
+
+int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de,int *ino, char scantype)
+{
+ int res;
+
+ res = (name)
+ ? raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+ name, NULL, ino, res_bh, res_de, scantype)
+ : raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+ NULL, NULL, ino, res_bh, res_de, scantype);
+ return res<0 ? res : 0;
+}
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
new file mode 100644
index 000000000..7896a4cfe
--- /dev/null
+++ b/fs/fat/mmap.c
@@ -0,0 +1,113 @@
+/*
+ * linux/fs/fat/mmap.c
+ *
+ * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+ *
+ * mmap handling for fat-based filesystems
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/msdos_fs.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long fat_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = fat_file_read (inode,&filp,(char*)page
+ ,need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct fat_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ fat_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+{
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &fat_file_mmap;
+ return 0;
+}
+
+
diff --git a/fs/fat/msbuffer.h b/fs/fat/msbuffer.h
new file mode 100644
index 000000000..5a052251d
--- /dev/null
+++ b/fs/fat/msbuffer.h
@@ -0,0 +1,15 @@
+/* Number of bytes to readahead on disc access */
+#define FAT_READAHEAD (18*1024)
+
+struct buffer_head *fat_bread (struct super_block *sb, int block);
+struct buffer_head *fat_getblk (struct super_block *sb, int block);
+void fat_brelse (struct super_block *sb, struct buffer_head *bh);
+void fat_mark_buffer_dirty (struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val);
+void fat_set_uptodate (struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int fat_is_uptodate (struct super_block *sb, struct buffer_head *bh);
+void fat_ll_rw_block (struct super_block *sb, int opr,
+ int nbreq, struct buffer_head *bh[32]);
diff --git a/fs/fat/tables.c b/fs/fat/tables.c
new file mode 100644
index 000000000..3e8379374
--- /dev/null
+++ b/fs/fat/tables.c
@@ -0,0 +1,280 @@
+/*
+ * linux/fs/fat/tables.c
+ *
+ * ASCII / Unicode translation tables for VFAT filename handling.
+ * By Gordon Chaffee.
+ *
+ * Note: This file is used by all fat-based filesystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "tables.h"
+
+unsigned char fat_uni2code[64] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
+};
+
+unsigned char fat_code2uni[256] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0x3f, 0xff, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+ 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, /* 0x98-0x9F */
+ 0xFF, 0xAD, 0xBD, 0x9C, 0xCF, 0xBE, 0xDD, 0xF5, /* 0xA0-0xA7 */
+ 0xF9, 0xB8, 0x00, 0xAE, 0xAA, 0xF0, 0x00, 0xEE, /* 0xA8-0xAF */
+ 0xF8, 0xF1, 0xFD, 0xFC, 0xEF, 0xE6, 0xF4, 0xFA, /* 0xB0-0xB7 */
+ 0xF7, 0xFB, 0x00, 0xAF, 0xAC, 0xAB, 0xF3, 0x00, /* 0xB8-0xBF */
+ 0xB7, 0xB5, 0xB6, 0xC7, 0x8E, 0x8F, 0x92, 0x80, /* 0xC0-0xC7 */
+ 0xD4, 0x90, 0xD2, 0xD3, 0xDE, 0xD6, 0xD7, 0xD8, /* 0xC8-0xCF */
+ 0x00, 0xA5, 0xE3, 0xE0, 0xE2, 0xE5, 0x99, 0x9E, /* 0xD0-0xD7 */
+ 0x9D, 0xEB, 0xE9, 0xEA, 0x9A, 0xED, 0xE8, 0xE1, /* 0xD8-0xDF */
+ 0xA1, 0xA0, 0x83, 0xC6, 0x84, 0x86, 0x91, 0x87, /* 0xE0-0xE7 */
+ 0x8A, 0x82, 0x88, 0x89, 0x8D, 0x00, 0x8C, 0x8B, /* 0xE8-0xEF */
+ 0xD0, 0xA4, 0x95, 0xA2, 0x93, 0xE4, 0x94, 0xF6, /* 0xF0-0xF7 */
+ 0x9B, 0x97, 0xA3, 0x96, 0x81, 0xEC, 0xE7, 0x98 /* 0xF8-0xFF */
+};
+
+
+static unsigned char page25[256] = {
+ 0xC4, 0x00, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, /* 0x08-0x0F */
+ 0xBF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xD9, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, /* 0x18-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, /* 0x28-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, /* 0x38-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4F */
+ 0xCD, 0xBA, 0x00, 0x00, 0xC9, 0x00, 0x00, 0xBB, /* 0x50-0x57 */
+ 0x00, 0x00, 0xC8, 0x00, 0x00, 0xBC, 0x00, 0x00, /* 0x58-0x5F */
+ 0xCC, 0x00, 0x00, 0xB9, 0x00, 0x00, 0xCB, 0x00, /* 0x60-0x67 */
+ 0x00, 0xCA, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, /* 0x68-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7F */
+
+ 0xDF, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8F */
+ 0x00, 0xB0, 0xB1, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9F */
+ 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xF8-0xFF */
+};
+
+
+unsigned char *fat_uni2asc_pg[256] = {
+ page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, page25
+};
+
+/* Conversion from ASCII name characters to the shortname character
+ * should probably just just use XXX
+ */
+unsigned char fat_a2alias[256] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x08-0x0F */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, /* 0x18-0x1F */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x28-0x2F */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x38-0x3F */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x48-0x4F */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, /* 0x58-0x5F */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x68-0x6F */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, /* 0x78-0x7F */
+
+ 0x80, 0x9A, 0x90, 0xB6, 0x8E, 0xB7, 0x8F, 0x80, /* 0x80-0x87 */
+ 0xD2, 0xD3, 0xD4, 0xD8, 0xD7, 0xDE, 0x8E, 0x8F, /* 0x88-0x8F */
+ 0x90, 0x92, 0x92, 0xE2, 0x99, 0xE3, 0xEA, 0xEB, /* 0x90-0x97 */
+/*_~1*/ 0x98, 0x99, 0x9A, 0x9D, 0x9C, 0x9D, 0x9E, 0x9F, /* 0x98-0x9F */
+ 0xB5, 0xD6, 0xE0, 0xE9, 0xA5, 0xA5, 0xA6, 0xA7, /* 0xA0-0xA7 */
+ 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, /* 0xA8-0xAF */
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, /* 0xB0-0xB7 */
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, /* 0xB8-0xBF */
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, /* 0xC0-0xC7 */
+ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, /* 0xC8-0xCF */
+ 0xD1, 0xD1, 0xD2, 0xD3, 0xD4, 0x49, 0xD6, 0xD7, /* 0xD0-0xD7 */
+ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, /* 0xD8-0xDF */
+ 0xE0, 0xE1, 0xE2, 0xE3, 0x05, 0x05, 0xE6, 0xE8, /* 0xE0-0xE7 */
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xED, 0xED, 0xEE, 0xEF, /* 0xE8-0xEF */
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, /* 0xF0-0xF7 */
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF /* 0xF8-0xFF */
+};
+
+struct unicode_value fat_a2uni[256] = {
+/* 0x00 */
+{0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00},
+{0x04, 0x00}, {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x00},
+{0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0x0B, 0x00},
+{0x0C, 0x00}, {0x0D, 0x00}, {0x0E, 0x00}, {0x0F, 0x00},
+/* 0x10 */
+{0x10, 0x00}, {0x11, 0x00}, {0x12, 0x00}, {0x13, 0x00},
+{0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00},
+{0x18, 0x00}, {0x19, 0x00}, {0x1A, 0x00}, {0x1B, 0x00},
+{0x1C, 0x00}, {0x1D, 0x00}, {0x1E, 0x00}, {0x1F, 0x00},
+/* 0x20 */
+{0x20, 0x00}, {0x21, 0x00}, {0x22, 0x00}, {0x23, 0x00},
+{0x24, 0x00}, {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00},
+{0x28, 0x00}, {0x29, 0x00}, {0x2A, 0x00}, {0x2B, 0x00},
+{0x2C, 0x00}, {0x2D, 0x00}, {0x2E, 0x00}, {0x2F, 0x00},
+/* 0x30 */
+{0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+{0x34, 0x00}, {0x35, 0x00}, {0x36, 0x00}, {0x37, 0x00},
+{0x38, 0x00}, {0x39, 0x00}, {0x3A, 0x00}, {0x3B, 0x00},
+{0x3C, 0x00}, {0x3D, 0x00}, {0x3E, 0x00}, {0x3F, 0x00},
+/* 0x40 */
+{0x40, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x43, 0x00},
+{0x44, 0x00}, {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00},
+{0x48, 0x00}, {0x49, 0x00}, {0x4A, 0x00}, {0x4B, 0x00},
+{0x4C, 0x00}, {0x4D, 0x00}, {0x4E, 0x00}, {0x4F, 0x00},
+/* 0x50 */
+{0x50, 0x00}, {0x51, 0x00}, {0x52, 0x00}, {0x53, 0x00},
+{0x54, 0x00}, {0x55, 0x00}, {0x56, 0x00}, {0x57, 0x00},
+{0x58, 0x00}, {0x59, 0x00}, {0x5A, 0x00}, {0x5B, 0x00},
+{0x5C, 0x00}, {0x5D, 0x00}, {0x5E, 0x00}, {0x5F, 0x00},
+/* 0x60 */
+{0x60, 0x00}, {0x61, 0x00}, {0x62, 0x00}, {0x63, 0x00},
+{0x64, 0x00}, {0x65, 0x00}, {0x66, 0x00}, {0x67, 0x00},
+{0x68, 0x00}, {0x69, 0x00}, {0x6A, 0x00}, {0x6B, 0x00},
+{0x6C, 0x00}, {0x6D, 0x00}, {0x6E, 0x00}, {0x6F, 0x00},
+/* 0x70 */
+{0x70, 0x00}, {0x71, 0x00}, {0x72, 0x00}, {0x73, 0x00},
+{0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0x77, 0x00},
+{0x78, 0x00}, {0x79, 0x00}, {0x7A, 0x00}, {0x7B, 0x00},
+{0x7C, 0x00}, {0x7D, 0x00}, {0x7E, 0x00}, {0x7F, 0x00},
+/* 0x80 */
+{0xC7, 0x00}, {0xFC, 0x00}, {0xE9, 0x00}, {0xE2, 0x00},
+{0xE4, 0x00}, {0xE0, 0x00}, {0xE5, 0x00}, {0xE7, 0x00},
+{0xEA, 0x00}, {0xEB, 0x00}, {0xE8, 0x00}, {0xEF, 0x00},
+{0xEE, 0x00}, {0xEC, 0x00}, {0xC4, 0x00}, {0xC5, 0x00},
+/* 0x90 */
+{0xC9, 0x00}, {0xE6, 0x00}, {0xC6, 0x00}, {0xF4, 0x00},
+{0xF6, 0x00}, {0xF2, 0x00}, {0xFB, 0x00}, {0xF9, 0x00},
+{0xFF, 0x00}, {0xD6, 0x00}, {0xDC, 0x00}, {0xF8, 0x00},
+{0xA3, 0x00}, {0xD8, 0x00}, {0xD7, 0x00}, {0x92, 0x00},
+/* 0xA0 */
+{0xE1, 0x00}, {0xE0, 0x00}, {0xF3, 0x00}, {0xFA, 0x00},
+{0xF1, 0x00}, {0xD1, 0x00}, {0xAA, 0x00}, {0xBA, 0x00},
+{0xBF, 0x00}, {0xAE, 0x00}, {0xAC, 0x00}, {0xBD, 0x00},
+{0xBC, 0x00}, {0xA1, 0x00}, {0xAB, 0x00}, {0xBB, 0x00},
+/* 0xB0 */
+{0x91, 0x25}, {0x92, 0x25}, {0x93, 0x25}, {0x02, 0x25},
+{0x24, 0x25}, {0xC1, 0x00}, {0xC2, 0x00}, {0xC0, 0x00},
+{0xA9, 0x00}, {0x63, 0x25}, {0x51, 0x25}, {0x57, 0x25},
+{0x5D, 0x25}, {0xA2, 0x00}, {0xA5, 0x00}, {0x10, 0x25},
+/* 0xC0 */
+{0x14, 0x25}, {0x34, 0x25}, {0x2C, 0x25}, {0x1C, 0x25},
+{0x00, 0x25}, {0x3C, 0x25}, {0xE3, 0x00}, {0xC3, 0x00},
+{0x5A, 0x25}, {0x54, 0x25}, {0x69, 0x25}, {0x66, 0x25},
+{0x60, 0x25}, {0x50, 0x25}, {0x6C, 0x25}, {0xA4, 0x00},
+/* 0xD0 */
+{0xF0, 0x00}, {0xD0, 0x00}, {0xCA, 0x00}, {0xCB, 0x00},
+{0xC8, 0x00}, {0x31, 0x01}, {0xCD, 0x00}, {0xCE, 0x00},
+{0xCF, 0x00}, {0x18, 0x25}, {0x0C, 0x25}, {0x88, 0x25},
+{0x84, 0x25}, {0xA6, 0x00}, {0xCC, 0x00}, {0x80, 0x25},
+/* 0xE0 */
+{0xD3, 0x00}, {0xDF, 0x00}, {0xD4, 0x00}, {0xD2, 0x00},
+{0xF5, 0x00}, {0xD5, 0x00}, {0xB5, 0x00}, {0xFE, 0x00},
+{0xDE, 0x00}, {0xDA, 0x00}, {0xDB, 0x00}, {0xD9, 0x00},
+{0xFD, 0x00}, {0xDD, 0x00}, {0xAF, 0x00}, {0xB4, 0x00},
+/* 0xF0 */
+{0xAD, 0x00}, {0xB1, 0x00}, {0x17, 0x20}, {0xBE, 0x00},
+{0xB6, 0x00}, {0xA7, 0x00}, {0xF7, 0x00}, {0xB8, 0x00},
+{0xB0, 0x00}, {0xA8, 0x00}, {0xB7, 0x00}, {0xB9, 0x00},
+{0xB3, 0x00}, {0xB2, 0x00}, {0xA0, 0x25}, {0xA0, 0x00}};
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fat/tables.h b/fs/fat/tables.h
new file mode 100644
index 000000000..5b431d281
--- /dev/null
+++ b/fs/fat/tables.h
@@ -0,0 +1,35 @@
+struct unicode_value {
+ unsigned char uni1;
+ unsigned char uni2;
+};
+
+extern unsigned char fat_a2alias[]; /* Ascii to alias name conversion table */
+extern struct unicode_value fat_a2uni[]; /* Ascii to Unicode conversion table */
+extern unsigned char *fat_uni2asc_pg[];
+
+/*
+ * Since Linux can't deal with Unicode in filenames, these provide
+ * a method to encode the Unicode names in a manner that the vfat
+ * filesystem can them decode back to Unicode. This conversion
+ * only occurs when the filesystem was mounted with the 'uni_xlate' mount
+ * option.
+ */
+extern unsigned char fat_uni2code[];
+extern unsigned char fat_code2uni[];
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */