summaryrefslogtreecommitdiffstats
path: root/fs/fat
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-06 23:51:34 +0000
commit230e5ab6a084ed50470f101934782dbf54b0d06b (patch)
tree5dd821c8d33f450470588e7a543f74bf74306e9e /fs/fat
parentc9b1c8a64c6444d189856f1e26bdcb8b4cd0113a (diff)
Merge with Linux 2.1.67.
Diffstat (limited to 'fs/fat')
-rw-r--r--fs/fat/buffer.c18
-rw-r--r--fs/fat/cache.c49
-rw-r--r--fs/fat/dir.c69
-rw-r--r--fs/fat/fatfs_syms.c9
-rw-r--r--fs/fat/file.c59
-rw-r--r--fs/fat/inode.c490
-rw-r--r--fs/fat/misc.c81
-rw-r--r--fs/fat/mmap.c7
-rw-r--r--fs/fat/tables.c212
9 files changed, 562 insertions, 432 deletions
diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c
index 2a6fc6b74..4c827711d 100644
--- a/fs/fat/buffer.c
+++ b/fs/fat/buffer.c
@@ -10,22 +10,31 @@
#include <linux/fs.h>
#include <linux/msdos_fs.h>
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+
struct buffer_head *fat_bread (
struct super_block *sb,
int block)
{
struct buffer_head *ret = NULL;
- /* Note that the blocksize is 512, 1024 or 2048, but the first read
- is always of size 1024 (or 2048). Doing readahead may be counterproductive
- or just plain wrong. */
+ PRINTK(("fat_bread: block=0x%x\n", block));
+ /*
+ * Note that the blocksize is 512, 1024 or 2048, but the first read
+ * is always of size 1024 (or 2048). 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;
if (sb->s_blocksize == 1024){
real = bread (sb->s_dev,block>>1,1024);
- }else{
+ } else {
real = bread (sb->s_dev,block>>2,2048);
}
@@ -82,6 +91,7 @@ struct buffer_head *fat_getblk (
int block)
{
struct buffer_head *ret = NULL;
+ PRINTK(("fat_getblk: block=0x%x\n", block));
if (sb->s_blocksize == 512){
ret = getblk (sb->s_dev,block,512);
}else{
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
index 5fdcb772e..ece255ca1 100644
--- a/fs/fat/cache.c
+++ b/fs/fat/cache.c
@@ -12,6 +12,11 @@
#include "msbuffer.h"
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
static struct fat_cache *fat_cache,cache[FAT_CACHE];
@@ -22,11 +27,13 @@ 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;
+ int copy,first,last,next,b;
if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
return 0;
- if (MSDOS_SB(sb)->fat_bits == 16) {
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ first = last = nr*4;
+ } else if (MSDOS_SB(sb)->fat_bits == 16) {
first = last = nr*2;
} else {
first = nr*3/2;
@@ -46,13 +53,19 @@ int fat_access(struct super_block *sb,int nr,int new_value)
return 0;
}
}
- if (MSDOS_SB(sb)->fat_bits == 16) {
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = CF_LE_L(((unsigned long *) bh->b_data)[(first &
+ (SECTOR_SIZE-1)) >> 2]);
+ if (next >= 0xffffff7) next = -1;
+ PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%d\n", b, nr, first, next));
+
+ } else 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 {
+ } else {
p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
p_last = &((unsigned char *) bh2->b_data)[(first+1) &
(SECTOR_SIZE-1)];
@@ -61,10 +74,13 @@ int fat_access(struct super_block *sb,int nr,int new_value)
if (next >= 0xff7) next = -1;
}
if (new_value != -1) {
- if (MSDOS_SB(sb)->fat_bits == 16)
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ ((unsigned long *) bh->b_data)[(first & (SECTOR_SIZE-1)) >>
+ 2] = CT_LE_L(new_value);
+ } else if (MSDOS_SB(sb)->fat_bits == 16) {
((unsigned short *) bh->b_data)[(first & (SECTOR_SIZE-1)) >>
1] = CT_LE_W(new_value);
- else {
+ } else {
if (nr & 1) {
*p_first = (*p_first & 0xf) | (new_value << 4);
*p_last = new_value >> 4;
@@ -172,7 +188,8 @@ printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
&& walk->ino == inode->i_ino
&& walk->file_cluster == f_clu) {
if (walk->disk_cluster != d_clu) {
- printk("FAT cache corruption");
+ printk("FAT cache corruption inode=%ld\n",
+ inode->i_ino);
fat_cache_inval_inode(inode);
return;
}
@@ -239,16 +256,17 @@ int get_cluster(struct inode *inode,int cluster)
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;
+ if ((sb->fat_bits != 32) &&
+ (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;
@@ -279,6 +297,7 @@ int fat_free(struct inode *inode,int skip)
fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
else {
MSDOS_I(inode)->i_start = 0;
+ MSDOS_I(inode)->i_logstart = 0;
mark_inode_dirty(inode);
}
lock_fat(inode->i_sb);
@@ -287,8 +306,12 @@ int fat_free(struct inode *inode,int skip)
fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
break;
}
- if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
+ if (MSDOS_SB(inode->i_sb)->free_clusters != -1) {
MSDOS_SB(inode->i_sb)->free_clusters++;
+ if (MSDOS_SB(inode->i_sb)->fat_bits == 32) {
+ fat_clusters_flush(inode->i_sb);
+ }
+ }
inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
}
unlock_fat(inode->i_sb);
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 494f7781b..7568f876a 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -11,8 +11,12 @@
* Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
*/
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+
+#include <linux/version.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
+#include <linux/nls.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/stat.h>
@@ -24,13 +28,11 @@
#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)
+static ssize_t fat_dir_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos)
{
return -EISDIR;
}
@@ -40,7 +42,7 @@ struct file_operations fat_dir_operations = {
fat_dir_read, /* read */
NULL, /* write - bad */
fat_readdir, /* readdir */
- NULL, /* poll - default */
+ NULL, /* select v2.0.x/poll v2.1.x - default */
fat_dir_ioctl, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
@@ -48,7 +50,9 @@ struct file_operations fat_dir_operations = {
file_fsync /* fsync */
};
-/* Convert Unicode string to ASCII. If uni_xlate is enabled and we
+/*
+ * Convert Unicode 16 to UTF8, translated unicode, or 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
@@ -56,10 +60,11 @@ struct file_operations fat_dir_operations = {
* into some trouble with long Unicode names, but ignore that right now.
*/
static int
-uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
+uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate,
+ struct nls_table *nls)
{
unsigned char *ip, *op;
- unsigned char page, pg_off;
+ unsigned char ch, cl;
unsigned char *uni_page;
unsigned short val;
@@ -67,21 +72,21 @@ uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
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];
+ cl = *ip++;
+ ch = *ip++;
+
+ uni_page = nls->page_uni2charset[ch];
+ if (uni_page && uni_page[cl]) {
+ *op++ = uni_page[cl];
} else {
if (uni_xlate == 1) {
*op++ = ':';
- val = (pg_off << 8) + page;
- op[2] = fat_uni2code[val & 0x3f];
+ val = (cl << 8) + ch;
+ op[2] = fat_uni2esc[val & 0x3f];
val >>= 6;
- op[1] = fat_uni2code[val & 0x3f];
+ op[1] = fat_uni2esc[val & 0x3f];
val >>= 6;
- *op = fat_uni2code[val & 0x3f];
+ *op = fat_uni2esc[val & 0x3f];
op += 3;
} else {
*op++ = '?';
@@ -92,6 +97,19 @@ uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
return (op - ascii);
}
+#if 0
+static void dump_de(struct msdos_dir_entry *de)
+{
+ int i;
+ unsigned char *p = (unsigned char *) de;
+ printk("[");
+
+ for (i = 0; i < 32; i++, p++) {
+ printk("%02x ", *p);
+ }
+ printk("]\n");
+}
+#endif
int fat_readdirx(
struct inode *inode,
struct file *filp,
@@ -115,7 +133,9 @@ int fat_readdirx(
unsigned char alias_checksum = 0; /* Make compiler warning go away */
unsigned char long_slots = 0;
int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ int utf8 = MSDOS_SB(sb)->options.utf8;
unsigned char *unicode = NULL;
+ struct nls_table *nls = MSDOS_SB(sb)->nls_io;
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
@@ -138,6 +158,9 @@ int fat_readdirx(
is_long = 0;
ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
while (ino > -1) {
+#if 0
+ dump_de(de);
+#endif
/* Check for long filename entry */
if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
is_long = 0;
@@ -217,7 +240,6 @@ int fat_readdirx(
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];
}
@@ -226,6 +248,11 @@ int fat_readdirx(
PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
is_long = 0;
}
+ if (utf8) {
+ long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
+ } else {
+ long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
+ }
}
if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
@@ -390,7 +417,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
switch (cmd) {
case VFAT_IOCTL_READDIR_BOTH: {
struct dirent *d1 = (struct dirent *)arg;
- err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+ err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
if (err)
return err;
put_user(0, &d1->d_reclen);
@@ -400,7 +427,7 @@ int fat_dir_ioctl(struct inode * inode, struct file * filp,
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));
+ err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
if (err)
return err;
return fat_readdirx(inode,filp,(void *)arg,
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
index c7ec96030..60e71ed98 100644
--- a/fs/fat/fatfs_syms.c
+++ b/fs/fat/fatfs_syms.c
@@ -4,6 +4,8 @@
* Exported kernel symbols for the low-level FAT-based fs support.
*
*/
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+#include <linux/version.h>
#include <linux/config.h>
#include <linux/module.h>
@@ -15,16 +17,14 @@
extern struct file_operations fat_dir_operations;
-EXPORT_SYMBOL(fat_a2alias);
-EXPORT_SYMBOL(fat_a2uni);
EXPORT_SYMBOL(fat_add_cluster);
EXPORT_SYMBOL(fat_bmap);
EXPORT_SYMBOL(fat_brelse);
EXPORT_SYMBOL(fat_cache_inval_inode);
-EXPORT_SYMBOL(fat_code2uni);
EXPORT_SYMBOL(fat_date_unix2dos);
EXPORT_SYMBOL(fat_delete_inode);
EXPORT_SYMBOL(fat_dir_operations);
+EXPORT_SYMBOL(fat_esc2uni);
EXPORT_SYMBOL(fat_file_read);
EXPORT_SYMBOL(fat_file_write);
EXPORT_SYMBOL(fat_fs_panic);
@@ -44,8 +44,7 @@ EXPORT_SYMBOL(fat_scan);
EXPORT_SYMBOL(fat_smap);
EXPORT_SYMBOL(fat_statfs);
EXPORT_SYMBOL(fat_truncate);
-EXPORT_SYMBOL(fat_uni2asc_pg);
-EXPORT_SYMBOL(fat_uni2code);
+EXPORT_SYMBOL(fat_uni2esc);
EXPORT_SYMBOL(fat_unlock_creation);
EXPORT_SYMBOL(fat_write_inode);
diff --git a/fs/fat/file.c b/fs/fat/file.c
index ca35cc28f..f3a4f6cec 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -6,6 +6,8 @@
* regular file handling primitives for fat-based filesystems
*/
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+#include <linux/version.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/fs.h>
@@ -32,7 +34,7 @@ static struct file_operations fat_file_operations = {
fat_file_read, /* read */
fat_file_write, /* write */
NULL, /* readdir - bad */
- NULL, /* poll - default */
+ NULL, /* select v2.0.x/poll v2.1.x - default */
NULL, /* ioctl - default */
generic_file_mmap, /* mmap */
NULL, /* no special open is needed */
@@ -60,6 +62,7 @@ struct inode_operations fat_file_inode_operations = {
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
@@ -71,7 +74,7 @@ static struct file_operations fat_file_operations_1024 = {
fat_file_read, /* read */
fat_file_write, /* write */
NULL, /* readdir - bad */
- NULL, /* poll - default */
+ NULL, /* select v2.0.x/poll v2.1.x - default */
NULL, /* ioctl - default */
fat_mmap, /* mmap */
NULL, /* no special open is needed */
@@ -152,12 +155,13 @@ static void fat_prefetch (
/*
Read a file into user space
*/
-long fat_file_read(
- struct inode *inode,
+ssize_t fat_file_read(
struct file *filp,
char *buf,
- unsigned long count)
+ size_t count,
+ loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
struct super_block *sb = inode->i_sb;
char *start = buf;
char *end = buf + count;
@@ -175,7 +179,7 @@ long fat_file_read(
printk("fat_file_read: mode = %07o\n",inode->i_mode);
return -EINVAL;
}
- if (filp->f_pos >= inode->i_size || count == 0) return 0;
+ if (*ppos >= 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
@@ -185,15 +189,15 @@ long fat_file_read(
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));
+ PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,*ppos,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 count_max = (*ppos & (SECTOR_SIZE-1)) + count;
int to_reada; /* How many block to read all at once */
- pre.file_sector = filp->f_pos >> SECTOR_BITS;
+ pre.file_sector = *ppos >> 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){
@@ -211,7 +215,7 @@ long fat_file_read(
}
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
+ while ((left_in_file = inode->i_size - *ppos) > 0
&& buf < end){
struct buffer_head *bh = pre.bhlist[pre.nolist];
char *data;
@@ -226,27 +230,27 @@ long fat_file_read(
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));
+ PRINTK (("file_read pos %ld nblist %d %d %d\n",*ppos,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);
+ offset = *ppos & (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;
+ *ppos += size;
}else{
for (; size && buf < end; size--) {
char ch = *data++;
- filp->f_pos++;
+ ++*ppos;
if (ch == 26){
- filp->f_pos = inode->i_size;
+ *ppos = inode->i_size;
break;
}else if (ch != '\r'){
put_user(ch,buf++);
@@ -269,12 +273,13 @@ long fat_file_read(
/*
Write to a file either from user space
*/
-long fat_file_write(
- struct inode *inode,
+ssize_t fat_file_write(
struct file *filp,
const char *buf,
- unsigned long count)
+ size_t count,
+ loff_t *ppos)
{
+ struct inode *inode = filp->f_dentry->d_inode;
struct super_block *sb = inode->i_sb;
int sector,offset,size,left,written;
int error,carry;
@@ -300,23 +305,23 @@ long fat_file_write(
* but so what. That way leads to madness anyway.
*/
if (filp->f_flags & O_APPEND)
- filp->f_pos = inode->i_size;
+ *ppos = 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)))
+ while (!(sector = fat_smap(inode,*ppos >> SECTOR_BITS)))
if ((error = fat_add_cluster(inode)) < 0) break;
if (error) {
fat_truncate(inode);
break;
}
- offset = filp->f_pos & (SECTOR_SIZE-1);
+ offset = *ppos & (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)){
+ || *ppos + 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 */
@@ -333,7 +338,7 @@ long fat_file_write(
buf += size;
} else {
written = left = SECTOR_SIZE-offset;
- to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1));
+ to = (char *) bh->b_data+(*ppos & (SECTOR_SIZE-1));
if (carry) {
*to++ = '\n';
left--;
@@ -353,10 +358,10 @@ long fat_file_write(
}
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;
+ update_vm_cache(inode, *ppos, bh->b_data + (*ppos & (SECTOR_SIZE-1)), written);
+ *ppos += written;
+ if (*ppos > inode->i_size) {
+ inode->i_size = *ppos;
mark_inode_dirty(inode);
}
fat_set_uptodate(sb, bh, 1);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 8ffd91e3d..732d0b9ba 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -5,10 +5,12 @@
* VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
*/
+#include <linux/version.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/msdos_fs.h>
+#include <linux/nls.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
@@ -18,76 +20,137 @@
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/locks.h>
+#include <linux/malloc.h>
#include "msbuffer.h"
-#include "tables.h"
#include <asm/uaccess.h>
#include <asm/unaligned.h>
+/* #define FAT_PARANOIA 1 */
+#ifdef FAT_DEBUG
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
-
-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);
- }
-}
-
-void fat_delete_inode(struct inode *inode)
+/*
+ * Free any dependent inodes at the (effective) last use.
+ */
+static int fat_free_links(struct inode *inode)
{
- struct inode *depend, *linked;
- struct super_block *sb;
+ struct inode *depend, *linked, *old_inode;
+ int success = 0;
+ /*
+ * Clear the fields first to avoid races
+ */
depend = MSDOS_I(inode)->i_depend;
+ MSDOS_I(inode)->i_depend = NULL;
linked = MSDOS_I(inode)->i_linked;
- sb = inode->i_sb;
+ MSDOS_I(inode)->i_linked = NULL;
- 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;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: depend inode is %ld, i_count=%d\n",
+depend->i_ino, depend->i_count);
+#endif
+ old_inode = MSDOS_I(depend)->i_old;
+ if (old_inode != inode) {
+ printk("fat_free_link: Invalid depend for inode %ld: "
+ "expected 0x%p, got 0x%p\n",
+ depend->i_ino, inode, old_inode);
+ goto out;
}
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;
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: linked inode is %ld, i_count=%d\n",
+linked->i_ino, linked->i_count);
+#endif
+ old_inode = MSDOS_I(linked)->i_oldlink;
+ if (old_inode != inode) {
+ printk("fat_free_link: Invalid link for inode %ld: "
+ "expected 0x%p, got 0x%p\n",
+ linked->i_ino, inode, old_inode);
+ goto out;
}
MSDOS_I(linked)->i_oldlink = NULL;
iput(linked);
}
-done:
+ success = 1;
+out:
+ return success;
+}
+
+/*
+ * This is a little tricky, as we may have links and may be linked
+ * by other inodes. Also, we're subject to race conditions ...
+ */
+void fat_put_inode(struct inode *inode)
+{
+ int last_use = 1;
+
+ /*
+ * Check whether we're a dependent of other inodes ...
+ */
+ if (MSDOS_I(inode)->i_oldlink)
+ last_use++;
+ if (MSDOS_I(inode)->i_old)
+ last_use++;
+
+ if (inode->i_count <= last_use) {
+#ifdef FAT_PARANOIA
+printk("fat_put_inode: last use for %ld, i_count=%d\n",
+inode->i_ino, inode->i_count);
+#endif
+ if (inode->i_nlink) {
+ if (MSDOS_I(inode)->i_busy)
+ fat_cache_inval_inode(inode);
+ fat_free_links(inode);
+ }
+ }
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+ fat_cache_inval_inode(inode);
+ inode->i_size = 0;
+ fat_truncate(inode);
+ if (!fat_free_links(inode))
+ fat_fs_panic(inode->i_sb,"..."); /* is this necessary? */
clear_inode(inode);
}
void fat_put_super(struct super_block *sb)
{
+ lock_super(sb);
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ fat_clusters_flush(sb);
+ }
fat_cache_inval_dev(sb->s_dev);
set_blocksize (sb->s_dev,BLOCK_SIZE);
- lock_super(sb);
+ if (MSDOS_SB(sb)->nls_disk) {
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ MSDOS_SB(sb)->nls_disk = NULL;
+ MSDOS_SB(sb)->options.codepage = 0;
+ }
+ if (MSDOS_SB(sb)->nls_io) {
+ unload_nls(MSDOS_SB(sb)->nls_io);
+ MSDOS_SB(sb)->nls_io = NULL;
+ }
+ /*
+ * Note: the iocharset option might have been specified
+ * without enabling nls_io, so check for it here.
+ */
+ if (MSDOS_SB(sb)->options.iocharset) {
+ kfree(MSDOS_SB(sb)->options.iocharset);
+ MSDOS_SB(sb)->options.iocharset = NULL;
+ }
sb->s_dev = 0;
unlock_super(sb);
MOD_DEC_USE_COUNT;
@@ -98,35 +161,53 @@ void fat_put_super(struct super_block *sb)
static int parse_options(char *options,int *fat, int *blksize, int *debug,
struct fat_mount_options *opts)
{
- char *this_char,*value;
+ char *this_char,*value,save,*savep;
+ char *p;
+ int ret = 1, len;
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;
+ opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
+ opts->codepage = 0;
+ opts->utf8 = 0;
+ opts->iocharset = NULL;
*debug = *fat = 0;
- if (!options) return 1;
- for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
- if ((value = strchr(this_char,'=')) != NULL)
+ if (!options)
+ goto out;
+ save = 0;
+ savep = NULL;
+ for (this_char = strtok(options,","); this_char;
+ this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ save = *value;
+ savep = value;
*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(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 ret = 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(value,"binary"))
+ opts->conversion = 'b';
+ else if (!strcmp(value,"text"))
+ opts->conversion = 't';
+ else if (!strcmp(value,"auto"))
+ opts->conversion = 'a';
+ else ret = 0;
}
else if (!strcmp(this_char,"dots")) {
opts->dotsOK = 1;
@@ -140,73 +221,110 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
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 ret = 0;
}
else if (!strcmp(this_char,"uid")) {
- if (!value || !*value)
- return 0;
- opts->fs_uid = simple_strtoul(value,&value,0);
- if (*value)
- return 0;
+ if (!value || !*value) ret = 0;
+ else {
+ opts->fs_uid = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ }
}
else if (!strcmp(this_char,"gid")) {
- if (!value || !*value)
- return 0;
- opts->fs_gid = simple_strtoul(value,&value,0);
- if (*value)
- return 0;
+ if (!value || !*value) ret= 0;
+ else {
+ opts->fs_gid = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ }
}
else if (!strcmp(this_char,"umask")) {
- if (!value || !*value)
- return 0;
- opts->fs_umask = simple_strtoul(value,&value,8);
- if (*value)
- return 0;
+ if (!value || !*value) ret = 0;
+ else {
+ opts->fs_umask = simple_strtoul(value,&value,8);
+ if (*value) ret = 0;
+ }
}
else if (!strcmp(this_char,"debug")) {
- if (value) return 0;
- *debug = 1;
+ if (value) ret = 0;
+ else *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;
+ if (!value || !*value) ret = 0;
+ else {
+ *fat = simple_strtoul(value,&value,0);
+ if (*value || (*fat != 12 && *fat != 16 &&
+ *fat != 32))
+ ret = 0;
+ }
}
else if (!strcmp(this_char,"quiet")) {
- if (value) return 0;
- opts->quiet = 1;
+ if (value) ret = 0;
+ else opts->quiet = 1;
}
else if (!strcmp(this_char,"blocksize")) {
- *blksize = simple_strtoul(value,&value,0);
- if (*value)
- return 0;
- if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){
- printk ("MSDOS FS: Invalid blocksize (512, 1024 or 2048)\n");
+ if (*value) ret = 0;
+ else if (*blksize != 512 &&
+ *blksize != 1024 &&
+ *blksize != 2048) {
+ printk ("MSDOS FS: Invalid blocksize "
+ "(512, 1024, or 2048)\n");
}
}
else if (!strcmp(this_char,"sys_immutable")) {
- if (value)
- return 0;
- opts->sys_immutable = 1;
+ if (value) ret = 0;
+ else opts->sys_immutable = 1;
+ }
+ else if (!strcmp(this_char,"codepage")) {
+ opts->codepage = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ else printk ("MSDOS FS: Using codepage %d\n",
+ opts->codepage);
}
+ else if (!strcmp(this_char,"iocharset")) {
+ p = value;
+ while (*value && *value != ',') value++;
+ len = value - p;
+ if (len) {
+ char * buffer = kmalloc(len+1, GFP_KERNEL);
+ if (buffer) {
+ opts->iocharset = buffer;
+ memcpy(buffer, p, len);
+ buffer[len] = 0;
+ printk("MSDOS FS: IO charset %s\n",
+ buffer);
+ } else
+ ret = 0;
+ }
+ }
+
+ if (this_char != options) *(this_char-1) = ',';
+ if (value) *savep = save;
+ if (ret == 0)
+ break;
}
- return 1;
+out:
+ return ret;
}
-
-/* Read the super block of an MS-DOS FS. */
-
-struct super_block *fat_read_super(struct super_block *sb,void *data, int silent)
+/*
+ * Read the super block of an MS-DOS FS.
+ *
+ * Note that this may be called from vfat_read_super
+ * with some fields already initialized.
+ */
+struct super_block *
+fat_read_super(struct super_block *sb, void *data, int silent)
{
+ struct inode *root_inode;
struct buffer_head *bh;
- struct msdos_boot_sector *b;
+ struct fat_boot_sector *b;
+ char *p;
int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
- int debug,error,fat;
+ int debug,error,fat,cp;
int blksize = 512;
+ int fat32;
struct fat_mount_options opts;
- struct inode *root_inode;
+ char buf[50];
MOD_INC_USE_COUNT;
if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
@@ -215,13 +333,14 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
printk ("MSDOS: Hardware sector size is %d\n",blksize);
}
}
+
+ opts.isvfat = MSDOS_SB(sb)->options.isvfat;
if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
- || (blksize != 512 && blksize != 1024 && blksize != 2048))
- {
- sb->s_dev = 0;
- MOD_DEC_USE_COUNT;
- return NULL;
- }
+ || (blksize != 512 && blksize != 1024 && blksize != 2048))
+ goto out_fail;
+ /* N.B. we should parse directly into the sb structure */
+ memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
+
cache_init();
lock_super(sb);
if( blksize > 1024 )
@@ -240,13 +359,10 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
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;
+ goto out_no_bread;
}
- 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
@@ -265,15 +381,39 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
/* don't divide by zero */
+ b = (struct fat_boot_sector *) bh->b_data;
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;
+ if (!b->fat_length && b->fat32_length) {
+ struct fat_boot_fsinfo *fsinfo;
+
+ /* Must be FAT32 */
+ fat32 = 1;
+ MSDOS_SB(sb)->fat_length= CF_LE_W(b->fat32_length)*sector_mult;
+ MSDOS_SB(sb)->root_cluster = CF_LE_L(b->root_cluster);
+ MSDOS_SB(sb)->fsinfo_offset =
+ CF_LE_W(b->info_sector) * logical_sector_size + 0x1e0;
+ fsinfo = (struct fat_boot_fsinfo *)
+ &bh->b_data[MSDOS_SB(sb)->fsinfo_offset];
+ if (CF_LE_L(fsinfo->signature) != 0x61417272) {
+ printk("fat_read_super: Did not find valid FSINFO "
+ "signature. Found 0x%x\n",
+ CF_LE_L(fsinfo->signature));
+ } else {
+ MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters);
+ }
+ } else {
+ fat32 = 0;
+ MSDOS_SB(sb)->fat_length = CF_LE_W(b->fat_length)*sector_mult;
+ MSDOS_SB(sb)->root_cluster = 0;
+ MSDOS_SB(sb)->free_clusters = -1; /* Don't know yet */
+ }
+ MSDOS_SB(sb)->dir_start= CF_LE_W(b->reserved)*sector_mult+
+ b->fats*MSDOS_SB(sb)->fat_length;
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((
@@ -288,8 +428,9 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
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;
+ MSDOS_SB(sb)->fat_bits = fat32 ? 32 :
+ (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 &
@@ -315,43 +456,83 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
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=%d,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
- MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\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,
- CF_LE_W(get_unaligned((unsigned short *) &b->sectors)),
- CF_LE_L(b->total_sect),logical_sector_size);
+ CF_LE_W(*(unsigned short *) &b->sectors),
+ (unsigned long)b->total_sect,logical_sector_size,
+ MSDOS_SB(sb)->root_cluster,MSDOS_SB(sb)->free_clusters);
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;
- }
+ if (error)
+ goto out_invalid;
+
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));
- root_inode = iget(sb,MSDOS_ROOT_INO);
- sb->s_root = d_alloc_root(root_inode, NULL);
- if (!sb->s_root) {
- sb->s_dev = 0;
- printk("get root inode failed\n");
- MOD_DEC_USE_COUNT;
- return NULL;
+ cp = opts.codepage ? opts.codepage : 437;
+ sprintf(buf, "cp%d", cp);
+ MSDOS_SB(sb)->nls_disk = load_nls(buf);
+ if (! MSDOS_SB(sb)->nls_disk) {
+ /* Fail only if explicit charset specified */
+ if (opts.codepage != 0)
+ goto out_fail;
+ MSDOS_SB(sb)->options.codepage = 0; /* already 0?? */
+ MSDOS_SB(sb)->nls_disk = load_nls_default();
+ }
+
+ MSDOS_SB(sb)->nls_io = NULL;
+ if (MSDOS_SB(sb)->options.isvfat && !opts.utf8) {
+ p = opts.iocharset ? opts.iocharset : "iso8859-1";
+ MSDOS_SB(sb)->nls_io = load_nls(p);
+ if (! MSDOS_SB(sb)->nls_io) {
+ /* Fail only if explicit charset specified */
+ if (opts.iocharset)
+ goto out_unload_nls;
+ MSDOS_SB(sb)->nls_io = load_nls_default();
+ }
}
+
+ root_inode = iget(sb, MSDOS_ROOT_INO);
+ if (!root_inode)
+ goto out_no_root;
+ sb->s_root = d_alloc_root(root_inode, NULL);
+ if (!sb->s_root)
+ goto out_no_root;
return sb;
-}
+out_no_root:
+ printk("get root inode failed\n");
+ iput(root_inode);
+ if (MSDOS_SB(sb)->nls_io)
+ unload_nls(MSDOS_SB(sb)->nls_io);
+out_unload_nls:
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ goto out_fail;
+out_invalid:
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev %s.\n",
+ kdevname(sb->s_dev));
+ goto out_fail;
+out_no_bread:
+ printk("FAT bread failed\n");
+out_fail:
+ if (opts.iocharset) {
+ printk("VFS: freeing iocharset=%s\n", opts.iocharset);
+ kfree(opts.iocharset);
+ }
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz)
{
@@ -386,7 +567,7 @@ int fat_bmap(struct inode *inode,int block)
int cluster,offset;
sb = MSDOS_SB(inode->i_sb);
- if (inode->i_ino == MSDOS_ROOT_INO) {
+ if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) {
return sb->dir_start + block;
}
cluster = block/sb->cluster_size;
@@ -412,6 +593,8 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
struct msdos_dir_entry *raw_entry;
int nr;
+ PRINTK(("fat_read_inode: inode=%p, sb->dir_start=0x%x\n",
+ inode, MSDOS_SB(sb)->dir_start));
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;
@@ -423,17 +606,33 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
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);
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster;
+ if ((nr = MSDOS_I(inode)->i_start) != 0) {
+ while (nr != -1) {
+ inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size;
+ if (!(nr = fat_access(sb,nr,-1))) {
+ printk("Directory %ld: bad FAT\n",
+ inode->i_ino);
+ break;
+ }
+ }
+ }
+ } else {
+ MSDOS_I(inode)->i_start = 0;
+ 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_logstart = 0;
+
MSDOS_I(inode)->i_attrs = 0;
inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ inode->i_nlink = fat_subdirs(inode)+2;
+ /* subdirs (neither . nor ..) plus . and "self" */
return;
}
if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
@@ -449,6 +648,11 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
inode->i_op = fs_dir_inode_ops;
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ MSDOS_I(inode)->i_start |=
+ (CF_LE_W(raw_entry->starthi) << 16);
+ }
+ MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_nlink = fat_subdirs(inode);
/* includes .., compensating for "self" */
#ifdef DEBUG
@@ -458,7 +662,7 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
}
#endif
inode->i_size = 0;
- if ((nr = CF_LE_W(raw_entry->start)) != 0)
+ if ((nr = MSDOS_I(inode)->i_start) != 0)
while (nr != -1) {
inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->
i_sb)->cluster_size;
@@ -479,6 +683,11 @@ void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_o
? &fat_file_inode_operations_1024
: &fat_file_inode_operations;
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ MSDOS_I(inode)->i_start |=
+ (CF_LE_W(raw_entry->starthi) << 16);
+ }
+ MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
inode->i_nlink = 1;
inode->i_size = CF_LE_L(raw_entry->size);
}
@@ -549,7 +758,8 @@ void fat_write_inode(struct inode *inode)
}
raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
MSDOS_I(inode)->i_attrs;
- raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_start);
+ raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
+ raw_entry->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
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);
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index 5b71573b3..0fb7791ec 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -14,7 +14,11 @@
#include "msbuffer.h"
-#define PRINTK(x)
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
#define Printk(x) printk x
/* Well-known binary file extensions - of course there are many more */
@@ -106,6 +110,34 @@ void unlock_fat(struct super_block *sb)
wake_up(&MSDOS_SB(sb)->fat_wait);
}
+/* Flushes the number of free clusters on FAT32 */
+/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
+void fat_clusters_flush(struct super_block *sb)
+{
+ int offset;
+ struct buffer_head *bh;
+ struct fat_boot_fsinfo *fsinfo;
+
+ /* The fat32 boot fs info is at offset 0x3e0 by observation */
+ offset = MSDOS_SB(sb)->fsinfo_offset;
+ bh = fat_bread(sb, (offset >> SECTOR_BITS));
+ if (bh == NULL) {
+ printk("FAT bread failed in fat_clusters_flush\n");
+ return;
+ }
+ fsinfo = (struct fat_boot_fsinfo *)
+ &bh->b_data[offset & (SECTOR_SIZE-1)];
+
+ /* Sanity check */
+ if (CF_LE_L(fsinfo->signature) != 0x61417272) {
+ printk("fat_clusters_flush: Did not find valid FSINFO signature. Found 0x%x. offset=0x%x\n", CF_LE_L(fsinfo->signature), offset);
+ fat_brelse(sb, bh);
+ return;
+ }
+ fsinfo->free_clusters = CF_LE_L(MSDOS_SB(sb)->free_clusters);
+ fat_mark_buffer_dirty(sb, bh, 1);
+ fat_brelse(sb, bh);
+}
/*
* fat_add_cluster tries to allocate a new cluster and adds it to the file
@@ -119,7 +151,9 @@ int fat_add_cluster(struct inode *inode)
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)->fat_bits != 32) {
+ 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;
@@ -141,6 +175,8 @@ printk("free cluster: %d\n",nr);
fat_access(sb,nr,EOF_FAT(sb));
if (MSDOS_SB(sb)->free_clusters != -1)
MSDOS_SB(sb)->free_clusters--;
+ if (MSDOS_SB(sb)->fat_bits == 32)
+ fat_clusters_flush(sb);
unlock_fat(sb);
#ifdef DEBUG
printk("set to %x\n",fat_access(sb,nr,-1));
@@ -177,6 +213,7 @@ printk("last = %d\n",last);
if (last) fat_access(sb,last,nr);
else {
MSDOS_I(inode)->i_start = nr;
+ MSDOS_I(inode)->i_logstart = nr;
mark_inode_dirty(inode);
}
#ifdef DEBUG
@@ -260,6 +297,8 @@ void fat_date_unix2dos(int unix_date,unsigned short *time,
unix_date += 3600;
}
unix_date -= sys_tz.tz_minuteswest*60;
+ if (sys_tz.tz_dsttime) unix_date += 3600;
+
*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
(((unix_date/3600) % 24) << 11);
day = unix_date/86400-3652;
@@ -302,7 +341,7 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **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);
+ printk("Directory sread (sector 0x%x) failed\n",sector);
continue;
}
PRINTK (("get_entry apres sread\n"));
@@ -344,7 +383,13 @@ int fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
!(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;
+ done = !IS_FREE(data[entry].name) \
+ && ( \
+ ( \
+ (MSDOS_SB(sb)->fat_bits != 32) ? 0 : (CF_LE_W(data[entry].starthi) << 16) \
+ ) \
+ | CF_LE_W(data[entry].start) \
+ ) == *number;
#define RSS_FREE /* search for free entry */ \
{ \
@@ -397,6 +442,9 @@ static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
if (done) {
if (ino) *ino = sector*MSDOS_DPS+entry;
start = CF_LE_W(data[entry].start);
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ start |= (CF_LE_W(data[entry].starthi) << 16);
+ }
if (!res_bh)
fat_brelse(sb, bh);
else {
@@ -492,6 +540,7 @@ int fat_parent_ino(struct inode *dir,int locked)
static int zero = 0;
int error,curr,prev,nr;
+ PRINTK(("fat_parent_ino: Debug 0\n"));
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 */
@@ -500,18 +549,27 @@ int fat_parent_ino(struct inode *dir,int locked)
if (!locked) fat_unlock_creation();
return curr;
}
+ PRINTK(("fat_parent_ino: Debug 1 curr=%d\n", curr));
if (!curr) nr = MSDOS_ROOT_INO;
else {
+ PRINTK(("fat_parent_ino: Debug 2\n"));
if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
NULL,NULL,SCAN_ANY)) < 0) {
+ PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev));
if (!locked) fat_unlock_creation();
return prev;
}
+ PRINTK(("fat_parent_ino: Debug 4 prev=%d\n", prev));
+ if (prev == 0 && MSDOS_SB(dir->i_sb)->fat_bits == 32) {
+ prev = MSDOS_SB(dir->i_sb)->root_cluster;
+ }
if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
NULL,SCAN_ANY)) < 0) {
+ PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error));
if (!locked) fat_unlock_creation();
return error;
}
+ PRINTK(("fat_parent_ino: Debug 6 nr=%d\n", nr));
}
if (!locked) fat_unlock_creation();
return nr;
@@ -528,10 +586,12 @@ int fat_subdirs(struct inode *dir)
int count;
count = 0;
- if (dir->i_ino == MSDOS_ROOT_INO)
+ if ((dir->i_ino == MSDOS_ROOT_INO) &&
+ (MSDOS_SB(dir->i_sb)->fat_bits != 32)) {
(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 {
+ if ((dir->i_ino != MSDOS_ROOT_INO) &&
+ !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);
}
@@ -549,10 +609,7 @@ int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
{
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);
+ res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
+ name, NULL, ino, res_bh, res_de, scantype);
return res<0 ? res : 0;
}
diff --git a/fs/fat/mmap.c b/fs/fat/mmap.c
index fd66e51d1..545874a04 100644
--- a/fs/fat/mmap.c
+++ b/fs/fat/mmap.c
@@ -7,6 +7,9 @@
* mmap handling for fat-based filesystems
*/
+#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
+#include <linux/version.h>
+
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -59,8 +62,8 @@ static unsigned long fat_file_mmap_nopage(
{
unsigned long cur_fs = get_fs();
set_fs (KERNEL_DS);
- cur_read = fat_file_read (inode,&filp,(char*)page
- ,need_read);
+ cur_read = fat_file_read (&filp, (char*)page,
+ need_read, &filp.f_pos);
set_fs (cur_fs);
}
if (cur_read != need_read){
diff --git a/fs/fat/tables.c b/fs/fat/tables.c
index 3e8379374..c4ad91c3c 100644
--- a/fs/fat/tables.c
+++ b/fs/fat/tables.c
@@ -1,7 +1,7 @@
/*
* linux/fs/fat/tables.c
*
- * ASCII / Unicode translation tables for VFAT filename handling.
+ * Unicode escape translation tables for VFAT filename handling.
* By Gordon Chaffee.
*
* Note: This file is used by all fat-based filesystems.
@@ -9,10 +9,9 @@
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/msdos_fs.h>
-#include "tables.h"
-
-unsigned char fat_uni2code[64] = {
+unsigned char fat_uni2esc[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',
@@ -23,7 +22,7 @@ unsigned char fat_uni2code[64] = {
'u', 'v', 'w', 'x', 'y', 'z', '+', '-'
};
-unsigned char fat_code2uni[256] = {
+unsigned char fat_esc2uni[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,
@@ -58,209 +57,6 @@ unsigned char fat_code2uni[256] = {
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.