diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/Makefile | 15 | ||||
-rw-r--r-- | fs/fat/buffer.c | 155 | ||||
-rw-r--r-- | fs/fat/cache.c | 298 | ||||
-rw-r--r-- | fs/fat/dir.c | 434 | ||||
-rw-r--r-- | fs/fat/fatfs_syms.c | 58 | ||||
-rw-r--r-- | fs/fat/file.c | 385 | ||||
-rw-r--r-- | fs/fat/inode.c | 596 | ||||
-rw-r--r-- | fs/fat/misc.c | 557 | ||||
-rw-r--r-- | fs/fat/mmap.c | 113 | ||||
-rw-r--r-- | fs/fat/msbuffer.h | 15 | ||||
-rw-r--r-- | fs/fat/tables.c | 280 | ||||
-rw-r--r-- | fs/fat/tables.h | 35 |
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: + */ |