[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#272029: marked as done (xbox: fatx fs support)



Your message dated Sat, 5 Jul 2008 00:47:24 +0200
with message-id <20080704224724.GE30354@stro.at>
and subject line Re: FATX filesystem support
has caused the Debian Bug report #272029,
regarding xbox: fatx fs support
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
272029: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=272029
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: kernel
Severity: wishlist
Tags: patch

The FATX filesystem is a derivative of the FAT filesystem minus some legacy
fields and redundant information.

It is used by Microsoft Xbox in hard drive and memory cards (or USB sticks).

The attached patches come from http://www.xbox-linux.org/ and are known to
work reliably.

-- System Information:
Debian Release: 3.1
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: i386 (i686)
Kernel: Linux 2.4.27-1-686
Locale: LANG=C, LC_CTYPE=C (ignored: LC_ALL set to C)
diff -Nur kernel-source-2.4.27-2.4.27.old/debian/patches/076_fatx_filesystem.diff kernel-source-2.4.27-2.4.27/debian/patches/076_fatx_filesystem.diff
--- kernel-source-2.4.27-2.4.27.old/debian/patches/076_fatx_filesystem.diff	1970-01-01 01:00:00.000000000 +0100
+++ kernel-source-2.4.27-2.4.27/debian/patches/076_fatx_filesystem.diff	2004-09-14 23:00:07.000000000 +0200
@@ -0,0 +1,2803 @@
+
+  FATX support.  Obtained from http://www.xbox-linux.org/.
+
+diff -Nur kernel-source-2.4.27-2.4.27.old/Documentation/Configure.help kernel-source-2.4.27-2.4.27/Documentation/Configure.help
+--- kernel-source-2.4.27-2.4.27.old/Documentation/Configure.help	2004-09-13 22:17:08.000000000 +0200
++++ kernel-source-2.4.27-2.4.27/Documentation/Configure.help	2004-09-13 22:18:03.000000000 +0200
+@@ -17094,6 +17179,26 @@
+   root partition (the one containing the directory /) cannot be a
+   module, so saying M could be dangerous.  If unsure, say N.
+ 
++FATX (Xbox) fs support
++CONFIG_FATX_FS
++  This adds support for the FATX filesystem as found in Microsoft's Xbox.
++
++  The FATX filesystem is a derivative of the FAT filesystem minus some
++  legacy fields and redundant information. For lengthier discussions on
++  the filesystem, see:
++
++  http://xbox-linux.sourceforge.net/docs/fatxfat.html
++  http://xbox-linux.sourceforge.net/docs/hdpartfs.html
++  http://xbox-linux.sourceforge.net/docs/hackingfatx.html
++
++  If you are running Linux on the Xbox, say Y unless you know what
++  you're doing. Otherwise, say N.
++
++  If you want to compile this as a module ( = code which can be
++  inserted in and removed from the running kernel whenever you want),
++  say M here and read <file:Documentation/modules.txt>.  The module
++  will be called fatx.o.
++
+ /proc file system support
+ CONFIG_PROC_FS
+   This is a virtual file system providing information about the status
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/Config.in kernel-source-2.4.27-2.4.27/fs/Config.in
+--- kernel-source-2.4.27-2.4.27.old/fs/Config.in	2004-09-13 22:17:08.000000000 +0200
++++ kernel-source-2.4.27-2.4.27/fs/Config.in	2004-09-13 22:18:04.000000000 +0200
+@@ -49,6 +49,7 @@
+ dep_tristate '  MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+ dep_tristate '    UMSDOS: Unix-like file system on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+ dep_tristate '  VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
++tristate 'FATX (Xbox) fs support' CONFIG_FATX_FS
+ dep_tristate 'EFS file system support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL
+ dep_tristate 'Journalling Flash File System (JFFS) support' CONFIG_JFFS_FS $CONFIG_MTD
+ if [ "$CONFIG_JFFS_FS" = "y" -o "$CONFIG_JFFS_FS" = "m" ] ; then
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/Makefile kernel-source-2.4.27-2.4.27/fs/Makefile
+--- kernel-source-2.4.27-2.4.27.old/fs/Makefile	2004-09-13 22:17:08.000000000 +0200
++++ kernel-source-2.4.27-2.4.27/fs/Makefile	2004-09-13 22:18:04.000000000 +0200
+@@ -32,6 +32,7 @@
+ subdir-$(CONFIG_INTERMEZZO_FS)	+= intermezzo
+ subdir-$(CONFIG_MINIX_FS)	+= minix
+ subdir-$(CONFIG_FAT_FS)		+= fat
++subdir-$(CONFIG_FATX_FS)	+= fatx
+ subdir-$(CONFIG_UMSDOS_FS)	+= umsdos
+ subdir-$(CONFIG_MSDOS_FS)	+= msdos
+ subdir-$(CONFIG_VFAT_FS)	+= vfat
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/Makefile kernel-source-2.4.27-2.4.27/fs/fatx/Makefile
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/Makefile	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/Makefile	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,17 @@
++#
++# Makefile for the Linux fatx filesystem routines.
++#
++# 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 := fatx.o
++
++export-objs := fatxfs_syms.o
++
++obj-y := namei.o cache.o dir.o file.o inode.o misc.o fatxfs_syms.o
++obj-m := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/README kernel-source-2.4.27-2.4.27/fs/fatx/README
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/README	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/README	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,59 @@
++there are some things, which should really checked in the FATX driver here.
++
++- creation of a file with touch creates a Fat entry, with filesize 0 and no
++cluster alocated (cluter=0)
++I am not sure, if the xbox will see this as "good" or not, and maybe deletes
++the entry or something else like
++
++- attributs
++there has to be a file-wrapper written, for grepping though the current fatx
++partition and look for all possible file-attributes
++
++- filepadding
++currently file(cluster) padding is been developed.
++Theoretically this should not harm the thing, but whom knows
++
++- file = multipe*0x4000 cluster size
++not even testet how the xbox handles this ? does it open a "fill" cluster,
++or stops it on the cluster end ? (where data end too)
++this als has to be cross-checked with the fatx linux driver
++
++- Fat entry's
++It supports Mulit-cluster filesystem , means extended FAT enetrys >256
++entrys.
++originally comming from the Fat module.
++this has to be tested on both Xbox (xbox os) and Linux too.
++stability is a different question.
++
++- chache system for the cluster thing
++Comming out of the original Fat driver, it supports caching.
++this is not tested, nobody knows exact.
++
++
++note:
++
++we have written the current fatx driver in this way, that the output looks
++100% exactly to Xbox like style.
++Some things are not yet finished (file end padding - cluster-padding)
++but this will come.
++
++the Driver is able, to build out of a pre-formatted image(or hdd, flash)
++with Ed's formatting tool, a compleate filesystem without errors.
++
++We are writing automated test tools, for comparing files and randoming files
++on the Xbox hdd on both Os's and make a Sha-1 compare of the files after.
++Theoretically, it should be .... (let's hope)
++
++The current driver seems a lot more stabile as the old driver, but it is
++really new !
++
++there could a lot of hidden problems there, like filerenaming with
++new_filenamelenght!=old_filenamelenght ==> not working or so.
++.. this was really a problem, we found it.. this was luck.
++
++so be very careful with working on real partitions.
++there could some very nasty bugs be inside
++
++
++ed & franz
++
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/cache.c kernel-source-2.4.27-2.4.27/fs/fatx/cache.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/cache.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/cache.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,318 @@
++/*
++ *  linux/fs/fatx/cache.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/fatx_fs.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/stat.h>
++
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++static struct fatx_cache *fatx_cache,cache[FATX_CACHE];
++static spinlock_t fatx_cache_lock = SPIN_LOCK_UNLOCKED;
++
++int fatx_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 copy, first = 0, last = 0, next, b;
++
++	next = 0;
++
++	if ((unsigned) (nr-2) >= FATX_SB(sb)->clusters)
++		return 0;
++	if (FATX_SB(sb)->fat_bits == 32) {
++		first = last = nr*4;
++	} else if (FATX_SB(sb)->fat_bits == 16) {
++		first = last = nr*2;
++	}
++
++	b = FATX_SB(sb)->fat_start + (first >> sb->s_blocksize_bits);
++	if (!(bh = sb_bread(sb, b))) {
++		PRINTK("FATX: bread in fatx_access failed\n");
++		return 0;
++	}
++	if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
++		bh2 = bh;
++	} else {
++		if (!(bh2 = sb_bread(sb, b+1))) {
++			if(bh) brelse(bh);
++			PRINTK("FATX: 2nd bread in fatx_access failed\n");
++			return 0;
++		}
++	}
++	if (FATX_SB(sb)->fat_bits == 32) {
++		p_first = p_last = NULL; /* GCC needs that stuff */
++		next = CF_LE_L(((__u32 *) bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]);
++		next &= 0xffffffff;
++		if (next >= EOC_FAT32) next = -1;
++	} else if (FATX_SB(sb)->fat_bits == 16) {
++		p_first = p_last = NULL; /* GCC needs that stuff */
++		next = CF_LE_W(((__u16 *) bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]);
++		if (next >= EOC_FAT16) next = -1;
++	}
++	PRINTK("FATX: fatx_access: 0x%x, nr=0x%x, first=0x%x, next=0x%x\n", b, nr, first, next);
++	if (new_value != -1) {
++		if (FATX_SB(sb)->fat_bits == 32) {
++			((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
++				= CT_LE_L(new_value);
++		} else if (FATX_SB(sb)->fat_bits == 16) {
++			((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
++				= CT_LE_W(new_value);
++		}
++		mark_buffer_dirty(bh);
++		for (copy = 1; copy < FATX_SB(sb)->fats; copy++) {
++			b = FATX_SB(sb)->fat_start + (first >> sb->s_blocksize_bits)
++				+ FATX_SB(sb)->fat_length * copy;
++			if (!(c_bh = sb_bread(sb, b)))
++				break;
++			if (bh != bh2) {
++				if (!(c_bh2 = sb_bread(sb, b+1))) {
++					if(c_bh) brelse(c_bh);
++					break;
++				}
++				memcpy(c_bh2->b_data, bh2->b_data, sb->s_blocksize);
++				mark_buffer_dirty(c_bh2);
++				if(c_bh2) brelse(c_bh2);
++			}
++			memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
++			mark_buffer_dirty(c_bh);
++			if(c_bh) brelse(c_bh);
++		}
++	}
++	if(bh) brelse(bh);
++	if (bh != bh2)
++		if(bh2) brelse(bh2);
++	return next;
++}
++
++void fatx_cache_init(void)
++{
++	static int initialized = 0;
++	int count;
++
++	spin_lock(&fatx_cache_lock);
++	if (initialized) {
++		spin_unlock(&fatx_cache_lock);
++		return;
++	}
++	fatx_cache = &cache[0];
++	for (count = 0; count < FATX_CACHE; count++) {
++		cache[count].device = 0;
++		cache[count].next = count == FATX_CACHE-1 ? NULL :
++		    &cache[count+1];
++	}
++	initialized = 1;
++	spin_unlock(&fatx_cache_lock);
++}
++
++
++void fatx_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
++{
++	struct fatx_cache *walk;
++	int first = FATX_I(inode)->i_start;
++
++	if (!first)
++		return;
++	spin_lock(&fatx_cache_lock);
++	for (walk = fatx_cache; walk; walk = walk->next)
++		if (inode->i_dev == walk->device
++		    && walk->start_cluster == first
++		    && 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) { 
++				spin_unlock(&fatx_cache_lock);
++				return;
++			}
++		}
++	spin_unlock(&fatx_cache_lock);
++#ifdef DEBUG
++printk("cache miss\n");
++#endif
++}
++
++
++#ifdef DEBUG
++static void list_cache(void)
++{
++	struct fatx_cache *walk;
++
++	for (walk = fatx_cache; walk; walk = walk->next) {
++		if (walk->device)
++			printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
++			       walk->start_cluster, walk->file_cluster,
++			       walk->disk_cluster);
++		else printk("-- ");
++	}
++	printk("\n");
++}
++#endif
++
++
++void fatx_cache_add(struct inode *inode,int f_clu,int d_clu)
++{
++	struct fatx_cache *walk,*last;
++	int first = FATX_I(inode)->i_start;
++
++	last = NULL;
++	spin_lock(&fatx_cache_lock);
++	for (walk = fatx_cache; walk->next; walk = (last = walk)->next)
++		if (inode->i_dev == walk->device
++		    && walk->start_cluster == first
++		    && walk->file_cluster == f_clu) {
++			if (walk->disk_cluster != d_clu) {
++				printk("FAT cache corruption inode=%ld\n",
++					inode->i_ino);
++				spin_unlock(&fatx_cache_lock);
++				fatx_cache_inval_inode(inode);
++				return;
++			}
++			/* update LRU */
++			if (last == NULL) {
++				spin_unlock(&fatx_cache_lock);
++				return;
++			}
++			last->next = walk->next;
++			walk->next = fatx_cache;
++			fatx_cache = walk;
++#ifdef DEBUG
++list_cache();
++#endif
++			spin_unlock(&fatx_cache_lock);
++			return;
++		}
++	walk->device = inode->i_dev;
++	walk->start_cluster = first;
++	walk->file_cluster = f_clu;
++	walk->disk_cluster = d_clu;
++	last->next = NULL;
++	walk->next = fatx_cache;
++	fatx_cache = walk;
++	spin_unlock(&fatx_cache_lock);
++#ifdef DEBUG
++list_cache();
++#endif
++}
++
++
++/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
++   fixes itself after a while. */
++
++void fatx_cache_inval_inode(struct inode *inode)
++{
++	struct fatx_cache *walk;
++	int first = FATX_I(inode)->i_start;
++
++	spin_lock(&fatx_cache_lock);
++	for (walk = fatx_cache; walk; walk = walk->next)
++		if (walk->device == inode->i_dev
++		    && walk->start_cluster == first)
++			walk->device = 0;
++	spin_unlock(&fatx_cache_lock);
++}
++
++
++void fatx_cache_inval_dev(kdev_t device)
++{
++	struct fatx_cache *walk;
++
++	spin_lock(&fatx_cache_lock);
++	for (walk = fatx_cache; walk; walk = walk->next)
++		if (walk->device == device)
++			walk->device = 0;
++	spin_unlock(&fatx_cache_lock);
++}
++
++
++int fatx_get_cluster(struct inode *inode,int cluster)
++{
++	int nr,count;
++
++	if (!(nr = FATX_I(inode)->i_start)) return 0;
++	if (!cluster) return nr;
++	count = 0;
++	for (fatx_cache_lookup(inode,cluster,&count,&nr); count < cluster;
++	    count++) {
++		if ((nr = fatx_access(inode->i_sb,nr,-1)) == -1) return 0;
++		if (!nr) return 0;
++	}
++	fatx_cache_add(inode,cluster,nr);
++	return nr;
++}
++
++unsigned long fatx_bmap(struct inode *inode,unsigned long sector)
++{
++	struct super_block *sb = inode->i_sb;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++	unsigned long cluster, offset, last_block;
++
++	if ((inode->i_ino == FATX_ROOT_INO || (S_ISDIR(inode->i_mode) &&
++	     !FATX_I(inode)->i_start))) {
++		if (sector >= sbi->dir_entries >> sbi->dir_per_block_bits)
++			return 0;
++		return sector + sbi->dir_start;
++	}
++	
++	last_block = (FATX_I(inode)->mmu_private + (sb->s_blocksize - 1))
++		>> sb->s_blocksize_bits;
++	if (sector >= last_block)
++		return 0;
++
++	cluster = sector / sbi->cluster_size;
++	offset  = sector % sbi->cluster_size;
++	if (!(cluster = fatx_get_cluster(inode, cluster)))
++		return 0;
++
++	return (cluster - 2) * sbi->cluster_size + sbi->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 fatx_free(struct inode *inode,int skip)
++{
++	int nr,last;
++
++	if (!(nr = FATX_I(inode)->i_start)) return 0;
++	last = 0;
++	while (skip--) {
++		last = nr;
++		if ((nr = fatx_access(inode->i_sb,nr,-1)) == -1) return 0;
++		if (!nr) {
++			printk("fatx_free: skipped EOF\n");
++			return -EIO;
++		}
++	}
++	if (last) {
++		fatx_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
++		fatx_cache_inval_inode(inode);
++	} else {
++		fatx_cache_inval_inode(inode);
++		FATX_I(inode)->i_start = 0;
++		FATX_I(inode)->i_logstart = 0;
++		mark_inode_dirty(inode);
++	}
++	lock_fatx(inode->i_sb);
++	while (nr != -1) {
++		if (!(nr = fatx_access(inode->i_sb,nr,0))) {
++			fatx_fs_panic(inode->i_sb,"fatx_free: deleting beyond EOF");
++			break;
++		}
++		if (FATX_SB(inode->i_sb)->free_clusters != -1) {
++			FATX_SB(inode->i_sb)->free_clusters++;
++		}
++		inode->i_blocks -= (1 << FATX_SB(inode->i_sb)->cluster_bits) / 512;
++	}
++	unlock_fatx(inode->i_sb);
++	return 0;
++}
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/dir.c kernel-source-2.4.27-2.4.27/fs/fatx/dir.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/dir.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/dir.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,432 @@
++/*
++ *  linux/fs/fatx/dir.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/fs.h>
++#include <linux/fatx_fs.h>
++#include <linux/nls.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 <linux/ctype.h>
++
++#include <asm/uaccess.h>
++
++#define DEBUG
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++static inline void fatx_printname(const char *name, int length)
++{
++	int i;
++	for(i=0;i<length;i++) {
++		PRINTK("%c",name[i]);
++	}
++}
++
++/*
++ * 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.
++ */
++static int fatx_raw_scan_sector(struct super_block *sb,	int sector,
++		const char *name, int name_length, int *number,
++		int *ino, struct buffer_head **res_bh,
++		struct fatx_dir_entry **res_de )
++{
++	struct nls_table *t = FATX_SB(sb)->nls_io;
++	struct buffer_head *bh;
++	struct fatx_dir_entry *data;
++	int entry,start,done = 0;
++
++	PRINTK("FATX: fatx_raw_scan_sector: sector=%08lX\n",(long)sector);
++	
++	if (!(bh = sb_bread(sb,sector))) {
++		printk("FATX: fatx_raw_scan_sector: sb_bread failed\n");
++		return -EIO;
++	}
++	data = (struct fatx_dir_entry *) bh->b_data;
++	for (entry = 0; entry < FATX_SB(sb)->dir_per_block; entry++) {
++		if (FATX_END_OF_DIR(&data[entry])) {
++			//no more entries to look through...
++			if(bh) brelse(bh);
++			PRINTK("FATX: fatx_raw_scan_sector: END OF DIR\n");
++			return -ENOENT;
++		} else if (name) { //search for name
++			done = 	(data[entry].name_length == name_length) &&
++				!strncmp(data[entry].name,name,name_length);
++		} else if (!ino) { /* count subdirectories */
++			done = 0;
++			if (!FATX_IS_FREE(&data[entry]) && (data[entry].attr & ATTR_DIR))
++				(*number)++;
++		} else if (number) { /* search for start cluster */
++			done = !FATX_IS_FREE(&data[entry]) && 
++				(CF_LE_L(data[entry].start) == *number);
++		} else { /* search for free entry */
++			done = FATX_IS_FREE(&data[entry]);
++		}
++		if (done) {
++			if (ino)
++				*ino = sector * FATX_SB(sb)->dir_per_block + entry;
++			start = CF_LE_L(data[entry].start);
++			if (!res_bh) {
++				if(bh) brelse(bh);
++			} else {
++				*res_bh = bh;
++				*res_de = &data[entry];
++			}
++			PRINTK("FATX: fatx_raw_scan_sector: found: start=%08lX\n",(long)start);
++			return start;
++		}
++	}
++	if(bh) brelse(bh);
++	PRINTK("FATX: fatx_raw_scan_sector: entry not in sector %08lX\n",(long)sector);
++	return -EAGAIN;
++}
++
++/*
++ * 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 fatx_raw_scan_root(struct super_block *sb, const char *name,
++		int name_length, int *number, int *ino,
++		struct buffer_head **res_bh, struct fatx_dir_entry **res_de )
++{
++	int count,cluster;
++
++	for (count = 0; count < FATX_SB(sb)->cluster_size; count++) {
++		if ((cluster = fatx_raw_scan_sector(sb,FATX_SB(sb)->dir_start + count, name,name_length,number,ino,res_bh,res_de)) >= 0)
++			return cluster;
++		if (cluster == -ENOENT) {
++			//end of dir...act like all sectors scanned and !found
++			PRINTK("FATX: fatx_raw_scan_root cluster %d\n",cluster);
++			return cluster;
++		}
++	}
++	
++	PRINTK("FATX: fatx_raw_scan_root leave\n");
++
++	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 fatx_raw_scan_nonroot(struct super_block *sb, int start,
++		const char *name, int name_length, int *number,
++		int *ino, struct buffer_head **res_bh,
++		struct fatx_dir_entry **res_de )
++{
++	int count,cluster;
++	
++	PRINTK("FATX: fatx_raw_scan_nonroot: entered (start=%08lX)\n",(long)start);
++
++	do {
++		for (count = 0; count < FATX_SB(sb)->cluster_size; count++) {
++			if ((cluster = fatx_raw_scan_sector(sb,FATX_SB(sb)->data_start + (FATX_SB(sb)->cluster_size * (start - 2) ) + count, name,name_length,number,ino,res_bh,res_de)) >= 0)
++				return cluster;
++			if (cluster == -ENOENT) {
++				//EOD: act like all sectors scanned and !found
++				return cluster;
++			}
++		}
++		if (!(start = fatx_access(sb,start,-1))) {
++			printk("FATX: fatx_raw_scan_nonroot: start sector %lX not in use\n",(long)start);
++			fatx_fs_panic(sb,"FATX error");
++			break;
++		}
++	}
++	while (start != -1);
++	return -ENOENT;
++}
++
++/*
++ * 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 fatx_scan(struct inode *dir, const char *name, int name_length,
++		struct buffer_head **res_bh, struct fatx_dir_entry **res_de,
++		int *ino )
++{
++	int res;
++
++	if (FATX_I(dir)->i_start)
++		res = fatx_raw_scan_nonroot(dir->i_sb,FATX_I(dir)->i_start,name,name_length,NULL,ino,res_bh,res_de);
++	else
++		res = fatx_raw_scan_root(dir->i_sb,name,name_length,NULL,ino,res_bh,res_de);
++
++	return res<0 ? res : 0;
++}
++
++/*
++ * See if directory is empty
++ */
++int fatx_dir_empty(struct inode *dir)
++{
++	loff_t pos;
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++	int ino,result = 0;
++
++	pos = 0;
++	bh = NULL;
++	while (fatx_get_entry(dir,&pos,&bh,&de,&ino) > -1) {
++		if (FATX_END_OF_DIR(de)) {
++			break;
++		}
++		if (!FATX_IS_FREE(de)) {
++			result = -ENOTEMPTY;
++			break;
++		}
++	}
++	if (bh)
++		brelse(bh);
++
++	return result;
++}
++
++/*
++ * fatx_subdirs counts the number of sub-directories of dir. It can be run
++ * on directories being created.
++ */
++int fatx_subdirs(struct inode *dir)
++{
++	int count;
++
++	count = 0;
++	if (dir->i_ino == FATX_ROOT_INO) {
++		fatx_raw_scan_root(dir->i_sb,NULL,0,&count,NULL,NULL,NULL);
++	} else {
++		if ((dir->i_ino != FATX_ROOT_INO) && !FATX_I(dir)->i_start) {
++			return 0; /* in mkdir */
++		} else {
++			fatx_raw_scan_nonroot(dir->i_sb,FATX_I(dir)->i_start,
++			                      NULL,0,&count,NULL,NULL,NULL);
++		}
++	}
++	return count;
++}
++
++int fatx_do_add_entry(
++		struct inode *dir,
++		struct buffer_head **bh,
++		struct fatx_dir_entry **de,
++		int *ino)
++{
++	loff_t offset, curr;
++	struct buffer_head *new_bh;
++
++	offset = curr = 0;
++	*bh = NULL;
++	while (fatx_get_entry(dir,&curr,bh,de,ino) > -1) {
++		if (FATX_IS_FREE(*de)) {
++			PRINTK("FATX: fatx_do_add_entry: found free entry\n");
++			return offset;
++		}
++		if (FATX_END_OF_DIR(*de)) {
++			struct buffer_head *eod_bh = NULL;
++			struct fatx_dir_entry *eod_de = NULL;
++			int eod_ino;
++			
++			PRINTK("FATX: fatx_do_add_entry: found EOD at %lX\n",(long)(*de));
++			//make sure the next one isn't first in new cluster
++			if (fatx_get_entry(dir,&curr,&eod_bh,&eod_de,&eod_ino) > -1) {
++				//EOD in same cluster...find proper de and mark new EOD
++				eod_de->name_length = 0xFF;
++				mark_buffer_dirty(eod_bh);
++				PRINTK("FATX: fatx_do_add_entry: marked new EOD at %lX\n",(long)eod_de);
++				if(eod_bh) brelse(eod_bh);
++			} else {
++				//we will take the easy out...do nothing...
++				//assume fat table used to indicate EOD
++				//if this is wrong, need to fatx_extend_dir
++				//making first entry in next cluster EOD
++				printk("FATX: fatx_do_add_entry: EOD marked by FAT\n");
++				printk("FATX: ...:offset=%08lX, curr=%08lX\n",
++						(unsigned long)offset,(unsigned long)curr);
++			}
++			PRINTK("FATX: fatx_do_add_entry: using entry at %lX\n",(long)(*de));
++			return offset;
++		}
++		offset = curr;
++	}
++	PRINTK("FATX: fatx_do_add_entry: need to extend dir\n");
++	if (dir->i_ino == FATX_ROOT_INO) {
++		printk("FATX: fatx_do_add_entry: but it's root dir...can't extend\n");
++		return -ENOSPC;
++	}
++	new_bh = fatx_extend_dir(dir);
++	if (!new_bh) {
++		PRINTK("FATX: fatx_do_add_entry: fatx_extend_dir failed...no space?\n");
++		return -ENOSPC;
++	}
++	if(new_bh) brelse(new_bh);
++	fatx_get_entry(dir,&curr,bh,de,ino);
++	(*de)[1].name_length = 0xFF;
++	PRINTK("FATX: fatx_do_add_entry: using entry at %ld\n",(long)offset);
++	return offset;
++}
++
++int fatx_new_dir(struct inode *dir, struct inode *parent)
++{
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++
++	if ((bh = fatx_extend_dir(dir)) == NULL) {
++		printk("FATX: fatx_new_dir: failed to get new cluster...no space?\n");
++		return -ENOSPC;
++	}
++	/* zeroed out, so... */
++	de = (struct fatx_dir_entry*)&bh->b_data[0];
++	de[0].attr = de[1].attr = ATTR_DIR;
++	de[0].name_length = 0xFF; //end of dir marker
++	de[0].start = CT_LE_W(FATX_I(dir)->i_logstart);
++	de[1].start = CT_LE_W(FATX_I(parent)->i_logstart);
++	mark_buffer_dirty(bh);
++	if(bh) brelse(bh);
++	dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++	mark_inode_dirty(dir);
++
++	return 0;
++}
++
++// sure to hope this is correct...
++int fatx_readdir(struct file *filp, void *dirent, filldir_t filldir)
++{
++	struct inode *inode = filp->f_dentry->d_inode;
++	struct inode *tmpi;
++	struct super_block *sb = inode->i_sb;
++	struct fatx_dir_entry *de;
++	struct buffer_head *bh;
++	int ino, inum;
++	loff_t cpos = 0;	//file position (dir position)
++	int offset = 0;		//cpos offset for root dir handling
++	int entry = 0;		//next filldir entry location
++
++	PRINTK("FATX: fatx_readdir entered\n");
++	
++	cpos = filp->f_pos;
++	
++	if (cpos == 0) {
++		if (filldir(dirent,".",1,entry++,inode->i_ino,DT_DIR)<0) {
++			printk("\nFATX: fatx_readdir exiting in root defaults\n");
++			return 0;
++		}
++		cpos += 1 << FATX_DIR_BITS;
++	}
++	
++	if (cpos == 1 << FATX_DIR_BITS) {
++		if (filldir(dirent,"..",2,entry++,
++		            filp->f_dentry->d_parent->d_inode->i_ino,DT_DIR)<0) {
++			printk("\nFATX: fatx_readdir exiting in root defaults\n");
++			filp->f_pos = 1 << FATX_DIR_BITS;
++			return 0;
++		}
++		cpos += 1 << FATX_DIR_BITS;
++	}
++	
++	offset = 2 << FATX_DIR_BITS;
++	cpos -= offset;
++
++ 	bh = NULL;
++
++	while(fatx_get_entry(inode,&cpos,&bh,&de,&ino) != -1) {
++		if (FATX_END_OF_DIR(de)) {
++			PRINTK("FATX: entry %ld marked as END OF DIR\n",(long)(cpos >> FATX_DIR_BITS));
++			cpos -= 1 << FATX_DIR_BITS; // make sure it comes back to here if re-entered
++			break;		//done...end of dir.
++		}
++		
++		if (FATX_IS_FREE(de)) {
++			PRINTK("FATX: entry %ld marked as FREE\n",(long)(cpos >> FATX_DIR_BITS));
++			continue;
++		}
++
++		tmpi = fatx_iget(sb, ino);
++		if (tmpi) {
++			inum = tmpi->i_ino;
++			iput(tmpi);
++		} else {
++			inum = iunique(sb, FATX_ROOT_INO);
++		}
++
++		if (filldir(dirent,de->name,de->name_length,entry++,inum,
++		            (de->attr & ATTR_DIR) ? DT_DIR : DT_REG ) < 0 ) {
++			break;
++		}
++		PRINTK("\nFATX: fatx_readdir: dir entry %3d: ",(int)entry);
++		fatx_printname(de->name,de->name_length);
++		PRINTK("\n");
++	}
++
++	filp->f_pos = cpos + offset;		
++	if (bh)
++		brelse(bh);
++	
++	PRINTK("\nFATX: fatx_readdir leaving\n");
++	
++	return 0;
++}
++
++struct file_operations fatx_dir_operations = {
++	.read		= generic_read_dir,
++	.readdir	= fatx_readdir,
++	.ioctl		= NULL,
++	.fsync		= file_fsync,
++};
++
++/* This assumes that size of cluster is above the 32*slots */
++
++int fatx_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
++		  struct fatx_dir_entry **de, int *ino)
++{
++	loff_t offset, curr;
++	int row;
++	struct buffer_head *new_bh;
++
++	offset = curr = 0;
++	*bh = NULL;
++	row = 0;
++	while (fatx_get_entry(dir,&curr,bh,de,ino) > -1) {
++		if (IS_FREE((*de)->name)) {
++			if (++row == slots)
++				return offset;
++		} else {
++			row = 0;
++			offset = curr;
++		}
++	}
++	if (dir->i_ino == FATX_ROOT_INO) 
++		return -ENOSPC;
++	new_bh = fatx_extend_dir(dir);
++	if (!new_bh)
++		return -ENOSPC;
++	if(new_bh) brelse(new_bh);
++	do fatx_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
++	return offset;
++}
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/fatxfs_syms.c kernel-source-2.4.27-2.4.27/fs/fatx/fatxfs_syms.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/fatxfs_syms.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/fatxfs_syms.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ *  linux/fs/fatx/fatxfs_syms.c
++ *
++ *  Exported kernel symbols for the FATX filesystem.
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mm.h>
++#include <linux/fatx_fs.h>
++#include <linux/init.h>
++
++unsigned int fatx_debug = 0;
++
++MODULE_PARM(fatx_debug,"i");
++MODULE_PARM_DESC(fatx_debug,"turn on fatx debugging output");
++
++EXPORT_SYMBOL(fatx_lookup);
++EXPORT_SYMBOL(fatx_create);
++EXPORT_SYMBOL(fatx_rmdir);
++EXPORT_SYMBOL(fatx_mkdir);
++EXPORT_SYMBOL(fatx_rename);
++EXPORT_SYMBOL(fatx_unlink);
++
++EXPORT_SYMBOL(fatx_new_dir);
++EXPORT_SYMBOL(fatx_get_block);
++EXPORT_SYMBOL(fatx_clear_inode);
++EXPORT_SYMBOL(fatx_date_unix2dos);
++EXPORT_SYMBOL(fatx_delete_inode);
++EXPORT_SYMBOL(fatx_get_entry);
++EXPORT_SYMBOL(fatx_notify_change);
++EXPORT_SYMBOL(fatx_put_super);
++EXPORT_SYMBOL(fatx_attach);
++EXPORT_SYMBOL(fatx_detach);
++EXPORT_SYMBOL(fatx_build_inode);
++EXPORT_SYMBOL(fatx_read_super);
++EXPORT_SYMBOL(fatx_readdir);
++EXPORT_SYMBOL(fatx_scan);
++EXPORT_SYMBOL(fatx_statfs);
++EXPORT_SYMBOL(fatx_write_inode);
++EXPORT_SYMBOL(fatx_get_cluster);
++EXPORT_SYMBOL(fatx_add_entries);
++EXPORT_SYMBOL(fatx_dir_empty);
++EXPORT_SYMBOL(fatx_truncate);
++
++static DECLARE_FSTYPE_DEV(fatx_fs_type, "fatx", fatx_read_super);
++
++static int __init init_fatx_fs(void)
++{
++	printk("FATX driver 0.0.1\n");
++	fatx_hash_init();
++        return register_filesystem(&fatx_fs_type);
++}
++
++static void __exit exit_fatx_fs(void)
++{
++        unregister_filesystem(&fatx_fs_type);
++}
++
++module_init(init_fatx_fs)
++module_exit(exit_fatx_fs)
++MODULE_LICENSE("GPL");
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/file.c kernel-source-2.4.27-2.4.27/fs/fatx/file.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/file.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/file.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,111 @@
++/*
++ *  linux/fs/fatx/file.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/sched.h>
++#include <linux/locks.h>
++#include <linux/fs.h>
++#include <linux/fatx_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>
++
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++struct file_operations fatx_file_operations = {
++	.llseek		= generic_file_llseek,
++	.read		= fatx_file_read,
++	.write		= fatx_file_write,
++	.mmap		= generic_file_mmap,
++	.fsync		= file_fsync,
++};
++
++struct inode_operations fatx_file_inode_operations = {
++	.truncate	= fatx_truncate,
++	.setattr	= fatx_notify_change,
++};
++
++ssize_t fatx_file_read(	struct file *filp, char *buf, size_t count, loff_t *ppos)
++{
++	return generic_file_read(filp,buf,count,ppos);
++}
++
++
++int fatx_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
++{
++	struct super_block *sb = inode->i_sb;
++	unsigned long phys;
++
++	phys = fatx_bmap(inode, iblock);
++	if (phys) {
++		bh_result->b_dev = inode->i_dev;
++		bh_result->b_blocknr = phys;
++		bh_result->b_state |= (1UL << BH_Mapped);
++		return 0;
++	}
++	if (!create)
++		return 0;
++	if (iblock << sb->s_blocksize_bits != FATX_I(inode)->mmu_private) {
++		BUG();
++		return -EIO;
++	}
++	if (!(iblock % FATX_SB(inode->i_sb)->cluster_size)) {
++		if (fatx_add_cluster(inode) < 0)
++			return -ENOSPC;
++	}
++	FATX_I(inode)->mmu_private += sb->s_blocksize;
++	phys = fatx_bmap(inode, iblock);
++	if (!phys)
++		BUG();
++	bh_result->b_dev = inode->i_dev;
++	bh_result->b_blocknr = phys;
++	bh_result->b_state |= (1UL << BH_Mapped);
++	bh_result->b_state |= (1UL << BH_New);
++	return 0;
++}
++
++ssize_t fatx_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
++{
++	struct inode *inode = filp->f_dentry->d_inode;
++	int retval;
++
++	retval = generic_file_write(filp, buf, count, ppos);
++	if (retval > 0) {
++		inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME;
++		FATX_I(inode)->i_attrs |= ATTR_ARCH;
++		mark_inode_dirty(inode);
++	}
++	return retval;
++}
++
++void fatx_truncate(struct inode *inode)
++{
++	struct fatx_sb_info *sbi = FATX_SB(inode->i_sb);
++	int cluster;
++
++	/* Why no return value?  Surely the disk could fail... */
++	if (IS_RDONLY (inode))
++		return /* -EPERM */;
++	if (IS_IMMUTABLE(inode))
++		return /* -EPERM */;
++	cluster = 1 << sbi->cluster_bits;
++	/* 
++	 * This protects against truncating a file bigger than it was then
++	 * trying to write into the hole.
++	 */
++	if (FATX_I(inode)->mmu_private > inode->i_size)
++		FATX_I(inode)->mmu_private = inode->i_size;
++
++	fatx_free(inode, (inode->i_size + (cluster - 1)) >> sbi->cluster_bits);
++	FATX_I(inode)->i_attrs |= ATTR_ARCH;
++	inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME;
++	mark_inode_dirty(inode);
++}
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/inode.c kernel-source-2.4.27-2.4.27/fs/fatx/inode.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/inode.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/inode.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,663 @@
++/*
++ *  linux/fs/fatx/inode.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>	//needed for 'event'
++#include <linux/blkdev.h>	//needed for 'blk_size'
++#include <linux/locks.h>
++#include <linux/fatx_fs.h>
++#include <linux/fatx_fs_sb.h>
++#include <linux/slab.h>
++#include <linux/bitops.h>
++#include <linux/major.h>
++#include <linux/stat.h>
++#include <linux/locks.h>
++#include <linux/smp_lock.h>
++ 
++
++#define CONFIG_NLS_DEFAULT "iso8859-15"
++
++#define FAT_HASH_BITS   8
++#define FAT_HASH_SIZE    (1UL << FAT_HASH_BITS)
++#define FAT_HASH_MASK    (FAT_HASH_SIZE-1)
++
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++static int fatx_writepage(struct page *page)
++{
++	return block_write_full_page(page,fatx_get_block);
++}
++
++static int fatx_readpage(struct file *file, struct page *page)
++{
++	return block_read_full_page(page,fatx_get_block);
++}
++
++static int fatx_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
++{
++	return cont_prepare_write(page,from,to,fatx_get_block,
++		&FATX_I(page->mapping->host)->mmu_private);
++}
++
++static int _fatx_bmap(struct address_space *mapping, long block)
++{
++	return generic_block_bmap(mapping,block,fatx_get_block);
++}
++
++struct address_space_operations fatx_aops = {
++	.readpage	= fatx_readpage,
++	.writepage	= fatx_writepage,
++	.sync_page	= block_sync_page,
++	.prepare_write	= fatx_prepare_write,
++	.commit_write	= generic_commit_write,
++	.bmap		= _fatx_bmap
++};
++
++void fatx_put_super(struct super_block *sb)
++{
++	fatx_cache_inval_dev(sb->s_dev);
++	set_blocksize(sb->s_dev,BLOCK_SIZE);
++        if (FATX_SB(sb)->nls_io) {
++		unload_nls(FATX_SB(sb)->nls_io);
++		FATX_SB(sb)->nls_io = NULL;
++	}
++}
++
++static struct list_head fatx_inode_hashtable[FAT_HASH_SIZE];
++spinlock_t fatx_inode_lock = SPIN_LOCK_UNLOCKED;
++
++void fatx_hash_init(void)
++{
++	int i;
++	for(i = 0; i < FAT_HASH_SIZE; i++) {
++		INIT_LIST_HEAD(&fatx_inode_hashtable[i]);
++	}
++}
++
++static inline unsigned long fatx_hash(struct super_block *sb, int i_pos)
++{
++	unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
++	tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
++	return tmp & FAT_HASH_MASK;
++}
++
++void fatx_attach(struct inode *inode, int i_pos)
++{
++	spin_lock(&fatx_inode_lock);
++	FATX_I(inode)->i_location = i_pos;
++	list_add(&FATX_I(inode)->i_fat_hash,
++		fatx_inode_hashtable + fatx_hash(inode->i_sb, i_pos));
++	spin_unlock(&fatx_inode_lock);
++}
++
++void fatx_detach(struct inode *inode)
++{
++	spin_lock(&fatx_inode_lock);
++	FATX_I(inode)->i_location = 0;
++	list_del(&FATX_I(inode)->i_fat_hash);
++	INIT_LIST_HEAD(&FATX_I(inode)->i_fat_hash);
++	spin_unlock(&fatx_inode_lock);
++}
++
++struct inode *fatx_iget(struct super_block *sb, int i_pos)
++{
++	struct list_head *p = fatx_inode_hashtable + fatx_hash(sb, i_pos);
++	struct list_head *walk;
++	struct fatx_inode_info *i;
++	struct inode *inode = NULL;
++
++	spin_lock(&fatx_inode_lock);
++	list_for_each(walk, p) {
++		i = list_entry(walk, struct fatx_inode_info, i_fat_hash);
++		if (i->i_fat_inode->i_sb != sb)
++			continue;
++		if (i->i_location != i_pos)
++			continue;
++		inode = igrab(i->i_fat_inode);
++		if (inode)
++			break;
++	}
++	spin_unlock(&fatx_inode_lock);
++	return inode;
++}
++
++/* doesn't deal with root inode */
++static void fatx_fill_inode(struct inode *inode, struct fatx_dir_entry *de)
++{
++	struct super_block *sb = inode->i_sb;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++	int nr;
++
++	INIT_LIST_HEAD(&FATX_I(inode)->i_fat_hash);
++	FATX_I(inode)->i_location = 0;
++	FATX_I(inode)->i_fat_inode = inode;
++	inode->i_uid = sbi->options.fs_uid;
++	inode->i_gid = sbi->options.fs_gid;
++	inode->i_version = ++event;
++	inode->i_generation = CURRENT_TIME;
++	
++	if ((de->attr & ATTR_DIR) && !FATX_IS_FREE(de)) {
++		inode->i_generation &= ~1;
++		inode->i_mode = FATX_MKMODE(de->attr,S_IRWXUGO & 
++			~sbi->options.fs_umask) | S_IFDIR;
++		inode->i_op = sbi->dir_ops;
++		inode->i_fop = &fatx_dir_operations;
++
++		FATX_I(inode)->i_start = CF_LE_L(de->start);
++		FATX_I(inode)->i_logstart = FATX_I(inode)->i_start;
++		inode->i_nlink = fatx_subdirs(inode) + 2;
++		    /* 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
++		if ((nr = FATX_I(inode)->i_start) != 0)
++			while (nr != -1) {
++				inode->i_size += 1 << sbi->cluster_bits;
++				if (!(nr = fatx_access(sb, nr, -1))) {
++					printk("Directory %ld: bad FAT\n",
++					    inode->i_ino);
++					break;
++				}
++			}
++		FATX_I(inode)->mmu_private = inode->i_size;
++	} else { /* not a directory */
++		inode->i_generation |= 1;
++		inode->i_mode = FATX_MKMODE(de->attr,S_IRWXUGO & ~sbi->options.fs_umask) | S_IFREG;
++		FATX_I(inode)->i_start = CF_LE_L(de->start);
++		FATX_I(inode)->i_logstart = FATX_I(inode)->i_start;
++		inode->i_size = CF_LE_L(de->size);
++	        inode->i_op = &fatx_file_inode_operations;
++	        inode->i_fop = &fatx_file_operations;
++		inode->i_mapping->a_ops = &fatx_aops;
++		FATX_I(inode)->mmu_private = inode->i_size;
++	}
++	FATX_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
++	/* this is as close to the truth as we can get ... */
++	inode->i_blksize = 1 << sbi->cluster_bits;
++	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
++			   & ~(inode->i_blksize - 1)) >> 9;
++	inode->i_mtime = inode->i_atime =
++		fatx_date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
++	inode->i_ctime = fatx_date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate));
++}
++
++struct inode *fatx_build_inode(	struct super_block *sb,	struct fatx_dir_entry *de, int ino, int *res )
++{
++	struct inode *inode;
++	*res = 0;
++	inode = fatx_iget(sb, ino);
++	if (inode)
++		goto out;
++	inode = new_inode(sb);
++	*res = -ENOMEM;
++	if (!inode)
++		goto out;
++	*res = 0;
++	inode->i_ino = iunique(sb, FATX_ROOT_INO);
++	fatx_fill_inode(inode, de);
++	fatx_attach(inode, ino);
++	insert_inode_hash(inode);
++out:
++	return inode;
++}
++
++static unsigned int fatx_get_total_size ( struct super_block *sb )
++{
++	int major = MAJOR(sb->s_dev);
++	
++	if (blk_size[major]) {
++		int minor = MINOR(sb->s_dev);
++		return blk_size[major][minor] << 1;
++	}
++	return 0;
++}
++
++/*
++ * parse super block values out of FATX "boot block"
++ * unlike the other FAT variants, much of the data is calculated from the
++ * the partition information.
++ */
++int fatx_parse_boot_block ( struct super_block *sb, struct buffer_head *bh )
++{
++	struct fatx_boot_sector *b = (struct fatx_boot_sector *)bh->b_data;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++	int logical_sector_size, hard_blksize;
++	unsigned int total_sectors;
++	unsigned long cl_count;
++	unsigned long fat_length;
++
++	PRINTK("FATX: entered fatx_parse_boot_block\n");
++	
++	if (b->magic != FATX_BOOTBLOCK_MAGIC) {
++		printk("FATX: boot block signature not found.  Not FATX?\n");
++		return -1;
++	}
++		
++	PRINTK("FATX: fatx_magic: %08lX\n",(unsigned long)b->magic);
++			
++	logical_sector_size = 512;
++	
++	sbi->cluster_size = CLUSTER_SIZE;
++	
++	PRINTK("FATX: cluster_size: %d\n",(int)sbi->cluster_size);
++	
++	//sb->s_block_size enters as hardware block (sector) size
++	hard_blksize = sb->s_blocksize;
++	sb->s_blocksize = logical_sector_size;
++	sb->s_blocksize_bits = ffs(logical_sector_size) - 1;
++
++	//figure total sector count
++	total_sectors = fatx_get_total_size(sb);
++	
++	PRINTK("FATX: total_sectors for given device: %ld\n",(unsigned long)total_sectors);
++	
++	sbi->cluster_bits = 14;
++	sbi->fats = 1;
++	
++	//hmm...fat should start right after boot block sectors (first 8)
++	sbi->fat_start = 8;	//this might be: + CF_LE_W(b->fatx_unknown)
++	sbi->root_cluster = 0;
++	sbi->dir_per_block = logical_sector_size/sizeof(struct fatx_dir_entry);
++	sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
++	sbi->dir_entries = 256;
++	
++	//check cluster count
++	
++	cl_count = total_sectors / sbi->cluster_size;
++
++	if( cl_count >= 0xfff4 ) {
++		//FATX-32
++		sb->s_maxbytes = FATX32_MAX_NON_LFS;
++		sbi->fat_bits = 32;
++	} else {
++		//FATX-16
++		sb->s_maxbytes = FATX16_MAX_NON_LFS;
++		sbi->fat_bits = 16;
++	}
++
++	fat_length = cl_count * (sbi->fat_bits>>3);		
++	if(fat_length % 4096) {
++		fat_length = ((fat_length / 4096) + 1) * 4096;
++	}
++	sbi->fat_length = fat_length / logical_sector_size;
++
++	sbi->dir_start = sbi->fat_start + sbi->fat_length;
++	sbi->data_start = sbi->dir_start + CLUSTER_SIZE;
++	sbi->clusters = ((total_sectors-sbi->data_start) / sbi->cluster_size);
++	sbi->free_clusters = -1; /* Don't know yet */
++	
++	PRINTK("FATX: logical_sector_size:	%d\n",(int)logical_sector_size);
++	PRINTK("FATX: fat_length:		%d\n",(int)sbi->fat_length);
++	PRINTK("FATX: spc_bits:			%d\n",sbi->fat_bits>>3);
++	PRINTK("FATX: fat_start:		%d\n",(int)sbi->fat_start);
++	PRINTK("FATX: dir_start:		%d\n",(int)sbi->dir_start);
++	PRINTK("FATX: data_start:		%d\n",(int)sbi->data_start);
++	PRINTK("FATX: clusters:			%ld\n",(unsigned long)sbi->clusters);
++	PRINTK("FATX: fat_bits:			%d\n",(int)sbi->fat_bits);
++	PRINTK("FATX: fat_length:		%d\n",(int)sbi->fat_length);
++	PRINTK("FATX: root_dir_sectors:		%d\n",(int)CLUSTER_SIZE);
++	PRINTK("FATX: dir_per_block:		%d\n",(int)sbi->dir_per_block);
++	PRINTK("FATX: dir_per_block_bits:	%d\n",(int)sbi->dir_per_block_bits);
++	PRINTK("FATX: dir_entries :		%d\n",(int)sbi->dir_entries);
++	PRINTK("FATX: cluster_bits:		%d\n",(int)sbi->cluster_bits);
++	
++	PRINTK("FATX: leaving fatx_parse_boot_block\n");
++		
++	return 0;
++}
++
++static void fatx_read_root(struct inode *inode)
++{
++	struct super_block *sb = inode->i_sb;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++
++	INIT_LIST_HEAD(&FATX_I(inode)->i_fat_hash);
++	FATX_I(inode)->i_location = 0;
++	FATX_I(inode)->i_fat_inode = inode;
++	inode->i_uid = sbi->options.fs_uid;
++	inode->i_gid = sbi->options.fs_gid;
++	inode->i_version = ++event;
++	inode->i_generation = 0;
++	inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR;
++	inode->i_op = sbi->dir_ops;
++	inode->i_fop = &fatx_dir_operations;
++	
++	FATX_I(inode)->i_start = FATX_ROOT_INO;
++	inode->i_size = sbi->dir_entries * sizeof(struct fatx_dir_entry);
++
++	inode->i_blksize = 1 << sbi->cluster_bits;
++	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
++			   & ~(inode->i_blksize - 1)) >> 9;
++	FATX_I(inode)->i_logstart = 0;
++	FATX_I(inode)->mmu_private = inode->i_size;
++
++	FATX_I(inode)->i_attrs = 0;
++	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
++	FATX_I(inode)->i_ctime_ms = 0;
++	inode->i_nlink = fatx_subdirs(inode) + 2;
++}
++
++/* The public inode operations for the fatx fs */
++struct inode_operations fatx_dir_inode_operations = {
++	.create		= fatx_create,
++	.lookup		= fatx_lookup,
++	.unlink		= fatx_unlink,
++	.mkdir		= fatx_mkdir,
++	.rmdir		= fatx_rmdir,
++	.rename		= fatx_rename,
++	.setattr	= fatx_notify_change,
++};
++
++void fatx_write_inode(struct inode *inode, int wait)
++{
++	struct super_block *sb = inode->i_sb;
++	struct buffer_head *bh;
++	struct fatx_dir_entry *raw_entry;
++	unsigned int i_pos;
++	
++	PRINTK("FATX: fatx_write_inode: entered\n");
++
++retry:
++	i_pos = FATX_I(inode)->i_location;
++	if (inode->i_ino == FATX_ROOT_INO || !i_pos) {
++		return;
++	}
++	lock_kernel();
++	if (!(bh = sb_bread(sb, i_pos >> FATX_SB(sb)->dir_per_block_bits))) {
++		PRINTK("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos);
++		fatx_fs_panic(sb, "fatx_write_inode: unable to read i-node block");
++		unlock_kernel();
++		return;
++	}
++	spin_lock(&fatx_inode_lock);
++	if (i_pos != FATX_I(inode)->i_location) {
++		spin_unlock(&fatx_inode_lock);
++		if(bh) brelse(bh);
++		unlock_kernel();
++		goto retry;
++	}
++
++	raw_entry = &((struct fatx_dir_entry *) (bh->b_data))
++	    [i_pos & (FATX_SB(sb)->dir_per_block - 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 |= FATX_MKATTR(inode->i_mode) |
++	    FATX_I(inode)->i_attrs;
++	raw_entry->start = CT_LE_L(FATX_I(inode)->i_logstart);
++	
++	PRINTK("FATX: fatx_write_inode: start == %08lX (LE=%08lX)\n",
++			(long)FATX_I(inode)->i_logstart,
++			(long)CT_LE_L(FATX_I(inode)->i_logstart));
++	
++	fatx_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);
++	
++	fatx_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);
++	raw_entry->atime = CT_LE_W(raw_entry->ctime);
++	raw_entry->adate = CT_LE_W(raw_entry->cdate);
++	
++	spin_unlock(&fatx_inode_lock);
++	mark_buffer_dirty(bh);
++	if(bh) brelse(bh);
++	unlock_kernel();
++	
++	PRINTK("FATX: fatx_write_inode: leaving\n");
++}
++
++int fatx_statfs(struct super_block *sb,struct statfs *buf)
++{
++	int free,nr;
++
++	lock_fatx(sb);
++        if (FATX_SB(sb)->free_clusters != -1)
++		free = FATX_SB(sb)->free_clusters;
++	else {
++		free = 0;
++		for (nr = 2; nr < FATX_SB(sb)->clusters+2; nr++)
++			if (!fatx_access(sb,nr,-1)) free++;
++		FATX_SB(sb)->free_clusters = free;
++	}
++	unlock_fatx(sb);
++	buf->f_type = sb->s_magic;
++	buf->f_bsize = 1 << FATX_SB(sb)->cluster_bits;
++	buf->f_blocks = FATX_SB(sb)->clusters;
++	buf->f_bfree = free;
++	buf->f_bavail = free;
++	buf->f_namelen = FATX_MAX_NAME_LENGTH;
++	return 0;
++}
++
++
++void fatx_delete_inode(struct inode *inode)
++{
++	if (!is_bad_inode(inode)) {
++		lock_kernel();
++		inode->i_size = 0;
++		fatx_truncate(inode);
++		unlock_kernel();
++	}
++	clear_inode(inode);
++}
++
++void fatx_clear_inode(struct inode *inode)
++{
++	if (is_bad_inode(inode))
++		return;
++	lock_kernel();
++	spin_lock(&fatx_inode_lock);
++	fatx_cache_inval_inode(inode);
++	list_del(&FATX_I(inode)->i_fat_hash);
++	spin_unlock(&fatx_inode_lock);
++	unlock_kernel();
++}
++
++static struct super_operations fatx_sops = { 
++	.write_inode	= fatx_write_inode,
++	.delete_inode	= fatx_delete_inode,
++	.put_super	= fatx_put_super,
++	.statfs		= fatx_statfs,
++	.clear_inode	= fatx_clear_inode,
++	.read_inode	= make_bad_inode,
++};
++
++static int parse_options(char *options,struct fatx_mount_options *opts)
++{
++        char *this_char,*value,save,*savep;
++        int ret = 1;
++
++        opts->fs_uid = current->uid;
++        opts->fs_gid = current->gid;
++        opts->fs_umask = current->fs->umask;
++        opts->quiet = 0;
++
++        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,"uid")) {
++                        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) ret= 0;
++                        else {
++                                opts->fs_gid = simple_strtoul(value,&value,0);
++                                if (*value) ret = 0;
++                        }
++                }
++                else if (!strcmp(this_char,"umask")) {
++                        if (!value || !*value) ret = 0;
++                        else {
++                                opts->fs_umask = simple_strtoul(value,&value,8);
++                                if (*value) ret = 0;
++                        }
++                }
++                else if (!strcmp(this_char,"quiet")) {
++                        if (value) ret = 0;
++                        else opts->quiet = 1;
++                }
++                if (this_char != options) *(this_char-1) = ',';
++                if (value) *savep = save;
++                if (ret == 0)
++                        break;
++        }
++out:
++        return ret;
++}
++
++struct super_block *fatx_read_super(struct super_block *sb,void *data, int silent)
++{
++	struct inode *root_inode;
++	struct buffer_head *bh;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++	int hard_blksize;
++	int error;
++
++	PRINTK("FATX: entering fatx_read_super\n");
++
++	sbi->private_data = NULL;
++
++	sbi->dir_ops = &fatx_dir_inode_operations;
++
++	sb->s_op = &fatx_sops;
++
++	hard_blksize = get_hardsect_size(sb->s_dev);
++	if (!hard_blksize)
++		hard_blksize = 512;
++
++	//store fat value parsed into fatx_bits...possibly overridden later
++	sbi->fat_bits = 0;
++	
++        if (!parse_options((char *) data, &(sbi->options)))
++                goto out_fail;
++	
++	fatx_cache_init();
++
++	sb->s_blocksize = hard_blksize;
++	set_blocksize(sb->s_dev, hard_blksize);
++	bh = sb_bread(sb, 0);
++	if (bh == NULL) {
++		PRINTK("FATX: unable to read boot sector\n");
++		goto out_fail;
++	}
++
++	// insert call(s) to superblock parsing
++	error = fatx_parse_boot_block(sb,bh);
++	brelse(bh);
++
++	if (error)
++		goto out_invalid;
++
++	set_blocksize(sb->s_dev, sb->s_blocksize);
++
++	sb->s_magic = FATX_BOOTBLOCK_MAGIC;
++	/* set up enough so that it can read an inode */
++	init_MUTEX(&sbi->fat_lock);
++	sbi->prev_free = 0;
++
++	
++	sbi->nls_io = NULL;
++	if (! sbi->nls_io)
++		sbi->nls_io = load_nls_default();
++	
++	root_inode = new_inode(sb);
++	if (!root_inode)
++		goto out_unload_nls;
++	root_inode->i_ino = FATX_ROOT_INO;
++	fatx_read_root(root_inode);
++	insert_inode_hash(root_inode);
++	sb->s_root = d_alloc_root(root_inode);
++	if (!sb->s_root)
++		goto out_no_root;
++
++	PRINTK("FATX: leave fatx_read_super\n");
++	
++	return sb;
++
++out_no_root:
++	PRINTK("FATX: get root inode failed\n");
++	iput(root_inode);
++out_unload_nls:
++	unload_nls(sbi->nls_io);
++	goto out_fail;
++out_invalid:
++	if (!silent) {
++		PRINTK("VFS: Can't find a valid FAT filesystem on dev %s.\n",
++			kdevname(sb->s_dev));
++	}
++out_fail:
++	if(sbi->private_data)
++		kfree(sbi->private_data);
++	sbi->private_data = NULL;
++ 
++	return NULL;
++}
++		
++int fatx_notify_change(struct dentry * dentry, struct iattr * attr)
++{
++	struct super_block *sb = dentry->d_sb;
++	struct inode *inode = dentry->d_inode;
++	int error;
++
++	/* FAT cannot truncate to a longer file */
++	if (attr->ia_valid & ATTR_SIZE) {
++		if (attr->ia_size > inode->i_size)
++			return -EPERM;
++	}
++
++	error = inode_change_ok(inode, attr);
++	if (error)
++		return FATX_SB(sb)->options.quiet ? 0 : error;
++
++	if (((attr->ia_valid & ATTR_UID) && 
++	     (attr->ia_uid != FATX_SB(sb)->options.fs_uid)) ||
++	    ((attr->ia_valid & ATTR_GID) && 
++	     (attr->ia_gid != FATX_SB(sb)->options.fs_gid)) ||
++	    ((attr->ia_valid & ATTR_MODE) &&
++	     (attr->ia_mode & ~FATX_VALID_MODE)))
++		error = -EPERM;
++
++	if (error)
++		return FATX_SB(sb)->options.quiet ? 0 : error;
++
++	error = inode_setattr(inode, attr);
++	if (error)
++		return error;
++
++	if (S_ISDIR(inode->i_mode))
++		inode->i_mode |= S_IXUGO;
++
++	inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
++	    & ~FATX_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
++	    ~FATX_SB(sb)->options.fs_umask;
++	return 0;
++}
++
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/misc.c kernel-source-2.4.27-2.4.27/fs/fatx/misc.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/misc.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/misc.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,272 @@
++/*
++ *  linux/fs/fatx/misc.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/fatx_fs.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/stat.h>
++#include <linux/time.h>
++#include <linux/types.h>
++
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++/*
++ * fatx_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 fatx_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).\n  %s\n", kdevname(s->s_dev), msg);
++	if (not_ro)
++		printk("  File system has been set read-only\n");
++}
++
++static int day_n[] = {  0, 31, 59, 90,120,151,181,212,243,273,304,334,0,0,0,0 };
++		/*    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
++
++extern struct timezone sys_tz;
++
++int fatx_date_dos2unix(unsigned short time,unsigned short date)
++{
++	int month,year,secs,days;
++
++	/* first subtract and mask after that... Otherwise, if
++	   date == 0, bad things happen */
++	month=  ((date >> 5) - 1) & 15;
++	year =  date >> 9;
++	days =  (date & 31)-1+day_n[month]+(year/4)+(year+30)*365;
++	//skipped and current leap years
++	days += ((year & 3) == 0 && month < 2 ? 0 : 1) + 7; 
++	
++	secs =  (time & 31)*2;		//seconds into curr minute
++	secs += 60*((time >> 5) & 63);	//minutes into curr hour
++	secs += 3600*(time >> 11);	//hours into curr day
++	secs += 86400*days;		//days (from 1.1.70)
++	
++	secs += sys_tz.tz_minuteswest*60;
++	return secs;
++}
++
++
++/*
++ * fat_add_cluster tries to allocate a new cluster and adds it to the
++ * file represented by inode.
++ */
++int fatx_add_cluster(struct inode *inode)
++{
++	struct super_block *sb = inode->i_sb;
++	int count, nr, limit, last, curr, file_cluster;
++	int cluster_size = FATX_SB(sb)->cluster_size;
++	int res = -ENOSPC;
++	
++	lock_fatx(sb);
++
++	if (FATX_SB(sb)->free_clusters == 0) {
++		unlock_fatx(sb);
++		return res;
++	}
++	limit = FATX_SB(sb)->clusters;
++	nr = limit; /* to keep GCC happy */
++	for (count = 0; count < limit; count++) {
++		nr = ((count + FATX_SB(sb)->prev_free) % limit) + 2;
++		if (fatx_access(sb, nr, -1) == 0)
++			break;
++	}
++	if (count >= limit) {
++		FATX_SB(sb)->free_clusters = 0;
++		unlock_fatx(sb);
++		return res;
++	}
++	
++	FATX_SB(sb)->prev_free = (count + FATX_SB(sb)->prev_free + 1) % limit;
++	fatx_access(sb, nr, EOF_FAT(sb));
++	if (FATX_SB(sb)->free_clusters != -1)
++		FATX_SB(sb)->free_clusters--;
++	
++	unlock_fatx(sb);
++	
++	/* 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.
++	*/
++	last = file_cluster = 0;
++	if ((curr = FATX_I(inode)->i_start) != 0) {
++		fatx_cache_lookup(inode, INT_MAX, &last, &curr);
++		file_cluster = last;
++		while (curr && curr != -1){
++			file_cluster++;
++			if (!(curr = fatx_access(sb, last = curr,-1))) {
++				fatx_fs_panic(sb, "File without EOF");
++				return res;
++			}
++		}
++	}
++	if (last) {
++		fatx_access(sb, last, nr);
++		fatx_cache_add(inode, file_cluster, nr);
++	} else {
++		FATX_I(inode)->i_start = nr;
++		FATX_I(inode)->i_logstart = nr;
++		mark_inode_dirty(inode);
++	}
++	if (file_cluster
++	    != inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) {
++		printk ("file_cluster badly computed!!! %d <> %ld\n",
++			file_cluster,
++			inode->i_blocks / cluster_size / (sb->s_blocksize / 512));
++		fatx_cache_inval_inode(inode);
++	}
++	inode->i_blocks += (1 << FATX_SB(sb)->cluster_bits) / 512;
++	return nr;
++}
++
++/* Convert linear UNIX date to a MS-DOS time/date pair. */
++
++void fatx_date_unix2dos(int unix_date,unsigned short *time,
++    unsigned short *date)
++{
++	int day,year,nl_day,month;
++
++	unix_date -= sys_tz.tz_minuteswest*60;
++
++	/* bound low end at Jan 1 GMT 00:00:00 2000. */
++	if (unix_date < ((30 * 365) + 7) * 24 * 60 * 60) {
++		unix_date = ((30 * 365) + 7) * 24 * 60 * 60;
++	}
++		
++	*time = (unix_date % 60)/2 + 			//seconds
++		(((unix_date/60) % 60) << 5) +		//minutes
++		(((unix_date/3600) % 24) << 11);	//hours
++	
++	day = unix_date/86400-(30 * 365 + 7);		//days (from 1.1.2000)
++	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);
++}
++
++struct buffer_head *fatx_extend_dir(struct inode *inode)
++{
++	struct super_block *sb = inode->i_sb;
++	int nr, sector, last_sector;
++	struct buffer_head *bh, *res = NULL;
++	int cluster_size = FATX_SB(sb)->cluster_size;
++
++	if (inode->i_ino == FATX_ROOT_INO)
++			return res;
++
++	nr = fatx_add_cluster(inode);
++	if (nr < 0)
++		return res;
++	
++	sector = FATX_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 = sb_getblk(sb, sector)))
++			printk("getblk failed\n");
++		else {
++			memset(bh->b_data, 0xFF, sb->s_blocksize);
++			mark_buffer_uptodate(bh, 1);
++			mark_buffer_dirty(bh);
++			if (!res)
++				res = bh;
++			else
++				if(bh) brelse(bh);
++		}
++	}
++	if (inode->i_size & (sb->s_blocksize - 1)) {
++		fatx_fs_panic(sb, "Odd directory size");
++		inode->i_size = (inode->i_size + sb->s_blocksize)
++			& ~(sb->s_blocksize - 1);
++	}
++	inode->i_size += 1 << FATX_SB(sb)->cluster_bits;
++	FATX_I(inode)->mmu_private += 1 << FATX_SB(sb)->cluster_bits;
++	mark_inode_dirty(inode);
++
++	return res;
++}
++
++/* 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.
++   AV. Most often we do it item-by-item. Makes sense to optimize.
++   AV. OK, there we go: if both bh and de are non-NULL we assume that we just
++   AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
++   AV. It's done in fatx_get_entry() (inlined), here the slow case lives.
++   AV. Additionally, when we return -1 (i.e. reached the end of directory)
++   AV. we make bh NULL. 
++ */
++
++int fatx_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
++    struct fatx_dir_entry **de, int *ino)
++{
++	struct super_block *sb = dir->i_sb;
++	struct fatx_sb_info *sbi = FATX_SB(sb);
++	int sector, offset;
++
++	while (1) {
++		offset = *pos;
++		PRINTK("get_entry offset %d\n",offset);
++		if (*bh)
++			if(*bh) brelse(*bh);
++		*bh = NULL;
++		if ((sector = fatx_bmap(dir,offset >> sb->s_blocksize_bits)) == -1)
++			return -1;
++		PRINTK("FATX: get_entry sector %d %p\n",sector,*bh);
++		PRINTK("FATX: get_entry sector apres brelse\n");
++		if (!sector)
++			return -1; /* beyond EOF */
++		*pos += sizeof(struct fatx_dir_entry);
++		if (!(*bh = sb_bread(sb, sector))) {
++			printk("Directory sread (sector 0x%x) failed\n",sector);
++			continue;
++		}
++		PRINTK("FATX: get_entry apres sread\n");
++
++		offset &= sb->s_blocksize - 1;
++		*de = (struct fatx_dir_entry *) ((*bh)->b_data + offset);
++		*ino = (sector << sbi->dir_per_block_bits) + (offset >> FATX_DIR_BITS);
++
++		return 0;
++	}
++}
++
++void lock_fatx(struct super_block *sb)
++{
++	down(&(FATX_SB(sb)->fat_lock));
++}
++
++void unlock_fatx(struct super_block *sb)
++{
++	up(&(FATX_SB(sb)->fat_lock));
++}
++
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/fatx/namei.c kernel-source-2.4.27-2.4.27/fs/fatx/namei.c
+--- kernel-source-2.4.27-2.4.27.old/fs/fatx/namei.c	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/fs/fatx/namei.c	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,409 @@
++/*
++ *  linux/fs/fatx/namei.c
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#define __NO_VERSION__
++#include <linux/module.h>
++
++#include <linux/sched.h>
++#include <linux/fatx_fs.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++
++#include <asm/uaccess.h>
++
++#define PRINTK(format, args...) do { if (fatx_debug) printk( format, ##args ); } while(0)
++
++/* Characters that are undesirable in an MS-DOS file name */
++  
++static char bad_chars[] = "*?<>|\";";
++
++/*
++ * Formats a FATX file name. Rejects invalid names. 
++ */
++
++static int fatx_format_name( const char *name,int len,char *out_name )
++{
++	int i;
++	char trash[FATX_MAX_NAME_LENGTH];
++
++	if (len > FATX_MAX_NAME_LENGTH) return -EINVAL;
++	
++	if (out_name == NULL) out_name = trash;
++	
++	memset(out_name,0xFF,FATX_MAX_NAME_LENGTH);
++	
++	//check for bad characters in name
++	for(i=0; i<len; i++) {
++		if (strchr(bad_chars,name[i])) return -EINVAL;
++		out_name[i] = name[i];
++	}
++
++	return 0;
++}
++
++/*
++ * Locates a directory entry.  Uses unformatted name. 
++ */
++
++static int fatx_find( struct inode *dir, const char *name, int len, struct buffer_head **bh, 
++		struct fatx_dir_entry **de, int *ino )
++{
++	//verify its a valid name
++	if (fatx_format_name(name,len,NULL) < 0) return -ENOENT;
++
++	PRINTK("FATX: fatx_find\n");
++	
++	//find the name in the directory
++	return fatx_scan(dir,name,len,bh,de,ino);
++
++}
++
++/* 
++ * Get inode using directory and name 
++ */
++
++struct dentry *fatx_lookup(struct inode *dir,struct dentry *dentry)
++{
++	struct super_block *sb = dir->i_sb;
++	struct inode *inode = NULL;
++	struct fatx_dir_entry *de;
++	struct buffer_head *bh = NULL;
++	int ino,res;
++	
++	PRINTK("FATX: fatx_lookup\n");
++
++	res = fatx_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
++			&de, &ino);
++
++	if (res == -ENOENT)
++		goto add;
++	if (res < 0)
++		goto out;
++	inode = fatx_build_inode(sb, de, ino, &res);
++	if (res)
++		goto out;
++add:
++	d_add(dentry, inode);
++	res = 0;
++out:
++	if (bh) brelse(bh);
++	return ERR_PTR(res);
++}
++
++/* 
++ * Creates a directory entry (name is already formatted). 
++ */
++
++static int fatx_add_entry(
++		struct inode *dir, 
++		const char *name,
++		int len,
++		struct buffer_head **bh,
++		struct fatx_dir_entry **de,
++		int *ino,
++		int is_dir )
++{
++	int res;
++
++	if ((res = fatx_do_add_entry(dir, bh, de, ino))<0)
++		return res;
++	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++	mark_inode_dirty(dir);
++	memset((*de)->name,0xFF,FATX_MAX_NAME_LENGTH);
++	memcpy((*de)->name,name,len);
++	(*de)->name_length = len;
++	(*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
++	(*de)->start = 0;
++	fatx_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
++	(*de)->size = 0;
++	mark_buffer_dirty(*bh);
++	return 0;
++}
++
++/* 
++ * Create a file 
++ */
++
++int fatx_create(struct inode *dir,struct dentry *dentry,int mode)
++{
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++	struct inode *inode;
++	int ino,res;
++	const char *name = dentry->d_name.name;
++	int name_length = dentry->d_name.len;
++	char szFormatName[FATX_MAX_NAME_LENGTH];
++	
++	res = fatx_format_name(name,name_length,szFormatName);
++	if (res < 0)
++		return res;
++	
++	if (fatx_scan(dir,name,name_length,&bh,&de,&ino) >= 0) {
++		if(bh) brelse(bh);
++		return -EINVAL;
++ 	}
++	inode = NULL;
++	res = fatx_add_entry(dir, szFormatName, name_length, &bh, &de, &ino, 0);
++	if (res)
++		return res;
++	inode = fatx_build_inode(dir->i_sb, de, ino, &res);
++	if(bh) brelse(bh);
++	if (!inode)
++		return res;
++	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
++	mark_inode_dirty(inode);
++	d_instantiate(dentry, inode);
++	return 0;
++}
++
++/*
++ * Remove a directory 
++ */
++
++int fatx_rmdir(struct inode *dir, struct dentry *dentry)
++{
++	struct inode *inode = dentry->d_inode;
++	int res,ino;
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++
++	bh = NULL;
++	res = fatx_find(dir, dentry->d_name.name, dentry->d_name.len,
++				&bh, &de, &ino);
++	if (res < 0)
++		goto rmdir_done;
++	/*
++	 * Check whether the directory is not in use, then check
++	 * whether it is empty.
++	 */
++	res = fatx_dir_empty(inode);
++	if (res)
++		goto rmdir_done;
++
++	de->name_length = DELETED_FLAG;
++	mark_buffer_dirty(bh);
++	fatx_detach(inode);
++	inode->i_nlink = 0;
++	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++	dir->i_nlink--;
++	mark_inode_dirty(inode);
++	mark_inode_dirty(dir);
++	res = 0;
++
++rmdir_done:
++	if(bh) brelse(bh);
++	return res;
++}
++
++/*
++ * Make a directory 
++ */
++
++int fatx_mkdir(struct inode *dir,struct dentry *dentry,int mode)
++{
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++	struct inode *inode;
++	int res;
++	const char *name = dentry->d_name.name;
++	int name_length = dentry->d_name.len;
++	int ino;
++	char szFormatName[FATX_MAX_NAME_LENGTH];
++	
++	res = fatx_format_name(name,name_length,szFormatName);
++	if (res < 0)
++		return res;
++	if (fatx_scan(dir,name,name_length,&bh,&de,&ino) >= 0)
++		goto out_exist;
++
++	res = fatx_add_entry(dir, szFormatName, name_length, &bh, &de, &ino, 1);
++	if (res)
++		goto out_unlock;
++	inode = fatx_build_inode(dir->i_sb, de, ino, &res);
++	if (!inode) {
++		if(bh) brelse(bh);
++		goto out_unlock;
++	}
++	res = 0;
++
++	dir->i_nlink++;
++	inode->i_nlink = 2; /* no need to mark them dirty */
++
++	res = fatx_new_dir(inode, dir);
++	if (res)
++		goto mkdir_error;
++
++	if(bh) brelse(bh);
++	d_instantiate(dentry, inode);
++	res = 0;
++
++out_unlock:
++	return res;
++
++mkdir_error:
++	printk(KERN_WARNING "fatx_mkdir: error=%d, attempting cleanup\n", res);
++	inode->i_nlink = 0;
++	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++	dir->i_nlink--;
++	mark_inode_dirty(inode);
++	mark_inode_dirty(dir);
++	de->name_length = DELETED_FLAG;
++	mark_buffer_dirty(bh);
++	if(bh) brelse(bh);
++	fatx_detach(inode);
++	iput(inode);
++	goto out_unlock;
++
++out_exist:
++	if(bh) brelse(bh);
++	res = -EINVAL;
++	goto out_unlock;
++}
++
++/*
++ * Unlink a file 
++ */
++
++int fatx_unlink( struct inode *dir, struct dentry *dentry)
++{
++	struct inode *inode = dentry->d_inode;
++	int res,ino;
++	struct buffer_head *bh;
++	struct fatx_dir_entry *de;
++
++	bh = NULL;
++	res = fatx_find(dir, dentry->d_name.name, dentry->d_name.len,
++			&bh, &de, &ino);
++	if (res < 0)
++		goto unlink_done;
++
++	de->name_length = DELETED_FLAG;
++	mark_buffer_dirty(bh);
++	fatx_detach(inode);
++	if(bh) brelse(bh);
++	inode->i_nlink = 0;
++	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++	mark_inode_dirty(inode);
++	mark_inode_dirty(dir);
++	res = 0;
++unlink_done:
++	return res;
++}
++
++static int do_fatx_rename(struct inode *old_dir, struct dentry *old_dentry,
++		struct inode *new_dir, struct dentry *new_dentry,
++		struct buffer_head *old_bh, struct fatx_dir_entry *old_de,
++		int old_ino )
++{
++	struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
++	struct fatx_dir_entry *new_de,*dotdot_de;
++	struct inode *old_inode,*new_inode;
++	int new_ino, dotdot_ino;
++	int error;
++	int is_dir;
++	const char *new_name = new_dentry->d_name.name;
++	int new_name_len = new_dentry->d_name.len;
++
++	PRINTK("FATX: do_fatx_rename: entered\n");
++	
++	old_inode = old_dentry->d_inode;
++	new_inode = new_dentry->d_inode;
++	is_dir = S_ISDIR(old_inode->i_mode);
++
++	error = fatx_scan(new_dir,new_name,new_name_len,&new_bh,&new_de,&new_ino);
++	if (error>=0 &&!new_inode)
++		goto degenerate_case;
++
++	if (!new_bh) {
++		error = fatx_add_entry(	new_dir, new_name, new_name_len, &new_bh, 
++				&new_de, &new_ino, is_dir );
++		if (error)
++			goto out;
++	}
++	new_dir->i_version = ++event;
++
++	/* There we go */
++
++	if (new_inode)
++		fatx_detach(new_inode);
++	old_de->name_length = DELETED_FLAG;
++	mark_buffer_dirty(old_bh);
++	fatx_detach(old_inode);
++	fatx_attach(old_inode, new_ino);
++	FATX_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
++	mark_inode_dirty(old_inode);
++	old_dir->i_version = ++event;
++	old_dir->i_ctime = old_dir->i_mtime = old_dir->i_atime = CURRENT_TIME;
++	mark_inode_dirty(old_dir);
++	if (new_inode) {
++		new_inode->i_nlink--;
++		new_inode->i_ctime = new_inode->i_atime = CURRENT_TIME;
++		mark_inode_dirty(new_inode);
++	}
++	if (dotdot_bh) {
++		dotdot_de->start = CT_LE_W(FATX_I(new_dir)->i_logstart);
++
++		mark_buffer_dirty(dotdot_bh);
++		old_dir->i_nlink--;
++		mark_inode_dirty(old_dir);
++		if (new_inode) {
++			new_inode->i_nlink--;
++			mark_inode_dirty(new_inode);
++		} else {
++			new_dir->i_nlink++;
++			mark_inode_dirty(new_dir);
++		}
++	}
++	error = 0;
++out:
++	if(new_bh) brelse(new_bh);
++	if(dotdot_bh) brelse(dotdot_bh);
++	PRINTK("FATX: do_fatx_rename: leaving (normal)\n");
++	return error;
++
++degenerate_case:
++	error = -EINVAL;
++	if (new_de!=old_de)
++		goto out;
++	FATX_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
++	mark_inode_dirty(old_inode);
++	old_dir->i_version = ++event;
++	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
++	mark_inode_dirty(old_dir);
++	PRINTK("FATX: do_fatx_rename: leaving (degenerate)\n");
++	return 0;
++}
++
++/*
++ * Rename, a wrapper for rename_same_dir & rename_diff_dir 
++ */
++
++int fatx_rename(struct inode *old_dir,struct dentry *old_dentry,
++		 struct inode *new_dir,struct dentry *new_dentry)
++{
++	struct buffer_head *old_bh;
++	struct fatx_dir_entry *old_de;
++	int old_ino, error;
++
++	error = fatx_format_name(old_dentry->d_name.name,old_dentry->d_name.len,NULL);
++	if (error < 0)
++		goto rename_done;
++	error = fatx_format_name(new_dentry->d_name.name,new_dentry->d_name.len,NULL);
++	if (error < 0)
++		goto rename_done;
++
++	error = fatx_scan(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_bh, &old_de, &old_ino );
++	if (error < 0)
++		goto rename_done;
++
++	error = do_fatx_rename( old_dir, old_dentry, new_dir, new_dentry,
++			old_bh, old_de, (ino_t)old_ino );
++	if(old_bh) brelse(old_bh);
++
++rename_done:
++	return error;
++}
+diff -Nur kernel-source-2.4.27-2.4.27.old/fs/nls/Config.in kernel-source-2.4.27-2.4.27/fs/nls/Config.in
+--- kernel-source-2.4.27-2.4.27.old/fs/nls/Config.in	2003-08-25 13:44:43.000000000 +0200
++++ kernel-source-2.4.27-2.4.27/fs/nls/Config.in	2004-09-13 22:18:04.000000000 +0200
+@@ -13,7 +13,8 @@
+ if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
+ 	-o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" \
+ 	-o "$CONFIG_SMB_NLS" = "y" -o "$CONFIG_JFS_FS" != "n" \
+-	-o "$CONFIG_BEFS_FS" != "n" -o "$CONFIG_HFSPLUS_FS" != "n" ]; then
++	-o "$CONFIG_BEFS_FS" != "n" -o "$CONFIG_HFSPLUS_FS" != "n" \
++	-o "$CONFIG_FATX_FS" != "n" ]; then
+   define_bool CONFIG_NLS y
+ else
+   define_bool CONFIG_NLS n
+diff -Nur kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs.h kernel-source-2.4.27-2.4.27/include/linux/fatx_fs.h
+--- kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs.h	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/include/linux/fatx_fs.h	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,238 @@
++#ifndef _LINUX_FATX_FS_H
++#define _LINUX_FATX_FS_H
++
++#ifdef __KERNEL__
++
++/*
++ *
++ *  The FATX filesystem constants/structures
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++#include <linux/fs.h>
++#include <linux/stat.h>
++#include <linux/fd.h>
++#include <linux/dcache.h>
++#include <linux/nls.h>
++
++#include <asm/byteorder.h>
++
++#define FATX_ROOT_INO  1 /* == MINIX_ROOT_INO */
++
++#define FATX_CACHE    8 /* FAT cache size */
++
++#define FATX_SB(s) (&((s)->u.fatx_sb))
++#define FATX_I(i) (&((i)->u.fatx_i))
++
++//#define FATX32_MAX_NON_LFS     ((1ULL<<32) - 1)
++#define FATX32_MAX_NON_LFS     ((1UL<<32) - 1)
++#define FATX16_MAX_NON_LFS     ((1UL<<30) - 1)
++	
++#define CLUSTER_SIZE 32
++#define ATTR_RO      1  /* read-only */
++#define ATTR_HIDDEN  2  /* hidden */
++#define ATTR_SYS     4  /* system */
++#define ATTR_DIR     16 /* directory */
++#define ATTR_ARCH    0  /* archived */
++#define ATTR_UNUSED  (ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN)
++
++#define ATTR_NONE    0 /* no attribute bits */
++
++#define FATX_BOOTBLOCK_MAGIC cpu_to_le32(0x58544146)
++#define FATX_MAX_NAME_LENGTH 42
++#define FATX_DIR_BITS 6
++
++#define DELETED_FLAG 0xe5 /* marks file as deleted when in name_length */
++
++//FF == end of directory info, E5 == deleted entry
++#define FATX_IS_FREE(de) ((de)->name_length==DELETED_FLAG)
++#define FATX_END_OF_DIR(de) ((de)->name_length==0xFF)
++
++#define IS_FREE(n) (!*(n) || *(const unsigned char *) (n) == DELETED_FLAG)
++
++#define CF_LE_W(v) le16_to_cpu(v)
++#define CF_LE_L(v) le32_to_cpu(v)
++#define CT_LE_W(v) cpu_to_le16(v)
++#define CT_LE_L(v) cpu_to_le32(v)
++
++#define EOC_FAT16 0xFFF8	// end of chain marker
++#define EOC_FAT32 0xFFFFFFF8	// end of chain marker
++#define EOF_FAT16 0xFFF8	// end of file marker
++#define EOF_FAT32 0xFFFFFFF8	// end of file marker
++#define EOF_FAT(s) (FATX_SB(s)->fat_bits == 32 ? EOF_FAT32 : EOF_FAT16)
++
++#define FATX_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)
++
++/* Convert attribute bits and a mask to the UNIX mode. */
++#define FATX_MKMODE(a,m) (m & (a & ATTR_RO ? S_IRUGO|S_IXUGO : S_IRWXUGO))
++
++/* Convert the UNIX mode to FATX attribute bits. */
++#define FATX_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
++
++struct fatx_boot_sector {
++        __u32	magic;		/* "FATX" */
++	__u32	volume_id;	/* Volume ID */
++        __u32	cluster_size;	/* sectors/cluster */
++	__u16	fats;		/* number of FATs */
++	__u32	unknown;
++};
++
++struct fatx_dir_entry {
++        __u8	name_length;	/* length of filename (bytes) */
++	__u8	attr;		/* attribute bits */
++        __s8	name[42];	/* filename */
++	__u32	start;		/* first cluster */
++	__u32	size;		/* file size (in bytes) */
++	__u16	time,date;	/* time, date */
++	__u16	ctime,cdate;	/* Creation time */
++	__u16	atime,adate;	/* Last access time */
++};
++
++struct fatx_boot_fsinfo {
++	__u32   signature1;	/* 0x41615252L */
++	__u32   reserved1[120];	/* Nothing as far as I can tell */
++	__u32   signature2;	/* 0x61417272L */
++	__u32   free_clusters;	/* Free cluster count.  -1 if unknown */
++	__u32   next_cluster;	/* Most recently allocated cluster.
++				 * Unused under Linux. */
++	__u32   reserved2[4];
++};
++
++struct fatx_cache {
++	kdev_t device; /* device number. 0 means unused. */
++	int start_cluster; /* first cluster of the chain. */
++	int file_cluster; /* cluster number in the file. */
++	int disk_cluster; /* cluster number on disk. */
++	struct fatx_cache *next; /* next cache entry */
++};
++
++/* fatx/cache.c */
++extern int fatx_access(struct super_block *sb, int nr, int new_value);
++extern unsigned long fatx_bmap(struct inode *inode, unsigned long sector);
++extern void fatx_cache_init(void);
++extern void fatx_cache_lookup(struct inode *inode, int cluster, int *f_clu,
++			     int *d_clu);
++extern void fatx_cache_add(struct inode *inode, int f_clu, int d_clu);
++extern void fatx_cache_inval_inode(struct inode *inode);
++extern void fatx_cache_inval_dev(kdev_t device);
++extern int fatx_get_cluster(struct inode *inode, int cluster);
++extern int fatx_free(struct inode *inode, int skip);
++
++/* fatx/dir.c */
++extern struct file_operations fat_dir_operations;
++extern int fatx_readdir(struct file *filp, void *dirent, filldir_t filldir);
++extern int fatx_dir_empty(struct inode *dir);
++extern int fatx_add_entries(struct inode *dir, int slots, struct buffer_head **bh,
++			   struct fatx_dir_entry **de, int *ino);
++extern int fatx_new_dir(struct inode *dir, struct inode *parent);
++
++/* fat/file.c */
++extern struct file_operations fatx_file_operations;
++extern struct inode_operations fatx_file_inode_operations;
++extern ssize_t fatx_file_read(struct file *filp, char *buf, size_t count,
++			     loff_t *ppos);
++extern int fatx_get_block(struct inode *inode, long iblock,
++			 struct buffer_head *bh_result, int create);
++extern ssize_t fatx_file_write(struct file *filp, const char *buf, size_t count,
++			      loff_t *ppos);
++extern void fatx_truncate(struct inode *inode);
++
++/* fat/inode.c */
++extern void fatx_hash_init(void);
++extern void fatx_attach(struct inode *inode, int i_pos);
++extern void fatx_detach(struct inode *inode);
++extern struct inode *fatx_iget(struct super_block *sb, int i_pos);
++extern struct inode *fatx_build_inode(
++		struct super_block *sb,
++		struct fatx_dir_entry *de, 
++		int ino, 
++		int *res);
++
++extern void fatx_delete_inode(struct inode *inode);
++extern void fatx_clear_inode(struct inode *inode);
++extern void fatx_put_super(struct super_block *sb);
++
++typedef int (*fatx_boot_block_parse_func)(
++		struct super_block *sb, 
++		struct buffer_head *bh );
++typedef void (*fatx_read_root_func)(struct inode *inode);
++
++extern int fatx_statfs(struct super_block *sb, struct statfs *buf);
++extern void fatx_write_inode(struct inode *inode, int wait);
++extern int fatx_notify_change(struct dentry * dentry, struct iattr * attr);
++
++extern struct address_space_operations fatx_aops;
++extern spinlock_t fatx_inode_lock;
++
++
++/* fatx/namei.c - these are for the xbox's FATX */
++extern struct dentry *fatx_lookup(struct inode *dir, struct dentry *);
++extern int fatx_create(struct inode *dir, struct dentry *dentry, int mode);
++extern int fatx_rmdir(struct inode *dir, struct dentry *dentry);
++extern int fatx_unlink(struct inode *dir, struct dentry *dentry);
++extern int fatx_mkdir(struct inode *dir, struct dentry *dentry, int mode);
++extern int fatx_rename(struct inode *old_dir, struct dentry *old_dentry,
++		       struct inode *new_dir, struct dentry *new_dentry);
++extern struct super_block *fatx_read_super(struct super_block *sb, void *data,
++					   int silent);
++
++/* fatx/fatxfs_syms.c */
++extern unsigned int fatx_debug;
++
++extern struct file_system_type fatx_fs_type;
++
++extern int fatx_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
++		struct fatx_dir_entry **de,int *ino );
++
++extern int fatx_scan(struct inode *dir, const char *name, int name_length,
++		struct buffer_head **res_bh,struct fatx_dir_entry **res_de,
++		int *ino);
++		
++/* miscelaneous support code for fatx fs */
++extern int fatx_date_dos2unix( unsigned short, unsigned short );
++extern void fatx_date_unix2dos( int, unsigned short *, unsigned short * );
++
++static inline unsigned char fatx_tolower(struct nls_table *t, unsigned char c)
++{
++	unsigned char nc = t->charset2lower[c];
++
++	return nc ? nc : c;
++}
++
++static inline unsigned char fatx_toupper(struct nls_table *t, unsigned char c)
++{
++	unsigned char nc = t->charset2upper[c];
++
++	return nc ? nc : c;
++}
++
++static inline int fatx_strnicmp(struct nls_table *t, const unsigned char *s1,
++		const unsigned char *s2, int len )
++{
++	while(len--) {
++		if (fatx_tolower(t, *s1++) != fatx_tolower(t, *s2++))
++			return 1;
++	}
++	return 0;
++}
++
++/* directory code for fatx fs */
++extern int fatx_do_add_entry(struct inode *dir,	struct buffer_head **bh,
++		struct fatx_dir_entry **de, int *ino);
++		
++extern int fatx_dir_empty(struct inode *dir);
++extern int fatx_subdirs(struct inode *dir);
++		
++extern struct file_operations fatx_dir_operations;
++#endif /* __KERNEL__ */
++
++extern int fatx_access(struct super_block *sb, int nr, int new_value);
++extern void fatx_fs_panic(struct super_block *s, const char *msg);
++extern struct buffer_head *fatx_extend_dir(struct inode *inode);
++extern int fatx_add_cluster(struct inode *inode);
++extern void lock_fatx(struct super_block *sb);
++extern void unlock_fatx(struct super_block *sb);
++
++#endif /* _LINUX_FATX_FS_H */
+diff -Nur kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs_i.h kernel-source-2.4.27-2.4.27/include/linux/fatx_fs_i.h
+--- kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs_i.h	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/include/linux/fatx_fs_i.h	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,23 @@
++#ifndef _FATX_FS_I
++#define _FATX_FS_I
++
++/*
++ * 
++ *  FATX file system inode data in memory
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++struct fatx_inode_info {
++	unsigned long mmu_private;
++	int i_start;	/* first cluster or 0 */
++	int i_logstart;	/* logical first cluster */
++	int i_attrs;	/* unused attribute bits */
++	int i_ctime_ms;	/* unused change time in milliseconds */
++	int i_location;	/* on-disk position of directory entry or 0 */
++	struct inode *i_fat_inode;	/* struct inode of this one */
++	struct list_head i_fat_hash;	/* hash by i_location */
++};
++
++#endif
+diff -Nur kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs_sb.h kernel-source-2.4.27-2.4.27/include/linux/fatx_fs_sb.h
+--- kernel-source-2.4.27-2.4.27.old/include/linux/fatx_fs_sb.h	1970-01-01 01:00:00.000000000 +0100
++++ kernel-source-2.4.27-2.4.27/include/linux/fatx_fs_sb.h	2004-09-13 22:18:04.000000000 +0200
+@@ -0,0 +1,46 @@
++#ifndef _FATX_FS_SB
++#define _FATX_FS_SB
++
++/*
++ *  FATX file system in-core superblock data
++ *
++ *  Written 2003 by Edgar Hucek and Lehner Franz
++ *
++ */
++
++struct fatx_mount_options {
++	uid_t fs_uid;
++	gid_t fs_gid;
++	unsigned short fs_umask;
++	unsigned short codepage;  /* Codepage for shortname conversions */
++	unsigned short shortname; /* flags for shortname display/create rule */
++	unsigned char name_check; /* r = relaxed, n = normal, s = strict */
++	unsigned char conversion; /* b = binary, t = text, a = auto */
++	unsigned quiet:1;         /* set = fake successful chmods and chowns */
++};
++
++struct fatx_sb_info {
++	unsigned short cluster_size; /* sectors/cluster */
++	unsigned short cluster_bits; /* sectors/cluster */
++	unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
++	unsigned short fat_start;
++	unsigned long fat_length;    /* FAT start & length (sec.) */
++	unsigned long dir_start;
++	unsigned short dir_entries;  /* root dir start & entries */
++	unsigned long data_start;    /* first data sector */
++	unsigned long clusters;      /* number of clusters */
++	unsigned long root_cluster;  /* first cluster of the root directory */
++	unsigned long fsinfo_sector; /* FAT32 fsinfo offset from start of disk */
++	struct semaphore fat_lock;
++	int prev_free;               /* previously returned free cluster number */
++	int free_clusters;           /* -1 if undefined */
++	struct fatx_mount_options options;
++	struct nls_table *nls_disk;  /* Codepage used on disk */
++	struct nls_table *nls_io;    /* Charset used for input and display */
++	void *dir_ops;		     /* Opaque; default directory operations */
++	void *private_data;
++	int dir_per_block;	     /* dir entries per block */
++	int dir_per_block_bits;	     /* log2(dir_per_block) */
++};
++
++#endif
+diff -Nur kernel-source-2.4.27-2.4.27.old/include/linux/fs.h kernel-source-2.4.27-2.4.27/include/linux/fs.h
+--- kernel-source-2.4.27-2.4.27.old/include/linux/fs.h	2004-09-13 22:17:09.000000000 +0200
++++ kernel-source-2.4.27-2.4.27/include/linux/fs.h	2004-09-13 22:18:04.000000000 +0200
+@@ -326,6 +326,7 @@
+ #include <linux/usbdev_fs_i.h>
+ #include <linux/jffs2_fs_i.h>
+ #include <linux/cramfs_fs_sb.h>
++#include <linux/fatx_fs_i.h>
+ 
+ /*
+  * Attribute flags.  These should be or-ed together to figure out what
+@@ -521,6 +522,7 @@
+ 		struct socket			socket_i;
+ 		struct usbdev_inode_info        usbdev_i;
+ 		struct jffs2_inode_info		jffs2_i;
++		struct fatx_inode_info          fatx_i;
+ 		void				*generic_ip;
+ 	} u;
+ };
+@@ -735,6 +737,7 @@
+ #include <linux/usbdev_fs_sb.h>
+ #include <linux/cramfs_fs_sb.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/fatx_fs_sb.h>
+ 
+ extern struct list_head super_blocks;
+ extern spinlock_t sb_lock;
+@@ -794,6 +797,7 @@
+ 		struct usbdev_sb_info   usbdevfs_sb;
+ 		struct jffs2_sb_info	jffs2_sb;
+ 		struct cramfs_sb_info	cramfs_sb;
++		struct fatx_sb_info     fatx_sb;
+ 		void			*generic_sbp;
+ 	} u;
+ 	/*
diff -Nur kernel-source-2.4.27-2.4.27.old/debian/patches/series/2.4.27-6 kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6
--- kernel-source-2.4.27-2.4.27.old/debian/patches/series/2.4.27-6	1970-01-01 01:00:00.000000000 +0100
+++ kernel-source-2.4.27-2.4.27/debian/patches/series/2.4.27-6	2004-09-14 23:28:06.000000000 +0200
@@ -0,0 +1 @@
++ 076_fatx_filesystem.diff
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/386 kernel-image-2.4.27-i386-2.4.27/config/386
--- kernel-image-2.4.27-i386-2.4.27.old/config/386	2004-08-25 02:30:29.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/386	2004-09-15 00:14:23.000000000 +0200
@@ -1706,6 +1706,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/586tsc kernel-image-2.4.27-i386-2.4.27/config/586tsc
--- kernel-image-2.4.27-i386-2.4.27.old/config/586tsc	2004-08-25 02:30:30.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/586tsc	2004-09-15 00:14:33.000000000 +0200
@@ -1713,6 +1713,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/686 kernel-image-2.4.27-i386-2.4.27/config/686
--- kernel-image-2.4.27-i386-2.4.27.old/config/686	2004-08-25 02:30:30.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/686	2004-09-15 00:14:38.000000000 +0200
@@ -1715,6 +1715,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/686-smp kernel-image-2.4.27-i386-2.4.27/config/686-smp
--- kernel-image-2.4.27-i386-2.4.27.old/config/686-smp	2004-08-25 02:30:29.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/686-smp	2004-09-15 00:14:43.000000000 +0200
@@ -1716,6 +1716,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/default kernel-image-2.4.27-i386-2.4.27/config/default
--- kernel-image-2.4.27-i386-2.4.27.old/config/default	2004-08-25 02:30:30.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/default	2004-09-15 00:15:25.000000000 +0200
@@ -724,6 +724,7 @@
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
+# CONFIG_FATX_FS is not set
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/k6 kernel-image-2.4.27-i386-2.4.27/config/k6
--- kernel-image-2.4.27-i386-2.4.27.old/config/k6	2004-08-25 02:30:29.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/k6	2004-09-15 00:14:51.000000000 +0200
@@ -1708,6 +1708,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/k7 kernel-image-2.4.27-i386-2.4.27/config/k7
--- kernel-image-2.4.27-i386-2.4.27.old/config/k7	2004-08-25 02:30:30.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/k7	2004-09-15 00:14:55.000000000 +0200
@@ -1715,6 +1715,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set
diff -ur kernel-image-2.4.27-i386-2.4.27.old/config/k7-smp kernel-image-2.4.27-i386-2.4.27/config/k7-smp
--- kernel-image-2.4.27-i386-2.4.27.old/config/k7-smp	2004-08-25 02:30:30.000000000 +0200
+++ kernel-image-2.4.27-i386-2.4.27/config/k7-smp	2004-09-15 00:15:02.000000000 +0200
@@ -1716,6 +1716,7 @@
 CONFIG_MSDOS_FS=m
 CONFIG_UMSDOS_FS=m
 CONFIG_VFAT_FS=m
+CONFIG_FATX_FS=m
 CONFIG_EFS_FS=m
 # CONFIG_JFFS_FS is not set
 # CONFIG_JFFS2_FS is not set

--- End Message ---
--- Begin Message ---
as explained here we'd expect such patches to go through
upstream, as they have the review process for it
-> http://wiki.debian.org/DebianKernelPatchAcceptanceGuidelines

reading their wiki they speak about fs corruptions,
so it seems needed.

anyway thanks for the report.

-- 
maks


--- End Message ---

Reply to: