diff -u --recursive --new-file v2.3.1/linux/CREDITS linux/CREDITS
--- v2.3.1/linux/CREDITS	Fri May 14 18:55:11 1999
+++ linux/CREDITS	Fri May 14 08:59:54 1999
@@ -661,7 +661,7 @@
 E: rgooch@atnf.csiro.au
 D: parent process death signal to children
 D: prctl() syscall
-D: /proc/mtrr support to manipulate MTRRs on Pentium Pro's
+D: /proc/mtrr support to manipulate MTRRs on Intel P6 family
 S: CSIRO Australia Telescope National Facility
 S: P.O. Box 76, Epping
 S: New South Wales, 2121
@@ -758,6 +758,13 @@
 S: 77 Clarence Mews
 S: London SE16 1GD
 S: United Kingdom
+
+N: Bart Hartgers
+E: bart@etpmod.phys.tue.nl
+D: MTRR emulation with Centaur MCRs
+S: Gen Stedmanstraat 212
+S: 5623 HZ Eindhoven
+S: The Netherlands
 
 N: Kai Harrekilde-Petersen
 E: khp@dolphinics.no
diff -u --recursive --new-file v2.3.1/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.3.1/linux/Documentation/Configure.help	Fri May 14 18:55:11 1999
+++ linux/Documentation/Configure.help	Fri May 14 08:59:54 1999
@@ -8855,6 +8855,9 @@
 
   The AMD K6-2 (stepping 8 and above) and K6-3 processors have two
   MTRRs. These are supported.
+  
+  The Centaur C6 (WinChip) has 8 MCRs, allowing write-combining. These
+  are supported.
 
   Saying Y here also fixes a problem with buggy SMP BIOSes which only
   set the MTRRs for the boot CPU and not the secondary CPUs. This can
diff -u --recursive --new-file v2.3.1/linux/Documentation/filesystems/hpfs.txt linux/Documentation/filesystems/hpfs.txt
--- v2.3.1/linux/Documentation/filesystems/hpfs.txt	Wed Jun 24 14:30:07 1998
+++ linux/Documentation/filesystems/hpfs.txt	Thu May 13 23:48:20 1999
@@ -1,27 +1,271 @@
-Linux can read, but not write, OS/2 HPFS partitions.
+Read/Write HPFS 1.98b
+1998-1999, Mikulas Patocka
 
-Mount options are the same as for msdos partitions.
+email: mikulas@artax.karlin.mff.cuni.cz
+homepage: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
 
-  uid=nnn	All files in the partition will be owned by user id nnn.
-  gid=nnn	All files in the partition will be in group nnn.
-  umask=nnn	The permission mask (see umask(1)) for the partition.
-  conv=binary   Data is returned exactly as is, with CRLFs.  [default]
-  conv=text     (Carriage return, line feed) is replaced with newline.
-  conv=auto     Chooses, file by file, conv=binary or conv=text (by guessing)
+CREDITS:
+Chris Smith, 1993, original read-only HPFS, some code and hpfs structures file
+	is taken from it
+Jacques Gelinas, MSDos mmap, Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+Werner Almesberger, 1992, 1993, MSDos option parser & CR/LF conversion
 
-There are mount options unique to HPFS.
+Mount options
 
-  case=lower	Convert file names to lower case.  [default]
-  case=asis     Return file names as is, in mixed case.
+uid=xxx,gid=xxx,umask=xxx (default uid=gid=0 umask=default_system_umask)
+	Set owner/group/mode for files that do not have it specified in extended
+	attributes. Mode is inverted umask - for example umask 027 gives owner
+	all permission, group read permission and anybody else no access. Note
+	that for files mode is anded with 0666. If you want files to have 'x'
+	rights, you must use extended attributes.
+case=lower,asis (default asis)
+	File name lowercasing in readdir.
+conv=binary,text,auto (default binary)
+	CR/LF -> LF conversion, if auto, decision is made according to extension
+	- there is a list of text extensions (I thing it's better to not convert
+	text file than to damage binary file). If you want to change that list,
+	change it in the source. Original readonly HPFS contained some strange
+	heuristic alghoritm that I removed. I thing it's danger to let the
+	computer decide whether file is text or binary. For example, DJGPP
+	binaries contain small text message at the beginning and they could be
+	misidentified and damaged under some circumstances.
+check=none,normal,strict (default normal)
+	Check level. Selecting none will cause only little speedup and big
+	danger. I tried to write it so that it won't crash if check=normal on
+	corrupted filesystems. check=strict means many superflous checks -
+	used for debugging (for example it checks if file is allocated in
+	bitmaps when accessing it).
+errors=continue,remount-ro,panic (default remount-ro)
+	Behaviour when filesystem errors found.
+chkdsk=no,errors,always (default errors)
+	When to mark filesystem dirty so that OS/2 checks it.
+eas=no,ro,rw (default rw)
+	What to do with extended attributes. 'no' - ignore them and use always
+	values specified in uid/gid/mode options. 'ro' - read extended
+	attributes but do not create them. 'rw' - create extended attributes
+	when you use chmod/chown/chgrp/mknod/ln -s on the filesystem.
+timeshift=(-)nnn (default 0)
+	Shifts the time by nnn seconds. For example, if you see under linux
+	one hour more, than under os/2, use timeshift=-3600.
 
-  nocheck	Proceed even if "Improperly stopped flag is set"
 
-Case is not significant in filename matching, like real HPFS.
+File names
 
+As in OS/2, filenames are case insensitive. However, shell thinks that names
+are case sensitive, so for example when you create a file FOO, you can use
+'cat FOO', 'cat Foo', 'cat foo' or 'cat F*' but not 'cat f*'. Note, that you
+also won't be able to compile linux kernel (and maybe other things) on HPFS
+because kernel creates different files with names like bootsect.S and
+bootsect.s. When searching for file thats name has characters >= 128, codepages
+are used - see below.
+OS/2 ignores dots and spaces at the end of file name, so this driver does. If
+you create 'a. ...', the file 'a' will be created, but you can still access it
+under names 'a.', 'a..', 'a .  . . ' etc.
 
-Command line example
-    mkdir -p /os2/c
-    mount -t hpfs -o uid=100,gid=100 /dev/sda6 /os2/c
 
-/etc/fstab example
-    /dev/sdb5	/d/f    hpfs    ro,uid=402,gid=402,umask=002
+Extended attributes
+
+On HPFS partion, OS/2 can associate to each file a special information called
+extended attributes. Extended attributes are pairs of (key,value) where key is
+an ascii string identifying that attribute and value is any string of bytes of
+variable length. OS/2 stores window and icon positions and file types there. So
+why not use it for unix-specific info like file owner or access rights? This
+driver can do it. If you chown/chgrp/chmod on a hpfs partion, extended
+attributes with keys "UID", "GID" or "MODE" and 2-byte values are created. Only
+that extended attributes those value differs from defaults specified in mount
+options are created. Once created, the extended attributes are never deleted,
+they're just changed. It means that when your default uid=0 and you type
+something like 'chown luser file; chown root file' the file will contain
+extended attribute UID=0. And when you umount the fs and mount it again with
+uid=luser_uid, the file will be still owned by root! If you chmod file to 444,
+extended attribute "MODE" will not be set, this special case is done by setting
+read-only flag. When you mknod a block or char device, besides "MODE", the
+special 4-byte extended attribute "DEV" will be created containing the device
+number. Currently this driver cannot resize extended attributes - it means
+that if somebody (I don't know who?) has set "UID", "GID", "MODE" or "DEV"
+attributes with different sizes, they won't be rewritten and changing these
+values doesn't work.
+
+
+Symlinks
+
+You can do symlinks on HPFS partion, symlinks are achieved by setting extended
+attribute named "SYMLINK" with symlink value. Like on ext2, you can chown and
+chgrp symlinks but I don't know what is it good for. chmoding symlink results
+in chmoding file where symlink points. These symlinks are just for Linux use and
+incompatible with OS/2. OS/2 PmShell symlinks are not supported because they are
+stored in very crazy way. They tried to do it so that link changes when file is
+moved ... sometimes it works. But the link is partly stored in directory
+extended attributes and partly in OS2SYS.INI. I don't want (and don't know how)
+to analyze or change OS2SYS.INI.
+
+
+Codepages
+
+HPFS can contain several uppercasing tables for several codepages and each
+file has a pointer to codepage it's name is in. However OS/2 was created in
+America where people don't care much about codepages and so multiple codepages
+support is quite buggy. I have Czech OS/2 working in codepage 852 on my disk.
+Once I booted English OS/2 working in cp 850 and I created a file on my 852
+partion. It marked file name codepage as 850 - good. But when I again booted
+Czech OS/2, the file was completely inaccessible under any name. It seems that
+OS/2 uppercases the search pattern with it's system code page (852) and file
+name it's comparing to with its code page (850). These could never match. Is it
+really what IBM developers wanted? But problems countinue. When I created in
+Czech OS/2 another file in that direcotry, that file was inaccesible too. OS/2
+probably uses different uppercasing method when searching where to place a file
+(note, that files in HPFS directory must be sorted) and when searching for
+a file. Finally when I opened this directory in PmShell, PmShell crashed (the
+funny thing was that, when rebooted, PmShell tried to reopen this directory
+again :-). chkdsk happily ignores these errors and only low-level disk
+modification saved me.  Never mix different language versions of OS/2 on one
+system although HPFS was designed to allow that.
+OK, I could implement complex codepage support to this driver but I think it
+would cause more problems than benefit with such buggy implementation in OS/2.
+So this driver simply uses first codepage it finds for uppercasing and
+lowercasing no matter what's file codepage index. Usually all file names are in
+this codepage - if you don't try to do what I described above :-)
+
+
+Known bugs
+
+HPFS386 on OS/2 server is not supported. HPFS386 installed on normal OS/2 client
+should work. If you have OS/2 server, use only read-only mode. I don't know how
+to handle some HPFS386 structures like access control list or extended perm
+list, I don't know how to delete them when file is deleted and how to not
+overwrite them with extended attributes. Send me some info on these structures
+and I'll make it. However, this driver should detect presence of HPFS386
+structures, remount read-only and not destroy them (I hope).
+
+When there's not enough space for extended attributes, they will be truncated
+and no error is returned.
+
+OS/2 can't access files if the path is longer than about 256 chars but this
+driver allows you to do it. chkdsk ignores such errors.
+
+Sometimes you won't be able to delete some files on a very full filesystem
+(returning error ENOSPC). That's because file in non-leaf node in directory tree
+(one directory, if it's large, has dirents in tree on HPFS) must be replaced
+with another node when deleted. And that new file might have larger name than
+the old one so the new name doesn't fit in directory node (dnode). And that
+would result in directory tree splitting, that takes disk space. Workaround is
+to delete other files that are leaf (probability that the file is non-leaf is
+about 1/50) or to truncate file first to make some space.
+You encounter this problem only if you have many directories so that
+preallocated directory band is full i.e.
+	number_of_directories / size_of_filesystem_in_mb > 4.
+
+You can't delete open directories.
+
+You can't rename over directories (what is it good for?).
+
+Renaming files so that only case changes doesn't work. This driver supports it
+but vfs doesn't. Something like 'mv file FILE' won't work.
+
+When you try to create file with bad filename (too long or containing forbidden
+characters), you get ENOENT error instead of EINVAL or ENAMETOOLONG.
+
+All atimes and directory mtimes are not updated. That's because of performance
+reasons. If you extremely wish to update them, let me know, I'll write it (but
+it will be slow).
+
+When the system is out of memory and swap, it may slightly corrupt filesystem
+(lost files, unbalanced directories). (I guess all filesystem may do it).
+
+When compiled, you get warning: function declaration isn't a prototype. Does
+anybody know what does it mean?
+
+
+What does "unbalanced tree" message mean?
+
+Old versions of this driver created sometimes unbalanced dnode trees. OS/2
+chkdsk doesn't scream if the tree is unbalanced (and sometimes creates
+unbalanced trees too :-) but both HPFS and HPFS386 contain bug that it rarely
+crashes when the tree is not balanced. This driver handles unbalanced trees
+correctly and writes warning if it finds them. If you see this message, this is
+probably because of directories created with old version of this driver.
+Workaround is to move all files from that directory to another and then back
+again. Do it in Linux, not OS/2! If you see this message in directory that is
+whole created by this driver, it is BUG - let me know about it.
+
+
+Bugs in OS/2
+
+When you have two (or more) lost directories pointing each to other, chkdsk
+locks up when repairing filesystem.
+
+Sometimes (I think it's random) when you create a file with one-char name under
+OS/2, OS/2 marks it as 'long'. chkdsk then removes this flag saying "Minor fs
+error corrected".
+
+File names like "a .b" are marked as 'long' by OS/2 but chkdsk "corrects" it and
+marks them as short (and writes "minor fs error corrected"). This bug is not in
+HPFS386.
+
+Codepage bugs decsribed above.
+
+If you don't install fixpacks, there are many, many more...
+
+
+History
+
+0.90 First public release
+0.91 Fixed bug that caused shooting to memory when write_inode was called on
+	open inode (rarely happened)
+0.92 Fixed a little memory leak in freeing directory inodes
+0.93 Fixed bug that locked up the machine when there were too many filenames
+	with first 15 characters same
+     Fixed write_file to zero file when writing behind file end
+0.94 Fixed a little memory leak when trying to delete busy file or directory
+0.95 Fixed a bug that i_hpfs_parent_dir was not updated when moving files
+1.90 First version for 2.1.1xx kernels
+1.91 Fixed a bug that chk_sectors failed when sectors were at the end of disk
+     Fixed a race-condition when write_inode is called while deleting file
+     Fixed a bug that could possibly happen (with very low probability) when
+     	using 0xff in filenames
+     Rewritten locking to avoid race-conditions
+     Mount option 'eas' now works
+     Fsync no longer returns error
+     Files beginning with '.' are marked hidden
+     Remount support added
+     Alloc is not so slow when filesystem becomes full
+     Atimes are no more updated because it slows down operation
+     Code cleanup (removed all commented debug prints)
+1.92 Corrected a bug when sync was called just before closing file
+1.93 Modified, so that it works with kernels >= 2.1.131, I don't know if it
+	works with previous versions
+     Fixed a possible problem with disks > 64G (but I don't have one, so I can't
+     	test it)
+     Fixed a file overflow at 2G
+     Added new option 'timeshift'
+     Changed behaviour on HPFS386: It is now possible to operate on HPFS386 in
+     	read-only mode
+     Fixed a bug that slowed down alloc and prevented allocating 100% space
+     	(this bug was not destructive)
+1.94 Added workaround for one bug in Linux
+     Fixed one buffer leak
+     Fixed some incompatibilities with large extended attributes (but it's still
+	not 100% ok, I have no info on it and OS/2 doesn't want to create them)
+     Rewritten allocation
+     Fixed a bug with i_blocks (du sometimes didn't display correct values)
+     Directories have no longer archive attribute set (some programs don't like
+	it)
+     Fixed a bug that it set badly one flag in large anode tree (it was not
+	destructive)
+1.95 Fixed one buffer leak, that could happen on corrupted filesystem
+     Fixed one bug in allocation in 1.94
+1.96 Added workaround for one bug in OS/2 (HPFS locked up, HPFS386 reported
+	error sometimes when opening directories in PMSHELL)
+     Fixed a possible bitmap race
+     Fixed possible problem on large disks
+     You can now delete open files
+     Fixed a nondestructive race in rename
+1.97 Support for HPFS v3 (on large partitions)
+     Fixed a bug that it didn't allow creation of files > 128M (it should be 2G)
+1.97.1 Changed names of global symbols
+       Fixed a bug when chmoding or chowning root directory
+1.98 Fixed a deadlock when using old_readdir
+     Better directory handling; workaround for "unbalanced tree" bug in OS/2
+
+
+ vim: set textwidth=80:
diff -u --recursive --new-file v2.3.1/linux/Documentation/svga.txt linux/Documentation/svga.txt
--- v2.3.1/linux/Documentation/svga.txt	Fri Jul 10 15:18:29 1998
+++ linux/Documentation/svga.txt	Fri May 14 12:47:01 1999
@@ -1,5 +1,5 @@
-		       Video Mode Selection Support 2.11
-	      (c) 1995--1997 Martin Mares, <mj@k332.feld.cvut.cz>
+		       Video Mode Selection Support 2.13
+		    (c) 1995--1999 Martin Mares, <mj@ucw.cz>
 --------------------------------------------------------------------------------
 
 1. Intro
@@ -9,6 +9,11 @@
 to usage of the BIOS, the selection is limited to boot time (before the
 kernel decompression starts) and works only on 80X86 machines.
 
+   **  Short intro for the impatient: Just use vga=ask for the first time,
+   **  enter `scan' on the video mode prompt, pick the mode you want to use,
+   **  remember its mode ID (the four-digit hexadecimal number) and then
+   **  set the vga parameter to this number (converted to decimal first).
+
    The video mode to be used is selected by a kernel parameter which can be
 specified in the kernel Makefile (the SVGA_MODE=... line) or by the "vga=..."
 option of LILO (or some other boot loader you use) or by the "vidmode" utility
@@ -268,3 +273,4 @@
 		- Removed the doc section describing adding of new probing
 		  functions as I try to get rid of _all_ hardware probing here.
 2.12 (25-May-98)- Added support for VESA frame buffer graphics.
+2.13 (14-May-99)- Minor documentation fixes.
diff -u --recursive --new-file v2.3.1/linux/Makefile linux/Makefile
--- v2.3.1/linux/Makefile	Fri May 14 18:55:11 1999
+++ linux/Makefile	Fri May 14 16:04:47 1999
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 1
+SUBLEVEL = 2
 EXTRAVERSION =
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.3.1/linux/arch/alpha/kernel/alpha_ksyms.c	Sat Jan 16 17:02:50 1999
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Fri May 14 12:41:22 1999
@@ -52,6 +52,7 @@
 EXPORT_SYMBOL(local_irq_count);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(disable_irq_nosync);
 EXPORT_SYMBOL(screen_info);
 EXPORT_SYMBOL(perf_irq);
 
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/fpreg.c linux/arch/alpha/kernel/fpreg.c
--- v2.3.1/linux/arch/alpha/kernel/fpreg.c	Sun Aug  9 12:09:05 1998
+++ linux/arch/alpha/kernel/fpreg.c	Fri May 14 12:41:22 1999
@@ -1,10 +1,10 @@
 /*
- * kernel/fpreg.c
+ * arch/alpha/kernel/fpreg.c
  *
  * (C) Copyright 1998 Linus Torvalds
  */
 
-#ifdef __alpha_cix__
+#if defined(__alpha_cix__) || defined(__alpha_fix__)
 #define STT(reg,val)  asm volatile ("ftoit $f"#reg",%0" : "=r"(val));
 #else
 #define STT(reg,val)  asm volatile ("stt $f"#reg",%0" : "=m"(val));
@@ -52,7 +52,7 @@
 	return val;
 }
 
-#ifdef __alpha_cix__
+#if defined(__alpha_cix__) || defined(__alpha_fix__)
 #define LDT(reg,val)  asm volatile ("itoft %0,$f"#reg : : "r"(val));
 #else
 #define LDT(reg,val)  asm volatile ("ldt $f"#reg",%0" : : "m"(val));
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/head.S linux/arch/alpha/kernel/head.S
--- v2.3.1/linux/arch/alpha/kernel/head.S	Mon Oct 12 11:40:12 1998
+++ linux/arch/alpha/kernel/head.S	Fri May 14 12:41:22 1999
@@ -32,24 +32,26 @@
 
 #ifdef __SMP__
 	.align 3
-	.globl	__start_cpu
-	.ent	__start_cpu
-	/* On entry here from SRM console, the HWPCB of this processor
-	   has been loaded, and $27 contains the task pointer */
-__start_cpu:
-	.prologue 0
-	/* First order of business, load the GP */
-	br	$26,1f
-1:	ldgp	$29,0($26)
-	/* We need to get current loaded up with our first task...  */
-	mov	$27,$8
-	/* Set FEN */
-	lda	$16,1($31)
-	call_pal PAL_wrfen
-	/* ... and then we can start the processor.  */
-	jsr	$26,start_secondary
+	.globl	__smp_callin
+	.ent	__smp_callin
+	/* On entry here from SRM console, the HWPCB of the per-cpu
+	   slot for this processor has been loaded.  We've arranged
+	   for the UNIQUE value for this process to contain the PCBB
+	   of the target idle task.  */
+__smp_callin:
+	.prologue 1
+	ldgp	$29,0($27)	# First order of business, load the GP.
+
+	call_pal PAL_rduniq	# Grab the target PCBB.
+	mov	$0,$16		# Install it.
+	call_pal PAL_swpctx
+
+	lda	$8,0x3fff	# Find "current".
+	bic	$30,$8,$8
+	
+	jsr	$26,smp_callin
 	call_pal PAL_halt
-	.end __start_cpu
+	.end __smp_callin
 #endif /* __SMP__ */
 
 	.align 3
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c
--- v2.3.1/linux/arch/alpha/kernel/irq.c	Sat Jan 16 17:02:50 1999
+++ linux/arch/alpha/kernel/irq.c	Fri May 14 12:41:22 1999
@@ -192,13 +192,21 @@
 }
 
 void
-disable_irq(unsigned int irq_nr)
+disable_irq_nosync(unsigned int irq_nr)
 {
 	unsigned long flags;
 
 	save_and_cli(flags);
 	mask_irq(irq_nr);
 	restore_flags(flags);
+}
+
+void
+disable_irq(unsigned int irq_nr)
+{
+	/* This works non-SMP, and SMP until we write code to distribute
+	   interrupts to more that cpu 0.  */
+	disable_irq_nosync(irq_nr);
 }
 
 void
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.3.1/linux/arch/alpha/kernel/process.c	Mon May 10 09:55:21 1999
+++ linux/arch/alpha/kernel/process.c	Fri May 14 12:41:23 1999
@@ -75,33 +75,46 @@
 	return 0;
 }
 
-static void __attribute__((noreturn))
-do_cpu_idle(void)
+#ifdef __SMP__
+void
+cpu_idle(void *unused)
 {
 	/* An endless idle loop with no priority at all.  */
 	current->priority = 0;
+	current->counter = -100;
+
 	while (1) {
-		check_pgt_cache();
-		run_task_queue(&tq_scheduler);
-		current->counter = 0;
-		schedule();
-	}
-}
+		/* FIXME -- EV6 and LCA45 know how to power down
+		   the CPU.  */
 
-#ifdef __SMP__
-void
-cpu_idle(void *unused)
-{
-	do_cpu_idle();
+		/* Although we are an idle CPU, we do not want to 
+		   get into the scheduler unnecessarily.  */
+		if (current->need_resched) {
+			schedule();
+			check_pgt_cache();
+		}
+	}
 }
 #endif
 
 asmlinkage int
 sys_idle(void)
 {
-	if (current->pid == 0)
-        	do_cpu_idle();
-	return -EPERM;
+	if (current->pid != 0)
+		return -EPERM;
+
+	/* An endless idle loop with no priority at all.  */
+	current->priority = 0;
+	current->counter = -100;
+	init_idle();
+
+	while (1) {
+		/* FIXME -- EV6 and LCA45 know how to power down
+		   the CPU.  */
+
+		schedule();
+		check_pgt_cache();
+	}
 }
 
 void
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/proto.h linux/arch/alpha/kernel/proto.h
--- v2.3.1/linux/arch/alpha/kernel/proto.h	Sun Feb 21 19:06:36 1999
+++ linux/arch/alpha/kernel/proto.h	Fri May 14 12:41:23 1999
@@ -151,6 +151,8 @@
 extern void setup_smp(void);
 extern int smp_info(char *buffer);
 extern void handle_ipi(struct pt_regs *);
+extern void smp_percpu_timer_interrupt(struct pt_regs *);
+extern int smp_boot_cpuid;
 
 /* bios32.c */
 extern void reset_for_srm(void);
@@ -178,7 +180,7 @@
 extern void wrmces(unsigned long mces);
 extern void cserve_ena(unsigned long);
 extern void cserve_dis(unsigned long);
-extern void __start_cpu(unsigned long);
+extern void __smp_callin(void);
 
 /* entry.S */
 extern void entArith(void);
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- v2.3.1/linux/arch/alpha/kernel/signal.c	Mon Oct 12 11:40:12 1998
+++ linux/arch/alpha/kernel/signal.c	Fri May 14 12:41:23 1999
@@ -24,6 +24,9 @@
 #include <asm/sigcontext.h>
 #include <asm/ucontext.h>
 
+#include "proto.h"
+
+
 #define DEBUG_SIG 0
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/smp.c linux/arch/alpha/kernel/smp.c
--- v2.3.1/linux/arch/alpha/kernel/smp.c	Mon May 10 09:55:21 1999
+++ linux/arch/alpha/kernel/smp.c	Fri May 14 12:41:23 1999
@@ -18,6 +18,7 @@
 #include <asm/ptrace.h>
 #include <asm/atomic.h>
 
+#include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
@@ -29,6 +30,8 @@
 #include <asm/unistd.h>
 
 #include "proto.h"
+#include "irq.h"
+
 
 #define DEBUG_SMP 0
 #if DEBUG_SMP
@@ -37,62 +40,44 @@
 #define DBGS(args)
 #endif
 
-struct ipi_msg_flush_tb_struct {
-	volatile unsigned int flush_tb_mask;
-	union {
-		struct mm_struct *	flush_mm;
-		struct vm_area_struct *	flush_vma;
-	} p;
-	unsigned long flush_addr;
-	unsigned long flush_end;
-};
-
-static struct ipi_msg_flush_tb_struct ipi_msg_flush_tb __cacheline_aligned;
-static spinlock_t flush_tb_lock = SPIN_LOCK_UNLOCKED;
-
+/* A collection of per-processor data.  */
 struct cpuinfo_alpha cpu_data[NR_CPUS];
 
-spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED;
-spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
-
-unsigned int boot_cpu_id = 0;
-static int smp_activated = 0;
+/* A collection of single bit ipi messages.  */
+static struct {
+	unsigned long bits __cacheline_aligned;
+} ipi_data[NR_CPUS];
 
-int smp_found_config = 0; /* Have we found an SMP box */
-static int max_cpus = -1;
+enum ipi_message_type {
+        IPI_RESCHEDULE,
+        IPI_CALL_FUNC,
+        IPI_CPU_STOP,
+};
 
-unsigned int cpu_present_map = 0;
+spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED;
 
-int smp_num_cpus = 1;
-int smp_num_probed = 0; /* Internal processor count */
+/* Set to a secondary's cpuid when it comes online.  */
+static unsigned long smp_secondary_alive;
 
-int smp_threads_ready = 0;
-volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};
-volatile unsigned long smp_spinning[NR_CPUS] = { 0, };
+unsigned long cpu_present_mask;	/* Which cpus ids came online.  */
 
+static int max_cpus = -1;	/* Command-line limitation.  */
+int smp_boot_cpuid;		/* Which processor we booted from.  */
+int smp_num_probed;		/* Internal processor count */
+int smp_num_cpus = 1;		/* Number that came online.  */
+int smp_threads_ready;		/* True once the per process idle is forked. */
 cycles_t cacheflush_time;
 
-unsigned int prof_multiplier[NR_CPUS];
-unsigned int prof_counter[NR_CPUS];
-
-volatile int ipi_bits[NR_CPUS] __cacheline_aligned;
-
-unsigned long boot_cpu_palrev;
-
-volatile int smp_commenced = 0;
-volatile int smp_processors_ready = 0;
-
-volatile int cpu_number_map[NR_CPUS];
-volatile int cpu_logical_map[NR_CPUS];
+int cpu_number_map[NR_CPUS];
+int __cpu_logical_map[NR_CPUS];
 
 extern void calibrate_delay(void);
-extern struct thread_struct * original_pcb_ptr;
-
-static void smp_setup_percpu_timer(void);
-static void secondary_cpu_start(int, struct task_struct *);
-static void send_cpu_msg(char *, int);
+extern asmlinkage void entInt(void);
 
-/* Process bootcommand SMP options, like "nosmp" and "maxcpus=" */
+
+/*
+ * Process bootcommand SMP options, like "nosmp" and "maxcpus=".
+ */
 void __init
 smp_setup(char *str, int *ints)
 {
@@ -102,100 +87,87 @@
 		max_cpus = 0;
 }
 
-static void __init
-smp_store_cpu_info(int id)
+/*
+ * Called by both boot and secondaries to move global data into
+ *  per-processor storage.
+ */
+static inline void __init
+smp_store_cpu_info(int cpuid)
 {
-	/* This is it on Alpha, so far. */
-	cpu_data[id].loops_per_sec = loops_per_sec;
+	cpu_data[cpuid].loops_per_sec = loops_per_sec;
 }
 
-void __init
-smp_commence(void)
+/*
+ * Ideally sets up per-cpu profiling hooks.  Doesn't do much now...
+ */
+static inline void __init
+smp_setup_percpu_timer(int cpuid)
 {
-	/* Lets the callin's below out of their loop. */
-	mb();
-	smp_commenced = 1;
+	cpu_data[cpuid].prof_counter = 1;
+	cpu_data[cpuid].prof_multiplier = 1;
+
+#ifdef NOT_YET_PROFILING
+	load_profile_irq(mid_xlate[cpu], lvl14_resolution);
+	if (cpu == smp_boot_cpuid)
+		enable_pil_irq(14);
+#endif
 }
 
+/*
+ * Where secondaries begin a life of C.
+ */
 void __init
 smp_callin(void)
 {
 	int cpuid = hard_smp_processor_id();
 
 	DBGS(("CALLIN %d state 0x%lx\n", cpuid, current->state));
-#ifdef HUH
-	local_flush_cache_all();
-	local_flush_tlb_all();
-#endif
-#if 0
-	set_irq_udt(mid_xlate[boot_cpu_id]);
-#endif
+
+	/* Turn on machine checks.  */
+	wrmces(7);
+
+	/* Set trap vectors.  */
+	trap_init();
+
+	/* Set interrupt vector.  */
+	wrent(entInt, 0);
+
+	/* Setup the scheduler for this processor.  */
+	init_idle();
 
 	/* Get our local ticker going. */
-	smp_setup_percpu_timer();
+	smp_setup_percpu_timer(cpuid);
 
-#if 0
+	/* Must have completely accurate bogos.  */
+	__sti();
 	calibrate_delay();
-#endif
 	smp_store_cpu_info(cpuid);
-#ifdef HUH
-	local_flush_cache_all();
-	local_flush_tlb_all();
-#endif
 
 	/* Allow master to continue. */
-	set_bit(cpuid, (unsigned long *)&cpu_callin_map[cpuid]);
-#ifdef HUH
-	local_flush_cache_all();
-	local_flush_tlb_all();
-#endif
-
-#ifdef NOT_YET
-	while(!task[cpuid] || current_set[cpuid] != task[cpuid])
-	        barrier();
-#endif
+	wmb();
+	smp_secondary_alive = cpuid;
 
-#ifdef HUH
-	local_flush_cache_all();
-	local_flush_tlb_all();
-#endif
-#if 0
-	__sti();
-#endif
-}
-
-asmlinkage int __init
-start_secondary(void *unused)
-{
-	extern asmlinkage void entInt(void);
-	extern void paging_init_secondary(void);
+	/* Wait for the go code.  */
+	while (!smp_threads_ready)
+		barrier();
 
-	wrmces(7);
-	paging_init_secondary();
-	trap_init();
-	wrent(entInt, 0);
+	printk(KERN_INFO "SMP: commencing CPU %d current %p\n",
+	       cpuid, current);
 
-	smp_callin();
-	while (!smp_commenced)
-		barrier();
-#if 1
-	printk("start_secondary: commencing CPU %d current %p\n",
-	       hard_smp_processor_id(), current);
-#endif
+	/* Do nothing.  */
 	cpu_idle(NULL);
 }
 
+
+/*
+ * Rough estimation for SMP scheduling, this is the number of cycles it
+ * takes for a fully memory-limited process to flush the SMP-local cache.
+ *
+ * We are not told how much cache there is, so we have to guess.
+ */
 static void __init
 smp_tune_scheduling (void)
 {
-	/*
-	 * Rough estimation for SMP scheduling, this is the number of
-	 * cycles it takes for a fully memory-limited process to flush
-	 * the SMP-local cache.
-	 *
-	 * We are not told how much cache there is, so we have to guess.
-	 */
-
 	struct percpu_struct *cpu;
 	unsigned long on_chip_cache;
 	unsigned long freq;
@@ -231,259 +203,159 @@
 	cacheflush_time = freq / 1024 * on_chip_cache / 5000;
 }
 
-
 /*
- *      Cycle through the processors sending START msgs to boot each.
+ * Send a message to a secondary's console.  "START" is one such
+ * interesting message.  ;-)
  */
-void __init
-smp_boot_cpus(void)
+static void
+send_secondary_console_msg(char *str, int cpuid)
 {
-	int cpucount = 0;
-	int i, first, prev;
-
-	printk("Entering SMP Mode.\n");
-
-#if 0
-	__sti();
-#endif
-
-	for(i=0; i < NR_CPUS; i++) {
-		cpu_number_map[i] = -1;
-		cpu_logical_map[i] = -1;
-	        prof_counter[i] = 1;
-	        prof_multiplier[i] = 1;
-		ipi_bits[i] = 0;
-	}
-
-	cpu_number_map[boot_cpu_id] = 0;
-	cpu_logical_map[0] = boot_cpu_id;
-	current->processor = boot_cpu_id; /* ??? */
+	struct percpu_struct *cpu;
+	register char *cp1, *cp2;
+	unsigned long cpumask;
+	size_t len;
+	long timeout;
 
-	smp_store_cpu_info(boot_cpu_id);
-	smp_tune_scheduling();
-#ifdef NOT_YET
-	printk("CPU%d: ", boot_cpu_id);
-	print_cpu_info(&cpu_data[boot_cpu_id]);
-	set_irq_udt(mid_xlate[boot_cpu_id]);
-#endif
-	smp_setup_percpu_timer();
-#ifdef HUH
-	local_flush_cache_all();
-#endif
-	if (smp_num_probed == 1)
-		return;  /* Not an MP box. */
+	cpu = (struct percpu_struct *)
+		((char*)hwrpb
+		 + hwrpb->processor_offset
+		 + cpuid * hwrpb->processor_size);
 
-#if NOT_YET
-	/*
-	 * If SMP should be disabled, then really disable it!
-	 */
-	if (!max_cpus)
-	{
-		smp_found_config = 0;
-	        printk(KERN_INFO "SMP mode deactivated.\n");
-	}
-#endif
+	cpumask = (1L << cpuid);
+	if (hwrpb->txrdy & cpumask)
+		goto delay1;
+	ready1:
 
-	for (i = 0; i < NR_CPUS; i++) {
+	cp2 = str;
+	len = strlen(cp2);
+	*(unsigned int *)&cpu->ipc_buffer[0] = len;
+	cp1 = (char *) &cpu->ipc_buffer[1];
+	memcpy(cp1, cp2, len);
 
-		if (i == boot_cpu_id)
-			continue;
+	/* atomic test and set */
+	wmb();
+	set_bit(cpuid, &hwrpb->rxrdy);
 
-	        if (cpu_present_map & (1 << i)) {
-	                struct task_struct *idle;
-	                int timeout;
-
-	                /* Cook up an idler for this guy. */
-	                kernel_thread(start_secondary, NULL, CLONE_PID);
-	                idle = task[++cpucount];
-			if (!idle)
-				panic("No idle process for CPU %d", i);
-	                idle->processor = i;
-
-			DBGS(("smp_boot_cpus: CPU %d state 0x%lx flags 0x%lx\n",
-			      i, idle->state, idle->flags));
-
-	                /* whirrr, whirrr, whirrrrrrrrr... */
-#ifdef HUH
-	                local_flush_cache_all();
-#endif
-	                secondary_cpu_start(i, idle);
+	if (hwrpb->txrdy & cpumask)
+		goto delay2;
+	ready2:
+	return;
 
-	                /* wheee... it's going... wait for 5 secs...*/
-	                for (timeout = 0; timeout < 50000; timeout++) {
-				if (cpu_callin_map[i])
-					break;
-	                        udelay(100);
-	                }
-	                if (cpu_callin_map[i]) {
-				/* Another "Red Snapper". */
-				cpu_number_map[i] = cpucount;
-	                        cpu_logical_map[cpucount] = i;
-	                } else {
-				cpucount--;
-	                        printk("smp_boot_cpus: Processor %d"
-				       " is stuck 0x%lx.\n", i, idle->flags);
-	                }
-	        }
-	        if (!(cpu_callin_map[i])) {
-			cpu_present_map &= ~(1 << i);
-	                cpu_number_map[i] = -1;
-	        }
-	}
-#ifdef HUH
-	local_flush_cache_all();
-#endif
-	if (cpucount == 0) {
-		printk("smp_boot_cpus: ERROR - only one Processor found.\n");
-	        cpu_present_map = (1 << smp_processor_id());
-	} else {
-		unsigned long bogosum = 0;
-	        for (i = 0; i < NR_CPUS; i++) {
-			if (cpu_present_map & (1 << i))
-				bogosum += cpu_data[i].loops_per_sec;
-	        }
-	        printk("smp_boot_cpus: Total of %d Processors activated"
-		       " (%lu.%02lu BogoMIPS).\n",
-	               cpucount + 1,
-	               (bogosum + 2500)/500000,
-	               ((bogosum + 2500)/5000)%100);
-	        smp_activated = 1;
-	        smp_num_cpus = cpucount + 1;
+delay1:
+	/* Wait one second.  Note that jiffies aren't ticking yet.  */
+	for (timeout = 100000; timeout > 0; --timeout) {
+		if (!(hwrpb->txrdy & cpumask))
+			goto ready1;
+		udelay(10);
+		barrier();
 	}
+	goto timeout;
 
-	/* Setup CPU list for IRQ distribution scheme. */
-	first = prev = -1;
-	for (i = 0; i < NR_CPUS; i++) {
-		if (cpu_present_map & (1 << i)) {
-			if (first == -1)
-				first = i;
-			if (prev != -1)
-				cpu_data[i].next = i;
-	                prev = i;
-	        }
+delay2:
+	/* Wait one second.  */
+	for (timeout = 100000; timeout > 0; --timeout) {
+		if (!(hwrpb->txrdy & cpumask))
+			goto ready2;
+		udelay(10);
+		barrier();
 	}
-	cpu_data[prev].next = first;
+	goto timeout;
 
-	/* Ok, they are spinning and ready to go. */
-	smp_processors_ready = 1;
+timeout:
+	printk("Processor %x not ready\n", cpuid);
+	return;
 }
 
-static void __init
-smp_setup_percpu_timer(void)
+/*
+ * A secondary console wants to send a message.  Receive it.
+ */
+static void
+recv_secondary_console_msg(void)
 {
-	int cpu = smp_processor_id();
-
-	prof_counter[cpu] = prof_multiplier[cpu] = 1;
-#ifdef NOT_YET
-	load_profile_irq(mid_xlate[cpu], lvl14_resolution);
-	if (cpu == boot_cpu_id)
-		enable_pil_irq(14);
-#endif
-}
-
-extern void update_one_process(struct task_struct *p, unsigned long ticks,
-	                       unsigned long user, unsigned long system,
-			       int cpu);
+	int mycpu, i, cnt;
+	unsigned long txrdy = hwrpb->txrdy;
+	char *cp1, *cp2, buf[80];
+	struct percpu_struct *cpu;
 
-void
-smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
-	int cpu = smp_processor_id();
+	DBGS(("recv_secondary_console_msg: TXRDY 0x%lx.\n", txrdy));
 
-#ifdef NOT_YET
-	clear_profile_irq(mid_xlate[cpu]);
-	if(!user_mode(regs))
-		alpha_do_profile(regs->pc);
-#endif
+	mycpu = hard_smp_processor_id();
 
-	if (!--prof_counter[cpu]) {
-		int user = user_mode(regs);
-	        if (current->pid) {
-			update_one_process(current, 1, user, !user, cpu);
+	for (i = 0; i < NR_CPUS; i++) {
+		if (!(txrdy & (1L << i)))
+			continue;
 
-	                if (--current->counter < 0) {
-				current->counter = 0;
-	                        current->need_resched = 1;
-	                }
+		DBGS(("recv_secondary_console_msg: "
+		      "TXRDY contains CPU %d.\n", i));
 
-	                spin_lock(&ticker_lock);
-	                if (user) {
-				if (current->priority < DEF_PRIORITY) {
-					kstat.cpu_nice++;
-					kstat.per_cpu_nice[cpu]++;
-				} else {
-					kstat.cpu_user++;
-					kstat.per_cpu_user[cpu]++;
-				}
-	                } else {
-				kstat.cpu_system++;
-				kstat.per_cpu_system[cpu]++;
-	                }
-	                spin_unlock(&ticker_lock);
-	        }
-	        prof_counter[cpu] = prof_multiplier[cpu];
-	}
-}
+		cpu = (struct percpu_struct *)
+		  ((char*)hwrpb
+		   + hwrpb->processor_offset
+		   + i * hwrpb->processor_size);
 
-int __init
-setup_profiling_timer(unsigned int multiplier)
-{
-#ifdef NOT_YET
-	int i;
-	unsigned long flags;
+ 		printk(KERN_INFO "recv_secondary_console_msg: on %d from %d"
+		       " HALT_REASON 0x%lx FLAGS 0x%lx\n",
+		       mycpu, i, cpu->halt_reason, cpu->flags);
 
-	/* Prevent level14 ticker IRQ flooding. */
-	if((!multiplier) || (lvl14_resolution / multiplier) < 500)
-	        return -EINVAL;
+		cnt = cpu->ipc_buffer[0] >> 32;
+		if (cnt <= 0 || cnt >= 80)
+			strcpy(buf, "<<< BOGUS MSG >>>");
+		else {
+			cp1 = (char *) &cpu->ipc_buffer[11];
+			cp2 = buf;
+			strcpy(cp2, cp1);
+			
+			while ((cp2 = strchr(cp2, '\r')) != 0) {
+				*cp2 = ' ';
+				if (cp2[1] == '\n')
+					cp2[1] = ' ';
+			}
+		}
 
-	save_and_cli(flags);
-	for(i = 0; i < NR_CPUS; i++) {
-	        if(cpu_present_map & (1 << i)) {
-	                load_profile_irq(mid_xlate[i], lvl14_resolution / multip
-lier);
-	                prof_multiplier[i] = multiplier;
-	        }
+		printk(KERN_INFO "recv_secondary_console_msg: on %d "
+		       "message is '%s'\n", mycpu, buf);
 	}
-	restore_flags(flags);
 
-	return 0;
-
-#endif
-  return -EINVAL;
-}
-
-/* Only broken Intel needs this, thus it should not even be
-   referenced globally.  */
-
-void __init
-initialize_secondary(void)
-{
+	hwrpb->txrdy = 0;
 }
 
-static void __init
+/*
+ * Convince the console to have a secondary cpu begin execution.
+ */
+static int __init
 secondary_cpu_start(int cpuid, struct task_struct *idle)
 {
 	struct percpu_struct *cpu;
-	int timeout;
+	struct pcb_struct *hwpcb;
+	long timeout;
 	  
 	cpu = (struct percpu_struct *)
 		((char*)hwrpb
 		 + hwrpb->processor_offset
 		 + cpuid * hwrpb->processor_size);
+	hwpcb = (struct pcb_struct *) cpu->hwpcb;
 
-	/* Set context to idle thread this CPU will use when running
-	   assumption is that the idle thread is all set to go... ??? */
-	memcpy(&cpu->hwpcb[0], &idle->tss, sizeof(struct pcb_struct));
-	cpu->hwpcb[4] = cpu->hwpcb[0]; /* UNIQUE set to KSP ??? */
+	/* Initialize the CPU's HWPCB to something just good enough for
+	   us to get started.  Immediately after starting, we'll swpctx
+	   to the target idle task's tss.  Reuse the stack in the mean
+	   time.  Precalculate the target PCBB.  */
+	hwpcb->ksp = (unsigned long) idle + sizeof(union task_union) - 16;
+	hwpcb->usp = 0;
+	hwpcb->ptbr = idle->tss.ptbr;
+	hwpcb->pcc = 0;
+	hwpcb->asn = 0;
+	hwpcb->unique = virt_to_phys(&idle->tss);
+	hwpcb->flags = idle->tss.pal_flags;
+	hwpcb->res1 = hwpcb->res2 = 0;
 
-	DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx\n",
-	      cpu->hwpcb[0], cpu->hwpcb[2], hwrpb->vptb));
+	DBGS(("KSP 0x%lx PTBR 0x%lx VPTBR 0x%lx UNIQUE 0x%lx\n",
+	      hwpcb->ksp, hwpcb->ptbr, hwrpb->vptb, hwcpb->unique));
 	DBGS(("Starting secondary cpu %d: state 0x%lx pal_flags 0x%lx\n",
 	      cpuid, idle->state, idle->tss.pal_flags));
 
 	/* Setup HWRPB fields that SRM uses to activate secondary CPU */
-	hwrpb->CPU_restart = __start_cpu;
-	hwrpb->CPU_restart_data = (unsigned long) idle;
+	hwrpb->CPU_restart = __smp_callin;
+	hwrpb->CPU_restart_data = (unsigned long) __smp_callin;
 
 	/* Recalculate and update the HWRPB checksum */
 	hwrpb_update_checksum(hwrpb);
@@ -495,99 +367,97 @@
 	/* SRM III 3.4.1.3 */
 	cpu->flags |= 0x22;	/* turn on Context Valid and Restart Capable */
 	cpu->flags &= ~1;	/* turn off Bootstrap In Progress */
-	mb();
+	wmb();
 
-	send_cpu_msg("START\r\n", cpuid);
+	send_secondary_console_msg("START\r\n", cpuid);
 
-	/* now, we wait... */
-	for (timeout = 10000; !(cpu->flags & 1); timeout--) {
-		if (timeout <= 0) {
-			printk("Processor %d failed to start\n", cpuid);
-	                        /* needed for pset_info to work */
-#if 0
-			ipc_processor_enable(cpu_to_processor(cpunum));
-#endif
-			return;
-		}
-		mdelay(1);
+	/* Wait 1 second for an ACK from the console.  Note that jiffies 
+	   aren't ticking yet.  */
+	for (timeout = 100000; timeout > 0; timeout--) {
+		if (cpu->flags & 1)
+			goto started;
+		udelay(10);
 		barrier();
 	}
+	printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid);
+	return -1;
+
+started:
 	DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid));
+	return 0;
 }
 
-static void
-send_cpu_msg(char *str, int cpuid)
+/*
+ * Bring one cpu online.
+ */
+static int __init
+smp_boot_one_cpu(int cpuid, int cpunum)
 {
-	struct percpu_struct *cpu;
-	register char *cp1, *cp2;
-	unsigned long cpumask;
-	size_t len;
-	int timeout;
-
-	cpu = (struct percpu_struct *)
-		((char*)hwrpb
-		 + hwrpb->processor_offset
-		 + cpuid * hwrpb->processor_size);
-
-	cpumask = (1L << cpuid);
-	if (hwrpb->txrdy & cpumask)
-		goto delay1;
-	ready1:
-
-	cp2 = str;
-	len = strlen(cp2);
-	*(unsigned int *)&cpu->ipc_buffer[0] = len;
-	cp1 = (char *) &cpu->ipc_buffer[1];
-	memcpy(cp1, cp2, len);
-
-	/* atomic test and set */
-	set_bit(cpuid, &hwrpb->rxrdy);
+	struct task_struct *idle;
+	long timeout;
 
-	if (hwrpb->txrdy & cpumask)
-		goto delay2;
-	ready2:
-	return;
-
-delay1:
-	for (timeout = 10000; timeout > 0; --timeout) {
-		if (!(hwrpb->txrdy & cpumask))
-			goto ready1;
-		udelay(100);
+	/* Cook up an idler for this guy.  Note that the address we give
+	   to kernel_thread is irrelevant -- it's going to start where
+	   HWRPB.CPU_restart says to start.  But this gets all the other
+	   task-y sort of data structures set up like we wish.  */
+	kernel_thread((void *)__smp_callin, NULL, CLONE_PID|CLONE_VM);
+	idle = task[cpunum];
+	if (!idle)
+		panic("No idle process for CPU %d", cpuid);
+	idle->processor = cpuid;
+
+	/* Schedule the first task manually.  */
+	/* ??? Ingo, what is this?  */
+	idle->has_cpu = 1;
+
+	DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n",
+	      cpuid, idle->state, idle->flags));
+
+	/* The secondary will change this once it is happy.  Note that
+	   secondary_cpu_start contains the necessary memory barrier.  */
+	smp_secondary_alive = -1;
+
+	/* Whirrr, whirrr, whirrrrrrrrr... */
+	if (secondary_cpu_start(cpuid, idle))
+		return -1;
+
+	/* We've been acked by the console; wait one second for the task
+	   to start up for real.  Note that jiffies aren't ticking yet.  */
+	for (timeout = 0; timeout < 100000; timeout++) {
+		if (smp_secondary_alive != -1)
+			goto alive;
+		udelay(10);
 		barrier();
 	}
-	goto timeout;
-
-delay2:
-	for (timeout = 10000; timeout > 0; --timeout) {
-		if (!(hwrpb->txrdy & cpumask))
-			goto ready2;
-		udelay(100);
-		barrier();
-	}
-	goto timeout;
 
-timeout:
-	printk("Processor %x not ready\n", cpuid);
-	return;
+	printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid);
+	return -1;
+
+alive:
+	/* Another "Red Snapper". */
+	cpu_number_map[cpuid] = cpunum;
+	__cpu_logical_map[cpunum] = cpuid;
+	return 0;
 }
 
 /*
- * setup_smp()
- *
- * called from arch/alpha/kernel/setup.c:setup_arch() when __SMP__ defined
+ * Called from setup_arch.  Detect an SMP system and which processors
+ * are present.
  */
 void __init
 setup_smp(void)
 {
 	struct percpu_struct *cpubase, *cpu;
 	int i;
-	  
-	boot_cpu_id = hard_smp_processor_id();
-	if (boot_cpu_id != 0) {
-		printk("setup_smp: boot_cpu_id != 0 (%d).\n", boot_cpu_id);
+
+	smp_boot_cpuid = hard_smp_processor_id();
+	if (smp_boot_cpuid != 0) {
+		printk(KERN_WARNING "SMP: Booting off cpu %d instead of 0?\n",
+		       smp_boot_cpuid);
 	}
 
 	if (hwrpb->nr_processors > 1) {
+		int boot_cpu_palrev;
 
 		DBGS(("setup_smp: nr_processors %ld\n",
 		      hwrpb->nr_processors));
@@ -601,10 +471,9 @@
 				((char *)cpubase + i*hwrpb->processor_size);
 			if ((cpu->flags & 0x1cc) == 0x1cc) {
 				smp_num_probed++;
-				/* assume here that "whami" == index */
-				cpu_present_map |= (1 << i);
-				if (i != boot_cpu_id)
-				  cpu->pal_revision = boot_cpu_palrev;
+				/* Assume here that "whami" == index */
+				cpu_present_mask |= (1L << i);
+				cpu->pal_revision = boot_cpu_palrev;
 			}
 
 			DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
@@ -614,76 +483,249 @@
 		}
 	} else {
 		smp_num_probed = 1;
-		cpu_present_map = (1 << boot_cpu_id);
+		cpu_present_mask = (1L << smp_boot_cpuid);
 	}
-	printk("setup_smp: %d CPUs probed, cpu_present_map 0x%x,"
-	       " boot_cpu_id %d\n",
-	       smp_num_probed, cpu_present_map, boot_cpu_id);
+
+	printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n",
+	       smp_num_probed, cpu_present_mask);
 }
 
-static void
-secondary_console_message(void)
+/*
+ * Called by smp_init bring all the secondaries online and hold them.
+ */
+void __init
+smp_boot_cpus(void)
 {
-	int mycpu, i, cnt;
-	unsigned long txrdy = hwrpb->txrdy;
-	char *cp1, *cp2, buf[80];
-	struct percpu_struct *cpu;
+	int cpu_count, i;
+	unsigned long bogosum;
 
-	DBGS(("secondary_console_message: TXRDY 0x%lx.\n", txrdy));
+	/* Take care of some initial bookkeeping.  */
+	memset(cpu_number_map, -1, sizeof(cpu_number_map));
+	memset(__cpu_logical_map, -1, sizeof(__cpu_logical_map));
+	memset(ipi_data, 0, sizeof(ipi_data));
+
+	cpu_number_map[smp_boot_cpuid] = 0;
+	__cpu_logical_map[0] = smp_boot_cpuid;
+	current->processor = smp_boot_cpuid;
 
-	mycpu = hard_smp_processor_id();
+	smp_store_cpu_info(smp_boot_cpuid);
+	smp_tune_scheduling();
+	smp_setup_percpu_timer(smp_boot_cpuid);
+
+	init_idle();
+
+	/* Nothing to do on a UP box, or when told not to.  */
+	if (smp_num_probed == 1 || max_cpus == 0) {
+	        printk(KERN_INFO "SMP mode deactivated.\n");
+		return;
+	}
 
+	printk(KERN_INFO "SMP starting up secondaries.\n");
+
+	cpu_count = 1;
 	for (i = 0; i < NR_CPUS; i++) {
-		if (!(txrdy & (1L << i)))
+		if (i == smp_boot_cpuid)
 			continue;
 
-		DBGS(("secondary_console_message: "
-		      "TXRDY contains CPU %d.\n", i));
+	        if (((cpu_present_mask >> i) & 1) == 0)
+			continue;
 
-		cpu = (struct percpu_struct *)
-		  ((char*)hwrpb
-		   + hwrpb->processor_offset
-		   + i * hwrpb->processor_size);
+		if (smp_boot_one_cpu(i, cpu_count))
+			continue;
 
- 		printk("secondary_console_message: on %d from %d"
-		       " HALT_REASON 0x%lx FLAGS 0x%lx\n",
-		       mycpu, i, cpu->halt_reason, cpu->flags);
+		cpu_count++;
+	}
 
-		cnt = cpu->ipc_buffer[0] >> 32;
-		if (cnt <= 0 || cnt >= 80)
-			strcpy(buf, "<<< BOGUS MSG >>>");
-		else {
-			cp1 = (char *) &cpu->ipc_buffer[11];
-			cp2 = buf;
-			strcpy(cp2, cp1);
-			
-			while ((cp2 = strchr(cp2, '\r')) != 0) {
-				*cp2 = ' ';
-				if (cp2[1] == '\n')
-					cp2[1] = ' ';
-			}
-		}
+	if (cpu_count == 1) {
+		printk(KERN_ERR "SMP: Only one lonely processor alive.\n");
+		return;
+	}
+
+	bogosum = 0;
+        for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_present_mask & (1L << i))
+			bogosum += cpu_data[i].loops_per_sec;
+        }
+	printk(KERN_INFO "SMP: Total of %d processors activated "
+	       "(%lu.%02lu BogoMIPS).\n",
+	       cpu_count, (bogosum + 2500) / 500000,
+	       ((bogosum + 2500) / 5000) % 100);
+
+	smp_num_cpus = cpu_count;
+}
+
+/*
+ * Called by smp_init to release the blocking online cpus once they 
+ * are all started.
+ */
+void __init
+smp_commence(void)
+{
+	/* smp_init sets smp_threads_ready -- that's enough.  */
+	mb();
+}
+
+/*
+ * Only broken Intel needs this, thus it should not even be
+ * referenced globally.
+ */
+
+void __init
+initialize_secondary(void)
+{
+}
+
+
+extern void update_one_process(struct task_struct *p, unsigned long ticks,
+	                       unsigned long user, unsigned long system,
+			       int cpu);
+
+void
+smp_percpu_timer_interrupt(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+	int user = user_mode(regs);
+	struct cpuinfo_alpha *data = &cpu_data[cpu];
+
+#ifdef NOT_YET_PROFILING
+	clear_profile_irq(mid_xlate[cpu]);
+	if (!user)
+		alpha_do_profile(regs->pc);
+#endif
+
+	if (!--data->prof_counter) {
+		/* We need to make like a normal interrupt -- otherwise
+		   timer interrupts ignore the global interrupt lock,
+		   which would be a Bad Thing.  */
+		irq_enter(cpu, TIMER_IRQ);
+
+		update_one_process(current, 1, user, !user, cpu);
+	        if (current->pid) {
+	                if (--current->counter < 0) {
+				current->counter = 0;
+	                        current->need_resched = 1;
+	                }
+
+	                if (user) {
+				if (current->priority < DEF_PRIORITY) {
+					kstat.cpu_nice++;
+					kstat.per_cpu_nice[cpu]++;
+				} else {
+					kstat.cpu_user++;
+					kstat.per_cpu_user[cpu]++;
+				}
+	                } else {
+				kstat.cpu_system++;
+				kstat.per_cpu_system[cpu]++;
+	                }
+	        }
 
-		printk("secondary_console_message: on %d message is '%s'\n",
-		       mycpu, buf);
+		data->prof_counter = data->prof_multiplier;
+		irq_exit(cpu, TIMER_IRQ);
 	}
+}
 
-	hwrpb->txrdy = 0;
+int __init
+setup_profiling_timer(unsigned int multiplier)
+{
+#ifdef NOT_YET_PROFILING
+	int i;
+	unsigned long flags;
+
+	/* Prevent level14 ticker IRQ flooding. */
+	if((!multiplier) || (lvl14_resolution / multiplier) < 500)
+	        return -EINVAL;
+
+	save_and_cli(flags);
+	for (i = 0; i < NR_CPUS; i++) {
+	        if (cpu_present_mask & (1L << i)) {
+	                load_profile_irq(mid_xlate[i],
+					 lvl14_resolution / multiplier);
+	                prof_multiplier[i] = multiplier;
+	        }
+	}
+	restore_flags(flags);
+
+	return 0;
+#else
+	return -EINVAL;
+#endif
 }
 
-enum ipi_message_type {
-	IPI_TLB_ALL,
-	IPI_TLB_MM,
-	IPI_TLB_PAGE,
-	IPI_RESCHEDULE,
-	IPI_CPU_STOP
+
+static void
+send_ipi_message(unsigned long to_whom, enum ipi_message_type operation)
+{
+	long i, j;
+
+	/* Reduce the number of memory barriers by doing two loops,
+	   one to set the bits, one to invoke the interrupts.  */
+
+	mb();	/* Order out-of-band data and bit setting. */
+
+	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
+		if (to_whom & j)
+			set_bit(operation, &ipi_data[i].bits);
+	}
+
+	mb();	/* Order bit setting and interrupt. */
+
+	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
+		if (to_whom & j)
+			wripir(i);
+	}
+}
+
+/* Structure and data for smp_call_function.  This is designed to 
+   minimize static memory requirements.  Plus it looks cleaner.  */
+
+struct smp_call_struct {
+	void (*func) (void *info);
+	void *info;
+	long wait;
+	atomic_t unstarted_count;
+	atomic_t unfinished_count;
 };
 
+static struct smp_call_struct *smp_call_function_data;
+
+/* Atomicly drop data into a shared pointer.  The pointer is free if
+   it is initially locked.  If retry, spin until free.  */
+
+static inline int
+pointer_lock (void *lock, void *data, int retry)
+{
+	void *old, *tmp;
+
+	mb();
+again:
+	/* Compare and swap with zero.  */
+	asm volatile (
+	"1:	ldq_l	%0,%1\n"
+	"	mov	%3,%2\n"
+	"	bne	%0,2f\n"
+	"	stq_c	%2,%1\n"
+	"	beq	%2,1b\n"
+	"2:"
+	: "=&r"(old), "=m"(*(void **)lock), "=&r"(tmp)
+	: "r"(data)
+	: "memory");
+
+	if (old == 0)
+		return 0;
+	if (! retry)
+		return -EBUSY;
+
+	while (*(void **)lock)
+		schedule();
+	goto again;
+}
+
 void
 handle_ipi(struct pt_regs *regs)
 {
 	int this_cpu = smp_processor_id();
-	volatile int * pending_ipis = &ipi_bits[this_cpu];
+	unsigned long *pending_ipis = &ipi_data[this_cpu].bits;
 	unsigned long ops;
 
 	DBGS(("handle_ipi: on CPU %d ops 0x%x PC 0x%lx\n",
@@ -699,190 +741,189 @@
 		ops &= ~which;
 		which = ffz(~which);
 
-		if (which < IPI_RESCHEDULE) {
-			if (which == IPI_TLB_ALL)
-				tbia();
-			else if (which == IPI_TLB_MM) {
-				struct mm_struct * mm;
-				mm = ipi_msg_flush_tb.p.flush_mm;
-				if (mm == current->mm)
-					flush_tlb_current(mm);
-			}
-			else /* IPI_TLB_PAGE */ {
-				struct vm_area_struct * vma;
-				struct mm_struct * mm;
-				unsigned long addr;
-
-				vma = ipi_msg_flush_tb.p.flush_vma;
-				mm = vma->vm_mm;
-				addr = ipi_msg_flush_tb.flush_addr;
-
-				if (mm == current->mm)
-					flush_tlb_current_page(mm, vma, addr);
-			}
-			clear_bit(this_cpu, &ipi_msg_flush_tb.flush_tb_mask);
-		}
-		else if (which == IPI_RESCHEDULE) {
+		if (which == IPI_RESCHEDULE) {
 			/* Reschedule callback.  Everything to be done
 			   is done by the interrupt return path.  */
 		}
+		else if (which == IPI_CALL_FUNC) {
+			struct smp_call_struct *data;
+			void (*func)(void *info);
+			void *info;
+			int wait;
+
+			data = smp_call_function_data;
+			func = data->func;
+			info = data->info;
+			wait = data->wait;
+
+			/* Notify the sending CPU that the data has been
+			   received, and execution is about to begin.  */
+			mb();
+			atomic_dec (&data->unstarted_count);
+
+			/* At this point the structure may be gone unless
+			   wait is true.  */
+			(*func)(info);
+
+			/* Notify the sending CPU that the task is done.  */
+			mb();
+			if (wait) atomic_dec (&data->unfinished_count);
+		}
 		else if (which == IPI_CPU_STOP) {
 			halt();
 		}
 		else {
-			printk(KERN_CRIT "unknown_ipi() on CPU %d: %lu\n",
+			printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n",
 			       this_cpu, which);
 		}
 	  } while (ops);
+
 	  mb();	/* Order data access and bit testing. */
 	}
 
 	cpu_data[this_cpu].ipi_count++;
 
 	if (hwrpb->txrdy)
-		secondary_console_message();
+		recv_secondary_console_msg();
 }
 
-static void
-send_ipi_message(unsigned long to_whom, enum ipi_message_type operation)
+void
+smp_send_reschedule(int cpu)
 {
-	long i, j;
-
-	/* Reduce the number of memory barriers by doing two loops,
-	   one to set the bits, one to invoke the interrupts.  */
-
-	mb();	/* Order out-of-band data and bit setting. */
-
-	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
-		if (to_whom & j)
-			set_bit(operation, &ipi_bits[i]);
-	}
-
-	mb();	/* Order bit setting and interrupt. */
+	send_ipi_message(1L << cpu, IPI_RESCHEDULE);
+}
 
-	for (i = 0, j = 1; i < NR_CPUS; ++i, j <<= 1) {
-		if (to_whom & j)
-			wripir(i);
-	}
+void
+smp_send_stop(void)
+{
+	unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id());
+	send_ipi_message(to_whom, IPI_CPU_STOP);
 }
 
+/*
+ * Run a function on all other CPUs.
+ *  <func>	The function to run. This must be fast and non-blocking.
+ *  <info>	An arbitrary pointer to pass to the function.
+ *  <retry>	If true, keep retrying until ready.
+ *  <wait>	If true, wait until function has completed on other CPUs.
+ *  [RETURNS]   0 on success, else a negative status code.
+ *
+ * Does not return until remote CPUs are nearly ready to execute <func>
+ * or are or have executed.
+ */
+
 int
-smp_info(char *buffer)
+smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
 {
-	long i;
-	unsigned long sum = 0;
-	for (i = 0; i < NR_CPUS; i++)
-		sum += cpu_data[i].ipi_count;
+	unsigned long to_whom = cpu_present_mask ^ (1L << smp_processor_id());
+	struct smp_call_struct data;
+	long timeout;
+	
+	data.func = func;
+	data.info = info;
+	data.wait = wait;
+	atomic_set(&data.unstarted_count, smp_num_cpus - 1);
+	atomic_set(&data.unfinished_count, smp_num_cpus - 1);
+
+	/* Aquire the smp_call_function_data mutex.  */
+	if (pointer_lock(&smp_call_function_data, &data, retry))
+		return -EBUSY;
+
+	/* Send a message to all other CPUs.  */
+	send_ipi_message(to_whom, IPI_CALL_FUNC);
+
+	/* Wait for a minimal response.  */
+	timeout = jiffies + HZ;
+	while (atomic_read (&data.unstarted_count) > 0
+	       && time_before (jiffies, timeout))
+		barrier();
 
-	return sprintf(buffer, "CPUs probed %d active %d map 0x%x IPIs %ld\n",
-		       smp_num_probed, smp_num_cpus, cpu_present_map, sum);
-}
+	/* We either got one or timed out -- clear the lock.  */
+	mb();
+	smp_call_function_data = 0;
+	if (atomic_read (&data.unstarted_count) > 0)
+		return -ETIMEDOUT;
+
+	/* Wait for a complete response, if needed.  */
+	if (wait) {
+		while (atomic_read (&data.unfinished_count) > 0)
+			barrier();
+	}
 
-void
-smp_send_reschedule(int cpu)
-{
-	send_ipi_message(1 << cpu, IPI_RESCHEDULE);
+	return 0;
 }
 
-void
-smp_send_stop(void)
+static void
+ipi_flush_tlb_all(void *ignored)
 {
-	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
-	send_ipi_message(to_whom, IPI_CPU_STOP);
+	tbia();
 }
 
 void
 flush_tlb_all(void)
 {
-	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
-	long timeout = 1000000;
-
-	spin_lock(&flush_tb_lock);
-
-	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	send_ipi_message(to_whom, IPI_TLB_ALL);
 	tbia();
 
-	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
-		udelay(1);
-		barrier();
-	}
-
-	if (timeout == 0) {
-		printk("flush_tlb_all: STUCK on CPU %d mask 0x%x\n",
-		       smp_processor_id(),
-		       ipi_msg_flush_tb.flush_tb_mask);
-		ipi_msg_flush_tb.flush_tb_mask = 0;
+	/* Although we don't have any data to pass, we do want to
+	   synchronize with the other processors.  */
+	if (smp_call_function(ipi_flush_tlb_all, NULL, 1, 1)) {
+		printk(KERN_CRIT "flush_tlb_all: timed out\n");
 	}
+}
 
-	spin_unlock(&flush_tb_lock);
+static void
+ipi_flush_tlb_mm(void *x)
+{
+	struct mm_struct *mm = (struct mm_struct *) x;
+	if (mm == current->mm)
+		flush_tlb_current(mm);
 }
 
 void
 flush_tlb_mm(struct mm_struct *mm)
 {
-	unsigned long to_whom = cpu_present_map ^ (1 << smp_processor_id());
-	long timeout = 1000000;
-
-	spin_lock(&flush_tb_lock);
-
-	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	ipi_msg_flush_tb.p.flush_mm = mm;
-	send_ipi_message(to_whom, IPI_TLB_MM);
-
-	if (mm != current->mm)
-		flush_tlb_other(mm);
-	else
+	if (mm == current->mm)
 		flush_tlb_current(mm);
+	else
+		flush_tlb_other(mm);
 
-	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
-		udelay(1);
-		barrier();
+	if (smp_call_function(ipi_flush_tlb_mm, mm, 1, 1)) {
+		printk(KERN_CRIT "flush_tlb_mm: timed out\n");
 	}
+}
 
-	if (timeout == 0) {
-		printk("flush_tlb_mm: STUCK on CPU %d mask 0x%x\n",
-		       smp_processor_id(),
-		       ipi_msg_flush_tb.flush_tb_mask);
-		ipi_msg_flush_tb.flush_tb_mask = 0;
-	}
+struct flush_tlb_page_struct {
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+	unsigned long addr;
+};
 
-	spin_unlock(&flush_tb_lock);
+static void
+ipi_flush_tlb_page(void *x)
+{
+	struct flush_tlb_page_struct *data = (struct flush_tlb_page_struct *)x;
+	if (data->mm == current->mm)
+		flush_tlb_current_page(data->mm, data->vma, data->addr);
 }
 
 void
 flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
 {
-	int cpu = smp_processor_id();
-	unsigned long to_whom = cpu_present_map ^ (1 << cpu);
-	struct mm_struct * mm = vma->vm_mm;
-	int timeout = 1000000;
-
-	spin_lock(&flush_tb_lock);
-
-	ipi_msg_flush_tb.flush_tb_mask = to_whom;
-	ipi_msg_flush_tb.p.flush_vma = vma;
-	ipi_msg_flush_tb.flush_addr = addr;
-	send_ipi_message(to_whom, IPI_TLB_PAGE);
-
-	if (mm != current->mm)
-		flush_tlb_other(mm);
-	else
-		flush_tlb_current_page(mm, vma, addr);
+	struct flush_tlb_page_struct data;
+	struct mm_struct *mm = vma->vm_mm;
 
-	while (ipi_msg_flush_tb.flush_tb_mask && --timeout) {
-		udelay(1);
-		barrier();
-	}
+	data.vma = vma;
+	data.mm = mm;
+	data.addr = addr;
 
-	if (timeout == 0) {
-		printk("flush_tlb_page: STUCK on CPU %d mask 0x%x\n",
-		       smp_processor_id(),
-		       ipi_msg_flush_tb.flush_tb_mask);
-		ipi_msg_flush_tb.flush_tb_mask = 0;
+	if (mm == current->mm)
+		flush_tlb_current_page(mm, vma, addr);
+	else
+		flush_tlb_other(mm);
+	
+	if (smp_call_function(ipi_flush_tlb_page, &data, 1, 1)) {
+		printk(KERN_CRIT "flush_tlb_page: timed out\n");
 	}
-
-	spin_unlock(&flush_tb_lock);
 }
 
 void
@@ -892,6 +933,20 @@
 	flush_tlb_mm(mm);
 }
 
+
+int
+smp_info(char *buffer)
+{
+	long i;
+	unsigned long sum = 0;
+	for (i = 0; i < NR_CPUS; i++)
+		sum += cpu_data[i].ipi_count;
+
+	return sprintf(buffer, "CPUs probed %d active %d map 0x%lx IPIs %ld\n",
+		       smp_num_probed, smp_num_cpus, cpu_present_mask, sum);
+}
+
+
 #if DEBUG_SPINLOCK
 
 #ifdef MANAGE_SPINLOCK_IPL
@@ -932,17 +987,16 @@
 spin_lock(spinlock_t * lock)
 {
 	long tmp;
-	long stuck = 1<<27;
+	long stuck;
 	void *inline_pc = __builtin_return_address(0);
 	unsigned long started = jiffies;
 	int printed = 0;
 	int cpu = smp_processor_id();
 	long old_ipl = spinlock_raise_ipl(lock);
 
+	stuck = 1L << 28;
  try_again:
 
-	stuck = 0x10000000; /* was 4G, now 256M */
-
 	/* Use sub-sections to put the actual loop at the end
 	   of this object file's text section so as to perfect
 	   branch prediction.  */
@@ -961,19 +1015,16 @@
 	"	blbs	%0,2b\n"
 	"	br	1b\n"
 	".previous"
-	: "=r" (tmp),
-	  "=m" (__dummy_lock(lock)),
-	  "=r" (stuck)
-	: "2" (stuck));
+	: "=r" (tmp), "=m" (__dummy_lock(lock)), "=r" (stuck)
+	: "1" (__dummy_lock(lock)), "2" (stuck));
 
 	if (stuck < 0) {
-		if (!printed) {
-			printk("spinlock stuck at %p(%d) owner %s at %p\n",
-			       inline_pc, cpu, lock->task->comm,
-			       lock->previous);
-			printed = 1;
-		}
-		stuck = 1<<30;
+		printk(KERN_WARNING
+		       "spinlock stuck at %p(%d) owner %s at %p(%d) st %ld\n",
+		       inline_pc, cpu, lock->task->comm, lock->previous,
+		       lock->task->processor, lock->task->state);
+		stuck = 1L << 36;
+		printed = 1;
 		goto try_again;
 	}
 
@@ -984,7 +1035,7 @@
 	lock->task = current;
 
 	if (printed) {
-		printk("spinlock grabbed at %p(%d) %ld ticks\n",
+		printk(KERN_WARNING "spinlock grabbed at %p(%d) %ld ticks\n",
 		       inline_pc, cpu, jiffies - started);
 	}
 }
@@ -1006,7 +1057,7 @@
 	return ret;
 }
 #endif /* DEBUG_SPINLOCK */
-
+
 #if DEBUG_RWLOCK
 void write_lock(rwlock_t * lock)
 {
@@ -1038,18 +1089,17 @@
 	"	blt	%1,8b\n"
 	"	br	1b\n"
 	".previous"
-	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy)
-	, "=&r" (stuck_lock), "=&r" (stuck_reader)
-	: "0" (__dummy_lock(lock))
-	, "3" (stuck_lock), "4" (stuck_reader)
-	);
+	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (regy),
+	  "=&r" (stuck_lock), "=&r" (stuck_reader)
+	: "0" (__dummy_lock(lock)), "3" (stuck_lock), "4" (stuck_reader));
 
 	if (stuck_lock < 0) {
-		printk("write_lock stuck at %p\n", inline_pc);
+		printk(KERN_WARNING "write_lock stuck at %p\n", inline_pc);
 		goto try_again;
 	}
 	if (stuck_reader < 0) {
-		printk("write_lock stuck on readers at %p\n", inline_pc);
+		printk(KERN_WARNING "write_lock stuck on readers at %p\n",
+		       inline_pc);
 		goto try_again;
 	}
 }
@@ -1079,11 +1129,10 @@
 	"	br	1b\n"
 	".previous"
 	: "=m" (__dummy_lock(lock)), "=&r" (regx), "=&r" (stuck_lock)
-	: "0" (__dummy_lock(lock)), "2" (stuck_lock)
-	);
+	: "0" (__dummy_lock(lock)), "2" (stuck_lock));
 
 	if (stuck_lock < 0) {
-		printk("read_lock stuck at %p\n", inline_pc);
+		printk(KERN_WARNING "read_lock stuck at %p\n", inline_pc);
 		goto try_again;
 	}
 }
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
--- v2.3.1/linux/arch/alpha/kernel/time.c	Sat Apr 24 17:54:08 1999
+++ linux/arch/alpha/kernel/time.c	Fri May 14 12:41:23 1999
@@ -42,6 +42,9 @@
 #include "proto.h"
 #include "irq.h"
 
+extern rwlock_t xtime_lock;
+extern volatile unsigned long lost_ticks;	/*kernel/sched.c*/
+
 static int set_rtc_mmss(unsigned long);
 
 
@@ -86,15 +89,15 @@
 	long nticks;
 
 #ifdef __SMP__
-	extern void smp_percpu_timer_interrupt(struct pt_regs *);
-	extern unsigned int boot_cpu_id;
-	/* when SMP, do this for *all* CPUs, 
-	   but only do the rest for the boot CPU */
+	/* When SMP, do this for *all* CPUs, but only do the rest for
+           the boot CPU.  */
 	smp_percpu_timer_interrupt(regs);
-	if (smp_processor_id() != boot_cpu_id)
-	  return;
+	if (smp_processor_id() != smp_boot_cpuid)
+		return;
 #endif
 
+	write_lock(&xtime_lock);
+
 	/*
 	 * Calculate how many ticks have passed since the last update,
 	 * including any previous partial leftover.  Save any resulting
@@ -124,6 +127,8 @@
 		int tmp = set_rtc_mmss(xtime.tv_sec);
 		state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0);
 	}
+
+	write_unlock(&xtime_lock);
 }
 
 /*
@@ -226,7 +231,8 @@
 {
 	void (*irq_handler)(int, void *, struct pt_regs *);
 	unsigned int year, mon, day, hour, min, sec, cc1, cc2;
-	unsigned long cycle_freq, diff, one_percent;
+	unsigned long cycle_freq, one_percent;
+	long diff;
 
 	/*
 	 * The Linux interpretation of the CMOS clock register contents:
@@ -242,7 +248,7 @@
 
 	if (!est_cycle_freq) {
 		/* Sometimes the hwrpb->cycle_freq value is bogus. 
-	   	Go another round to check up on it and see.  */
+		   Go another round to check up on it and see.  */
 		do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
 		do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
 		cc2 = rpcc();
@@ -279,8 +285,7 @@
 	mon = CMOS_READ(RTC_MONTH);
 	year = CMOS_READ(RTC_YEAR);
 
-	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
-	{
+	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
 		BCD_TO_BIN(sec);
 		BCD_TO_BIN(min);
 		BCD_TO_BIN(hour);
@@ -328,18 +333,24 @@
 void
 do_gettimeofday(struct timeval *tv)
 {
-	unsigned long flags, delta_cycles, delta_usec;
-	unsigned long sec, usec;
-	__u32 now;
-	extern volatile unsigned long lost_ticks;	/*kernel/sched.c*/
+	unsigned long sec, usec, lost, flags;
+	unsigned long delta_cycles, delta_usec, partial_tick;
 
-	now = rpcc();
-	save_and_cli(flags);
+	read_lock_irqsave(&xtime_lock, flags);
+
+	delta_cycles = rpcc() - state.last_time;
 	sec = xtime.tv_sec;
 	usec = xtime.tv_usec;
-	delta_cycles = now - state.last_time;
-	restore_flags(flags);
+	partial_tick = state.partial_tick;
+	lost = lost_ticks;
+
+	read_unlock_irqrestore(&xtime_lock, flags);
 
+#ifdef __SMP__
+	/* Until and unless we figure out how to get cpu cycle counters
+	   in sync and keep them there, we can't use the rpcc tricks.  */
+	delta_usec = lost * (1000000 / HZ);
+#else
 	/*
 	 * usec = cycles * ticks_per_cycle * 2**48 * 1e6 / (2**48 * ticks)
 	 *	= cycles * (s_t_p_c) * 1e6 / (2**48 * ticks)
@@ -354,13 +365,10 @@
 	 */
 
 	delta_usec = (delta_cycles * state.scaled_ticks_per_cycle 
-			+ state.partial_tick
-			+ (lost_ticks << FIX_SHIFT) ) * 15625;
+		      + partial_tick
+		      + (lost << FIX_SHIFT)) * 15625;
 	delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2;
-
-	/* the 'lost_tics' term above implements this:	
-	 * delta_usec += lost_ticks * (1000000 / HZ);
-	 */
+#endif
 
 	usec += delta_usec;
 	if (usec >= 1000000) {
@@ -375,13 +383,41 @@
 void
 do_settimeofday(struct timeval *tv)
 {
-	cli();
-	xtime = *tv;
+	unsigned long delta_usec;
+	long sec, usec;
+	
+	write_lock_irq(&xtime_lock);
+
+	/* The offset that is added into time in do_gettimeofday above
+	   must be subtracted out here to keep a coherent view of the
+	   time.  Without this, a full-tick error is possible.  */
+
+#ifdef __SMP__
+	delta_usec = lost_ticks * (1000000 / HZ);
+#else
+	delta_usec = rpcc() - state.last_time;
+	delta_usec = (delta_usec * state.scaled_ticks_per_cycle 
+		      + state.partial_tick
+		      + (lost_ticks << FIX_SHIFT)) * 15625;
+	delta_usec = ((delta_usec / ((1UL << (FIX_SHIFT-6-1)) * HZ)) + 1) / 2;
+#endif
+
+	sec = tv->tv_sec;
+	usec = tv->tv_usec;
+	usec -= delta_usec;
+	if (usec < 0) {
+		usec += 1000000;
+		sec -= 1;
+	}
+
+	xtime.tv_sec = sec;
+	xtime.tv_usec = usec;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti();
+
+	write_unlock_irq(&xtime_lock);
 }
 
 
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c
--- v2.3.1/linux/arch/alpha/kernel/traps.c	Mon May 10 09:55:21 1999
+++ linux/arch/alpha/kernel/traps.c	Fri May 14 12:41:23 1999
@@ -1,5 +1,5 @@
 /*
- * kernel/traps.c
+ * arch/alpha/kernel/traps.c
  *
  * (C) Copyright 1994 Linus Torvalds
  */
@@ -95,6 +95,9 @@
 {
 	if (regs->ps & 8)
 		return;
+#ifdef __SMP__
+	printk("CPU %d ", hard_smp_processor_id());
+#endif
 	printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err);
 	dik_show_regs(regs, r9_15);
 	dik_show_code((unsigned int *)regs->pc);
@@ -128,8 +131,8 @@
 	if (summary & 1) {
 		/* Software-completion summary bit is set, so try to
 		   emulate the instruction.  */
-		if (implver() == IMPLVER_EV6) {
-			/* Whee!  EV6 has precice exceptions.  */
+		if (!amask(AMASK_PRECISE_TRAP)) {
+			/* 21264 (except pass 1) has precise exceptions.  */
 			if (alpha_fp_emul(regs.pc - 4))
 				return;
 		} else {
@@ -138,14 +141,12 @@
 		}
 	}
 
-	lock_kernel();
 #if 0
 	printk("%s: arithmetic trap at %016lx: %02lx %016lx\n",
 		current->comm, regs.pc, summary, write_mask);
 #endif
 	die_if_kernel("Arithmetic fault", &regs, 0, 0);
 	send_sig(SIGFPE, current, 1);
-	unlock_kernel();
 }
 
 asmlinkage void
@@ -235,10 +236,8 @@
 	  unsigned long a2, unsigned long a3, unsigned long a4,
 	  unsigned long a5, struct pt_regs regs)
 {
-	lock_kernel();
 	die_if_kernel("Instruction fault", &regs, type, 0);
 	force_sig(SIGILL, current);
-	unlock_kernel();
 }
 
 
@@ -453,10 +452,8 @@
 		unsigned long newpc;
 		newpc = fixup_exception(una_reg, fixup, pc);
 
-		lock_kernel();
 		printk("Forwarding unaligned exception at %lx (%lx)\n",
 		       pc, newpc);
-		unlock_kernel();
 
 		(&regs)->pc = newpc;
 		return;
@@ -610,11 +607,9 @@
 			cnt = 0;
 		}
 		if (++cnt < 5) {
-			lock_kernel();
 			printk("%s(%d): unaligned trap at %016lx: %p %lx %ld\n",
 			       current->comm, current->pid,
 			       regs->pc - 4, va, opcode, reg);
-			unlock_kernel();
 		}
 		last_time = jiffies;
 	}
@@ -868,16 +863,12 @@
 
 give_sigsegv:
 	regs->pc -= 4;  /* make pc point to faulting insn */
-	lock_kernel();
 	send_sig(SIGSEGV, current, 1);
-	unlock_kernel();
 	return;
 
 give_sigbus:
 	regs->pc -= 4;
-	lock_kernel();
 	send_sig(SIGBUS, current, 1);
-	unlock_kernel();
 	return;
 }
 
diff -u --recursive --new-file v2.3.1/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c
--- v2.3.1/linux/arch/alpha/mm/init.c	Mon Oct 12 11:40:12 1998
+++ linux/arch/alpha/mm/init.c	Fri May 14 12:41:23 1999
@@ -256,26 +256,6 @@
 	return start_mem;
 }
 
-#ifdef __SMP__
-/*
- * paging_init_secondary(), called ONLY by secondary CPUs,
- * sets up current->tss contents appropriately and does a load_PCB.
- * note that current should be pointing at the idle thread task struct
- * for this CPU.
- */
-void
-paging_init_secondary(void)
-{
-	current->tss.ptbr = init_task.tss.ptbr;
-	current->tss.pal_flags = 1;
-	current->tss.flags = 0;
-	load_PCB(&current->tss);
-	tbia();
-
-	return;
-}
-#endif /* __SMP__ */
-
 #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_SRM)
 void
 srm_paging_stop (void)
diff -u --recursive --new-file v2.3.1/linux/arch/i386/boot/video.S linux/arch/i386/boot/video.S
--- v2.3.1/linux/arch/i386/boot/video.S	Tue Sep 29 21:03:35 1998
+++ linux/arch/i386/boot/video.S	Fri May 14 12:47:01 1999
@@ -1,14 +1,19 @@
 !
-!	Display adapter & video mode setup, version 2.12 (25-May-98)
+!	Display adapter & video mode setup, version 2.13 (14-May-99)
 !
-!	Copyright (C) 1995 -- 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+!	Copyright (C) 1995 -- 1999 Martin Mares <mj@ucw.cz>
 !	Based on the original setup.S code (C) Linus Torvalds and Mats Anderson
 !
+!	For further information, look at Documentation/svga.txt.
+!
 
 #include <linux/config.h> /* for CONFIG_VIDEO_* */
 
 ! Enable autodetection of SVGA adapters and modes. If you really need this
-! feature, drop me a mail as I think of removing it some day...
+! feature, drop me a mail as I think of removing it some day. You can
+! always enter `scan' to get the video mode table and then use the real
+! video mode numbers (those 4-digit hexadecimal numbers, NOT the menu
+! item numbers) which don't rely on any autodetection.
 #undef CONFIG_VIDEO_SVGA
 
 ! Enable autodetection of VESA modes
@@ -1939,7 +1944,7 @@
 badmdt:		.ascii	"You passed an undefined mode number."
 		db	0x0d, 0x0a, 0
 vesaer:		.ascii	"Error: Scanning of VESA modes failed. Please "
-		.ascii	"report to <mj@k332.feld.cvut.cz>."
+		.ascii	"report to <mj@ucw.cz>."
 		db	0x0d, 0x0a, 0
 old_name:	.ascii	"CGA/MDA/HGA"
 		db	0
diff -u --recursive --new-file v2.3.1/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.3.1/linux/arch/i386/defconfig	Fri May 14 18:55:11 1999
+++ linux/arch/i386/defconfig	Fri May 14 18:27:32 1999
@@ -305,6 +305,7 @@
 CONFIG_USB_MOUSE=y
 CONFIG_USB_KBD=y
 # CONFIG_USB_AUDIO is not set
+# CONFIG_USB_ACM is not set
 
 #
 # Filesystems
diff -u --recursive --new-file v2.3.1/linux/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- v2.3.1/linux/arch/i386/kernel/apm.c	Fri May 14 18:55:11 1999
+++ linux/arch/i386/kernel/apm.c	Thu May 13 23:22:05 1999
@@ -314,7 +314,7 @@
 static int			debug = 0;
 static int			apm_disabled = 0;
 
-static struct wait_queue *	process_list = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(process_list);
 static struct apm_bios_struct *	user_list = NULL;
 
 static struct timer_list	apm_timer;
diff -u --recursive --new-file v2.3.1/linux/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- v2.3.1/linux/arch/i386/kernel/mtrr.c	Mon May 10 10:32:45 1999
+++ linux/arch/i386/kernel/mtrr.c	Fri May 14 08:59:54 1999
@@ -1,6 +1,6 @@
 /*  Generic MTRR (Memory Type Range Register) driver.
 
-    Copyright (C) 1997-1998  Richard Gooch
+    Copyright (C) 1997-1999  Richard Gooch
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -196,6 +196,11 @@
     19990310   Richard Gooch <rgooch@atnf.csiro.au>
 	       Support K6-II/III based on Alan Cox's <alan@redhat.com> patches.
   v1.34
+    19990511   Bart Hartgers <bart@etpmod.phys.tue.nl>
+	       Support Centaur C6 MCR's.
+    19990512   Richard Gooch <rgooch@atnf.csiro.au>
+	       Minor cleanups.
+  v1.35
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -232,7 +237,7 @@
 #include <asm/hardirq.h>
 #include "irq.h"
 
-#define MTRR_VERSION            "1.34 (19990310)"
+#define MTRR_VERSION            "1.35 (19990512)"
 
 #define TRUE  1
 #define FALSE 0
@@ -313,8 +318,13 @@
     /*  Disable interrupts locally  */
     __save_flags (ctxt->flags); __cli ();
 
-    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) return;
-
+    switch (boot_cpu_data.x86_vendor)
+    {
+      case X86_VENDOR_AMD:
+      case X86_VENDOR_CENTAUR:
+	return;
+	/*break;*/
+    }
     /*  Save value of CR4 and clear Page Global Enable (bit 7)  */
     if (boot_cpu_data.x86_capability & X86_FEATURE_PGE)
 	asm volatile ("movl  %%cr4, %0\n\t"
@@ -352,12 +362,14 @@
 {
     unsigned long tmp;
 
-    if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+    switch (boot_cpu_data.x86_vendor)
     {
+      case X86_VENDOR_AMD:
+      case X86_VENDOR_CENTAUR:
 	__restore_flags (ctxt->flags);
 	return;
+	/*break;*/
     }
-
     /*  Flush caches and TLBs  */
     asm volatile ("wbinvd" : : : "memory" );
 
@@ -399,7 +411,9 @@
 	return (config & 0xff);
 	/*break;*/
       case X86_VENDOR_CYRIX:
-	/*  Cyrix have 8 ARRs */
+	/*  Cyrix have 8 ARRs  */
+      case X86_VENDOR_CENTAUR:
+        /*  and Centaur has 8 MCR's  */
 	return 8;
 	/*break;*/
       case X86_VENDOR_AMD:
@@ -422,6 +436,7 @@
 	/*break;*/
       case X86_VENDOR_CYRIX:
       case X86_VENDOR_AMD:
+      case X86_VENDOR_CENTAUR:
 	return 1;
 	/*break;*/
     }
@@ -450,7 +465,6 @@
 
     /* Clean up mask_lo so it gives the real address mask. */
     mask_lo = (mask_lo & 0xfffff000UL);
-    
     /* This works correctly if size is a power of two, i.e. a
        contiguous range. */
     *size = ~(mask_lo - 1);
@@ -480,7 +494,6 @@
 
     /* Enable interrupts if it was enabled previously */
     __restore_flags (flags);
-    
     shift = ((unsigned char *) base)[1] & 0x0f;
     *base &= 0xfffff000UL;
 
@@ -550,6 +563,20 @@
     return;
 }   /*  End Function amd_get_mtrr  */
 
+static struct
+{
+    unsigned long high;
+    unsigned long low;
+} centaur_mcr[8];
+
+static void centaur_get_mcr (unsigned int reg, unsigned long *base,
+			     unsigned long *size, mtrr_type *type)
+{
+    *base = centaur_mcr[reg].high & 0xfffff000;
+    *size = (~(centaur_mcr[reg].low & 0xfffff000))+1;
+    *type = MTRR_TYPE_WRCOMB;	/*  If it is there, it is write-combining  */
+}   /*  End Function centaur_get_mcr  */
+
 static void (*get_mtrr) (unsigned int reg, unsigned long *base,
 			 unsigned long *size, mtrr_type *type) = NULL;
 
@@ -647,11 +674,10 @@
     else
 	/* Set the register to the base (already shifted for us), the
 	   type (off by one) and an inverted bitmask of the size
-        	   
 	   The size is the only odd bit. We are fed say 512K
 	   We invert this and we get 111 1111 1111 1011 but
 	   if you subtract one and invert you get the desired
-	   111 1111 1111 1100 mask 
+	   111 1111 1111 1100 mask
 	   */
 	*(reg ? &high : &low)=(((~(size-1))>>15)&0x0001FFFC)|base|(type+1);
     /*
@@ -663,10 +689,36 @@
     if (do_safe) set_mtrr_done (&ctxt);
 }   /*  End Function amd_set_mtrr_up  */
 
+
+static void centaur_set_mcr_up (unsigned int reg, unsigned long base,
+				unsigned long size, mtrr_type type,
+				int do_safe)
+{
+    struct set_mtrr_context ctxt;
+    unsigned long low, high;
+
+    if (do_safe) set_mtrr_prepare( &ctxt );
+    if (size == 0)
+    {
+        /*  Disable  */
+        high = low = 0;
+    }
+    else
+    {
+        high = base & 0xfffff000; /* base works on 4K pages... */
+        low = ((~(size-1))&0xfffff000);
+        low |= 0x1f;		  /* only support write-combining... */
+    }
+    centaur_mcr[reg].high = high;
+    centaur_mcr[reg].low = low;
+    wrmsr (0x110 + reg, low, high);
+    if (do_safe) set_mtrr_done( &ctxt );
+}   /*  End Function centaur_set_mtrr_up  */
+
 static void (*set_mtrr_up) (unsigned int reg, unsigned long base,
 			    unsigned long size, mtrr_type type,
 			    int do_safe) = NULL;
-     
+
 #ifdef __SMP__
 
 struct mtrr_var_range
@@ -694,23 +746,21 @@
 {
     unsigned int lo, hi;
     int changed = FALSE;
-    
-    rdmsr(MTRRphysBase_MSR(index), lo, hi); 
 
+    rdmsr(MTRRphysBase_MSR(index), lo, hi);
     if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
 	|| (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
-	wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); 
+	wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
 	changed = TRUE;
     }
 
-    rdmsr(MTRRphysMask_MSR(index), lo, hi); 
+    rdmsr(MTRRphysMask_MSR(index), lo, hi);
 
     if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
 	|| (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
-	wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); 
+	wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
 	changed = TRUE;
     }
-    
     return changed;
 }   /*  End Function set_mtrr_var_range_testing  */
 
@@ -723,7 +773,6 @@
 
     for (i = 0; i < 2; i++)
 	rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
- 
     for (i = 0; i < 8; i++)
 	rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
 }   /*  End Function get_fixed_ranges  */
@@ -777,14 +826,13 @@
     unsigned long lo, dummy;
 
     nvrs = state->num_var_ranges = get_num_var_ranges();
-    vrs = state->var_ranges 
+    vrs = state->var_ranges
               = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL);
     if (vrs == NULL)
 	nvrs = state->num_var_ranges = 0;
 
     for (i = 0; i < nvrs; i++)
 	get_mtrr_var_range (i, &vrs[i]);
-    
     get_fixed_ranges (state->fixed_ranges);
 
     rdmsr (MTRRdefType_MSR, lo, dummy);
@@ -818,7 +866,6 @@
 
     if ( set_fixed_ranges_testing(state->fixed_ranges) )
 	change_mask |= MTRR_CHANGE_MASK_FIXED;
-    
     /*  Set_mtrr_restore restores the old value of MTRRdefType,
 	so to set it we fiddle with the saved value  */
     if ((ctxt->deftype_lo & 0xff) != state->def_type
@@ -831,7 +878,7 @@
     return change_mask;
 }   /*  End Function set_mtrr_state  */
 
- 
+
 static atomic_t undone_count;
 static volatile int wait_barrier_execute = FALSE;
 static volatile int wait_barrier_cache_enable = FALSE;
@@ -1025,13 +1072,22 @@
 	}
 	/*  Fall through  */
       case X86_VENDOR_CYRIX:
+      case X86_VENDOR_CENTAUR:
 	if ( (base & 0xfff) || (size & 0xfff) )
 	{
 	    printk ("mtrr: size and base must be multiples of 4 kiB\n");
 	    printk ("mtrr: size: %lx  base: %lx\n", size, base);
 	    return -EINVAL;
 	}
-	if (base + size < 0x100000)
+        if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR)
+	{
+	    if (type != MTRR_TYPE_WRCOMB)
+	    {
+		printk ("mtrr: only write-combining is supported\n");
+		return -EINVAL;
+	    }
+	}
+	else if (base + size < 0x100000)
 	{
 	    printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
 		    base, size);
@@ -1050,7 +1106,7 @@
 	}
 	break;
       case X86_VENDOR_AMD:
-    	/* Apply the K6 block alignment and size rules 
+    	/* Apply the K6 block alignment and size rules
     	   In order
     	      o Uncached or gathering only
     	      o 128K or bigger block
@@ -1572,6 +1628,30 @@
     if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n");
 }   /*  End Function cyrix_arr_init  */
 
+__initfunc(static void centaur_mcr_init (void))
+{
+    unsigned i;
+    struct set_mtrr_context ctxt;
+
+    set_mtrr_prepare (&ctxt);
+    /* Unfortunately, MCR's are read-only, so there is no way to
+     * find out what the bios might have done.
+     */
+    /* Clear all MCR's.
+     * This way we are sure that the centaur_mcr array contains the actual
+     * values. The disadvantage is that any BIOS tweaks are thus undone.
+     */
+    for (i = 0; i < 8; ++i)
+    {
+        centaur_mcr[i].high = 0;
+	centaur_mcr[i].low = 0;
+	wrmsr (0x110 + i , 0, 0);
+    }
+    /*  Throw the main write-combining switch...  */
+    wrmsr (0x120, 0x01f0001f, 0);
+    set_mtrr_done (&ctxt);
+}   /*  End Function centaur_mcr_init  */
+
 __initfunc(static void mtrr_setup (void))
 {
     printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MTRR_VERSION);
@@ -1582,7 +1662,6 @@
 	set_mtrr_up = intel_set_mtrr_up;
 	break;
       case X86_VENDOR_CYRIX:
-	printk ("mtrr: Using Cyrix style ARRs\n");
 	get_mtrr = cyrix_get_arr;
 	set_mtrr_up = cyrix_set_arr_up;
 	get_free_region = cyrix_get_free_region;
@@ -1591,6 +1670,10 @@
 	get_mtrr = amd_get_mtrr;
 	set_mtrr_up = amd_set_mtrr_up;
 	break;
+     case X86_VENDOR_CENTAUR:
+        get_mtrr = centaur_get_mcr;
+        set_mtrr_up = centaur_set_mcr_up;
+        break;
     }
 }   /*  End Function mtrr_setup  */
 
@@ -1611,6 +1694,9 @@
       case X86_VENDOR_CYRIX:
 	cyrix_arr_init ();
 	break;
+      case X86_VENDOR_CENTAUR:
+        centaur_mcr_init ();
+        break;
     }
 }   /*  End Function mtrr_init_boot_cpu  */
 
@@ -1675,6 +1761,9 @@
       case X86_VENDOR_CYRIX:
 	cyrix_arr_init ();
 	break;
+      case X86_VENDOR_CENTAUR:
+        centaur_mcr_init ();
+        break;
     }
 #  endif  /*  !__SMP__  */
 
diff -u --recursive --new-file v2.3.1/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.3.1/linux/arch/i386/kernel/setup.c	Mon May 10 10:32:45 1999
+++ linux/arch/i386/kernel/setup.c	Fri May 14 08:59:54 1999
@@ -9,6 +9,9 @@
  *  Force Cyrix 6x86(MX) and M II processors to report MTRR capability
  *  and fix against Cyrix "coma bug" by
  *      Zoltan Boszormenyi <zboszor@mol.hu> February 1999.
+ * 
+ *  Force Centaur C6 processors to report MTRR capability.
+ *      Bart Hartgers <bart@etpmod.phys.tue.nl>, May 199.
  */
 
 /*
@@ -861,6 +864,8 @@
 		/* lv|=(1<<6);	- may help too if the board can cope */
 		printk("now 0x%X", lv);
 		wrmsr(0x107, lv, hv);
+		/* Emulate MTRRs using Centaur's MCR. */
+		c->x86_capability |= X86_FEATURE_MTRR;	   	
 	}
 	printk("\n");
 }
diff -u --recursive --new-file v2.3.1/linux/drivers/block/hpt343.c linux/drivers/block/hpt343.c
--- v2.3.1/linux/drivers/block/hpt343.c	Fri May 14 18:55:13 1999
+++ linux/drivers/block/hpt343.c	Fri May 14 18:50:32 1999
@@ -370,7 +370,6 @@
 		inb(hpt343IoBase + 0x0011));
 #endif
 
-	pci_write_config_byte(dev, HPT343_PCI_INIT_REG, 0x80);
 	if (cmd & PCI_COMMAND_MEMORY) {
 		if (dev->rom_address) {
 			pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
diff -u --recursive --new-file v2.3.1/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.3.1/linux/drivers/block/ide-pci.c	Fri May 14 18:55:14 1999
+++ linux/drivers/block/ide-pci.c	Fri May 14 18:50:32 1999
@@ -240,7 +240,6 @@
 					dev->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO;
 
 				pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
-				pci_write_config_byte(dev, 0x80, 0x80);
 				if (!(pcicmd & PCI_COMMAND_MEMORY))
 					pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
 			}
diff -u --recursive --new-file v2.3.1/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.3.1/linux/drivers/char/mem.c	Mon May 10 10:18:34 1999
+++ linux/drivers/char/mem.c	Fri May 14 18:15:02 1999
@@ -51,14 +51,8 @@
 #if defined(CONFIG_PPC) || defined(CONFIG_MAC)
 extern void adbdev_init(void);
 #endif
-#ifdef CONFIG_USB_UHCI
-int uhci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI
-int ohci_init(void);
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-int ohci_hcd_init(void);
+#ifdef CONFIG_USB
+extern void usb_init(void);
 #endif
      
 static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp,
@@ -609,15 +603,7 @@
 		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 	rand_initialize();
 #ifdef CONFIG_USB
-#ifdef CONFIG_USB_UHCI
-	uhci_init();
-#endif
-#ifdef CONFIG_USB_OHCI
-	ohci_init();
-#endif
-#ifdef CONFIG_USB_OHCI_HCD
-        ohci_hcd_init(); 
-#endif
+	usb_init();
 #endif
 #if defined (CONFIG_FB)
 	fbmem_init();
diff -u --recursive --new-file v2.3.1/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.3.1/linux/drivers/scsi/ide-scsi.c	Thu Apr 22 19:24:50 1999
+++ linux/drivers/scsi/ide-scsi.c	Thu May 13 23:23:27 1999
@@ -40,12 +40,11 @@
 #include <linux/errno.h>
 #include <linux/hdreg.h>
 #include <linux/malloc.h>
+#include <linux/ide.h>
 
 #include <asm/io.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
-
-#include "../block/ide.h"
 
 #include "scsi.h"
 #include "hosts.h"
diff -u --recursive --new-file v2.3.1/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- v2.3.1/linux/drivers/scsi/sg.c	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/sg.c	Fri May 14 17:56:58 1999
@@ -1334,13 +1334,13 @@
         return sdp->headfp;
     }
     sfp = (Sg_fd *)sg_low_malloc(sizeof(Sg_fd), 0, SG_HEAP_KMAL, 0);
-    if (sfp) {
-        memset(sfp, 0, sizeof(Sg_fd));
-        sfp->my_mem_src = SG_HEAP_KMAL;
-    }
-    else
-        return NULL;
-        
+    if (!sfp)
+	return NULL;
+
+    memset(sfp, 0, sizeof(Sg_fd));
+    sfp->my_mem_src = SG_HEAP_KMAL;
+
+    init_waitqueue_head(&sfp->read_wait);    
     sfp->timeout = SG_DEFAULT_TIMEOUT;
     sfp->force_packid = SG_DEF_FORCE_PACK_ID;
     sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
diff -u --recursive --new-file v2.3.1/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v2.3.1/linux/drivers/scsi/sr_ioctl.c	Fri May 14 18:55:22 1999
+++ linux/drivers/scsi/sr_ioctl.c	Fri May 14 16:04:36 1999
@@ -122,11 +122,9 @@
             if (!quiet)
 		printk(KERN_ERR "sr%d: CDROM (ioctl) reports ILLEGAL "
 		       "REQUEST.\n", target);
-            if ((SCpnt->sense_buffer[12] == 0x20 ||
-	        SCpnt->sense_buffer[12] == 0x24) &&
+            if (SCpnt->sense_buffer[12] == 0x20 &&
                 SCpnt->sense_buffer[13] == 0x00) {
                 /* sense: Invalid command operation code */
-                /* or Invalid field in cdb */
                 err = -EDRIVE_CANT_DO_THIS;
             } else {
                 err = -EINVAL;
diff -u --recursive --new-file v2.3.1/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.3.1/linux/drivers/sound/dev_table.h	Mon Apr 12 16:18:27 1999
+++ linux/drivers/sound/dev_table.h	Thu May 13 23:22:48 1999
@@ -234,9 +234,9 @@
 	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
 
 	/* fields formerly in dmabuf.c */
-	struct wait_queue *in_sleeper;
-	struct wait_queue *out_sleeper;
-	struct wait_queue *poll_sleeper;
+	wait_queue_head_t in_sleeper;
+	wait_queue_head_t out_sleeper;
+	wait_queue_head_t poll_sleeper;
 
 	/* fields formerly in audio.c */
 	int audio_mode;
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/Config.in linux/drivers/usb/Config.in
--- v2.3.1/linux/drivers/usb/Config.in	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/Config.in	Fri May 14 18:09:57 1999
@@ -23,6 +23,7 @@
   bool 'USB mouse support' CONFIG_USB_MOUSE
   bool 'USB keyboard support' CONFIG_USB_KBD
   bool 'USB audio parsing support' CONFIG_USB_AUDIO
+  bool 'USB Abstract Control Model support' CONFIG_USB_ACM
 fi
 
 endmenu
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/Makefile linux/drivers/usb/Makefile
--- v2.3.1/linux/drivers/usb/Makefile	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/Makefile	Fri May 14 18:09:57 1999
@@ -24,6 +24,10 @@
   USBX_OBJS += mouse.o
 endif
 
+ifeq ($(CONFIG_USB_ACM),y)
+  USBX_OBJS += acm.o
+endif
+
 ifeq ($(CONFIG_USB_KBD),y)
   USBX_OBJS += keyboard.o keymap.o
 endif
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/acm.c linux/drivers/usb/acm.c
--- v2.3.1/linux/drivers/usb/acm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/usb/acm.c	Fri May 14 18:09:57 1999
@@ -0,0 +1,220 @@
+/*
+ * USB Abstract Control Model based on Brad Keryan's USB busmouse driver 
+ *
+ * Armin Fuerst 5/8/1999
+ *
+ * version 0.0: Driver sets up configuration, setus up data pipes, opens misc
+ * device. No actual data transfer is done, since we don't have bulk transfer,
+ * yet. Purely skeleton for now.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define USB_ACM_MINOR 32
+
+struct acm_state {
+	int present; /* this acm is plugged in */
+	int active; /* someone is has this acm's device open */
+	struct usb_device *dev;
+	unsigned int readpipe,writepipe;
+};
+
+static struct acm_state static_acm_state;
+
+spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED;
+
+static int acm_irq(int state, void *__buffer, void *dev_id)
+{
+/*
+	signed char *data = __buffer;
+        struct acm_state *acm = &static_acm_state; 
+	if(!acm->active)
+		return 1;
+*/
+
+	/*We should so something useful here*/
+	printk("ACM_USB_IRQ\n");
+
+	return 1;
+}
+
+static int release_acm(struct inode * inode, struct file * file)
+{
+	struct acm_state *acm = &static_acm_state;
+	printk("ACM_FILE_RELEASE\n");
+
+//	fasync_acm(-1, file, 0);
+	if (--acm->active)
+		return 0;
+	return 0;
+}
+
+static int open_acm(struct inode * inode, struct file * file)
+{
+	struct acm_state *acm = &static_acm_state;
+	printk("USB_FILE_OPEN\n");
+
+	if (!acm->present)
+		return -EINVAL;
+	if (acm->active++)
+		return 0;
+	return 0;
+}
+
+static ssize_t write_acm(struct file * file,
+       const char * buffer, size_t count, loff_t *ppos)
+{
+        char * buffer="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	struct acm_state *acm = &static_acm_state;
+	printk("USB_FILE_WRITE\n");
+		printk("writing:>%s<\n",buffer);
+		acm->dev->bus->op->bulk_msg(acm->dev,acm->writepipe,buffer, 26);
+		printk("done:>%s<\n",buffer);
+		printk("reading:>%s<\n",buffer);
+		acm->dev->bus->op->bulk_msg(acm->dev,acm->readpipe,buffer, 26);
+		printk("done:>%s<\n",buffer);
+	return -EINVAL;
+}
+
+static ssize_t read_acm(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+	printk("USB_FILE_READ\n");
+	return -EINVAL;
+}
+
+struct file_operations usb_acm_fops = {
+	NULL,		/* acm_seek */
+	read_acm,
+	write_acm,
+	NULL, 		/* acm_readdir */
+	NULL,	 	/* acm_poll */
+	NULL, 		/* acm_ioctl */
+	NULL,		/* acm_mmap */
+	open_acm,
+	NULL,		/* flush */
+	release_acm,
+	NULL,
+	NULL,		/*fasync*/
+};
+
+static struct miscdevice usb_acm = {
+	USB_ACM_MINOR, "USB ACM", &usb_acm_fops
+};
+
+static int acm_probe(struct usb_device *dev)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct acm_state *acm = &static_acm_state;
+	int cfgnum;
+
+	/* Only use CDC */
+	if (dev->descriptor.bDeviceClass != 2 ||
+	    dev->descriptor.bDeviceSubClass != 0 ||
+            dev->descriptor.bDeviceProtocol != 0)
+		return -1;
+
+	/*Now scan all configs for a ACM configuration*/
+	for (cfgnum=0;cfgnum<dev->descriptor.bNumConfigurations;cfgnum++) {
+		/* The first one should be Communications interface? */
+		interface = &dev->config[cfgnum].interface[0];
+		if (interface->bInterfaceClass != 2 ||
+		    interface->bInterfaceSubClass != 2 ||
+		    interface->bInterfaceProtocol != 1 ||
+	    	    interface->bNumEndpoints != 1)
+			continue;
+
+		/*Which uses an interrupt input */
+		endpoint = &interface->endpoint[0];
+		if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+		    (endpoint->bmAttributes & 3) != 3)
+			continue;
+			
+		/* The second one should be a Data interface? */
+		interface = &dev->config[cfgnum].interface[1];
+		if (interface->bInterfaceClass != 10 ||
+		    interface->bInterfaceSubClass != 0 ||
+		    interface->bInterfaceProtocol != 0 ||
+	    	    interface->bNumEndpoints != 2)
+			continue;
+
+		/*With a bulk input */
+		endpoint = &interface->endpoint[0];
+		if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+		    (endpoint->bmAttributes & 3) != 2)
+			continue;
+			
+		/*And a bulk output */
+		endpoint = &interface->endpoint[1];
+		if ((endpoint->bEndpointAddress & 0x80) == 0x80 ||
+		    (endpoint->bmAttributes & 3) != 2)
+			continue;
+
+		printk("USB ACM found\n");
+		usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
+		acm->dev=dev;
+		acm->readpipe=__create_pipe(dev,&dev->config[cfgnum].interface[1].endpoint[0]);
+		acm->writepipe=__create_pipe(dev,&dev->config[cfgnum].interface[1].endpoint[1]);
+		usb_request_irq(dev, usb_rcvctrlpipe(dev,&dev->config[cfgnum].interface[0].endpoint[0]), acm_irq, endpoint->bInterval, NULL);
+		acm->present = 1;
+		return 0;
+	}
+
+	return -1;
+}
+
+static void acm_disconnect(struct usb_device *dev)
+{
+	struct acm_state *acm = &static_acm_state;
+
+	/* this might need work */
+	acm->present = 0;
+}
+
+static struct usb_driver acm_driver = {
+	"acm",
+	acm_probe,
+	acm_disconnect,
+	{ NULL, NULL }
+};
+
+int usb_acm_init(void)
+{
+	struct acm_state *acm = &static_acm_state;
+
+	misc_register(&usb_acm);
+
+	acm->present = acm->active = 0;
+
+	usb_register(&acm_driver);
+	printk(KERN_INFO "USB ACM registered.\n");
+	return 0;
+}
+
+#if 0
+
+int init_module(void)
+{
+	return usb_acm_init();
+}
+
+void cleanup_module(void)
+{
+	/* this, too, probably needs work */
+	usb_deregister(&acm_driver);
+	misc_deregister(&usb_acm);
+}
+
+#endif
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/cpia.c linux/drivers/usb/cpia.c
--- v2.3.1/linux/drivers/usb/cpia.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/cpia.c	Fri May 14 18:33:12 1999
@@ -1246,7 +1246,7 @@
 /*
  * This should be a separate module.
  */
-int cpia_init(void)
+int usb_cpia_init(void)
 {
 	usb_register(&cpia_driver);
 
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/hub.c linux/drivers/usb/hub.c
--- v2.3.1/linux/drivers/usb/hub.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/hub.c	Fri May 14 18:32:32 1999
@@ -394,7 +394,7 @@
 /*
  * This should be a separate module.
  */
-int hub_init(void)
+int usb_hub_init(void)
 {
 	int pid;
 
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/inits.h linux/drivers/usb/inits.h
--- v2.3.1/linux/drivers/usb/inits.h	Fri Apr 30 08:20:30 1999
+++ linux/drivers/usb/inits.h	Fri May 14 18:09:57 1999
@@ -2,5 +2,6 @@
 int usb_kbd_init(void);
 int usb_audio_init(void);
 int hub_init(void);
+int usb_acm_init(void);
 void hub_cleanup(void);
 void usb_mouse_cleanup(void);
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/ohci-hcd.c linux/drivers/usb/ohci-hcd.c
--- v2.3.1/linux/drivers/usb/ohci-hcd.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/ohci-hcd.c	Fri May 14 18:48:55 1999
@@ -234,10 +234,14 @@
 	return usb_dev;
 }
 
+/* FIXME! */
+#define sohci_bulk_msg NULL
+
 struct usb_operations sohci_device_operations = {
 	sohci_usb_allocate,
 	sohci_usb_deallocate,
 	sohci_control_msg,
+	sohci_bulk_msg,
 	sohci_request_irq,
 };
 
@@ -1429,7 +1433,6 @@
 #endif
 
 
- int usb_mouse_init(void); 
 #ifdef MODULE
 
 void cleanup_module(void)
@@ -1460,16 +1463,6 @@
     if (retval < 0) break;
 
     
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
 #ifdef CONFIG_APM
 		apm_register_callback(&handle_apm_event);
 #endif
@@ -1478,12 +1471,3 @@
   }
   return retval;
 }
-
-void cleanup_drivers(void)
-{
-	 hub_cleanup(); 
-#ifdef CONFIG_USB_MOUSE
-	 usb_mouse_cleanup();
-#endif
-}
-
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/ohci.c linux/drivers/usb/ohci.c
--- v2.3.1/linux/drivers/usb/ohci.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/ohci.c	Fri May 14 18:48:55 1999
@@ -429,7 +429,7 @@
 	ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)),
 			TOGGLE_AUTO,
 			OHCI_TD_ROUND,
-			dev->data, DATA_BUF_LEN,
+			&dev->data, DATA_BUF_LEN,
 			dev_id, handler);
 	/*
 	 * TODO: be aware that OHCI won't advance out of the 4kb
@@ -728,6 +728,8 @@
 	return 0;
 }
 
+/* FIXME! */
+#define ohci_bulk_msg NULL
 
 /*
  * functions for the generic USB driver
@@ -736,6 +738,7 @@
 	ohci_usb_allocate,
 	ohci_usb_deallocate,
 	ohci_control_msg,
+	ohci_bulk_msg,
 	ohci_request_irq,
 };
 
@@ -819,6 +822,18 @@
 	/* Enable control lists */
 	writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control);
 
+	/* Force global power enable -gal@cs.uni-magdeburg.de */
+	/* 
+	 * This turns on global power switching for all the ports
+	 * and tells the HC that all of the ports should be powered on
+	 * all of the time.
+	 *
+	 * TODO: This could be battery draining for laptops.. We
+	 *       should implement power switching.
+	 */
+	writel_set( OHCI_ROOT_A_NPS, &ohci->regs->roothub.a );
+	writel_mask( ~((__u32)OHCI_ROOT_A_PSM), &ohci->regs->roothub.a );
+
 	/* Turn on power to the root hub ports (thanks Roman!) */
 	writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status );
 
@@ -1262,23 +1277,23 @@
 	 * Initialize the polling table to call interrupts at the
 	 * intended intervals.
 	 */
-	dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_32]);
+	dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_1]);
 	for (i = 1; i < NUM_INTS; i++) {
-		if (i & 1)
+		if (i & 16)
+			dev->hcca->int_table[i] =
+				virt_to_bus(&dev->ed[ED_INT_32]);
+		if (i & 8)
 			dev->hcca->int_table[i] =
 				virt_to_bus(&dev->ed[ED_INT_16]);
-		else if (i & 2)
+		if (i & 4)
 			dev->hcca->int_table[i] =
 				virt_to_bus(&dev->ed[ED_INT_8]);
-		else if (i & 4)
+		if (i & 2)
 			dev->hcca->int_table[i] =
 				virt_to_bus(&dev->ed[ED_INT_4]);
-		else if (i & 8)
+		if (i & 1)
 			dev->hcca->int_table[i] =
 				virt_to_bus(&dev->ed[ED_INT_2]);
-		else if (i & 16)
-			dev->hcca->int_table[i] =
-				virt_to_bus(&dev->ed[ED_INT_1]);
 	}
 
 	/*
@@ -1470,9 +1485,12 @@
 #ifdef OHCI_TIMER
 /*
  * Inspired by Iñaky's driver.  This function is a timer routine that
- * is called OHCI_TIMER_FREQ times per second.  It polls the root hub
- * for status changes as on my system things are acting a bit odd at
- * the moment..
+ * is called every OHCI_TIMER_FREQ ms.  It polls the root hub for
+ * status changes as on my system the RHSC interrupt just doesn't
+ * play well with others.. (so RHSC is turned off by default in this
+ * driver)
+ * [my controller is a "SiS 7001 USB (rev 16)"]
+ * -greg
  */
 static void ohci_timer_func (unsigned long ohci_ptr)
 {
@@ -1480,8 +1498,9 @@
 
 	ohci_root_hub_events(ohci);
 
-	/* press the snooze button... */
-	mod_timer(&ohci_timer, jiffies + (OHCI_TIMER_FREQ*HZ));
+	/* set the next timer */
+	mod_timer(&ohci_timer, jiffies + ((OHCI_TIMER_FREQ*HZ)/1000));
+
 } /* ohci_timer_func() */
 #endif
 
@@ -1507,9 +1526,10 @@
 
 #ifdef OHCI_TIMER
 	init_timer(&ohci_timer);
-	ohci_timer.expires = jiffies + (OHCI_TIMER_FREQ*HZ);
+	ohci_timer.expires = jiffies + ((OHCI_TIMER_FREQ*HZ)/1000);
 	ohci_timer.data = (unsigned long)ohci;
 	ohci_timer.function = ohci_timer_func;
+	add_timer(&ohci_timer);
 #endif
 
 	retval = -EBUSY;
@@ -1644,18 +1664,6 @@
 		if (retval < 0)
 			continue;
 
-		/* TODO check module params here to determine what to load */
-
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
 #ifdef CONFIG_APM
 		apm_register_callback(&handle_apm_event);
 #endif
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/ohci.h linux/drivers/usb/ohci.h
--- v2.3.1/linux/drivers/usb/ohci.h	Tue May 11 10:04:03 1999
+++ linux/drivers/usb/ohci.h	Fri May 14 18:45:47 1999
@@ -87,9 +87,14 @@
 /* get the head_td */
 #define ed_head_td(ed)	((ed)->_head_td & 0xfffffff0)
 
-/* save the carry flag while setting the head_td */
+/* save the carry & halted flag while setting the head_td */
 #define set_ed_head_td(ed, td)	((ed)->_head_td = (td) | ((ed)->_head_td & 3))
 
+/* Control the ED's halted flag */
+#define ohci_halt_ed(ed)	((ed)->_head_td |= 1)
+#define ohci_unhalt_ed(ed)	((ed)->_head_td &= ~(__u32)1)
+#define ohci_ed_halted(ed)	((ed)->_head_td & 1)
+
 #define OHCI_ED_SKIP	(1 << 14)
 #define OHCI_ED_MPS	(0x7ff << 16)
 /* FIXME: should cap at the USB max packet size [0x4ff] */
@@ -257,12 +262,12 @@
 /* 
  * Read a MMIO register and re-write it after ANDing with (m)
  */
-#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
+#define writel_mask(m, a) writel( (readl((unsigned long)(a))) & (__u32)(m), (unsigned long)(a) )
 
 /*
  * Read a MMIO register and re-write it after ORing with (b)
  */
-#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
+#define writel_set(b, a) writel( (readl((unsigned long)(a))) | (__u32)(b), (unsigned long)(a) )
 
 
 #define PORT_CCS	(1)		/* port current connect status */
@@ -289,6 +294,16 @@
 #define OHCI_ROOT_CRWE	(1 << 31)	/* Clear RemoteWakeupEnable */
 
 /*
+ * Root hub A register masks
+ */
+#define OHCI_ROOT_A_NPS	(1 << 9)
+#define OHCI_ROOT_A_PSM	(1 << 8)
+
+/*
+ * Root hub B register masks
+ */
+
+/*
  * Interrupt register masks
  */
 #define OHCI_INTR_SO	(1)
@@ -334,8 +349,10 @@
 	struct list_head interrupt_list;	/* List of interrupt active TDs for this OHCI */
 };
 
-#define OHCI_TIMER
-#define OHCI_TIMER_FREQ	(1)		/* frequency of OHCI status checks */
+#define OHCI_TIMER		/* enable the OHCI timer */
+#define OHCI_TIMER_FREQ	(234)	/* ms between each root hub status check */
+
+#undef OHCI_RHSC_INT		/* don't use root hub status interrupts */
 
 /* Debugging code */
 void show_ohci_ed(struct ohci_ed *ed);
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- v2.3.1/linux/drivers/usb/uhci.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/uhci.c	Fri May 14 18:36:17 1999
@@ -418,8 +418,8 @@
 	struct uhci_device *dev = usb_to_uhci(usb_dev);
 	unsigned long destination, status;
 	struct uhci_td *td;
-	int ret, i;
 	struct uhci_iso_td *isodesc;
+	int i;
 
 	isodesc = kmalloc(sizeof(*isodesc), GFP_KERNEL);
 	if (!isodesc) {
@@ -700,6 +700,186 @@
 	return ret;
 }
 
+
+/*
+ * Bulk thread operations: we just mark the last TD
+ * in a bulk thread as an interrupt TD, and wake up
+ * the front-end on completion.
+ *
+ * We need to remove the TD from the lists (both interrupt
+ * list and TD lists) by hand if something bad happens!
+ */
+static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup);
+
+static int uhci_bulk_completed(int status, void *buffer, void *dev_id)
+{
+	wake_up(&bulk_wakeup);
+	return 0;			/* Don't re-instate */
+}
+
+/* td points to the last td in the list, which interrupts on completion */
+static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct uhci_qh *bulk_qh = uhci_qh_allocate(dev);
+	struct uhci_td *curtd;
+
+
+
+	current->state = TASK_UNINTERRUPTIBLE;
+	add_wait_queue(&bulk_wakeup, &wait);
+
+	uhci_add_irq_list(dev->uhci, last, uhci_bulk_completed, NULL);
+	
+	/* FIXME: This is kinda kludged */
+	/* Walk the TD list and update the QH pointer */
+	{
+	int maxcount = 100;
+
+	curtd = first;
+	do {
+		curtd->qh = bulk_qh;
+		if (curtd->link & 1)
+			break;
+
+		curtd = bus_to_virt(curtd->link & ~0xF);
+		if (!--maxcount) {
+			printk("runaway tds!\n");
+			break;
+		}
+	} while (1);
+	}
+
+	uhci_insert_tds_in_qh(bulk_qh, first, last);
+
+//	bulk0 is empty here...	
+//	show_status(dev->uhci);
+//	show_queues(dev->uhci);
+	
+	/* Add it into the skeleton */
+	/*WARNING! HUB HAS NO BULK QH TIL NOW!!!!!!!!!!!*/
+	uhci_insert_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+
+//	now we're in the queue... but don't ask WHAT is in there ;-(
+//	show_status(dev->uhci);
+//	show_queues(dev->uhci);
+
+	schedule_timeout(HZ/10);
+
+	remove_wait_queue(&bulk_wakeup, &wait);
+
+	/* Clean up in case it failed.. */
+	uhci_remove_irq_list(last);
+
+#if 0
+	printk("Looking for tds [%p, %p]\n", dev->control_td, td);
+#endif
+
+	/* Remove it from the skeleton */
+	uhci_remove_qh(&dev->uhci->root_hub->skel_bulk0_qh, bulk_qh);
+
+	uhci_qh_deallocate(bulk_qh);
+
+	return uhci_td_result(dev, last);
+}
+
+/*
+ * Send or receive a bulk message on a pipe.
+ *
+ * Note that the "pipe" structure is set up to map
+ * easily to the uhci destination fields.
+ *
+ * A bulk message is only built up from
+ * the data phase
+ *
+ * The data phase can be an arbitrary number of TD's
+ * although we currently had better not have more than
+ * 31 TD's here.
+ *
+ * 31 TD's is a minimum of 248 bytes worth of bulk
+ * information.
+ */
+static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+{
+	struct uhci_device *dev = usb_to_uhci(usb_dev);
+	struct uhci_td *first, *td, *prevtd;
+	unsigned long destination, status;
+	int ret;
+
+	if (len > usb_maxpacket(usb_dev->maxpacketsize) * 31)
+		printk("Warning, too much data for a bulk packet, crashing\n");
+
+	/* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */
+	/*
+	  IS THIS NECCESARY? PERHAPS WE CAN JUST USE THE PIPE
+	  LOOK AT: usb_pipeout and the pipe bits
+	  I FORGOT WHAT IT EXACTLY DOES
+	*/
+	if (usb_pipeout(pipe)) {
+		destination = (pipe & 0x0007ff00) | 0xE1;
+	}
+	else {
+		destination = (pipe & 0x0007ff00) | 0x69;
+	}
+
+	/* Status:    slow/fast,       Active,    Short Packet Detect     Three Errors */
+	status = (pipe & (1 << 26)) | (1 << 23)   |   (1 << 29)       |    (3 << 27);
+
+	/*
+	 * Build the TDs for the bulk request
+	 */
+        first = td = uhci_td_allocate(dev);
+        prevtd = first; //This is fake, but at least it's not NULL
+	while (len > 0) {
+		/* Build the TD for control status */
+		int pktsze = len;
+		int maxsze = usb_maxpacket(pipe);
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td->status = status;					/* Status */
+		td->info = destination | ((pktsze-1) << 21);		/* pktsze bytes of data */
+		td->buffer = virt_to_bus(data);
+		td->backptr = &prevtd->link;
+
+		prevtd = td;
+		td = uhci_td_allocate(dev);
+		prevtd->link = 4 | virt_to_bus(td);			/* Update previous TD */
+
+		data += maxsze;
+		len -= maxsze;
+		/* Alternate Data0/1 (start with Data0) */
+		destination ^= 1 << 19;
+	}
+	td->link = 1;					/* Terminate */
+
+	/* Start it up.. */
+	ret = uhci_run_bulk(dev, first, td);
+
+	{
+		int maxcount = 100;
+		struct uhci_td *curtd = first;
+		unsigned int nextlink;
+
+		do {
+			nextlink = curtd->link;
+			uhci_remove_td(curtd);
+			uhci_td_deallocate(curtd);
+			if (nextlink & 1)	/* Tail? */
+				break;
+
+			curtd = bus_to_virt(nextlink & ~0xF);
+			if (!--maxcount) {
+				printk("runaway td's!?\n");
+				break;
+			}
+		} while (1);
+	}
+
+	return ret;
+}
+
 static struct usb_device *uhci_usb_allocate(struct usb_device *parent)
 {
 	struct usb_device *usb_dev;
@@ -787,6 +967,7 @@
 	uhci_usb_allocate,
 	uhci_usb_deallocate,
 	uhci_control_msg,
+	uhci_bulk_msg,
 	uhci_request_irq,
 };
 
@@ -1399,32 +1580,10 @@
 		if (retval < 0)
 			continue;
 
-#ifdef CONFIG_USB_MOUSE
-		usb_mouse_init();
-#endif
-#ifdef CONFIG_USB_KBD		
-		usb_kbd_init();
-#endif		
-		hub_init();
-#ifdef CONFIG_USB_AUDIO		
-		usb_audio_init();
-#endif		
-#ifdef CONFIG_USB_CPIA
-		cpia_init();
-#endif
 #ifdef CONFIG_APM
 		apm_register_callback(&handle_apm_event);
 #endif
-
 		return 0;
 	}
 	return retval;
-}
-
-void cleanup_drivers(void)
-{
-	hub_cleanup();
-#ifdef CONFIG_USB_MOUSE
-	usb_mouse_cleanup();
-#endif
 }
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/usb.c linux/drivers/usb/usb.c
--- v2.3.1/linux/drivers/usb/usb.c	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/usb.c	Fri May 14 18:43:45 1999
@@ -42,6 +42,55 @@
 
 #include "usb.h"
 
+#ifdef CONFIG_USB_UHCI
+int uhci_init(void);
+#endif
+#ifdef CONFIG_USB_OHCI
+int ohci_init(void);
+#endif
+#ifdef CONFIG_USB_OHCI_HCD
+int ohci_hcd_init(void);
+#endif
+
+int usb_init(void)
+{
+#ifdef CONFIG_USB_UHCI
+	uhci_init();
+#endif
+#ifdef CONFIG_USB_OHCI
+	ohci_init();
+#endif
+#ifdef CONFIG_USB_OHCI_HCD
+	ohci_hcd_init(); 
+#endif
+#ifdef CONFIG_USB_MOUSE
+	usb_mouse_init();
+#endif
+#ifdef CONFIG_USB_KBD
+	usb_kbd_init();
+#endif
+#ifdef CONFIG_USB_AUDIO
+	usb_audio_init();
+#endif
+#ifdef CONFIG_USB_ACM
+	usb_acm_init();
+#endif
+#ifdef CONFIG_USB_CPIA
+	usb_cpia_init();
+#endif
+
+	usb_hub_init();
+	return 0;
+}
+
+void cleanup_drivers(void)
+{
+	hub_cleanup();
+#ifdef CONFIG_USB_MOUSE
+	usb_mouse_cleanup();
+#endif
+}
+
 /*
  * We have a per-interface "registered driver" list.
  */
@@ -596,20 +645,20 @@
 
 int usb_get_configuration(struct usb_device *dev)
 {
-	unsigned int cfgno,size;
+	unsigned int cfgno;
 	unsigned char buffer[400];
 	unsigned char * bufptr;
 	
-	bufptr=buffer;
-        for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
+	bufptr = buffer;
+	for (cfgno = 0 ; cfgno < dev->descriptor.bNumConfigurations ; cfgno++) {
+		unsigned int size;
   		/* Get the first 8 bytes - guaranteed */
 	  	if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
 	    		return -1;
 
   	  	/* Get the full buffer */
 	  	size = *(unsigned short *)(bufptr+2);
-	  	if (bufptr+size > buffer+sizeof(buffer))
-	  	{
+	  	if (bufptr+size > buffer+sizeof(buffer)) {
 			printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
 			size = buffer+sizeof(buffer)-bufptr;
 		}
@@ -617,9 +666,9 @@
 			return -1;
 			
 		/* Prepare for next configuration */
-		bufptr+=size;
+		bufptr += size;
 	}
-	return usb_parse_configuration(dev, buffer, size);
+	return usb_parse_configuration(dev, buffer, bufptr - buffer);
 }
 
 /*
diff -u --recursive --new-file v2.3.1/linux/drivers/usb/usb.h linux/drivers/usb/usb.h
--- v2.3.1/linux/drivers/usb/usb.h	Fri May 14 18:55:23 1999
+++ linux/drivers/usb/usb.h	Fri May 14 18:45:36 1999
@@ -6,6 +6,14 @@
 #include <linux/list.h>
 #include <linux/sched.h>
 
+extern int usb_hub_init(void);
+extern int usb_kbd_init(void);
+extern int usb_cpia_init(void);
+extern int usb_mouse_init(void);
+
+extern void hub_cleanup(void);
+extern void usb_mouse_cleanup(void);
+
 static __inline__ void wait_ms(unsigned int ms)
 {
         current->state = TASK_UNINTERRUPTIBLE;
@@ -209,6 +217,7 @@
 	struct usb_device *(*allocate)(struct usb_device *);
 	int (*deallocate)(struct usb_device *);
 	int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
+	int (*bulk_msg)(struct usb_device *, unsigned int, void *, int);
 	int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
 };
 
@@ -262,6 +271,7 @@
 extern void usb_device_descriptor(struct usb_device *dev);
 
 extern int  usb_parse_configuration(struct usb_device *dev, void *buf, int len);
+extern void usb_destroy_configuration(struct usb_device *dev);
 
 /*
  * Calling this entity a "pipe" is glorifying it. A USB pipe
diff -u --recursive --new-file v2.3.1/linux/drivers/video/Config.in linux/drivers/video/Config.in
--- v2.3.1/linux/drivers/video/Config.in	Fri May 14 18:55:23 1999
+++ linux/drivers/video/Config.in	Thu May 13 23:48:20 1999
@@ -74,6 +74,7 @@
   fi
   if [ "$ARCH" = "i386" ]; then
     bool 'VESA VGA graphics console' CONFIG_FB_VESA
+    bool 'VGA 16-color graphics console' CONFIG_FB_VGA16
     define_bool CONFIG_VIDEO_SELECT y
   fi
   if [ "$CONFIG_VISWS" = "y" ]; then
@@ -141,6 +142,7 @@
     tristate 'Atari interleaved bitplanes (8 planes) support' CONFIG_FBCON_IPLAN2P8
 #   tristate 'Atari interleaved bitplanes (16 planes) support' CONFIG_FBCON_IPLAN2P16
     tristate 'Mac variable bpp packed pixels support' CONFIG_FBCON_MAC
+    bool 'VGA 16-color planar support' CONFIG_FBCON_VGA_PLANES
     tristate 'VGA characters/attributes support' CONFIG_FBCON_VGA
   else
     # Guess what we need
@@ -282,6 +284,9 @@
       if [ "$CONFIG_FB_MAC" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
 	define_bool CONFIG_FBCON_MAC  m
       fi
+    fi
+    if [ "$CONFIG_FB_VGA16" = "y" ]; then
+      define_bool CONFIG_FBCON_VGA_PLANES y
     fi
     if [ "$CONFIG_FB_MDA" = "y" -o "$CONFIG_FB_VGA" = "y" ]; then
       define_bool CONFIG_FBCON_VGA y
diff -u --recursive --new-file v2.3.1/linux/drivers/video/Makefile linux/drivers/video/Makefile
--- v2.3.1/linux/drivers/video/Makefile	Fri May 14 18:55:23 1999
+++ linux/drivers/video/Makefile	Thu May 13 23:48:20 1999
@@ -202,6 +202,10 @@
 L_OBJS += vesafb.o
 endif
 
+ifeq ($(CONFIG_FB_VGA16),y)
+L_OBJS += vga16fb.o
+endif
+
 ifeq ($(CONFIG_FB_VIRGE),y)
 L_OBJS += virgefb.o
 else
@@ -459,6 +463,14 @@
 else
   ifeq ($(CONFIG_FBCON_MFB),m)
   MX_OBJS += fbcon-mfb.o
+  endif
+endif
+
+ifeq ($(CONFIG_FBCON_VGA_PLANES),y)
+OX_OBJS += fbcon-vga-planes.o
+else
+  ifeq ($(CONFIG_FBCON_VGA_PLANES),m)
+  MX_OBJS += fbcon-vga-planes.o
   endif
 endif
 
diff -u --recursive --new-file v2.3.1/linux/drivers/video/fbcon-vga-planes.c linux/drivers/video/fbcon-vga-planes.c
--- v2.3.1/linux/drivers/video/fbcon-vga-planes.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/fbcon-vga-planes.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,364 @@
+/*
+ *  linux/drivers/video/fbcon-vga-planes.c -- Low level frame buffer operations
+ *				  for VGA 4-plane modes
+ *
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on code by Michael Schmitz
+ * Based on the old macfb.c 4bpp code by Alan Cox
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License.  See the file COPYING in the main directory of this
+ * archive for more details.  */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_buffer.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-vga-planes.h>
+
+#define GRAPHICS_ADDR_REG 0x3ce		/* Graphics address register. */
+#define GRAPHICS_DATA_REG 0x3cf		/* Graphics data register. */
+
+#define SET_RESET_INDEX 0		/* Set/Reset Register index. */
+#define ENABLE_SET_RESET_INDEX 1	/* Enable Set/Reset Register index. */
+#define DATA_ROTATE_INDEX 3		/* Data Rotate Register index. */
+#define GRAPHICS_MODE_INDEX 5		/* Graphics Mode Register index. */
+#define BIT_MASK_INDEX 8		/* Bit Mask Register index. */
+
+/* The VGA's weird architecture often requires that we read a byte and
+   write a byte to the same location.  It doesn't matter *what* byte
+   we write, however.  This is because all the action goes on behind
+   the scenes in the VGA's 32-bit latch register, and reading and writing
+   video memory just invokes latch behavior.
+
+   To avoid race conditions (is this necessary?), reading and writing
+   the memory byte should be done with a single instruction.  One
+   suitable instruction is the x86 bitwise OR.  The following
+   read-modify-write routine should optimize to one such bitwise
+   OR. */
+static inline void rmw(volatile char *p)
+{
+	*p |= 1;
+}
+
+/* Set the Graphics Mode Register.  Bits 0-1 are write mode, bit 3 is
+   read mode. */
+static inline void setmode(int mode)
+{
+	outb(GRAPHICS_MODE_INDEX, GRAPHICS_ADDR_REG);
+	outb(mode, GRAPHICS_DATA_REG);
+}
+
+/* Select the Bit Mask Register. */
+static inline void selectmask(void)
+{
+	outb(BIT_MASK_INDEX, GRAPHICS_ADDR_REG);
+}
+
+/* Set the value of the Bit Mask Register.  It must already have been
+   selected with selectmask(). */
+static inline void setmask(int mask)
+{
+	outb(mask, GRAPHICS_DATA_REG);
+}
+
+/* Set the Data Rotate Register.  Bits 0-2 are rotate count, bits 3-4
+   are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */
+static inline void setop(int op)
+{
+	outb(DATA_ROTATE_INDEX, GRAPHICS_ADDR_REG);
+	outb(op, GRAPHICS_DATA_REG);
+}
+
+/* Set the Enable Set/Reset Register.  The code here always uses value
+   0xf for this register.  */
+static inline void setsr(int sr)
+{
+	outb(ENABLE_SET_RESET_INDEX, GRAPHICS_ADDR_REG);
+	outb(sr, GRAPHICS_DATA_REG);
+}
+
+/* Set the Set/Reset Register. */
+static inline void setcolor(int color)
+{
+	outb(SET_RESET_INDEX, GRAPHICS_ADDR_REG);
+	outb(color, GRAPHICS_DATA_REG);
+}
+
+/* Set the value in the Graphics Address Register. */
+static inline void setindex(int index)
+{
+	outb(index, GRAPHICS_ADDR_REG);
+}
+
+void fbcon_vga_planes_setup(struct display *p)
+{
+}
+
+void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx,
+		   int height, int width)
+{
+	char *src;
+	char *dest;
+	int line_ofs;
+	int x;
+
+	setmode(1);
+	setop(0);
+	setsr(0xf);
+
+	sy *= fontheight(p);
+	dy *= fontheight(p);
+	height *= fontheight(p);
+
+	if (dy < sy || (dy == sy && dx < sx)) {
+		line_ofs = p->line_length - width;
+		dest = p->screen_base + dx + dy * p->line_length;
+		src = p->screen_base + sx + sy * p->line_length;
+		while (height--) {
+			for (x = 0; x < width; x++) 
+				*dest++ = *src++;
+			src += line_ofs;
+			dest += line_ofs;
+		}
+	} else {
+		line_ofs = p->line_length - width;
+		dest = p->screen_base + dx + width + (dy + height - 1) * p->line_length;
+		src = p->screen_base + sx + width + (sy + height - 1) * p->line_length;
+		while (height--) {
+			for (x = 0; x < width; x++)
+				*--dest = *--src;
+			src -= line_ofs;
+			dest -= line_ofs;
+		}
+	}
+}
+
+void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+		   int height, int width)
+{
+	int line_ofs = p->line_length - width;
+	char *where;
+	int x;
+	
+	setmode(0);
+	setop(0);
+	setsr(0xf);
+	setcolor(attr_bgcol_ec(p, conp));
+	selectmask();
+
+	setmask(0xff);
+
+	sy *= fontheight(p);
+	height *= fontheight(p);
+
+	where = p->screen_base + sx + sy * p->line_length;
+	while (height--) {
+		for (x = 0; x < width; x++)
+			*where++ = 0;
+		where += line_ofs;
+	}
+}
+
+void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+	int fg = attr_fgcol(p,c);
+	int bg = attr_bgcol(p,c);
+
+	int y;
+	u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+
+	setmode(0);
+	setop(0);
+	setsr(0xf);
+	setcolor(bg);
+	selectmask();
+
+	setmask(0xff);
+	for (y = 0; y < fontheight(p); y++, where += p->line_length) 
+		rmw(where);
+
+	where -= p->line_length * y;
+	setcolor(fg);
+	selectmask();
+	for (y = 0; y < fontheight(p); y++, where += p->line_length) 
+		if (cdat[y]) {
+			setmask(cdat[y]);
+			rmw(where);
+		}
+}
+
+void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+	int fg = attr_fgcol(p,c);
+	int bg = attr_bgcol(p,c);
+
+	int y;
+	u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+
+	setmode(2);
+	setop(0);
+	setsr(0xf);
+	setcolor(fg);
+	selectmask();
+
+	setmask(0xff);
+	*where = bg;
+	rmb();
+	*(volatile char*)where; /* fill latches */
+	setmode(3);
+	wmb();
+	for (y = 0; y < fontheight(p); y++, where += p->line_length) 
+		*where = cdat[y];
+	wmb();
+}
+
+/* 28.50 in my test */
+void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+		   int count, int yy, int xx)
+{
+	int fg = attr_fgcol(p,scr_readw(s));
+	int bg = attr_bgcol(p,scr_readw(s));
+
+	char *where;
+	int n;
+
+	setmode(2);
+	setop(0);
+	selectmask();
+
+	setmask(0xff);
+	where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+	*where = bg;
+	rmb();
+	*(volatile char*)where;
+	wmb();
+	selectmask();
+	for (n = 0; n < count; n++) {
+		int c = scr_readw(s++) & p->charmask;
+		u8 *cdat = p->fontdata + c * fontheight(p);
+		u8 *end = cdat + fontheight(p);
+
+		while (cdat < end) {
+			outb(*cdat++, GRAPHICS_DATA_REG);	
+			wmb();
+			*where = fg;
+			where += p->line_length;
+		}
+		where += 1 - p->line_length * fontheight(p);
+	}
+	
+	wmb();
+}
+
+/* 6.96 in my test */
+void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+		   int count, int yy, int xx)
+{
+	int fg = attr_fgcol(p,*s);
+	int bg = attr_bgcol(p,*s);
+
+	char *where;
+	int n;
+
+	setmode(2);
+	setop(0);
+	setsr(0xf);
+	setcolor(fg);
+	selectmask();
+
+	setmask(0xff);
+	where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+	*where = bg;
+	rmb();
+	*(volatile char*)where; /* fill latches with background */
+	setmode(3);	
+	wmb();
+	for (n = 0; n < count; n++) {
+		int y;
+		int c = *s++ & p->charmask;
+		u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+
+		for (y = 0; y < fontheight(p); y++, cdat++) {
+			*where = *cdat;
+			where += p->line_length;
+		}
+		where += 1 - p->line_length * fontheight(p);
+	}
+	
+	wmb();
+}
+
+void fbcon_vga_planes_revc(struct display *p, int xx, int yy)
+{
+	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+	int y;
+	
+	setmode(0);
+	setop(0x18);
+	setsr(0xf);
+	setcolor(0xf);
+	selectmask();
+
+	setmask(0xff);
+	for (y = 0; y < fontheight(p); y++) {
+		rmw(where);
+		where += p->line_length;
+	}
+}
+
+struct display_switch fbcon_vga_planes = {
+    fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear,
+    fbcon_vga_planes_putc, fbcon_vga_planes_putcs, fbcon_vga_planes_revc,
+    NULL, NULL, NULL, FONTWIDTH(8)
+};
+
+struct display_switch fbcon_ega_planes = {
+    fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear,
+    fbcon_ega_planes_putc, fbcon_ega_planes_putcs, fbcon_vga_planes_revc,
+    NULL, NULL, NULL, FONTWIDTH(8)
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+    return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
+
+
+    /*
+     *  Visible symbols for modules
+     */
+
+EXPORT_SYMBOL(fbcon_vga_planes);
+EXPORT_SYMBOL(fbcon_vga_planes_setup);
+EXPORT_SYMBOL(fbcon_vga_planes_bmove);
+EXPORT_SYMBOL(fbcon_vga_planes_clear);
+EXPORT_SYMBOL(fbcon_vga_planes_putc);
+EXPORT_SYMBOL(fbcon_vga_planes_putcs);
+EXPORT_SYMBOL(fbcon_vga_planes_revc);
+
+EXPORT_SYMBOL(fbcon_ega_planes);
+EXPORT_SYMBOL(fbcon_ega_planes_putc);
+EXPORT_SYMBOL(fbcon_ega_planes_putcs);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff -u --recursive --new-file v2.3.1/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.3.1/linux/drivers/video/fbcon.c	Thu Apr 22 19:30:08 1999
+++ linux/drivers/video/fbcon.c	Fri May 14 12:49:28 1999
@@ -91,6 +91,9 @@
 #include <asm/machdep.h>
 #include <asm/setup.h>
 #endif
+#ifdef CONFIG_FBCON_VGA_PLANES
+#include <asm/io.h>
+#endif
 #define INCLUDE_LINUX_LOGO_DATA
 #include <asm/linux_logo.h>
 
@@ -2237,6 +2240,35 @@
 	    done = 1;
 	}
 #endif
+#if defined(CONFIG_FBCON_VGA_PLANES)
+	if (depth == 4 && p->type == FB_TYPE_VGA_PLANES) {
+		outb_p(1,0x3ce); outb_p(0xf,0x3cf);
+		outb_p(3,0x3ce); outb_p(0,0x3cf);
+		outb_p(5,0x3ce); outb_p(0,0x3cf);
+
+		src = logo;
+		for (y1 = 0; y1 < LOGO_H; y1++) {
+			for (x1 = 0; x1 < LOGO_W / 2; x1++) {
+				dst = fb + y1*line + x1/4 + x/8;
+
+				outb_p(0,0x3ce);
+				outb_p(*src >> 4,0x3cf);
+				outb_p(8,0x3ce);
+				outb_p(1 << (7 - x1 % 4 * 2),0x3cf);
+				*(volatile char *) dst |= 1;
+
+				outb_p(0,0x3ce);
+				outb_p(*src & 0xf,0x3cf);
+				outb_p(8,0x3ce);
+				outb_p(1 << (7 - (1 + x1 % 4 * 2)),0x3cf);
+				*(volatile char *) dst |= 1;
+
+				src++;
+			}
+		}
+		done = 1;
+	}
+#endif			
     }
     
     if (p->fb_info->fbops->fb_rasterimg)
@@ -2300,3 +2332,4 @@
 EXPORT_SYMBOL(fb_display);
 EXPORT_SYMBOL(fbcon_redraw_bmove);
 EXPORT_SYMBOL(fbcon_dummy);
+EXPORT_SYMBOL(fb_con);
diff -u --recursive --new-file v2.3.1/linux/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- v2.3.1/linux/drivers/video/fbmem.c	Fri May 14 18:55:23 1999
+++ linux/drivers/video/fbmem.c	Thu May 13 23:48:20 1999
@@ -81,6 +81,8 @@
 extern void s3triofb_setup(char *options, int *ints);
 extern void vesafb_init(void);
 extern void vesafb_setup(char *options, int *ints);
+extern void vga16fb_init(void);
+extern void vga16fb_setup(char *options, int *ints);
 extern void matroxfb_init(void);
 extern void matroxfb_setup(char* options, int *ints);
 extern void hpfb_init(void);
@@ -163,6 +165,9 @@
 #endif
 #ifdef CONFIG_FB_VESA
 	{ "vesa", vesafb_init, vesafb_setup },
+#endif 
+#ifdef CONFIG_FB_VGA16
+	{ "vga16", vga16fb_init, vga16fb_setup },
 #endif 
 #ifdef CONFIG_FB_MATROX
 	{ "matrox", matroxfb_init, matroxfb_setup },
diff -u --recursive --new-file v2.3.1/linux/drivers/video/mdacon.c linux/drivers/video/mdacon.c
--- v2.3.1/linux/drivers/video/mdacon.c	Thu Feb 25 10:02:12 1999
+++ linux/drivers/video/mdacon.c	Fri May 14 17:46:07 1999
@@ -597,7 +597,7 @@
 	if (mda_first_vc > mda_last_vc)
 		return;
 
-	take_over_console(&mda_con, mda_first_vc, mda_last_vc, 0);
+	take_over_console(&mda_con, mda_first_vc-1, mda_last_vc-1, 0);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.3.1/linux/drivers/video/vga16fb.c linux/drivers/video/vga16fb.c
--- v2.3.1/linux/drivers/video/vga16fb.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/video/vga16fb.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,1065 @@
+/*
+ * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver
+ * 
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License.  See the file COPYING in the main directory of this
+ * archive for more details.  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-vga-planes.h>
+
+#define dac_reg	(0x3c8)
+#define dac_val	(0x3c9)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * card parameters
+ */
+
+static struct vga16fb_info {
+	struct fb_info  fb_info;
+	char *video_vbase;			/* 0xa0000 map address */
+	int isVGA;
+	
+	/* structure holding original VGA register settings when the
+           screen is blanked */
+	struct {
+		unsigned char	SeqCtrlIndex;		/* Sequencer Index reg.   */
+		unsigned char	CrtCtrlIndex;		/* CRT-Contr. Index reg.  */
+		unsigned char	CrtMiscIO;		/* Miscellaneous register */
+		unsigned char	HorizontalTotal;	/* CRT-Controller:00h */
+		unsigned char	HorizDisplayEnd;	/* CRT-Controller:01h */
+		unsigned char	StartHorizRetrace;	/* CRT-Controller:04h */
+		unsigned char	EndHorizRetrace;	/* CRT-Controller:05h */
+		unsigned char	Overflow;		/* CRT-Controller:07h */
+		unsigned char	StartVertRetrace;	/* CRT-Controller:10h */
+		unsigned char	EndVertRetrace;		/* CRT-Controller:11h */
+		unsigned char	ModeControl;		/* CRT-Controller:17h */
+		unsigned char	ClockingMode;		/* Seq-Controller:01h */
+	} vga_state;
+
+	int palette_blanked;
+	int vesa_blanked;
+} vga16fb;
+
+/* Some of the code below is taken from SVGAlib.  The original,
+   unmodified copyright notice for that code is below. */
+/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen                    */
+/*                                                                 */
+/* This library is free software; you can redistribute it and/or   */
+/* modify it without any restrictions. This library is distributed */
+/* in the hope that it will be useful, but without any warranty.   */
+
+/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
+/* partially copyrighted (C) 1993 by Hartmut Schirmer */
+
+/* VGA data register ports */
+#define CRT_DC  0x3D5		/* CRT Controller Data Register - color emulation */
+#define CRT_DM  0x3B5		/* CRT Controller Data Register - mono emulation */
+#define ATT_R   0x3C1		/* Attribute Controller Data Read Register */
+#define GRA_D   0x3CF		/* Graphics Controller Data Register */
+#define SEQ_D   0x3C5		/* Sequencer Data Register */
+#define MIS_R   0x3CC		/* Misc Output Read Register */
+#define MIS_W   0x3C2		/* Misc Output Write Register */
+#define IS1_RC  0x3DA		/* Input Status Register 1 - color emulation */
+#define IS1_RM  0x3BA		/* Input Status Register 1 - mono emulation */
+#define PEL_D   0x3C9		/* PEL Data Register */
+#define PEL_MSK 0x3C6		/* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0	0x3CC		/* Graphics enable processor 0 */
+#define GRA_E1	0x3CA		/* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC  0x3D4		/* CRT Controller Index - color emulation */
+#define CRT_IM  0x3B4		/* CRT Controller Index - mono emulation */
+#define ATT_IW  0x3C0		/* Attribute Controller Index & Data Write Register */
+#define GRA_I   0x3CE		/* Graphics Controller Index */
+#define SEQ_I   0x3C4		/* Sequencer Index */
+#define PEL_IW  0x3C8		/* PEL Write Index */
+#define PEL_IR  0x3C7		/* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRT_C   24		/* 24 CRT Controller Registers */
+#define ATT_C   21		/* 21 Attribute Controller Registers */
+#define GRA_C   9		/* 9  Graphics Controller Registers */
+#define SEQ_C   5		/* 5  Sequencer Registers */
+#define MIS_C   1		/* 1  Misc Output Register */
+
+#define CRTC_H_TOTAL		0
+#define CRTC_H_DISP		1
+#define CRTC_H_BLANK_START	2
+#define CRTC_H_BLANK_END	3
+#define CRTC_H_SYNC_START	4
+#define CRTC_H_SYNC_END		5
+#define CRTC_V_TOTAL		6
+#define CRTC_OVERFLOW		7
+#define CRTC_PRESET_ROW		8
+#define CRTC_MAX_SCAN		9
+#define CRTC_CURSOR_START	0x0A
+#define CRTC_CURSOR_END		0x0B
+#define CRTC_START_HI		0x0C
+#define CRTC_START_LO		0x0D
+#define CRTC_CURSOR_HI		0x0E
+#define CRTC_CURSOR_LO		0x0F
+#define CRTC_V_SYNC_START	0x10
+#define CRTC_V_SYNC_END		0x11
+#define CRTC_V_DISP_END		0x12
+#define CRTC_OFFSET		0x13
+#define CRTC_UNDERLINE		0x14
+#define CRTC_V_BLANK_START	0x15
+#define CRTC_V_BLANK_END	0x16
+#define CRTC_MODE		0x17
+#define CRTC_LINE_COMPARE	0x18
+#define CRTC_REGS		0x19
+
+#define ATC_MODE		0x10
+#define ATC_OVERSCAN		0x11
+#define ATC_PLANE_ENABLE	0x12
+#define ATC_PEL			0x13
+#define ATC_COLOR_PAGE		0x14
+
+#define SEQ_CLOCK_MODE		0x01
+#define SEQ_PLANE_WRITE		0x02
+#define SEQ_CHARACTER_MAP	0x03
+#define SEQ_MEMORY_MODE		0x04
+
+#define GDC_SR_VALUE		0x00
+#define GDC_SR_ENABLE		0x01
+#define GDC_COMPARE_VALUE	0x02
+#define GDC_DATA_ROTATE		0x03
+#define GDC_PLANE_READ		0x04
+#define GDC_MODE		0x05
+#define GDC_MISC		0x06
+#define GDC_COMPARE_MASK	0x07
+#define GDC_BIT_MASK		0x08
+
+struct vga16fb_par {
+	u8 crtc[CRTC_REGS];
+	u8 atc[ATT_C];
+	u8 gdc[GRA_C];
+	u8 seq[SEQ_C];
+	u8 misc;
+	u8 vss;
+	struct fb_var_screeninfo var;
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo vga16fb_defined = {
+	640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/
+	0,0,		/* virtual -> visible no offset */
+	4,		/* depth -> load bits_per_pixel */
+	0,		/* greyscale ? */
+	{0,0,0},	/* R */
+	{0,0,0},	/* G */
+	{0,0,0},	/* B */
+	{0,0,0},	/* transparency */
+	0,		/* standard pixel format */
+	FB_ACTIVATE_NOW,
+	-1,-1,
+	0,
+	39721, 48, 16, 39, 8,
+	96, 2, 0,	/* No sync info */
+	FB_VMODE_NONINTERLACED,
+	{0,0,0,0,0,0}
+};
+
+static struct display disp;
+static struct { u_short blue, green, red, pad; } palette[256];
+
+static int             currcon   = 0;
+
+/* --------------------------------------------------------------------- */
+
+	/*
+	 * Open/Release the frame buffer device
+	 */
+
+static int vga16fb_open(struct fb_info *info, int user)
+{
+	/*
+	 * Nothing, only a usage count for the moment
+	 */
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+static int vga16fb_release(struct fb_info *info, int user)
+{
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+static void vga16fb_pan_var(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+	u32 pos = (var->xres_virtual * var->yoffset + var->xoffset) >> 3;
+	outb(CRTC_START_HI, CRT_IC);
+	outb(pos >> 8, CRT_DC);
+	outb(CRTC_START_LO, CRT_IC);
+	outb(pos & 0xFF, CRT_DC);
+#if 0
+	/* if someone supports xoffset in bit resolution */
+	inb(IS1_RC);		/* reset flip-flop */
+	outb(ATC_PEL, ATT_IW);
+	outb(xoffset & 7, ATT_IW);
+	inb(IS1_RC);
+	outb(0x20, ATT_IW);
+#endif
+}
+
+static int vga16fb_update_var(int con, struct fb_info *info)
+{
+	vga16fb_pan_var(info, &fb_display[con].var);
+	return 0;
+}
+
+static int vga16fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			   struct fb_info *info)
+{
+	struct display *p;
+
+	if (con < 0)
+		p = &disp;
+	else
+		p = fb_display + con;
+
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id,"VGA16 VGA");
+
+	fix->smem_start = (char *) 0xa0000;
+	fix->smem_len = 65536;
+	fix->type = FB_TYPE_VGA_PLANES;
+	fix->visual = FB_VISUAL_PSEUDOCOLOR;
+	fix->xpanstep  = 8;
+	fix->ypanstep  = 1;
+	fix->ywrapstep = 0;
+	fix->line_length = p->var.xres_virtual / 8;
+	return 0;
+}
+
+static int vga16fb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+	if(con==-1)
+		memcpy(var, &vga16fb_defined, sizeof(struct fb_var_screeninfo));
+	else
+		*var=fb_display[con].var;
+	return 0;
+}
+
+static void vga16fb_set_disp(int con, struct vga16fb_info *info)
+{
+	struct fb_fix_screeninfo fix;
+	struct display *display;
+
+	if (con < 0)
+		display = &disp;
+	else
+		display = fb_display + con;
+
+	
+	vga16fb_get_fix(&fix, con, &info->fb_info);
+
+	display->screen_base = info->video_vbase;
+	display->visual = fix.visual;
+	display->type = fix.type;
+	display->type_aux = fix.type_aux;
+	display->ypanstep = fix.ypanstep;
+	display->ywrapstep = fix.ywrapstep;
+	display->line_length = fix.line_length;
+	display->next_line = fix.line_length;
+	display->can_soft_blank = 1;
+	display->inverse = 0;
+
+	if (info->isVGA)
+		display->dispsw = &fbcon_vga_planes;
+	else
+		display->dispsw = &fbcon_ega_planes;
+	display->scrollmode = SCROLL_YREDRAW;
+}
+
+static void vga16fb_encode_var(struct fb_var_screeninfo *var,
+			       const struct vga16fb_par *par,
+			       const struct vga16fb_info *info)
+{
+	*var = par->var;
+}
+
+static void vga16fb_clock_chip(struct vga16fb_par *par,
+			       unsigned int pixclock,
+			       const struct vga16fb_info *info)
+{
+	static struct {
+		u32 pixclock;
+		u8  misc;
+		u8  seq_clock_mode;
+	} *ptr, *best, vgaclocks[] = {
+		{ 79442 /* 12.587 */, 0x00, 0x08},
+		{ 70616 /* 14.161 */, 0x04, 0x08},
+		{ 39721 /* 25.175 */, 0x00, 0x00},
+		{ 35308 /* 28.322 */, 0x04, 0x00},
+		{     0 /* bad */,    0x00, 0x00}};
+	int err;
+
+	best = vgaclocks;
+	err = pixclock - best->pixclock;
+	if (err < 0) err = -err;
+	for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) {
+		int tmp;
+
+		tmp = pixclock - ptr->pixclock;
+		if (tmp < 0) tmp = -tmp;
+		if (tmp < err) {
+			err = tmp;
+			best = ptr;
+		}
+	}
+	par->misc |= best->misc;
+	par->seq[SEQ_CLOCK_MODE] |= best->seq_clock_mode;
+	par->var.pixclock = best->pixclock;		
+}
+			       
+#define FAIL(X) return -EINVAL
+
+static int vga16fb_decode_var(const struct fb_var_screeninfo *var,
+			      struct vga16fb_par *par,
+			      const struct vga16fb_info *info)
+{
+	u32 xres, right, hslen, left, xtotal;
+	u32 yres, lower, vslen, upper, ytotal;
+	u32 vxres, xoffset, vyres, yoffset;
+	u32 pos;
+	u8 r7, rMode;
+	int i;
+
+	if (var->bits_per_pixel != 4)
+		return -EINVAL;
+	xres = (var->xres + 7) & ~7;
+	vxres = (var->xres_virtual + 0xF) & ~0xF;
+	xoffset = (var->xoffset + 7) & ~7;
+	left = (var->left_margin + 7) & ~7;
+	right = (var->right_margin + 7) & ~7;
+	hslen = (var->hsync_len + 7) & ~7;
+
+	if (vxres < xres)
+		vxres = xres;
+	if (xres + xoffset > vxres)
+		xoffset = vxres - xres;
+
+	par->var.xres = xres;
+	par->var.right_margin = right;
+	par->var.hsync_len = hslen;
+	par->var.left_margin = left;
+	par->var.xres_virtual = vxres;
+	par->var.xoffset = xoffset;
+
+	xres >>= 3;
+	right >>= 3;
+	hslen >>= 3;
+	left >>= 3;
+	vxres >>= 3;
+	xtotal = xres + right + hslen + left;
+	if (xtotal >= 256)
+		FAIL("xtotal too big");
+	if (hslen > 32)
+		FAIL("hslen too big");
+	if (right + hslen + left > 64)
+		FAIL("hblank too big");
+	par->crtc[CRTC_H_TOTAL] = xtotal - 5;
+	par->crtc[CRTC_H_BLANK_START] = xres - 1;
+	par->crtc[CRTC_H_DISP] = xres - 1;
+	pos = xres + right;
+	par->crtc[CRTC_H_SYNC_START] = pos;
+	pos += hslen;
+	par->crtc[CRTC_H_SYNC_END] = pos & 0x1F;
+	pos += left - 2; /* blank_end + 2 <= total + 5 */
+	par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
+	if (pos & 0x20)
+		par->crtc[CRTC_H_SYNC_END] |= 0x80;
+
+	yres = var->yres;
+	lower = var->lower_margin;
+	vslen = var->vsync_len;
+	upper = var->upper_margin;
+	vyres = var->yres_virtual;
+	yoffset = var->yoffset;
+
+	if (yres > vyres)
+		vyres = yres;
+	if (vxres * vyres > 65536) {
+		vyres = 65536 / vxres;
+		if (vyres < yres)
+			return -ENOMEM;
+	}
+	if (yoffset + yres > vyres)
+		yoffset = vyres - yres;
+	par->var.yres = yres;
+	par->var.lower_margin = lower;
+	par->var.vsync_len = vslen;
+	par->var.upper_margin = upper;
+	par->var.yres_virtual = vyres;
+	par->var.yoffset = yoffset;
+
+	if (var->vmode & FB_VMODE_DOUBLE) {
+		yres <<= 1;
+		lower <<= 1;
+		vslen <<= 1;
+		upper <<= 1;
+	}
+	ytotal = yres + lower + vslen + upper;
+	if (ytotal > 1024) {
+		ytotal >>= 1;
+		yres >>= 1;
+		lower >>= 1;
+		vslen >>= 1;
+		upper >>= 1;
+		rMode = 0x04;
+	} else
+		rMode = 0x00;
+	if (ytotal > 1024)
+		FAIL("ytotal too big");
+	if (vslen > 16)
+		FAIL("vslen too big");
+	par->crtc[CRTC_V_TOTAL] = ytotal - 2;
+	r7 = 0x10;	/* disable linecompare */
+	if (ytotal & 0x100) r7 |= 0x01;
+	if (ytotal & 0x200) r7 |= 0x20;
+	par->crtc[CRTC_PRESET_ROW] = 0;
+	par->crtc[CRTC_MAX_SCAN] = 0x40;	/* 1 scanline, no linecmp */
+	par->var.vmode = var->vmode;
+	if (var->vmode & FB_VMODE_DOUBLE)
+		par->crtc[CRTC_MAX_SCAN] |= 0x80;
+	par->crtc[CRTC_CURSOR_START] = 0x20;
+	par->crtc[CRTC_CURSOR_END]   = 0x00;
+	pos = yoffset * vxres + (xoffset >> 3);
+	par->crtc[CRTC_START_HI]     = pos >> 8;
+	par->crtc[CRTC_START_LO]     = pos & 0xFF;
+	par->crtc[CRTC_CURSOR_HI]    = 0x00;
+	par->crtc[CRTC_CURSOR_LO]    = 0x00;
+	pos = yres - 1;
+	par->crtc[CRTC_V_DISP_END] = pos & 0xFF;
+	par->crtc[CRTC_V_BLANK_START] = pos & 0xFF;
+	if (pos & 0x100)
+		r7 |= 0x0A;	/* 0x02 -> DISP_END, 0x08 -> BLANK_START */
+	if (pos & 0x200) {
+		r7 |= 0x40;	/* 0x40 -> DISP_END */
+		par->crtc[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
+	}
+	pos += lower;
+	par->crtc[CRTC_V_SYNC_START] = pos & 0xFF;
+	if (pos & 0x100)
+		r7 |= 0x04;
+	if (pos & 0x200)
+		r7 |= 0x80;
+	pos += vslen;
+	par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) | 0x10; /* disabled IRQ */
+	pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
+	par->crtc[CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
+                     but some SVGA chips requires all 8 bits to set */
+	if (vxres >= 512)
+		FAIL("vxres too long");
+	par->crtc[CRTC_OFFSET] = vxres >> 1;
+	par->crtc[CRTC_UNDERLINE] = 0x1F;
+	par->crtc[CRTC_MODE] = rMode | 0xE3;
+	par->crtc[CRTC_LINE_COMPARE] = 0xFF;
+	par->crtc[CRTC_OVERFLOW] = r7;
+
+	par->vss = 0x00;	/* 3DA */
+
+	for (i = 0x00; i < 0x10; i++)
+		par->atc[i] = i;
+	par->atc[ATC_MODE] = 0x81;
+	par->atc[ATC_OVERSCAN] = 0x00;	/* 0 for EGA, 0xFF for VGA */
+	par->atc[ATC_PLANE_ENABLE] = 0x0F;
+	par->atc[ATC_PEL] = xoffset & 7;
+	par->atc[ATC_COLOR_PAGE] = 0x00;
+	
+	par->misc = 0xC3;	/* enable CPU, ports 0x3Dx, positive sync */
+	par->var.sync = var->sync;
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+		par->misc &= ~0x40;
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+		par->misc &= ~0x80;
+	
+	par->seq[SEQ_CLOCK_MODE] = 0x01;
+	par->seq[SEQ_PLANE_WRITE] = 0x0F;
+	par->seq[SEQ_CHARACTER_MAP] = 0x00;
+	par->seq[SEQ_MEMORY_MODE] = 0x06;
+	
+	par->gdc[GDC_SR_VALUE] = 0x00;
+	par->gdc[GDC_SR_ENABLE] = 0x0F;
+	par->gdc[GDC_COMPARE_VALUE] = 0x00;
+	par->gdc[GDC_DATA_ROTATE] = 0x20;
+	par->gdc[GDC_PLANE_READ] = 0;
+	par->gdc[GDC_MODE] = 0x00;
+	par->gdc[GDC_MISC] = 0x05;
+	par->gdc[GDC_COMPARE_MASK] = 0x0F;
+	par->gdc[GDC_BIT_MASK] = 0xFF;
+
+	vga16fb_clock_chip(par, var->pixclock, info);
+
+	par->var.bits_per_pixel = 4;
+	par->var.grayscale = var->grayscale;
+	par->var.red.offset = par->var.green.offset = par->var.blue.offset = 
+	par->var.transp.offset = 0;
+	par->var.red.length = par->var.green.length = par->var.blue.length =
+		(info->isVGA) ? 6 : 2;
+	par->var.transp.length = 0;
+	par->var.nonstd = 0;
+	par->var.activate = FB_ACTIVATE_NOW;
+	par->var.height = -1;
+	par->var.width = -1;
+	par->var.accel_flags = 0;
+	
+	return 0;
+}
+#undef FAIL
+
+static int vga16fb_set_par(const struct vga16fb_par *par,
+			   struct vga16fb_info *info)
+{
+	int i;
+
+	outb(inb(MIS_R) | 0x01, MIS_W);
+
+	/* Enable graphics register modification */
+	if (!info->isVGA) {
+		outb(0x00, GRA_E0);
+		outb(0x01, GRA_E1);
+	}
+	
+	/* update misc output register */
+	outb(par->misc, MIS_W);
+	
+	/* synchronous reset on */
+	outb(0x00, SEQ_I);
+	outb(0x01, SEQ_D);
+	
+	/* write sequencer registers */
+	outb(1, SEQ_I);
+	outb(par->seq[1] | 0x20, SEQ_D);
+	for (i = 2; i < SEQ_C; i++) {
+		outb(i, SEQ_I);
+		outb(par->seq[i], SEQ_D);
+	}
+	
+	/* synchronous reset off */
+	outb(0x00, SEQ_I);
+	outb(0x03, SEQ_D);
+	
+	/* deprotect CRT registers 0-7 */
+	outb(0x11, CRT_IC);
+	outb(par->crtc[0x11], CRT_DC);
+
+	/* write CRT registers */
+	for (i = 0; i < CRTC_REGS; i++) {
+		outb(i, CRT_IC);
+		outb(par->crtc[i], CRT_DC);
+	}
+	
+	/* write graphics controller registers */
+	for (i = 0; i < GRA_C; i++) {
+		outb(i, GRA_I);
+		outb(par->gdc[i], GRA_D);
+	}
+	
+	/* write attribute controller registers */
+	for (i = 0; i < ATT_C; i++) {
+		inb_p(IS1_RC);		/* reset flip-flop */
+		outb_p(i, ATT_IW);
+		outb_p(par->atc[i], ATT_IW);
+	}
+
+	/* Wait for screen to stabilize. */
+	mdelay(50);
+
+	outb(0x01, SEQ_I);
+	outb(par->seq[1], SEQ_D);
+
+	inb(IS1_RC);
+	outb(0x20, ATT_IW);
+	
+	return 0;
+}
+
+static int vga16fb_set_var(struct fb_var_screeninfo *var, int con,
+			  struct fb_info *fb)
+{
+	struct vga16fb_info *info = (struct vga16fb_info*)fb;
+	struct vga16fb_par par;
+	struct display *display;
+	int err;
+
+	if (con < 0)
+		display = fb->disp;
+	else
+		display = fb_display + con;
+	if ((err = vga16fb_decode_var(var, &par, info)) != 0)
+		return err;
+	vga16fb_encode_var(var, &par, info);
+	
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+		return 0;
+
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+		u32 oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+
+		oldxres = display->var.xres;
+		oldyres = display->var.yres;
+		oldvxres = display->var.xres_virtual;
+		oldvyres = display->var.yres_virtual;
+		oldbpp = display->var.bits_per_pixel;
+
+		display->var = *var;
+
+		if (oldxres != var->xres || oldyres != var->yres ||
+		    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+		    oldbpp != var->bits_per_pixel) {
+			vga16fb_set_disp(con, info);
+			if (info->fb_info.changevar)
+				info->fb_info.changevar(con);
+		}
+		if (con == currcon)
+			vga16fb_set_par(&par, info);
+	}
+
+	return 0;
+}
+
+static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
+{
+	static unsigned char map[] = { 000, 001, 010, 011 };
+	int val;
+	
+	val = map[red>>14] | ((map[green>>14]) << 1) | ((map[blue>>14]) << 2);
+	inb_p(0x3DA);   /* ! 0x3BA */
+	outb_p(regno, 0x3C0);
+	outb_p(val, 0x3C0);
+	inb_p(0x3DA);   /* some clones need it */
+	outb_p(0x20, 0x3C0); /* unblank screen */
+}
+
+static int vga16_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			  unsigned *blue, unsigned *transp,
+			  struct fb_info *fb_info)
+{
+	/*
+	 *  Read a single color register and split it into colors/transparent.
+	 *  Return != 0 for invalid regno.
+	 */
+
+	if (regno >= 16)
+		return 1;
+
+	*red   = palette[regno].red;
+	*green = palette[regno].green;
+	*blue  = palette[regno].blue;
+	*transp = 0;
+	return 0;
+}
+
+static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
+{
+	outb(regno,       dac_reg);
+	outb(red   >> 10, dac_val);
+	outb(green >> 10, dac_val);
+	outb(blue  >> 10, dac_val);
+}
+
+static int vga16_setcolreg(unsigned regno, unsigned red, unsigned green,
+			  unsigned blue, unsigned transp,
+			  struct fb_info *fb_info)
+{
+	int gray;
+
+	/*
+	 *  Set a single color register. The values supplied are
+	 *  already rounded down to the hardware's capabilities
+	 *  (according to the entries in the `var' structure). Return
+	 *  != 0 for invalid regno.
+	 */
+	
+	if (regno >= 16)
+		return 1;
+
+	palette[regno].red   = red;
+	palette[regno].green = green;
+	palette[regno].blue  = blue;
+	
+	if (currcon < 0)
+		gray = disp.var.grayscale;
+	else
+		gray = fb_display[currcon].var.grayscale;
+	if (gray) {
+		/* gray = 0.30*R + 0.59*G + 0.11*B */
+		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+	}
+	if (((struct vga16fb_info *) fb_info)->isVGA) 
+		vga16_setpalette(regno,red,green,blue);
+	else
+		ega16_setpalette(regno,red,green,blue);
+	
+	return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+	if (con != currcon)
+		return;
+	if (fb_display[con].cmap.len)
+		fb_set_cmap(&fb_display[con].cmap, 1, vga16_setcolreg, info);
+	else
+		fb_set_cmap(fb_default_cmap(16), 1, vga16_setcolreg,
+			    info);
+}
+
+static int vga16fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info)
+{
+	if (con == currcon) /* current console? */
+		return fb_get_cmap(cmap, kspc, vga16_getcolreg, info);
+	else if (fb_display[con].cmap.len) /* non default colormap? */
+		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+	else
+		fb_copy_cmap(fb_default_cmap(16),
+		     cmap, kspc ? 0 : 2);
+	return 0;
+}
+
+static int vga16fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			   struct fb_info *info)
+{
+	int err;
+
+	if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+		err = fb_alloc_cmap(&fb_display[con].cmap,16,0);
+		if (err)
+			return err;
+	}
+	if (con == currcon)			/* current console? */
+		return fb_set_cmap(cmap, kspc, vga16_setcolreg, info);
+	else
+		fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+	return 0;
+}
+
+static int vga16fb_pan_display(struct fb_var_screeninfo *var, int con,
+			       struct fb_info *info) 
+{
+	if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
+	    var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+		return -EINVAL;
+	if (con == currcon)
+		vga16fb_pan_var(info, var);
+	fb_display[con].var.xoffset = var->xoffset;
+	fb_display[con].var.yoffset = var->yoffset;
+	fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+	return 0;
+}
+
+static int vga16fb_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg, int con,
+		       struct fb_info *info)
+{
+	return -EINVAL;
+}
+
+static struct fb_ops vga16fb_ops = {
+	vga16fb_open,
+	vga16fb_release,
+	vga16fb_get_fix,
+	vga16fb_get_var,
+	vga16fb_set_var,
+	vga16fb_get_cmap,
+	vga16fb_set_cmap,
+	vga16fb_pan_display,
+	vga16fb_ioctl
+};
+
+void vga16fb_setup(char *options, int *ints)
+{
+	char *this_opt;
+	
+	vga16fb.fb_info.fontname[0] = '\0';
+	
+	if (!options || !*options)
+		return;
+	
+	for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+		if (!*this_opt) continue;
+		
+		if (!strncmp(this_opt, "font:", 5))
+			strcpy(vga16fb.fb_info.fontname, this_opt+5);
+	}
+}
+
+static int vga16fb_switch(int con, struct fb_info *fb)
+{
+	struct vga16fb_par par;
+	struct vga16fb_info *info = (struct vga16fb_info*)fb;
+
+	/* Do we have to save the colormap? */
+	if (fb_display[currcon].cmap.len)
+		fb_get_cmap(&fb_display[currcon].cmap, 1, vga16_getcolreg,
+			    fb);
+	
+	currcon = con;
+	vga16fb_decode_var(&fb_display[con].var, &par, info);
+	vga16fb_set_par(&par, info);
+	vga16fb_set_disp(con, info);
+
+	/* Install new colormap */
+	do_install_cmap(con, fb);
+/*	vga16fb_update_var(con, fb); */
+	return 1;
+}
+
+/* The following VESA blanking code is taken from vgacon.c.  The VGA
+   blanking code was originally by Huang shi chao, and modified by
+   Christoph Rimek (chrimek@toppoint.de) and todd j. derr
+   (tjd@barefoot.org) for Linux. */
+#define attrib_port	0x3c0
+#define seq_port_reg	0x3c4
+#define seq_port_val	0x3c5
+#define gr_port_reg	0x3ce
+#define gr_port_val	0x3cf
+#define video_misc_rd	0x3cc
+#define video_misc_wr	0x3c2
+#define vga_video_port_reg	0x3d4
+#define vga_video_port_val	0x3d5
+
+static void vga_vesa_blank(struct vga16fb_info *info, int mode)
+{
+	unsigned char SeqCtrlIndex;
+	unsigned char CrtCtrlIndex;
+	
+	cli();
+	SeqCtrlIndex = inb_p(seq_port_reg);
+	CrtCtrlIndex = inb_p(vga_video_port_reg);
+
+	/* save original values of VGA controller registers */
+	if(!info->vesa_blanked) {
+		info->vga_state.CrtMiscIO = inb_p(video_misc_rd);
+		sti();
+
+		outb_p(0x00,vga_video_port_reg);	/* HorizontalTotal */
+		info->vga_state.HorizontalTotal = inb_p(vga_video_port_val);
+		outb_p(0x01,vga_video_port_reg);	/* HorizDisplayEnd */
+		info->vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
+		outb_p(0x04,vga_video_port_reg);	/* StartHorizRetrace */
+		info->vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
+		outb_p(0x05,vga_video_port_reg);	/* EndHorizRetrace */
+		info->vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
+		outb_p(0x07,vga_video_port_reg);	/* Overflow */
+		info->vga_state.Overflow = inb_p(vga_video_port_val);
+		outb_p(0x10,vga_video_port_reg);	/* StartVertRetrace */
+		info->vga_state.StartVertRetrace = inb_p(vga_video_port_val);
+		outb_p(0x11,vga_video_port_reg);	/* EndVertRetrace */
+		info->vga_state.EndVertRetrace = inb_p(vga_video_port_val);
+		outb_p(0x17,vga_video_port_reg);	/* ModeControl */
+		info->vga_state.ModeControl = inb_p(vga_video_port_val);
+		outb_p(0x01,seq_port_reg);		/* ClockingMode */
+		info->vga_state.ClockingMode = inb_p(seq_port_val);
+	}
+
+	/* assure that video is enabled */
+	/* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
+	cli();
+	outb_p(0x01,seq_port_reg);
+	outb_p(info->vga_state.ClockingMode | 0x20,seq_port_val);
+
+	/* test for vertical retrace in process.... */
+	if ((info->vga_state.CrtMiscIO & 0x80) == 0x80)
+		outb_p(info->vga_state.CrtMiscIO & 0xef,video_misc_wr);
+
+	/*
+	 * Set <End of vertical retrace> to minimum (0) and
+	 * <Start of vertical Retrace> to maximum (incl. overflow)
+	 * Result: turn off vertical sync (VSync) pulse.
+	 */
+	if (mode & VESA_VSYNC_SUSPEND) {
+		outb_p(0x10,vga_video_port_reg);	/* StartVertRetrace */
+		outb_p(0xff,vga_video_port_val); 	/* maximum value */
+		outb_p(0x11,vga_video_port_reg);	/* EndVertRetrace */
+		outb_p(0x40,vga_video_port_val);	/* minimum (bits 0..3)  */
+		outb_p(0x07,vga_video_port_reg);	/* Overflow */
+		outb_p(info->vga_state.Overflow | 0x84,vga_video_port_val); /* bits 9,10 of vert. retrace */
+	}
+
+	if (mode & VESA_HSYNC_SUSPEND) {
+		/*
+		 * Set <End of horizontal retrace> to minimum (0) and
+		 *  <Start of horizontal Retrace> to maximum
+		 * Result: turn off horizontal sync (HSync) pulse.
+		 */
+		outb_p(0x04,vga_video_port_reg);	/* StartHorizRetrace */
+		outb_p(0xff,vga_video_port_val);	/* maximum */
+		outb_p(0x05,vga_video_port_reg);	/* EndHorizRetrace */
+		outb_p(0x00,vga_video_port_val);	/* minimum (0) */
+	}
+
+	/* restore both index registers */
+	outb_p(SeqCtrlIndex,seq_port_reg);
+	outb_p(CrtCtrlIndex,vga_video_port_reg);
+	sti();
+}
+
+static void vga_vesa_unblank(struct vga16fb_info *info)
+{
+	unsigned char SeqCtrlIndex;
+	unsigned char CrtCtrlIndex;
+	
+	cli();
+	SeqCtrlIndex = inb_p(seq_port_reg);
+	CrtCtrlIndex = inb_p(vga_video_port_reg);
+
+	/* restore original values of VGA controller registers */
+	outb_p(info->vga_state.CrtMiscIO,video_misc_wr);
+
+	outb_p(0x00,vga_video_port_reg);		/* HorizontalTotal */
+	outb_p(info->vga_state.HorizontalTotal,vga_video_port_val);
+	outb_p(0x01,vga_video_port_reg);		/* HorizDisplayEnd */
+	outb_p(info->vga_state.HorizDisplayEnd,vga_video_port_val);
+	outb_p(0x04,vga_video_port_reg);		/* StartHorizRetrace */
+	outb_p(info->vga_state.StartHorizRetrace,vga_video_port_val);
+	outb_p(0x05,vga_video_port_reg);		/* EndHorizRetrace */
+	outb_p(info->vga_state.EndHorizRetrace,vga_video_port_val);
+	outb_p(0x07,vga_video_port_reg);		/* Overflow */
+	outb_p(info->vga_state.Overflow,vga_video_port_val);
+	outb_p(0x10,vga_video_port_reg);		/* StartVertRetrace */
+	outb_p(info->vga_state.StartVertRetrace,vga_video_port_val);
+	outb_p(0x11,vga_video_port_reg);		/* EndVertRetrace */
+	outb_p(info->vga_state.EndVertRetrace,vga_video_port_val);
+	outb_p(0x17,vga_video_port_reg);		/* ModeControl */
+	outb_p(info->vga_state.ModeControl,vga_video_port_val);
+	outb_p(0x01,seq_port_reg);		/* ClockingMode */
+	outb_p(info->vga_state.ClockingMode,seq_port_val);
+
+	/* restore index/control registers */
+	outb_p(SeqCtrlIndex,seq_port_reg);
+	outb_p(CrtCtrlIndex,vga_video_port_reg);
+	sti();
+}
+
+static void vga_pal_blank(void)
+{
+	int i;
+
+	for (i=0; i<16; i++) {
+		outb_p (i, dac_reg) ;
+		outb_p (0, dac_val) ;
+		outb_p (0, dac_val) ;
+		outb_p (0, dac_val) ;
+	}
+}
+
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+static void vga16fb_blank(int blank, struct fb_info *fb_info)
+{
+	struct vga16fb_info *info = (struct vga16fb_info*)fb_info;
+
+	switch (blank) {
+	case 0:				/* Unblank */
+		if (info->vesa_blanked) {
+			vga_vesa_unblank(info);
+			info->vesa_blanked = 0;
+		}
+		if (info->palette_blanked) {
+			do_install_cmap(currcon, fb_info);
+			info->palette_blanked = 0;
+		}
+		break;
+	case 1:				/* blank */
+		vga_pal_blank();
+		info->palette_blanked = 1;
+		break;
+	default:			/* VESA blanking */
+		vga_vesa_blank(info, blank-1);
+		info->vesa_blanked = 1;
+		break;
+	}
+}
+
+__initfunc(void vga16fb_init(void))
+{
+	int i,j;
+
+	printk("vga16fb: initializing\n");
+
+        vga16fb.video_vbase = ioremap((unsigned long)0xa0000, 65536);
+	printk("vga16fb: mapped to 0x%p\n", vga16fb.video_vbase);
+
+	vga16fb.isVGA = ORIG_VIDEO_ISVGA;
+	vga16fb.palette_blanked = 0;
+	vga16fb.vesa_blanked = 0;
+
+	i = vga16fb.isVGA? 6 : 2;
+	
+	vga16fb_defined.red.length   = i;
+	vga16fb_defined.green.length = i;
+	vga16fb_defined.blue.length  = i;	
+	for(i = 0; i < 16; i++) {
+		j = color_table[i];
+		palette[i].red   = default_red[j];
+		palette[i].green = default_grn[j];
+		palette[i].blue  = default_blu[j];
+	}
+	if (vga16fb.isVGA)
+		request_region(0x3c0, 32, "vga+");
+	else
+		request_region(0x3C0, 32, "ega");
+
+	disp.var = vga16fb_defined;
+
+	/* name should not depend on EGA/VGA */
+	strcpy(vga16fb.fb_info.modename, "VGA16 VGA");
+	vga16fb.fb_info.changevar = NULL;
+	vga16fb.fb_info.node = -1;
+	vga16fb.fb_info.fbops = &vga16fb_ops;
+	vga16fb.fb_info.disp=&disp;
+	vga16fb.fb_info.switch_con=&vga16fb_switch;
+	vga16fb.fb_info.updatevar=&vga16fb_update_var;
+	vga16fb.fb_info.blank=&vga16fb_blank;
+	vga16fb.fb_info.flags=FBINFO_FLAG_DEFAULT;
+	vga16fb_set_disp(-1, &vga16fb);
+
+	if (register_framebuffer(&vga16fb.fb_info)<0)
+		return;
+
+	printk("fb%d: %s frame buffer device\n",
+	       GET_FB_IDX(vga16fb.fb_info.node), vga16fb.fb_info.modename);
+}
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff -u --recursive --new-file v2.3.1/linux/fs/Config.in linux/fs/Config.in
--- v2.3.1/linux/fs/Config.in	Sun Mar  7 15:25:23 1999
+++ linux/fs/Config.in	Thu May 13 23:50:15 1999
@@ -19,6 +19,10 @@
 dep_tristate '  UMSDOS: Unix-like filesystem on top of standard MSDOS filesystem' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
 dep_tristate '  VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
 
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'EFS filesystem support (read only) (experimental)' CONFIG_EFS_FS
+fi
+
 tristate 'ISO 9660 CDROM filesystem support' CONFIG_ISO9660_FS
 if [ "$CONFIG_ISO9660_FS" != "n" ]; then
   bool 'Microsoft Joliet CDROM extensions' CONFIG_JOLIET
diff -u --recursive --new-file v2.3.1/linux/fs/Makefile linux/fs/Makefile
--- v2.3.1/linux/fs/Makefile	Mon Aug 31 13:01:35 1998
+++ linux/fs/Makefile	Thu May 13 23:50:15 1999
@@ -17,8 +17,8 @@
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
-		hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd \
-		nls devpts adfs qnx4
+		hpfs sysv smbfs ncpfs ufs efs affs romfs autofs hfs lockd \
+		nfsd nls devpts adfs qnx4
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -188,6 +188,14 @@
 else
   ifeq ($(CONFIG_UFS_FS),m)
   MOD_SUB_DIRS += ufs
+  endif
+endif
+
+ifeq ($(CONFIG_EFS_FS),y)
+SUB_DIRS += efs
+else
+  ifeq ($(CONFIG_EFS_FS),m)
+  MOD_SUB_DIRS += efs
   endif
 endif
 
diff -u --recursive --new-file v2.3.1/linux/fs/buffer.c linux/fs/buffer.c
--- v2.3.1/linux/fs/buffer.c	Fri May 14 18:55:24 1999
+++ linux/fs/buffer.c	Thu May 13 23:18:20 1999
@@ -644,7 +644,9 @@
 				clear_bit(BH_Req, &bh->b_state);
 				bh->b_flushtime = 0;
 			}
-			remove_from_hash_queue(bh);
+			remove_from_queues(bh);
+			bh->b_dev=B_FREE;
+			insert_into_queues(bh);
 		}
 	}
 }
diff -u --recursive --new-file v2.3.1/linux/fs/efs/.cvsignore linux/fs/efs/.cvsignore
--- v2.3.1/linux/fs/efs/.cvsignore	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/.cvsignore	Thu May 13 23:50:15 1999
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff -u --recursive --new-file v2.3.1/linux/fs/efs/Makefile linux/fs/efs/Makefile
--- v2.3.1/linux/fs/efs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/Makefile	Thu May 13 23:50:15 1999
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux efs-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 := efs.o
+O_OBJS   := super.o inode.o namei.o dir.o file.o symlink.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.1/linux/fs/efs/dir.c linux/fs/efs/dir.c
--- v2.3.1/linux/fs/efs/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/dir.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,139 @@
+/*
+ * dir.c
+ *
+ * Copyright (c) 1999 Al Smith
+ */
+
+#include <linux/efs_fs.h>
+
+static int efs_readdir(struct file *, void *, filldir_t);
+
+static struct file_operations efs_dir_operations = {
+	NULL,			/* lseek */
+	NULL,			/* read */
+	NULL,			/* write */
+	efs_readdir,		/* readdir */
+	NULL,			/* poll */
+	NULL,			/* ioctl */
+	NULL,			/* mmap */
+	NULL,			/* open */
+	NULL,			/* flush */
+	NULL,			/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+struct inode_operations efs_dir_inode_operations = {
+	&efs_dir_operations,	/* default directory file-ops */
+	NULL,			/* create */
+	efs_lookup,		/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	efs_bmap,		/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) {
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct buffer_head *bh;
+
+	struct efs_dir		*dirblock;
+	struct efs_dentry	*dirslot;
+	efs_ino_t		inodenum;
+	efs_block_t		block;
+	int			slot, namelen;
+	char			*nameptr;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		return -EBADF;
+
+	if (inode->i_size & (EFS_DIRBSIZE-1))
+		printk(KERN_WARNING "EFS: WARNING: readdir(): directory size not a multiple of EFS_DIRBSIZE\n");
+
+	/* work out where this entry can be found */
+	block = filp->f_pos >> EFS_DIRBSIZE_BITS;
+
+	/* each block contains at most 256 slots */
+	slot  = filp->f_pos & 0xff;
+
+	/* look at all blocks */
+	while (block < inode->i_blocks) {
+		/* read the dir block */
+		bh = bread(inode->i_dev, efs_bmap(inode, block), EFS_DIRBSIZE);
+
+		if (!bh) {
+			printk(KERN_ERR "EFS: readdir(): failed to read dir block %d\n", block);
+			break;
+		}
+
+		dirblock = (struct efs_dir *) bh->b_data; 
+
+		if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
+			printk(KERN_ERR "EFS: readdir(): invalid directory block\n");
+			brelse(bh);
+			break;
+		}
+
+		while (slot < dirblock->slots) {
+			if (dirblock->space[slot] == 0) {
+				slot++;
+				continue;
+			}
+
+			dirslot  = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+
+			inodenum = be32_to_cpu(dirslot->inode);
+			namelen  = dirslot->namelen;
+			nameptr  = dirslot->name;
+
+#ifdef DEBUG
+			printk(KERN_DEBUG "EFS: readdir(): block %d slot %d/%d: inode %u, name \"%s\", namelen %u\n", block, slot, dirblock->slots-1, inodenum, nameptr, namelen);
+#endif
+			if (namelen > 0) {
+				/* found the next entry */
+				filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+
+				/* copy filename and data in dirslot */
+				filldir(dirent, nameptr, namelen, filp->f_pos, inodenum);
+
+				/* sanity check */
+				if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) {
+					printk(KERN_WARNING "EFS: directory entry %d exceeds directory block\n", slot);
+					slot++;
+					continue;
+				}
+
+				/* store position of next slot */
+				if (++slot == dirblock->slots) {
+					slot = 0;
+					block++;
+				}
+				brelse(bh);
+				filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+				return 0;
+			}
+			slot++;
+		}
+		brelse(bh);
+
+		slot = 0;
+		block++;
+	}
+
+	filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+	return 0;
+}
+
diff -u --recursive --new-file v2.3.1/linux/fs/efs/file.c linux/fs/efs/file.c
--- v2.3.1/linux/fs/efs/file.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/file.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,72 @@
+/*
+ * file.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/efs_fs.h>
+
+static struct file_operations efs_file_operations = {
+	NULL,			/* lseek */
+	generic_file_read,	/* read */
+	NULL,			/* write */
+	NULL,			/* readdir */
+	NULL,			/* poll */
+	NULL,			/* ioctl */
+	generic_file_mmap,	/* mmap */
+	NULL,			/* open */
+	NULL,			/* flush */
+	NULL,			/* release */
+	NULL,			/* fsync */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL			/* revalidate */
+};
+
+struct inode_operations efs_file_inode_operations = {
+	&efs_file_operations,	/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	generic_readpage,	/* readpage */
+	NULL,			/* writepage */
+	efs_bmap,		/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+ 
+int efs_bmap(struct inode *inode, efs_block_t block) {
+
+	if (block < 0) {
+		printk(KERN_WARNING "EFS: bmap(): block < 0\n");
+		return 0;
+	}
+
+	/* are we about to read past the end of a file ? */
+	if (!(block < inode->i_blocks)) {
+#ifdef DEBUG
+		/*
+		 * i have no idea why this happens as often as it does
+		 */
+		printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n",
+			block,
+			inode->i_blocks,
+			inode->i_size);
+#endif
+		return 0;
+	}
+
+	return efs_map_block(inode, block);
+}
+
diff -u --recursive --new-file v2.3.1/linux/fs/efs/inode.c linux/fs/efs/inode.c
--- v2.3.1/linux/fs/efs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/inode.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,301 @@
+/*
+ * inode.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang,
+ *              and from work (c) 1998 Mike Shaver.
+ */
+
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_sb.h>
+
+static inline void extent_copy(efs_extent *src, efs_extent *dst) {
+	/*
+	 * this is slightly evil. it doesn't just copy
+	 * efs_extent from src to dst, it also mangles
+	 * the bits so that dst ends up in cpu byte-order.
+	 */
+
+	dst->cooked.ex_magic  =  (unsigned int) src->raw[0];
+	dst->cooked.ex_bn     = ((unsigned int) src->raw[1] << 16) |
+				((unsigned int) src->raw[2] <<  8) |
+				((unsigned int) src->raw[3] <<  0);
+	dst->cooked.ex_length =  (unsigned int) src->raw[4];
+	dst->cooked.ex_offset = ((unsigned int) src->raw[5] << 16) |
+				((unsigned int) src->raw[6] <<  8) |
+				((unsigned int) src->raw[7] <<  0);
+	return;
+}
+
+void efs_read_inode(struct inode *inode) {
+	int i, inode_index;
+	dev_t device;
+	struct buffer_head *bh;
+	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
+	struct efs_inode_info *in = INODE_INFO(inode);
+	efs_block_t block, offset;
+	struct efs_dinode *efs_inode;
+  
+	/*
+	** EFS layout:
+	**
+	** |   cylinder group    |   cylinder group    |   cylinder group ..etc
+	** |inodes|data          |inodes|data          |inodes|data       ..etc
+	**
+	** work out the inode block index, (considering initially that the
+	** inodes are stored as consecutive blocks). then work out the block
+	** number of that inode given the above layout, and finally the
+	** offset of the inode within that block.
+	*/
+
+	inode_index = inode->i_ino /
+		(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
+
+	block = sb->fs_start + sb->first_block + 
+		(sb->group_size * (inode_index / sb->inode_blocks)) +
+		(inode_index % sb->inode_blocks);
+
+	offset = (inode->i_ino %
+			(EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
+		sizeof(struct efs_dinode);
+
+	bh = bread(inode->i_dev, block, EFS_BLOCKSIZE);
+	if (!bh) {
+		printk(KERN_WARNING "EFS: bread() failed at block %d\n", block);
+		goto read_inode_error;
+	}
+
+	efs_inode = (struct efs_dinode *) (bh->b_data + offset);
+    
+	inode->i_mode  = be16_to_cpu(efs_inode->di_mode);
+	inode->i_nlink = be16_to_cpu(efs_inode->di_nlink);
+	inode->i_uid   = be16_to_cpu(efs_inode->di_uid);
+	inode->i_gid   = be16_to_cpu(efs_inode->di_gid);
+	inode->i_size  = be32_to_cpu(efs_inode->di_size);
+	inode->i_atime = be32_to_cpu(efs_inode->di_atime);
+	inode->i_mtime = be32_to_cpu(efs_inode->di_mtime);
+	inode->i_ctime = be32_to_cpu(efs_inode->di_ctime);
+
+	/* this is the number of blocks in the file */
+	if (inode->i_size == 0) {
+		inode->i_blocks = 0;
+	} else {
+		inode->i_blocks = ((inode->i_size - 1) >> EFS_BLOCKSIZE_BITS) + 1;
+	}
+
+	/*
+	 * BUG: irix dev_t is 32-bits. linux dev_t is only 16-bits.
+	 *
+	 * apparently linux will change to 32-bit dev_t sometime during
+	 * linux 2.3.
+	 *
+	 * as is, this code maps devices that can't be represented in
+	 * 16-bits (ie major > 255 or minor > 255) to major = minor = 255.
+	 *
+	 * during 2.3 when 32-bit dev_t become available, we should test
+	 * to see whether odev contains 65535. if this is the case then we
+	 * should then do device = be32_to_cpu(efs_inode->di_u.di_dev.ndev).
+	 */
+    	device = be16_to_cpu(efs_inode->di_u.di_dev.odev);
+
+	/* get the number of extents for this object */
+	in->numextents = be16_to_cpu(efs_inode->di_numextents);
+	in->lastextent = 0;
+
+	/* copy the extents contained within the inode to memory */
+	for(i = 0; i < EFS_DIRECTEXTENTS; i++) {
+		extent_copy(&(efs_inode->di_u.di_extents[i]), &(in->extents[i]));
+		if (i < in->numextents && in->extents[i].cooked.ex_magic != 0) {
+			printk(KERN_WARNING "EFS: extent %d has bad magic number in inode %lu\n", i, inode->i_ino);
+			brelse(bh);
+			goto read_inode_error;
+		}
+	}
+
+	brelse(bh);
+   
+#ifdef DEBUG
+	printk(KERN_DEBUG "EFS: read_inode(): inode %lu, extents %d, mode %o\n",
+		inode->i_ino, in->numextents, inode->i_mode);
+#endif
+
+	switch (inode->i_mode & S_IFMT) {
+		case S_IFDIR: 
+			inode->i_op = &efs_dir_inode_operations; 
+			break;
+		case S_IFREG:
+			inode->i_op = &efs_file_inode_operations;
+			break;
+		case S_IFLNK:
+			inode->i_op = &efs_symlink_inode_operations;
+			break;
+		case S_IFCHR:
+			inode->i_rdev = device;
+			inode->i_op = &chrdev_inode_operations; 
+			break;
+		case S_IFBLK:
+			inode->i_rdev = device;
+			inode->i_op = &blkdev_inode_operations; 
+			break;
+		case S_IFIFO:
+			init_fifo(inode);
+			break;    
+		default:
+			printk(KERN_WARNING "EFS: unsupported inode mode %o\n", inode->i_mode);
+			goto read_inode_error;
+			break;
+	}
+
+	return;
+        
+read_inode_error:
+	printk(KERN_WARNING "EFS: failed to read inode %lu\n", inode->i_ino);
+	inode->i_mode = S_IFREG;
+	inode->i_atime = 0;
+	inode->i_ctime = 0;
+	inode->i_mtime = 0;
+	inode->i_nlink = 1;
+	inode->i_size = 0;
+	inode->i_blocks = 0;
+	inode->i_uid = 0;
+	inode->i_gid = 0;
+	inode->i_op = NULL;
+
+	return;
+}
+
+static inline efs_block_t
+efs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_sb_info *sb) {
+	efs_block_t start;
+	efs_block_t length;
+	efs_block_t offset;
+
+	/*
+	 * given an extent and a logical block within a file,
+	 * can this block be found within this extent ?
+	 */
+	start  = ptr->cooked.ex_bn;
+	length = ptr->cooked.ex_length;
+	offset = ptr->cooked.ex_offset;
+
+	if ((block >= offset) && (block < offset+length)) {
+		return(sb->fs_start + start + block - offset);
+	} else {
+		return 0;
+	}
+}
+
+efs_block_t efs_map_block(struct inode *inode, efs_block_t block) {
+	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
+	struct efs_inode_info *in = INODE_INFO(inode);
+	struct buffer_head    *bh = NULL;
+
+	int cur, last, first = 1;
+	int ibase, ioffset, dirext, direxts, indext, indexts;
+	efs_block_t iblock, result = 0, lastblock = 0;
+	efs_extent ext, *exts;
+
+	last = in->lastextent;
+
+	if (in->numextents <= EFS_DIRECTEXTENTS) {
+		/* first check the last extent we returned */
+		if ((result = efs_extent_check(&in->extents[last], block, sb)))
+			return result;
+    
+		/* if we only have one extent then nothing can be found */
+		if (in->numextents == 1) {
+			printk(KERN_ERR "EFS: map_block() failed to map (1 extent)\n");
+			return 0;
+		}
+
+		direxts = in->numextents;
+
+		/*
+		 * check the stored extents in the inode
+		 * start with next extent and check forwards
+		 */
+		for(dirext = 1; dirext < direxts; dirext++) {
+			cur = (last + dirext) % in->numextents;
+			if ((result = efs_extent_check(&in->extents[cur], block, sb))) {
+				in->lastextent = cur;
+				return result;
+			}
+		}
+
+		printk(KERN_ERR "EFS: map_block() failed to map block %u (dir)\n", block);
+		return 0;
+	}
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "EFS: map_block(): indirect search for logical block %u\n", block);
+#endif
+	direxts = in->extents[0].cooked.ex_offset;
+	indexts = in->numextents;
+
+	for(indext = 0; indext < indexts; indext++) {
+		cur = (last + indext) % indexts;
+
+		/*
+		 * work out which direct extent contains `cur'.
+		 *
+		 * also compute ibase: i.e. the number of the first
+		 * indirect extent contained within direct extent `cur'.
+		 *
+		 */
+		ibase = 0;
+		for(dirext = 0; cur < ibase && dirext < direxts; dirext++) {
+			ibase += in->extents[dirext].cooked.ex_length *
+				(EFS_BLOCKSIZE / sizeof(efs_extent));
+		}
+
+		if (dirext == direxts) {
+			/* should never happen */
+			printk(KERN_ERR "EFS: couldn't find direct extent for indirect extent %d (block %u)\n", cur, block);
+			if (bh) brelse(bh);
+			return 0;
+		}
+		
+		/* work out block number and offset of this indirect extent */
+		iblock = sb->fs_start + in->extents[dirext].cooked.ex_bn +
+			(cur - ibase) /
+			(EFS_BLOCKSIZE / sizeof(efs_extent));
+		ioffset = (cur - ibase) %
+			(EFS_BLOCKSIZE / sizeof(efs_extent));
+
+		if (first || lastblock != iblock) {
+			if (bh) brelse(bh);
+
+			bh = bread(inode->i_dev, iblock, EFS_BLOCKSIZE);
+			if (!bh) {
+				printk(KERN_ERR "EFS: bread() failed at block %d\n", iblock);
+				return 0;
+			}
+#ifdef DEBUG
+			printk(KERN_DEBUG "EFS: map_block(): read indirect extent block %d\n", iblock);
+#endif
+			first = 0;
+			lastblock = iblock;
+		}
+
+		exts = (efs_extent *) bh->b_data;
+
+		extent_copy(&(exts[ioffset]), &ext);
+
+		if (ext.cooked.ex_magic != 0) {
+			printk(KERN_ERR "EFS: extent %d has bad magic number in block %d\n", cur, iblock);
+			if (bh) brelse(bh);
+			return 0;
+		}
+
+		if ((result = efs_extent_check(&ext, block, sb))) {
+			if (bh) brelse(bh);
+			in->lastextent = cur;
+			return result;
+		}
+	}
+	if (bh) brelse(bh);
+	printk(KERN_ERR "EFS: map_block() failed to map block %u (indir)\n", block);
+	return 0;
+}  
+
diff -u --recursive --new-file v2.3.1/linux/fs/efs/namei.c linux/fs/efs/namei.c
--- v2.3.1/linux/fs/efs/namei.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/namei.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,75 @@
+/*
+ * namei.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/efs_fs.h>
+
+static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) {
+	struct buffer_head *bh;
+
+	int			slot, namelen;
+	char			*nameptr;
+	struct efs_dir		*dirblock;
+	struct efs_dentry	*dirslot;
+	efs_ino_t		inodenum;
+	efs_block_t		block;
+ 
+	if (inode->i_size & (EFS_DIRBSIZE-1))
+		printk(KERN_WARNING "EFS: WARNING: find_entry(): directory size not a multiple of EFS_DIRBSIZE\n");
+
+	for(block = 0; block < inode->i_blocks; block++) {
+
+		bh = bread(inode->i_dev, efs_bmap(inode, block), EFS_DIRBSIZE);
+		if (!bh) {
+			printk(KERN_ERR "EFS: find_entry(): failed to read dir block %d\n", block);
+			return 0;
+		}
+    
+		dirblock = (struct efs_dir *) bh->b_data;
+
+		if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
+			printk(KERN_ERR "EFS: find_entry(): invalid directory block\n");
+			brelse(bh);
+			return(0);
+		}
+
+		for(slot = 0; slot < dirblock->slots; slot++) {
+			dirslot  = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+
+			namelen  = dirslot->namelen;
+			nameptr  = dirslot->name;
+
+			if ((namelen == len) && (!memcmp(name, nameptr, len))) {
+				inodenum = be32_to_cpu(dirslot->inode);
+				brelse(bh);
+				return(inodenum);
+			}
+		}
+		brelse(bh);
+	}
+	return(0);
+}
+
+struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry) {
+	efs_ino_t inodenum;
+	struct inode * inode;
+
+	if (!dir || !S_ISDIR(dir->i_mode))
+		return ERR_PTR(-ENOENT);
+
+	inode = NULL;
+
+	inodenum = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
+	if (inodenum) {
+		if (!(inode = iget(dir->i_sb, inodenum)))
+			return ERR_PTR(-EACCES);
+	}
+
+	d_add(dentry, inode);
+	return NULL;
+}
+
diff -u --recursive --new-file v2.3.1/linux/fs/efs/super.c linux/fs/efs/super.c
--- v2.3.1/linux/fs/efs/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/super.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,250 @@
+/*
+ * super.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_vh.h>
+#include <linux/efs_fs_sb.h>
+
+static struct file_system_type efs_fs_type = {
+	"efs",			/* filesystem name */
+	FS_REQUIRES_DEV,	/* fs_flags */
+	efs_read_super,		/* entry function pointer */
+	NULL 			/* next */
+};
+
+static struct super_operations efs_superblock_operations = {
+	efs_read_inode,	/* read_inode */
+	NULL,		/* write_inode */
+	NULL,		/* put_inode */
+	NULL,		/* delete_inode */
+	NULL,		/* notify_change */
+	efs_put_super,	/* put_super */
+	NULL,		/* write_super */
+	efs_statfs,	/* statfs */
+	NULL		/* remount */
+};
+
+__initfunc(int init_efs_fs(void)) {
+	return register_filesystem(&efs_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void) {
+	printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
+	return init_efs_fs();
+}
+
+void cleanup_module(void) {
+	unregister_filesystem(&efs_fs_type);
+}
+#endif
+
+static efs_block_t efs_validate_vh(struct volume_header *vh) {
+	int		i;
+	unsigned int	cs, csum, *ui;
+	efs_block_t	sblock = 0; /* shuts up gcc */
+	struct pt_types	*pt_entry;
+	int		pt_type, slice = -1;
+
+	if (be32_to_cpu(vh->vh_magic) != VHMAGIC) {
+		/*
+		 * assume that we're dealing with a partition and allow
+		 * read_super() to try and detect a valid superblock
+		 * on the next block.
+		 */
+		return 0;
+	}
+
+	ui = ((unsigned int *) (vh + 1)) - 1;
+	for(csum = 0; ui >= ((unsigned int *) vh);) {
+		cs = *ui--;
+		csum += be32_to_cpu(cs);
+	}
+	if (csum) {
+		printk(KERN_INFO "EFS: SGI disklabel: checksum bad, label corrupted\n");
+		return 0;
+	}
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "EFS: bf: \"%16s\"\n", vh->vh_bootfile);
+
+	for(i = 0; i < NVDIR; i++) {
+		int	j;
+		char	name[VDNAMESIZE+1];
+
+		for(j = 0; j < VDNAMESIZE; j++) {
+			name[j] = vh->vh_vd[i].vd_name[j];
+		}
+		name[j] = (char) 0;
+
+		if (name[0]) {
+			printk(KERN_DEBUG "EFS: vh: %8s block: 0x%08x size: 0x%08x\n",
+				name,
+				(int) be32_to_cpu(vh->vh_vd[i].vd_lbn),
+				(int) be32_to_cpu(vh->vh_vd[i].vd_nbytes));
+		}
+	}
+#endif
+
+	for(i = 0; i < NPARTAB; i++) {
+		pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type);
+		for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) {
+			if (pt_type == pt_entry->pt_type) break;
+		}
+#ifdef DEBUG
+		if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) {
+			printk(KERN_DEBUG "EFS: pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n",
+				i,
+				(int) be32_to_cpu(vh->vh_pt[i].pt_firstlbn),
+				(int) be32_to_cpu(vh->vh_pt[i].pt_nblks),
+				pt_type,
+				(pt_entry->pt_name) ? pt_entry->pt_name : "unknown");
+		}
+#endif
+		if (IS_EFS(pt_type)) {
+			sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn);
+			slice = i;
+		}
+	}
+
+	if (slice == -1) {
+		printk(KERN_NOTICE "EFS: partition table contained no EFS partitions\n");
+	} else {
+		printk(KERN_INFO "EFS: using slice %d (type %s, offset 0x%x)\n",
+			slice,
+			(pt_entry->pt_name) ? pt_entry->pt_name : "unknown",
+			sblock);
+	}
+	return(sblock);
+}
+
+static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) {
+
+	if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic))) return -1;
+
+	sb->fs_magic     = be32_to_cpu(super->fs_magic);
+	sb->total_blocks = be32_to_cpu(super->fs_size);
+	sb->first_block  = be32_to_cpu(super->fs_firstcg);
+	sb->group_size   = be32_to_cpu(super->fs_cgfsize);
+	sb->data_free    = be32_to_cpu(super->fs_tfree);
+	sb->inode_free   = be32_to_cpu(super->fs_tinode);
+	sb->inode_blocks = be16_to_cpu(super->fs_cgisize);
+	sb->total_groups = be16_to_cpu(super->fs_ncg);
+    
+	return 0;    
+}
+
+struct super_block *efs_read_super(struct super_block *s, void *d, int silent) {
+	kdev_t dev = s->s_dev;
+	struct efs_sb_info *sb;
+	struct buffer_head *bh;
+
+	MOD_INC_USE_COUNT;
+	lock_super(s);
+  
+ 	sb = SUPER_INFO(s);
+
+	set_blocksize(dev, EFS_BLOCKSIZE);
+  
+	/* read the vh (volume header) block */
+	bh = bread(dev, 0, EFS_BLOCKSIZE);
+
+	if (!bh) {
+		printk(KERN_ERR "EFS: cannot read volume header\n");
+		goto out_no_fs_ul;
+	}
+
+	/*
+	 * if this returns zero then we didn't find any partition table.
+	 * this isn't (yet) an error - just assume for the moment that
+	 * the device is valid and go on to search for a superblock.
+	 */
+	sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data);
+	brelse(bh);
+
+	if (sb->fs_start == -1) {
+		goto out_no_fs_ul;
+	}
+
+	bh = bread(dev, sb->fs_start + EFS_SUPER, EFS_BLOCKSIZE);
+	if (!bh) {
+		printk(KERN_ERR "EFS: unable to read superblock\n");
+		goto out_no_fs_ul;
+	}
+		
+	if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
+		printk(KERN_WARNING "EFS: invalid superblock at block %u\n", sb->fs_start + EFS_SUPER);
+		brelse(bh);
+		goto out_no_fs_ul;
+	}
+	brelse(bh);
+ 
+	s->s_magic		= EFS_SUPER_MAGIC;
+	s->s_blocksize		= EFS_BLOCKSIZE;
+	s->s_blocksize_bits	= EFS_BLOCKSIZE_BITS;
+	if (!(s->s_flags & MS_RDONLY)) {
+#ifdef DEBUG
+		printk(KERN_INFO "EFS: forcing read-only mode\n");
+#endif
+		s->s_flags |= MS_RDONLY;
+	}
+	s->s_op   = &efs_superblock_operations;
+	s->s_dev  = dev;
+	s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE), NULL);
+	unlock_super(s);
+ 
+	if (!(s->s_root)) {
+		printk(KERN_ERR "EFS: get root inode failed\n");
+		goto out_no_fs;
+	}
+
+	if (check_disk_change(s->s_dev)) {
+		printk(KERN_ERR "EFS: device changed\n");
+		goto out_no_fs;
+	}
+
+	return(s);
+
+out_no_fs_ul:
+	unlock_super(s);
+out_no_fs:
+	s->s_dev = 0;
+	MOD_DEC_USE_COUNT;
+	return(NULL);
+}
+
+void efs_put_super(struct super_block *s) {
+	MOD_DEC_USE_COUNT;
+}
+
+int efs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) {
+	struct statfs ret;
+	struct efs_sb_info *sb = SUPER_INFO(s);
+
+	ret.f_type    = EFS_SUPER_MAGIC;	/* efs magic number */
+	ret.f_bsize   = EFS_BLOCKSIZE;		/* blocksize */
+	ret.f_blocks  = sb->total_groups *	/* total data blocks */
+			(sb->group_size - sb->inode_blocks);
+	ret.f_bfree   = sb->data_free;		/* free data blocks */
+	ret.f_bavail  = sb->data_free;		/* free blocks for non-root */
+	ret.f_files   = sb->total_groups *	/* total inodes */
+			sb->inode_blocks *
+			(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
+	ret.f_ffree   = sb->inode_free;	/* free inodes */
+	ret.f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */
+	ret.f_fsid.val[1] =  sb->fs_magic        & 0xffff; /* fs ID */
+	ret.f_namelen = EFS_MAXNAMELEN;		/* max filename length */
+
+	return copy_to_user(buf, &ret, bufsiz) ? -EFAULT : 0;
+}
+
diff -u --recursive --new-file v2.3.1/linux/fs/efs/symlink.c linux/fs/efs/symlink.c
--- v2.3.1/linux/fs/efs/symlink.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/efs/symlink.c	Thu May 13 23:50:15 1999
@@ -0,0 +1,102 @@
+/*
+ * symlink.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/malloc.h>
+#include <linux/efs_fs.h>
+
+static int efs_readlink(struct dentry *, char *, int);
+static struct dentry * efs_follow_link(struct dentry *, struct dentry *, unsigned int);
+
+struct inode_operations efs_symlink_inode_operations = {
+	NULL,			/* no symlink file-operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	efs_readlink,		/* readlink */
+	efs_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+static char *efs_linktarget(struct inode *in, int *len) {
+	char *name;
+	struct buffer_head * bh;
+	efs_block_t size = in->i_size;
+  
+	if (size > 2 * EFS_BLOCKSIZE) {
+		printk(KERN_ERR "EFS: linktarget(): name too long: %lu\n", in->i_size);
+		return NULL;
+	}
+  
+	if (!(name = kmalloc(size + 1, GFP_KERNEL)))
+		return NULL;
+  
+	/* read first 512 bytes of link target */
+	bh = bread(in->i_dev, efs_bmap(in, 0), EFS_BLOCKSIZE);
+	if (!bh) {
+		kfree(name);
+		printk(KERN_ERR "EFS: linktarget(): couldn't read block %d\n", efs_bmap(in, 0));
+		return NULL;
+	}
+
+	memcpy(name, bh->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size);
+	brelse(bh);
+
+	if (size > EFS_BLOCKSIZE) {
+		bh = bread(in->i_dev, efs_bmap(in, 1), EFS_BLOCKSIZE);
+		if (!bh) {
+			kfree(name);
+			printk(KERN_ERR "EFS: linktarget(): couldn't read block %d\n", efs_bmap(in, 1));
+			return NULL;
+		}
+		memcpy(name + EFS_BLOCKSIZE, bh->b_data, size - EFS_BLOCKSIZE);
+		brelse(bh);
+	}
+  
+	name[size] = (char) 0;
+	if (len) *len = size;
+
+	return name;
+}
+
+static struct dentry *efs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow) {
+	char *name;
+	struct inode *inode = dentry->d_inode;
+
+	if (!(name = efs_linktarget(inode, NULL))) {
+		dput(base);
+		return ERR_PTR(-ELOOP);
+	}
+	base = lookup_dentry(name, base, follow);
+	kfree(name);
+  
+	return base;
+}
+
+static int efs_readlink(struct dentry * dir, char * buf, int bufsiz) {
+	int rc;
+	char *name;
+	struct inode *inode = dir->d_inode;
+  
+	if (!(name = efs_linktarget(inode, &bufsiz))) return 0;
+	rc = copy_to_user(buf, name, bufsiz) ? -EFAULT : bufsiz;
+	kfree(name);
+
+	return rc;
+}
+
diff -u --recursive --new-file v2.3.1/linux/fs/ext2/truncate.c linux/fs/ext2/truncate.c
--- v2.3.1/linux/fs/ext2/truncate.c	Mon Oct  5 10:04:34 1998
+++ linux/fs/ext2/truncate.c	Thu May 13 17:34:51 1999
@@ -407,8 +407,9 @@
 			break;
 		if (IS_SYNC(inode) && (inode->i_state & I_DIRTY))
 			ext2_sync_inode (inode);
-		current->counter = 0;
-		schedule ();
+		run_task_queue(&tq_disk);
+		current->policy |= SCHED_YIELD;
+		schedule();
 	}
 	/*
 	 * If the file is not being truncated to a block boundary, the
diff -u --recursive --new-file v2.3.1/linux/fs/fat/cache.c linux/fs/fat/cache.c
--- v2.3.1/linux/fs/fat/cache.c	Wed Jan 21 17:46:56 1998
+++ linux/fs/fat/cache.c	Thu May 13 23:18:20 1999
@@ -2,6 +2,10 @@
  *  linux/fs/fat/cache.c
  *
  *  Written 1992,1993 by Werner Almesberger
+ *
+ *  Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
+ *	of inode number.
+ *  May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
  */
 
 #include <linux/msdos_fs.h>
@@ -62,6 +66,8 @@
 		p_first = p_last = NULL; /* GCC needs that stuff */
 		next = CF_LE_L(((unsigned long *) bh->b_data)[(first &
 		    (SECTOR_SIZE-1)) >> 2]);
+		/* Fscking Microsoft marketing department. Their "32" is 28. */
+		next &= 0xfffffff;
 		if (next >= 0xffffff7) next = -1;
 		PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%d\n", b, nr, first, next));
 
@@ -141,14 +147,13 @@
 void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
 {
 	struct fat_cache *walk;
+	int first = MSDOS_I(inode)->i_start;
 
-#ifdef DEBUG
-printk("cache lookup: <%s,%d> %d (%d,%d) -> ", kdevname(inode->i_dev),
-       inode->i_ino, cluster, *f_clu, *d_clu);
-#endif
+	if (!first)
+		return;
 	for (walk = fat_cache; walk; walk = walk->next)
 		if (inode->i_dev == walk->device
-		    && walk->ino == inode->i_ino
+		    && walk->start_cluster == first
 		    && walk->file_cluster <= cluster
 		    && walk->file_cluster > *f_clu) {
 			*d_clu = walk->disk_cluster;
@@ -171,7 +176,8 @@
 	for (walk = fat_cache; walk; walk = walk->next) {
 		if (walk->device)
 			printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
-			       walk->ino, walk->file_cluster, walk->disk_cluster);
+			       walk->start_cluster, walk->file_cluster,
+			       walk->disk_cluster);
 		else printk("-- ");
 	}
 	printk("\n");
@@ -182,15 +188,12 @@
 void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
 {
 	struct fat_cache *walk,*last;
+	int first = MSDOS_I(inode)->i_start;
 
-#ifdef DEBUG
-printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
-       inode->i_ino, f_clu, d_clu);
-#endif
 	last = NULL;
 	for (walk = fat_cache; walk->next; walk = (last = walk)->next)
 		if (inode->i_dev == walk->device
-		    && walk->ino == inode->i_ino
+		    && walk->start_cluster == first
 		    && walk->file_cluster == f_clu) {
 			if (walk->disk_cluster != d_clu) {
 				printk("FAT cache corruption inode=%ld\n",
@@ -209,7 +212,7 @@
 			return;
 		}
 	walk->device = inode->i_dev;
-	walk->ino = inode->i_ino;
+	walk->start_cluster = first;
 	walk->file_cluster = f_clu;
 	walk->disk_cluster = d_clu;
 	last->next = NULL;
@@ -227,10 +230,11 @@
 void fat_cache_inval_inode(struct inode *inode)
 {
 	struct fat_cache *walk;
+	int first = MSDOS_I(inode)->i_start;
 
 	for (walk = fat_cache; walk; walk = walk->next)
 		if (walk->device == inode->i_dev
-		    && walk->ino == inode->i_ino)
+		    && walk->start_cluster == first)
 			walk->device = 0;
 }
 
@@ -300,9 +304,11 @@
 			return -EIO;
 		}
 	}
-	if (last)
+	if (last) {
 		fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
-	else {
+		fat_cache_inval_inode(inode);
+	} else {
+		fat_cache_inval_inode(inode);
 		MSDOS_I(inode)->i_start = 0;
 		MSDOS_I(inode)->i_logstart = 0;
 		mark_inode_dirty(inode);
@@ -322,6 +328,5 @@
 		inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size;
 	}
 	unlock_fat(inode->i_sb);
-	fat_cache_inval_inode(inode);
 	return 0;
 }
diff -u --recursive --new-file v2.3.1/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v2.3.1/linux/fs/fat/dir.c	Fri Apr 23 21:20:37 1999
+++ linux/fs/fat/dir.c	Thu May 13 23:18:20 1999
@@ -9,7 +9,7 @@
  *
  *  VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
  *  Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
- *  Plugged buffer overrun in readdir(). AV
+ *  Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
  */
 
 #define ASC_LINUX_VERSION(V, P, S)	(((V) * 65536) + ((P) * 256) + (S))
@@ -25,6 +25,7 @@
 #include <linux/ioctl.h>
 #include <linux/dirent.h>
 #include <linux/mm.h>
+#include <linux/ctype.h>
 
 #include <asm/uaccess.h>
 
@@ -118,224 +119,348 @@
 	printk("]\n");
 }
 #endif
-int fat_readdirx(
-	struct inode *inode,
-	struct file *filp,
-	void *dirent,
-	fat_filldir_t fat_filldir,
-	filldir_t filldir,
-	int shortnames,
-	int longnames,
-	int both)
+static int memicmp(const char *s1, const char *s2, int len) {
+	while(len--) if (tolower(*s1++)!=tolower(*s2++)) return 1;
+	return 0;
+}
+
+/*
+ * Return values: negative -> error, 0 -> not found, positive -> found,
+ * value is the total amount of slots, including the shortname entry.
+ */
+int fat_search_long(
+	struct inode *inode, const char *name, int name_len, int anycase,
+	loff_t *spos, loff_t *lpos)
 {
 	struct super_block *sb = inode->i_sb;
 	int ino,i,i2,last;
 	char c;
-	struct buffer_head *bh;
+	struct buffer_head *bh = NULL;
 	struct msdos_dir_entry *de;
-	unsigned long oldpos = filp->f_pos;
-	unsigned long spos;
-	int is_long;
-	char longname[275];
-	unsigned char long_len = 0; /* Make compiler warning go away */
-	unsigned char alias_checksum = 0; /* Make compiler warning go away */
-	unsigned char long_slots = 0;
+	loff_t cpos = 0;
+	char bufname[14];
+	unsigned char long_slots;
 	int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
 	int utf8 = MSDOS_SB(sb)->options.utf8;
 	unsigned char *unicode = NULL;
 	struct nls_table *nls = MSDOS_SB(sb)->nls_io;
+	int res = 0;
 
-/* Fake . and .. for the root directory. */
-	if (inode->i_ino == MSDOS_ROOT_INO) {
-		while (oldpos < 2) {
-			if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
-				return 0;
-			oldpos++;
-			filp->f_pos++;
-		}
-		if (oldpos == 2)
-			filp->f_pos = 0;
-	}
-	if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
-		return -ENOENT;
-
- 	bh = NULL;
-	longname[0] = longname[1] = 0;
-	is_long = 0;
-	ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
-	while (ino > -1) {
-#if 0
-		dump_de(de);
-#endif
-		/* Check for long filename entry */
-		if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
-			is_long = 0;
-			oldpos = filp->f_pos;
-		} else if (MSDOS_SB(sb)->options.isvfat && de->attr ==  ATTR_EXT) {
-			int get_new_entry;
+	while(1) {
+		if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+			goto EODir;
+parse_record:
+		long_slots = 0;
+		if (de->name[0] == (__s8) DELETED_FLAG)
+			continue;
+		if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+			continue;
+		if (de->attr != ATTR_EXT && IS_FREE(de->name))
+			continue;
+		if (de->attr ==  ATTR_EXT) {
 			struct msdos_dir_slot *ds;
 			int offset;
 			unsigned char id;
 			unsigned char slot;
-			unsigned char slots = 0;
+			unsigned char slots;
+			unsigned char sum;
+			unsigned char alias_checksum;
 
 			if (!unicode) {
 				unicode = (unsigned char *)
 					__get_free_page(GFP_KERNEL);
-				if (!unicode)
+				if (!unicode) {
+					fat_brelse(sb, bh);
 					return -ENOMEM;
+				}
 			}
-
+parse_long:
+			slots = 0;
 			offset = 0;
 			ds = (struct msdos_dir_slot *) de;
 			id = ds->id;
-			if (id & 0x40) {
-				slots = id & ~0x40;
-				/*
-				 * Dirty, but not dirtier than the original,
-				 * and plugs the hole.
-				 */
-				if (slots > 20)
-					slots = 0;
-				else {
-					long_slots = slots;
-					is_long = 1;
-					alias_checksum = ds->alias_checksum;
-				}
-			}
+			if (!(id & 0x40))
+				continue;
+			slots = id & ~0x40;
+			if (slots > 20 || !slots)	/* ceil(256 * 2 / 26) */
+				continue;
+			long_slots = slots;
+			alias_checksum = ds->alias_checksum;
 
-			get_new_entry = 1;
 			slot = slots;
-			while (slot > 0) {
-				PRINTK(("1. get_new_entry: %d\n", get_new_entry));
-				if (ds->attr !=  ATTR_EXT) {
-					is_long = 0;
-					get_new_entry = 0;
-					break;
-				}
-				if ((ds->id & ~0x40) != slot) {
-					is_long = 0;
-					break;
-				}
-				if (ds->alias_checksum != alias_checksum) {
-					is_long = 0;
-					break;
-				}
+			while (1) {
 				slot--;
 				offset = slot * 26;
-				PRINTK(("2. get_new_entry: %d\n", get_new_entry));
 				memcpy(&unicode[offset], ds->name0_4, 10);
-				offset += 10;
-				memcpy(&unicode[offset], ds->name5_10, 12);
-				offset += 12;
-				memcpy(&unicode[offset], ds->name11_12, 4);
-				offset += 4;
+				memcpy(&unicode[offset+10], ds->name5_10, 12);
+				memcpy(&unicode[offset+22], ds->name11_12, 4);
+				offset += 26;
 
 				if (ds->id & 0x40) {
 					unicode[offset] = 0;
 					unicode[offset+1] = 0;
 				}
-				if (slot > 0) {
-					ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
-					PRINTK(("4. get_new_entry: %d\n", get_new_entry));
-					if (ino == -1) {
-						is_long = 0;
-						get_new_entry = 0;
-						break;
-					}
-					ds = (struct msdos_dir_slot *) de;
-				}
-				PRINTK(("5. get_new_entry: %d\n", get_new_entry));
+				if (fat_get_entry(inode,&cpos,&bh,&de,&ino)<0)
+					goto EODir;
+				if (slot == 0)
+					break;
+				ds = (struct msdos_dir_slot *) de;
+				if (ds->attr !=  ATTR_EXT)
+					goto parse_record;
+				if ((ds->id & ~0x40) != slot)
+					goto parse_long;
+				if (ds->alias_checksum != alias_checksum)
+					goto parse_long;
 			}
-		} else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
-			char bufname[14];
-			char *ptname = bufname;
-			int dotoffset = 0;
-			int was_long = is_long;
-
-			if (is_long) {
-				unsigned char sum;
-				for (sum = 0, i = 0; i < 11; i++) {
-					sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
-				}
+			if (de->name[0] == (__s8) DELETED_FLAG)
+				continue;
+			if (de->attr ==  ATTR_EXT)
+				goto parse_long;
+			if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+				continue;
+			for (sum = 0, i = 0; i < 11; i++)
+				sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+			if (sum != alias_checksum)
+				long_slots = 0;
+		}
 
-				if (sum != alias_checksum) {
-					PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
-					is_long = 0;
-					long_slots = 0;
-				}
-				if (utf8) {
-					long_len = utf8_wcstombs(longname, (__u16 *) unicode, sizeof(longname));
-				} else {
-					long_len = uni16_to_x8(longname, unicode, uni_xlate, nls);
-				}
-			}
+		for (i = 0, last = 0; i < 8;) {
+			if (!(c = de->name[i])) break;
+			if (c >= 'A' && c <= 'Z') c += 32;
+			if (c == 0x05) c = 0xE5;
+			if ((bufname[i++] = c) != ' ')
+				last = i;
+		}
+		i = last;
+		bufname[i++] = '.';
+		for (i2 = 0; i2 < 3; i2++) {
+			if (!(c = de->ext[i2])) break;
+			if (c >= 'A' && c <= 'Z') c += 32;
+			if ((bufname[i++] = c) != ' ')
+				last = i;
+		}
+		if (!last)
+			continue;
 
-			if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
-				bufname[0] = '.';
-				dotoffset = 1;
-				ptname = bufname+1;
-			}
-			for (i = 0, last = 0; i < 8; i++) {
-				if (!(c = de->name[i])) break;
-				if (c >= 'A' && c <= 'Z') c += 32;
-				/* see namei.c, msdos_format_name */
-				if (c == 0x05) c = 0xE5;
-				if (c != ' ')
-					last = i+1;
-				ptname[i] = c;
-			}
-			i = last;
-			ptname[i] = '.';
-			i++;
-			for (i2 = 0; i2 < 3; i2++) {
-				if (!(c = de->ext[i2])) break;
-				if (c >= 'A' && c <= 'Z') c += 32;
-				if (c != ' ')
-					last = i+1;
-				ptname[i] = c;
-				i++;
+		if (last==name_len)
+			if ((!anycase && !memcmp(name, bufname, last)) ||
+			    (anycase && !memicmp(name, bufname, last)))
+			goto Found;
+		if (long_slots) {
+			char longname[260];	/* 256 + 4 */
+			unsigned char long_len;
+			long_len = utf8
+				?utf8_wcstombs(longname, (__u16 *) unicode, 260)
+				:uni16_to_x8(longname, unicode, uni_xlate, nls);
+			if (long_len != name_len)
+				continue;
+			if ((!anycase && !memcmp(name, longname, long_len)) ||
+			    (anycase && !memicmp(name, longname, long_len)))
+				goto Found;
+		}
+	}
+
+Found:
+	fat_brelse(sb, bh);
+	res = long_slots + 1;
+	*spos = cpos - sizeof(struct msdos_dir_entry);
+	*lpos = cpos - res*sizeof(struct msdos_dir_entry);
+EODir:
+	if (unicode) {
+		free_page((unsigned long) unicode);
+	}
+	return res;
+}
+
+static int fat_readdirx(
+	struct inode *inode,
+	struct file *filp,
+	void *dirent,
+	filldir_t filldir,
+	int shortnames,
+	int both)
+{
+	struct super_block *sb = inode->i_sb;
+	int ino,inum,i,i2,last;
+	char c;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	unsigned long lpos;
+	loff_t cpos;
+	unsigned char long_slots;
+	int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+	int utf8 = MSDOS_SB(sb)->options.utf8;
+	unsigned char *unicode = NULL;
+	struct nls_table *nls = MSDOS_SB(sb)->nls_io;
+	char bufname[14];
+	char *ptname = bufname;
+	int dotoffset = 0;
+
+	cpos = filp->f_pos;
+/* Fake . and .. for the root directory. */
+	if (inode->i_ino == MSDOS_ROOT_INO) {
+		while (cpos < 2) {
+			if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO) < 0)
+				return 0;
+			cpos++;
+			filp->f_pos++;
+		}
+		if (cpos == 2)
+			cpos = 0;
+	}
+	if (cpos & (sizeof(struct msdos_dir_entry)-1))
+		return -ENOENT;
+
+ 	bh = NULL;
+GetNew:
+	long_slots = 0;
+	if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+		goto EODir;
+	/* Check for long filename entry */
+	if (MSDOS_SB(sb)->options.isvfat) {
+		if (de->name[0] == (__s8) DELETED_FLAG)
+			goto RecEnd;
+		if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+			goto RecEnd;
+		if (de->attr != ATTR_EXT && IS_FREE(de->name))
+			goto RecEnd;
+	} else {
+		if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
+			goto RecEnd;
+	}
+
+	if (MSDOS_SB(sb)->options.isvfat && de->attr ==  ATTR_EXT) {
+		struct msdos_dir_slot *ds;
+		int offset;
+		unsigned char id;
+		unsigned char slot;
+		unsigned char slots;
+		unsigned char sum;
+		unsigned char alias_checksum;
+
+		if (!unicode) {
+			unicode = (unsigned char *)
+				__get_free_page(GFP_KERNEL);
+			if (!unicode) {
+				filp->f_pos = cpos;
+				fat_brelse(sb, bh);
+				return -ENOMEM;
 			}
-			if ((i = last) != 0) {
-				if (!strcmp(de->name,MSDOS_DOT))
-					ino = inode->i_ino;
-				else if (!strcmp(de->name,MSDOS_DOTDOT))
-					ino = fat_parent_ino(inode,0);
-
-				if (shortnames || !is_long) {
-					if (both)
-						bufname[i+dotoffset] = '\0';
-					spos = oldpos;
-					if (was_long) {
-						spos = filp->f_pos - sizeof(struct msdos_dir_entry);
-					} else {
-						long_slots = 0;
-					}
-					if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
-						filp->f_pos = oldpos;
-						break;
-					}
-				}
-				if (is_long && longnames) {
-					if (both) {
-						memcpy(&longname[long_len+1], bufname, i+dotoffset);
-						long_len += i+dotoffset;
-					}
-					spos = filp->f_pos - sizeof(struct msdos_dir_entry);
-					if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
-						filp->f_pos = oldpos;
-						break;
-					}
-				}
-				oldpos = filp->f_pos;
+		}
+ParseLong:
+		slots = 0;
+		offset = 0;
+		ds = (struct msdos_dir_slot *) de;
+		id = ds->id;
+		if (!(id & 0x40))
+			goto RecEnd;
+		slots = id & ~0x40;
+		if (slots > 20 || !slots)	/* ceil(256 * 2 / 26) */
+			goto RecEnd;
+		long_slots = slots;
+		alias_checksum = ds->alias_checksum;
+
+		slot = slots;
+		while (1) {
+			slot--;
+			offset = slot * 26;
+			memcpy(&unicode[offset], ds->name0_4, 10);
+			memcpy(&unicode[offset+10], ds->name5_10, 12);
+			memcpy(&unicode[offset+22], ds->name11_12, 4);
+			offset += 26;
+
+			if (ds->id & 0x40) {
+				unicode[offset] = 0;
+				unicode[offset+1] = 0;
 			}
-			is_long = 0;
-		} else {
-			is_long = 0;
-			oldpos = filp->f_pos;
+			if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
+				goto EODir;
+			if (slot == 0)
+				break;
+			ds = (struct msdos_dir_slot *) de;
+			if (ds->attr !=  ATTR_EXT)
+				goto RecEnd;	/* XXX */
+			if ((ds->id & ~0x40) != slot)
+				goto ParseLong;
+			if (ds->alias_checksum != alias_checksum)
+				goto ParseLong;
 		}
-		ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);	
+		if (de->name[0] == (__s8) DELETED_FLAG)
+			goto RecEnd;
+		if (de->attr ==  ATTR_EXT)
+			goto ParseLong;
+		if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+			goto RecEnd;
+		for (sum = 0, i = 0; i < 11; i++)
+			sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+		if (sum != alias_checksum)
+			long_slots = 0;
+	}
+
+	if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
+		*ptname++ = '.';
+		dotoffset = 1;
+	}
+	for (i = 0, last = 0; i < 8;) {
+		if (!(c = de->name[i])) break;
+		if (c >= 'A' && c <= 'Z') c += 32;
+		/* see namei.c, msdos_format_name */
+		if (c == 0x05) c = 0xE5;
+		if ((ptname[i++] = c) != ' ')
+			last = i;
+	}
+	i = last;
+	ptname[i++] = '.';
+	for (i2 = 0; i2 < 3; i2++) {
+		if (!(c = de->ext[i2])) break;
+		if (c >= 'A' && c <= 'Z') c += 32;
+		if ((ptname[i++] = c) != ' ')
+			last = i;
+	}
+	if (!last)
+		goto RecEnd;
+
+	i = last + dotoffset;
+
+	lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
+	if (!memcmp(de->name,MSDOS_DOT,11))
+		inum = inode->i_ino;
+	else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
+/*		inum = fat_parent_ino(inode,0); */
+		inum = filp->f_dentry->d_parent->d_inode->i_ino;
+	} else {
+		struct inode *tmp = fat_iget(sb, ino);
+		if (tmp) {
+			inum = tmp->i_ino;
+			iput(tmp);
+		} else
+			inum = iunique(sb, MSDOS_ROOT_INO);
 	}
+
+	if (!long_slots||shortnames) {
+		if (both)
+			bufname[i] = '\0';
+		if (filldir(dirent, bufname, i, lpos, inum) < 0)
+			goto FillFailed;
+	} else {
+		char longname[275];
+		unsigned char long_len = utf8
+			? utf8_wcstombs(longname, (__u16 *) unicode, 275)
+			: uni16_to_x8(longname, unicode, uni_xlate, nls);
+		if (both) {
+			memcpy(&longname[long_len+1], bufname, i);
+			long_len += i;
+		}
+		if (filldir(dirent, longname, long_len, lpos, inum) < 0)
+			goto FillFailed;
+	}
+
+RecEnd:
+	filp->f_pos = cpos;
+	goto GetNew;
+EODir:
+	filp->f_pos = cpos;
+FillFailed:
 	if (bh)
 		fat_brelse(sb, bh);
 	if (unicode) {
@@ -344,39 +469,17 @@
 	return 0;
 }
 
-static int fat_filldir(
-	filldir_t filldir,
-	void * buf,
-	const char * name,
-	int name_len,
-	int is_long,
-	off_t offset,
-	off_t short_offset,
-	int long_slots,
-	ino_t ino)
-{
-	return filldir(buf, name, name_len, offset, ino);
-}
-
-int fat_readdir(
-	struct file *filp,
-	void *dirent,
-	filldir_t filldir)
+int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
-	return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
-			    0, 1, 0);
+	return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
 }
 
 static int vfat_ioctl_fill(
-	filldir_t filldir,
 	void * buf,
 	const char * name,
 	int name_len,
-	int is_long,
 	off_t offset,
-	off_t short_offset,
-	int long_slots,
 	ino_t ino)
 {
 	struct dirent *d1 = (struct dirent *)buf;
@@ -438,7 +541,7 @@
 			return err;
 		put_user(0, &d1->d_reclen);
 		return fat_readdirx(inode,filp,(void *)arg,
-				    vfat_ioctl_fill, NULL, 0, 1, 1);
+				    vfat_ioctl_fill, 0, 1);
 	}
 	case VFAT_IOCTL_READDIR_SHORT: {
 		struct dirent *d1 = (struct dirent *)arg;
@@ -447,7 +550,7 @@
 		if (err)
 			return err;
 		return fat_readdirx(inode,filp,(void *)arg,
-				    vfat_ioctl_fill, NULL, 1, 0, 1);
+				    vfat_ioctl_fill, 1, 1);
 	}
 	default:
 		/* forward ioctl to CVF extension */
@@ -459,6 +562,62 @@
 	}
 
 	return 0;
+}
+
+/***** See if directory is empty */
+int fat_dir_empty(struct inode *dir)
+{
+	loff_t pos;
+	struct buffer_head *bh;
+	struct msdos_dir_entry *de;
+	int ino,result = 0;
+
+	pos = 0;
+	bh = NULL;
+	while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -1) {
+		/* Ignore vfat longname entries */
+		if (de->attr == ATTR_EXT)
+			continue;
+		if (!IS_FREE(de->name) && 
+		    strncmp(de->name,MSDOS_DOT   , MSDOS_NAME) &&
+		    strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
+			result = -ENOTEMPTY;
+			break;
+		}
+	}
+	if (bh)
+		fat_brelse(dir->i_sb, bh);
+
+	return result;
+}
+
+/* This assumes that size of cluster is above the 32*slots */
+
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+		  struct msdos_dir_entry **de, int *ino)
+{
+	struct super_block *sb = dir->i_sb;
+	loff_t offset, curr;
+	int row;
+	int res;
+
+	offset = curr = 0;
+	*bh = NULL;
+	row = 0;
+	while (fat_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 == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32)) 
+		return -ENOSPC;
+	if ((res = fat_add_cluster(dir)) < 0) return res;
+	do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
+	return offset;
 }
 
 /*
diff -u --recursive --new-file v2.3.1/linux/fs/fat/fatfs_syms.c linux/fs/fat/fatfs_syms.c
--- v2.3.1/linux/fs/fat/fatfs_syms.c	Tue Apr 20 14:37:10 1999
+++ linux/fs/fat/fatfs_syms.c	Thu May 13 23:18:20 1999
@@ -18,9 +18,11 @@
 extern struct file_operations fat_dir_operations;
 
 EXPORT_SYMBOL(fat_add_cluster);
+EXPORT_SYMBOL(fat_add_cluster1);
 EXPORT_SYMBOL(fat_bmap);
 EXPORT_SYMBOL(fat_brelse);
 EXPORT_SYMBOL(fat_cache_inval_inode);
+EXPORT_SYMBOL(fat_clear_inode);
 EXPORT_SYMBOL(fat_date_unix2dos);
 EXPORT_SYMBOL(fat_delete_inode);
 EXPORT_SYMBOL(fat_dir_operations);
@@ -28,17 +30,18 @@
 EXPORT_SYMBOL(fat_file_read);
 EXPORT_SYMBOL(fat_file_write);
 EXPORT_SYMBOL(fat_fs_panic);
-EXPORT_SYMBOL(fat_get_entry);
+EXPORT_SYMBOL(fat__get_entry);
 EXPORT_SYMBOL(fat_lock_creation);
 EXPORT_SYMBOL(fat_mark_buffer_dirty);
 EXPORT_SYMBOL(fat_mmap);
 EXPORT_SYMBOL(fat_notify_change);
 EXPORT_SYMBOL(fat_parent_ino);
-EXPORT_SYMBOL(fat_put_inode);
 EXPORT_SYMBOL(fat_put_super);
-EXPORT_SYMBOL(fat_read_inode);
+EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL(fat_build_inode);
 EXPORT_SYMBOL(fat_read_super);
-EXPORT_SYMBOL(fat_readdirx);
+EXPORT_SYMBOL(fat_search_long);
 EXPORT_SYMBOL(fat_readdir);
 EXPORT_SYMBOL(fat_scan);
 EXPORT_SYMBOL(fat_smap);
@@ -54,9 +57,11 @@
 EXPORT_SYMBOL(unlock_fat);
 EXPORT_SYMBOL(fat_dir_ioctl);
 EXPORT_SYMBOL(fat_readpage);
-EXPORT_SYMBOL(fat_is_binary);
+EXPORT_SYMBOL(fat_add_entries);
+EXPORT_SYMBOL(fat_dir_empty);
 
 int init_fat_fs(void)
 {
+	fat_hash_init();
 	return 0;
 }
diff -u --recursive --new-file v2.3.1/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.3.1/linux/fs/fat/inode.c	Fri May 14 18:55:24 1999
+++ linux/fs/fat/inode.c	Thu May 13 23:18:20 1999
@@ -3,6 +3,7 @@
  *
  *  Written 1992,1993 by Werner Almesberger
  *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ *  Rewritten for the constant inumbers support by Al Viro
  *
  *  Fixes:
  *
@@ -45,34 +46,123 @@
 #  define PRINTK1(x)
 #endif
 
-void fat_put_inode(struct inode *inode)
+/*
+ * New FAT inode stuff. We do the following:
+ *	a) i_ino is constant and has nothing with on-disk location.
+ *	b) FAT manages its own cache of directory entries.
+ *	c) *This* cache is indexed by on-disk location.
+ *	d) inode has an associated directory entry, all right, but
+ *		it may be unhashed.
+ *	e) currently entries are stored within struct inode. That should
+ *		change.
+ *	f) we deal with races in the following way:
+ *		1. readdir() and lookup() do FAT-dir-cache lookup.
+ *		2. rename() unhashes the F-d-c entry and rehashes it in
+ *			a new place.
+ *		3. unlink() and rmdir() unhash F-d-c entry.
+ *		4. fat_write_inode() checks whether the thing is unhashed.
+ *			If it is we silently return. If it isn't we do bread(),
+ *			check if the location is still valid and retry if it
+ *			isn't. Otherwise we do changes.
+ *		5. Spinlock is used to protect hash/unhash/location check/lookup
+ *		6. fat_clear_inode() unhashes the F-d-c entry.
+ *		7. lookup() and readdir() do igrab() if they find a F-d-c entry
+ *			and consider negative result as cache miss.
+ */
+
+#define FAT_HASH_BITS	8
+#define FAT_HASH_SIZE	(1UL << FAT_HASH_BITS)
+#define FAT_HASH_MASK	(FAT_HASH_SIZE-1)
+static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
+spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
+
+void fat_hash_init(void) {
+	int i;
+	for(i=0;i<FAT_HASH_SIZE;i++) {
+		INIT_LIST_HEAD(&fat_inode_hashtable[i]);
+	}
+}
+
+static inline unsigned long fat_hash(struct super_block *sb, int i_pos)
 {
-	/*
-	 * Check whether we're a dependent of other inodes ...
-	 */ 
-	if (inode->i_count <= 1) {
-#ifdef FAT_PARANOIA
-printk("fat_put_inode: last use for (%p,%ld), i_count=%d\n",
-inode, inode->i_ino, inode->i_count); 
-#endif
-		if (inode->i_nlink) {
-			if (MSDOS_I(inode)->i_busy)
-				fat_cache_inval_inode(inode);
-		}
+	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 fat_attach(struct inode *inode, int i_pos) {
+	spin_lock(&fat_inode_lock);
+	MSDOS_I(inode)->i_location = i_pos;
+	list_add(&MSDOS_I(inode)->i_fat_hash,
+		fat_inode_hashtable+fat_hash(inode->i_sb, i_pos));
+	spin_unlock(&fat_inode_lock);
+}
+
+void fat_detach(struct inode *inode) {
+	spin_lock(&fat_inode_lock);
+	MSDOS_I(inode)->i_location = 0;
+	list_del(&MSDOS_I(inode)->i_fat_hash);
+	INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+	spin_unlock(&fat_inode_lock);
+}
+
+struct inode *fat_iget(struct super_block *sb, int i_pos) {
+	struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos);
+	struct list_head *walk;
+	struct msdos_inode_info *i;
+	struct inode *inode = NULL;
+	spin_lock(&fat_inode_lock);
+	for(walk=p->next;walk!=p;walk=walk->next) {
+		i = list_entry(walk, struct msdos_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);
 	}
+	spin_unlock(&fat_inode_lock);
+	return inode;
+}
+
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
+
+struct inode *fat_build_inode(struct super_block *sb,
+				struct msdos_dir_entry *de, int ino, int *res)
+{
+	struct inode *inode;
+	*res = 0;
+	inode = fat_iget(sb, ino);
+	if (inode)
+		goto out;
+	inode = get_empty_inode();
+	*res = -ENOMEM;
+	if (!inode)
+		goto out;
+	*res = 0;
+	inode->i_sb = sb;
+	inode->i_dev = sb->s_dev;
+	inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
+	fat_fill_inode(inode, de);
+	fat_attach(inode, ino);
+	insert_inode_hash(inode);
+out:
+	return inode;
 }
 
 void fat_delete_inode(struct inode *inode)
 {
-	/*
-	 * Make sure there are no active dependencies ...
-	 */
-	fat_cache_inval_inode(inode);
 	inode->i_size = 0;
 	fat_truncate(inode);
 	clear_inode(inode);
 }
 
+void fat_clear_inode(struct inode *inode)
+{
+	spin_lock(&fat_inode_lock);
+	fat_cache_inval_inode(inode);
+	list_del(&MSDOS_I(inode)->i_fat_hash);
+	spin_unlock(&fat_inode_lock);
+}
 
 void fat_put_super(struct super_block *sb)
 {
@@ -103,6 +193,8 @@
 		MSDOS_SB(sb)->options.iocharset = NULL;
 	}
 
+	if (MSDOS_SB(sb)->put_super_callback)
+		MSDOS_SB(sb)->put_super_callback(sb);
 	MOD_DEC_USE_COUNT;
 	return;
 }
@@ -267,6 +359,61 @@
 	return ret;
 }
 
+static void fat_read_root(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	int nr;
+
+	MSDOS_I(inode)->i_binary = 1;
+	INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+	MSDOS_I(inode)->i_location = 0;
+	MSDOS_I(inode)->i_fat_inode = inode;
+	inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
+	inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
+	inode->i_version = ++event;
+	inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
+	inode->i_op = MSDOS_SB(sb)->dir_ops;
+	if (MSDOS_SB(sb)->fat_bits == 32) {
+		MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster;
+		if ((nr = MSDOS_I(inode)->i_start) != 0) {
+			while (nr != -1) {
+				inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size;
+				if (!(nr = fat_access(sb,nr,-1))) {
+					printk("Directory %ld: bad FAT\n",
+					       inode->i_ino);
+					break;
+				}
+			}
+		}
+	} else {
+		MSDOS_I(inode)->i_start = 0;
+		inode->i_size = MSDOS_SB(sb)->dir_entries*
+			sizeof(struct msdos_dir_entry);
+	}
+	inode->i_blksize = MSDOS_SB(sb)->cluster_size* SECTOR_SIZE;
+	inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
+		    inode->i_blksize*MSDOS_SB(sb)->cluster_size;
+	MSDOS_I(inode)->i_logstart = 0;
+
+	MSDOS_I(inode)->i_attrs = 0;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+	MSDOS_I(inode)->i_ctime_ms = 0;
+	inode->i_nlink = fat_subdirs(inode)+2;
+}
+
+static struct super_operations fat_sops = { 
+	NULL,
+	fat_write_inode,
+	NULL,
+	fat_delete_inode,
+	fat_notify_change,
+	fat_put_super,
+	NULL,		/* write_super */
+	fat_statfs,
+	NULL,		/* remount */
+	fat_clear_inode
+};
+
 /*
  * Read the super block of an MS-DOS FS.
  *
@@ -274,7 +421,8 @@
  * with some fields already initialized.
  */
 struct super_block *
-fat_read_super(struct super_block *sb, void *data, int silent)
+fat_read_super(struct super_block *sb, void *data, int silent,
+		struct inode_operations *fs_dir_inode_ops)
 {
 	struct inode *root_inode;
 	struct buffer_head *bh;
@@ -296,6 +444,9 @@
 	MSDOS_SB(sb)->private_data = NULL;
 
 	MOD_INC_USE_COUNT;
+	MSDOS_SB(sb)->dir_ops = fs_dir_inode_ops;
+	MSDOS_SB(sb)->put_super_callback = NULL;
+	sb->s_op = &fat_sops;
 	if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
 		blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)];
 		if (blksize != 512){
@@ -491,9 +642,14 @@
 		}
 	}
 
-	root_inode = iget(sb, MSDOS_ROOT_INO);
+	root_inode=get_empty_inode();
 	if (!root_inode)
-		goto out_no_root;
+		goto out_unload_nls;
+	root_inode->i_sb = sb;
+	root_inode->i_dev = sb->s_dev;
+	root_inode->i_ino = MSDOS_ROOT_INO;
+	fat_read_root(root_inode);
+	insert_inode_hash(root_inode);
 	sb->s_root = d_alloc_root(root_inode, NULL);
 	if (!sb->s_root)
 		goto out_no_root;
@@ -590,71 +746,28 @@
 	return 0;
 }
 
-void fat_read_inode(struct inode *inode, struct inode_operations *fs_dir_inode_ops)
+/* doesn't deal with root inode */
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
 {
 	struct super_block *sb = inode->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *raw_entry;
 	int nr;
 
-	PRINTK1(("fat_read_inode: inode=%p, ino=%ld, sb->dir_start=0x%x\n",
-		 inode, inode->i_ino, MSDOS_SB(sb)->dir_start));
-	MSDOS_I(inode)->i_busy = 0;
 	MSDOS_I(inode)->i_binary = 1;
+	INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+	MSDOS_I(inode)->i_location = 0;
+	MSDOS_I(inode)->i_fat_inode = inode;
 	inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
 	inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
 	inode->i_version = ++event;
-	if (inode->i_ino == MSDOS_ROOT_INO) {
-		inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) |
-		    S_IFDIR;
-		inode->i_op = fs_dir_inode_ops;
-		if (MSDOS_SB(sb)->fat_bits == 32) {
-			MSDOS_I(inode)->i_start = MSDOS_SB(sb)->root_cluster;
-			if ((nr = MSDOS_I(inode)->i_start) != 0) {
-				while (nr != -1) {
-					inode->i_size += SECTOR_SIZE*MSDOS_SB(sb)->cluster_size;
-					if (!(nr = fat_access(sb,nr,-1))) {
-						printk("Directory %ld: bad FAT\n",
-						       inode->i_ino);
-						break;
-					}
-				}
-			}
-		} else {
-			MSDOS_I(inode)->i_start = 0;
-			inode->i_size = MSDOS_SB(sb)->dir_entries*
-				sizeof(struct msdos_dir_entry);
-		}
-		inode->i_blksize = MSDOS_SB(sb)->cluster_size*
-		    SECTOR_SIZE;
-		inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
-		    inode->i_blksize*MSDOS_SB(sb)->cluster_size;
-		MSDOS_I(inode)->i_logstart = 0;
-
-		MSDOS_I(inode)->i_attrs = 0;
-		inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
-		MSDOS_I(inode)->i_ctime_ms = 0;
-		inode->i_nlink = fat_subdirs(inode)+2;
-		    /* subdirs (neither . nor ..) plus . and "self" */
-		return;
-	}
-	if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
-		printk("dev = %s, ino = %ld\n",
-		       kdevname(inode->i_dev), inode->i_ino);
-		fat_fs_panic(sb, "fat_read_inode: unable to read i-node block");
-		return;
-	}
-	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-	    [inode->i_ino & (MSDOS_DPB-1)];
-	if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) {
-		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,S_IRWXUGO &
+	if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
+		inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
 		    ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
-		inode->i_op = fs_dir_inode_ops;
+		inode->i_op = MSDOS_SB(inode->i_sb)->dir_ops;
 
-		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+		MSDOS_I(inode)->i_start = CF_LE_W(de->start);
 		if (MSDOS_SB(sb)->fat_bits == 32) {
 			MSDOS_I(inode)->i_start |=
-				(CF_LE_W(raw_entry->starthi) << 16);
+				(CF_LE_W(de->starthi) << 16);
 		}
 		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
 		inode->i_nlink = fat_subdirs(inode);
@@ -677,10 +790,10 @@
 				}
 			}
 	} else { /* not a directory */
-		inode->i_mode = MSDOS_MKMODE(raw_entry->attr,
+		inode->i_mode = MSDOS_MKMODE(de->attr,
 		    ((IS_NOEXEC(inode) || 
 		      (MSDOS_SB(sb)->options.showexec &&
-		       !is_exec(raw_entry->ext)))
+		       !is_exec(de->ext)))
 		    	? S_IRUGO|S_IWUGO : S_IRWXUGO)
 		    & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
 		if (MSDOS_SB(sb)->cvf_format)
@@ -691,51 +804,58 @@
 		  inode->i_op = (sb->s_blocksize == 1024 || sb->s_blocksize == 2048)
 			? &fat_file_inode_operations_1024
 			: &fat_file_inode_operations;
-		MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+		MSDOS_I(inode)->i_start = CF_LE_W(de->start);
 		if (MSDOS_SB(sb)->fat_bits == 32) {
 			MSDOS_I(inode)->i_start |=
-				(CF_LE_W(raw_entry->starthi) << 16);
+				(CF_LE_W(de->starthi) << 16);
 		}
 		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
 		inode->i_nlink = 1;
-		inode->i_size = CF_LE_L(raw_entry->size);
+		inode->i_size = CF_LE_L(de->size);
 	}
-	if(raw_entry->attr & ATTR_SYS)
+	if(de->attr & ATTR_SYS)
 		if (MSDOS_SB(sb)->options.sys_immutable)
 			inode->i_flags |= S_IMMUTABLE;
 	MSDOS_I(inode)->i_binary =
-	    fat_is_binary(MSDOS_SB(sb)->options.conversion, raw_entry->ext);
-	MSDOS_I(inode)->i_attrs = raw_entry->attr & ATTR_UNUSED;
+	    fat_is_binary(MSDOS_SB(sb)->options.conversion, de->ext);
+	MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
 	/* this is as close to the truth as we can get ... */
 	inode->i_blksize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
 	inode->i_blocks = (inode->i_size+inode->i_blksize-1)/
 	    inode->i_blksize*MSDOS_SB(sb)->cluster_size;
 	inode->i_mtime = inode->i_atime =
-	    date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date));
+	    date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
 	inode->i_ctime =
 		MSDOS_SB(sb)->options.isvfat
-		? date_dos2unix(CF_LE_W(raw_entry->ctime),CF_LE_W(raw_entry->cdate))
+		? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
 		: inode->i_mtime;
-	MSDOS_I(inode)->i_ctime_ms = raw_entry->ctime_ms;
-	fat_brelse(sb, bh);
+	MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
 }
 
-
 void fat_write_inode(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *raw_entry;
+	int i_pos;
 
-	if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
-	if (!(bh = fat_bread(sb, inode->i_ino >> MSDOS_DPB_BITS))) {
-		printk("dev = %s, ino = %ld\n",
-		       kdevname(inode->i_dev), inode->i_ino);
+retry:
+	i_pos = MSDOS_I(inode)->i_location;
+	if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) return;
+	if (!(bh = fat_bread(sb, i_pos >> MSDOS_DPB_BITS))) {
+		printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos);
 		fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
 		return;
 	}
+	spin_lock(&fat_inode_lock);
+	if (i_pos != MSDOS_I(inode)->i_location) {
+		spin_unlock(&fat_inode_lock);
+		fat_brelse(sb, bh);
+		goto retry;
+	}
+
 	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-	    [inode->i_ino & (MSDOS_DPB-1)];
+	    [i_pos & (MSDOS_DPB-1)];
 	if (S_ISDIR(inode->i_mode)) {
 		raw_entry->attr = ATTR_DIR;
 		raw_entry->size = 0;
@@ -757,6 +877,7 @@
 		raw_entry->ctime = CT_LE_W(raw_entry->ctime);
 		raw_entry->cdate = CT_LE_W(raw_entry->cdate);
 	}
+	spin_unlock(&fat_inode_lock);
 	fat_mark_buffer_dirty(sb, bh, 1);
 	fat_brelse(sb, bh);
 }
diff -u --recursive --new-file v2.3.1/linux/fs/fat/misc.c linux/fs/fat/misc.c
--- v2.3.1/linux/fs/fat/misc.c	Fri May 14 18:55:24 1999
+++ linux/fs/fat/misc.c	Thu May 13 23:18:20 1999
@@ -144,17 +144,17 @@
  * represented by inode. The cluster is zero-initialized.
  */
 
-int fat_add_cluster(struct inode *inode)
+struct buffer_head *fat_add_cluster1(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	int count,nr,limit,last,curr,sector,last_sector,file_cluster;
-	struct buffer_head *bh;
+	struct buffer_head *bh, *res=NULL;
 	int cluster_size = MSDOS_SB(sb)->cluster_size;
 
 	if (MSDOS_SB(sb)->fat_bits != 32) {
-		if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
+		if (inode->i_ino == MSDOS_ROOT_INO) return res;
 	}
-	if (!MSDOS_SB(sb)->free_clusters) return -ENOSPC;
+	if (!MSDOS_SB(sb)->free_clusters) return res;
 	lock_fat(sb);
 	limit = MSDOS_SB(sb)->clusters;
 	nr = limit; /* to keep GCC happy */
@@ -170,7 +170,7 @@
 	if (count >= limit) {
 		MSDOS_SB(sb)->free_clusters = 0;
 		unlock_fat(sb);
-		return -ENOSPC;
+		return res;
 	}
 	fat_access(sb,nr,EOF_FAT(sb));
 	if (MSDOS_SB(sb)->free_clusters != -1)
@@ -202,7 +202,7 @@
 			if (!(curr = fat_access(sb,
 			    last = curr,-1))) {
 				fat_fs_panic(sb,"File without EOF");
-				return -ENOSPC;
+				return res;
 			}
 		}
 		PRINTK ((" --  "));
@@ -235,7 +235,10 @@
 			memset(bh->b_data,0,SECTOR_SIZE);
 			fat_set_uptodate(sb, bh, 1);
 			fat_mark_buffer_dirty(sb, bh, 1);
-			fat_brelse(sb, bh);
+			if (!res)
+				res=bh;
+			else
+				fat_brelse(sb, bh);
 		}
 	}
 	if (file_cluster != inode->i_blocks/cluster_size){
@@ -257,9 +260,17 @@
 #endif
 		mark_inode_dirty(inode);
 	}
-	return 0;
+	return res;
 }
 
+int fat_add_cluster(struct inode *inode)
+{
+	struct buffer_head *bh = fat_add_cluster1(inode);
+	if (!bh)
+		return -ENOSPC;
+	fat_brelse(inode->i_sb, bh);
+	return 0;
+}
 
 /* Linear day numbers of the respective 1sts in non-leap years. */
 
@@ -319,10 +330,17 @@
 
 /* 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. */
+   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 fat_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 fat_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
-    struct msdos_dir_entry **de)
+int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+    struct msdos_dir_entry **de, int *ino)
 {
 	struct super_block *sb = dir->i_sb;
 	int sector, offset;
@@ -330,15 +348,16 @@
 	while (1) {
 		offset = *pos;
 		PRINTK (("get_entry offset %d\n",offset));
+		if (*bh)
+			fat_brelse(sb, *bh);
+		*bh = NULL;
 		if ((sector = fat_smap(dir,offset >> SECTOR_BITS)) == -1)
 			return -1;
 		PRINTK (("get_entry sector %d %p\n",sector,*bh));
+		PRINTK (("get_entry sector apres brelse\n"));
 		if (!sector)
 			return -1; /* beyond EOF */
 		*pos += sizeof(struct msdos_dir_entry);
-		if (*bh)
-			fat_brelse(sb, *bh);
-		PRINTK (("get_entry sector apres brelse\n"));
 		if (!(*bh = fat_bread(sb, sector))) {
 			printk("Directory sread (sector 0x%x) failed\n",sector);
 			continue;
@@ -346,8 +365,9 @@
 		PRINTK (("get_entry apres sread\n"));
 		*de = (struct msdos_dir_entry *) ((*bh)->b_data+(offset &
 		    (SECTOR_SIZE-1)));
-		return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
+		*ino = (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
 		    MSDOS_DIR_BITS);
+		return 0;
 	}
 }
 
@@ -393,14 +413,6 @@
 #define RSS_FREE /* search for free entry */ \
     { \
 	done = IS_FREE(data[entry].name); \
-	if (done) { \
-	    inode = iget(sb,sector*MSDOS_DPS+entry); \
-	    if (inode) { \
-	    /* Directory slots of busy deleted files aren't available yet. */ \
-		done = !MSDOS_I(inode)->i_busy; \
-		iput(inode); \
-	    } \
-	} \
     }
 
 #define RSS_COUNT /* count subdirectories */ \
@@ -412,11 +424,10 @@
 
 static int raw_scan_sector(struct super_block *sb,int sector,const char *name,
     int *number,int *ino,struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de,char scantype)
+    struct msdos_dir_entry **res_de)
 {
 	struct buffer_head *bh;
 	struct msdos_dir_entry *data;
-	struct inode *inode;
 	int entry,start,done;
 
 	if (!(bh = fat_bread(sb,sector)))
@@ -426,11 +437,6 @@
 /* RSS_COUNT:  if (data[entry].name == name) done=true else done=false. */
 		if (name) {
 			RSS_NAME
-			if (done && scantype) {   /* scantype != SCAN_ANY */
-				done = (data[entry].attr & ATTR_HIDDEN)
-					? (scantype==SCAN_HID)
-					: (scantype==SCAN_NOTHID);
-			}
 		} else {
 			if (!ino) RSS_COUNT
 			else {
@@ -464,13 +470,13 @@
  */
 
 static int raw_scan_root(struct super_block *sb,const char *name,int *number,int *ino,
-    struct buffer_head **res_bh,struct msdos_dir_entry **res_de,char scantype)
+    struct buffer_head **res_bh,struct msdos_dir_entry **res_de)
 {
 	int count,cluster;
 
 	for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
 		if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count,
-		    name,number,ino,res_bh,res_de,scantype)) >= 0) return cluster;
+		    name,number,ino,res_bh,res_de)) >= 0) return cluster;
 	}
 	return -ENOENT;
 }
@@ -483,7 +489,7 @@
 
 static int raw_scan_nonroot(struct super_block *sb,int start,const char *name,
     int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry
-    **res_de,char scantype)
+    **res_de)
 {
 	int count,cluster;
 
@@ -494,7 +500,7 @@
 		for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 			if ((cluster = raw_scan_sector(sb,(start-2)*
 			    MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+
-			    count,name,number,ino,res_bh,res_de,scantype)) >= 0)
+			    count,name,number,ino,res_bh,res_de)) >= 0)
 				return cluster;
 		}
 		if (!(start = fat_access(sb,start,-1))) {
@@ -519,12 +525,12 @@
 
 static int raw_scan(struct super_block *sb, int start, const char *name,
     int *number, int *ino, struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de, char scantype)
+    struct msdos_dir_entry **res_de)
 {
 	if (start) return raw_scan_nonroot
-		(sb,start,name,number,ino,res_bh,res_de,scantype);
+		(sb,start,name,number,ino,res_bh,res_de);
 	else return raw_scan_root
-		(sb,name,number,ino,res_bh,res_de,scantype);
+		(sb,name,number,ino,res_bh,res_de);
 }
 
 
@@ -532,6 +538,11 @@
  * fat_parent_ino returns the inode number of the parent directory of dir.
  * File creation has to be deferred while fat_parent_ino is running to
  * prevent renames.
+ *
+ * AV. Bad, bad, bad... We need a mapping that would give us inode by
+ * first cluster. Sheeeeit... OK, we can do it on fat_fill_inode() and
+ * update on fat_add_cluster(). When will we remove it? fat_clear_inode()
+ * and fat_truncate() to zero?
  */
 
 int fat_parent_ino(struct inode *dir,int locked)
@@ -544,7 +555,7 @@
 	if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
 	if (!locked) fat_lock_creation(); /* prevent renames */
 	if ((curr = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,
-	    &zero,NULL,NULL,NULL,SCAN_ANY)) < 0) {
+	    &zero,NULL,NULL,NULL)) < 0) {
 		if (!locked) fat_unlock_creation();
 		return curr;
 	}
@@ -553,7 +564,7 @@
 	else {
 		PRINTK(("fat_parent_ino: Debug 2\n"));
 		if ((prev = raw_scan(dir->i_sb,curr,MSDOS_DOTDOT,&zero,NULL,
-		    NULL,NULL,SCAN_ANY)) < 0) {
+		    NULL,NULL)) < 0) {
 			PRINTK(("fat_parent_ino: Debug 3 prev=%d\n", prev));
 			if (!locked) fat_unlock_creation();
 			return prev;
@@ -563,7 +574,7 @@
 			prev = MSDOS_SB(dir->i_sb)->root_cluster;
 		}
 		if ((error = raw_scan(dir->i_sb,prev,NULL,&curr,&nr,NULL,
-		    NULL,SCAN_ANY)) < 0) {
+		    NULL)) < 0) {
 			PRINTK(("fat_parent_ino: Debug 5 error=%d\n", error));
 			if (!locked) fat_unlock_creation();
 			return error;
@@ -587,12 +598,12 @@
 	count = 0;
 	if ((dir->i_ino == MSDOS_ROOT_INO) &&
 	    (MSDOS_SB(dir->i_sb)->fat_bits != 32)) {
-		(void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+		(void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL);
 	} else {
 		if ((dir->i_ino != MSDOS_ROOT_INO) &&
 		    !MSDOS_I(dir)->i_start) return 0; /* in mkdir */
 		else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
-		    NULL,&count,NULL,NULL,NULL,SCAN_ANY);
+		    NULL,&count,NULL,NULL,NULL);
 	}
 	return count;
 }
@@ -604,11 +615,11 @@
  */
 
 int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
-    struct msdos_dir_entry **res_de,int *ino, char scantype)
+    struct msdos_dir_entry **res_de,int *ino)
 {
 	int res;
 
 	res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,
-		       name, NULL, ino, res_bh, res_de, scantype);
+		       name, NULL, ino, res_bh, res_de);
 	return res<0 ? res : 0;
 }
diff -u --recursive --new-file v2.3.1/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.3.1/linux/fs/filesystems.c	Sun Sep  6 12:55:38 1998
+++ linux/fs/filesystems.c	Thu May 13 23:50:15 1999
@@ -22,6 +22,7 @@
 #include <linux/ncp_fs.h>
 #include <linux/affs_fs.h>
 #include <linux/ufs_fs.h>
+#include <linux/efs_fs.h>
 #include <linux/romfs_fs.h>
 #include <linux/auto_fs.h>
 #include <linux/qnx4_fs.h>
@@ -127,6 +128,10 @@
 
 #ifdef CONFIG_UFS_FS
 	init_ufs_fs();
+#endif
+
+#ifdef CONFIG_EFS_FS
+	init_efs_fs();
 #endif
 
 #ifdef CONFIG_AUTOFS_FS
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/Makefile linux/fs/hpfs/Makefile
--- v2.3.1/linux/fs/hpfs/Makefile	Wed Jun 24 14:30:10 1998
+++ linux/fs/hpfs/Makefile	Thu May 13 23:48:20 1999
@@ -1,14 +1,5 @@
-#
-# Makefile for the Linux HPFS 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 (not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile.
-
 O_TARGET := hpfs.o
-O_OBJS   := hpfs_fs.o hpfs_caps.o
-M_OBJS   := $(O_TARGET)
+O_OBJS := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o mmap.o name.o namei.o super.o
+M_OBJS := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/alloc.c linux/fs/hpfs/alloc.c
--- v2.3.1/linux/fs/hpfs/alloc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/alloc.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,425 @@
+/*
+ *  linux/fs/hpfs/alloc.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  HPFS bitmap operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Check if a sector is allocated in bitmap
+ * This is really slow. Turned on only if chk==2
+ */
+
+static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
+{
+	struct quad_buffer_head qbh;
+	unsigned *bmp;
+	if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
+	if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
+		hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
+		goto fail1;
+	}
+	hpfs_brelse4(&qbh);
+	if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+		unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
+		if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
+		if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
+			hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
+			goto fail1;
+		}
+		hpfs_brelse4(&qbh);
+	}
+	return 0;
+	fail1:
+	hpfs_brelse4(&qbh);
+	fail:
+	return 1;
+}
+
+/*
+ * Check if sector(s) have proper number and additionally check if they're
+ * allocated in bitmap.
+ */
+	
+int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
+{
+	if (start + len < start || start < 0x12 ||
+	    start + len > s->s_hpfs_fs_size) {
+	    	hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
+		return 1;
+	}
+	if (s->s_hpfs_chk>=2) {
+		int i;
+		for (i = 0; i < len; i++)
+			if (chk_if_allocated(s, start + i, msg)) return 1;
+	}
+	return 0;
+}
+
+static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
+{
+	struct quad_buffer_head qbh;
+	unsigned *bmp;
+	unsigned bs = near & ~0x3fff;
+	unsigned nr = (near & 0x3fff) & ~(n - 1);
+	/*unsigned mnr;*/
+	unsigned i, q;
+	int a, b;
+	secno ret = 0;
+	if (n != 1 && n != 4) {
+		hpfs_error(s, "Bad allocation size: %d", n);
+		return 0;
+	}
+	lock_super(s);
+	if (bs != ~0x3fff) {
+		if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
+	} else {
+		if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls;
+	}
+	/*if (!tstbits(bmp, nr + n, n + forward)) {
+		ret = bs + nr;
+		goto rt;
+	}
+	if (!tstbits(bmp, nr + 2*n, n + forward)) {
+		ret = bs + nr + n;
+		goto rt;
+	}*/
+	q = nr + n; b = 0;
+	while ((a = tstbits(bmp, q, n + forward))) {
+		q += a;
+		if (n != 1) q = ((q-1)&~(n-1))+n;
+		if (!b) {
+			if (q>>5 != nr>>5) {
+				b = 1;
+				q = nr & 0x1f;
+			}
+		} else if (q > nr) break;
+	}
+	if (!a) {
+		ret = bs + q;
+		goto rt;
+	}
+	nr >>= 5;
+	for (i = nr + 1; i != nr; i++, i &= 0x1ff) {
+		if (!bmp[i]) continue;
+		if (n + forward >= 0x3f && bmp[i] != -1) continue;
+		q = i<<5;
+		if (i > 0) {
+			unsigned k = bmp[i-1];
+			while (k & 0x80000000) {
+				q--; k <<= 1;
+			}
+		}
+		if (n != 1) q = ((q-1)&~(n-1))+n;
+		while ((a = tstbits(bmp, q, n + forward))) {
+			q += a;
+			if (n != 1) q = ((q-1)&~(n-1))+n;
+			if (q>>5 > i) break;
+		}
+		if (!a) {
+			ret = bs + q;
+			goto rt;
+		}
+	}
+	rt:
+	if (ret) {
+		if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+			hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
+			ret = 0;
+			goto b;
+		}
+		bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
+		hpfs_mark_4buffers_dirty(&qbh);
+	}
+	b:
+	hpfs_brelse4(&qbh);
+	uls:
+	unlock_super(s);
+	return ret;
+}
+
+/*
+ * Allocation strategy:	1) search place near the sector specified
+ *			2) search bitmap where free sectors last found
+ *			3) search all bitmaps
+ *			4) search all bitmaps ignoring number of pre-allocated
+ *				sectors
+ */
+
+secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
+{
+	secno sec;
+	unsigned i;
+	unsigned n_bmps;
+	int b = s->s_hpfs_c_bitmap;
+	int f_p = 0;
+	if (forward < 0) {
+		forward = -forward;
+		f_p = 1;
+	}
+	if (lock) hpfs_lock_creation(s);
+	if (near && near < s->s_hpfs_fs_size)
+		if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
+	if (b != -1) {
+		if (b < 0x10000000) if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) goto ret;
+		else if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
+	}	
+	n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+	for (i = 0; i < n_bmps / 2; i++) {
+		if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) {
+			s->s_hpfs_c_bitmap = n_bmps/2+i;
+			goto ret;
+		}	
+		if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) {
+			s->s_hpfs_c_bitmap = n_bmps/2-i-1;
+			goto ret;
+		}
+	}
+	if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) {
+		s->s_hpfs_c_bitmap = n_bmps-1;
+		goto ret;
+	}
+	if (!f_p) {
+		for (i = 0; i < n_bmps; i++)
+			if ((sec = alloc_in_bmp(s, i << 14, n, 0))) {
+				s->s_hpfs_c_bitmap = 0x10000000 + i;
+				goto ret;
+			}
+	}
+	sec = 0;
+	ret:
+	if (sec && f_p) {
+		for (i = 0; i < forward; i++) {
+			if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
+				hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
+				sec = 0;
+				break;
+			}
+		}
+	}
+	if (lock) hpfs_unlock_creation(s);
+	return sec;
+}
+
+static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
+{
+	unsigned nr = near;
+	secno sec;
+	if (nr < s->s_hpfs_dirband_start)
+		nr = s->s_hpfs_dirband_start;
+	if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
+		nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
+	nr -= s->s_hpfs_dirband_start;
+	nr >>= 2;
+	if (lock) hpfs_lock_creation(s);
+	sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
+	if (lock) hpfs_unlock_creation(s);
+	if (!sec) return 0;
+	return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
+}
+
+/* Alloc sector if it's free */
+
+int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
+{
+	struct quad_buffer_head qbh;
+	unsigned *bmp;
+	lock_super(s);
+	if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
+	if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
+		bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		unlock_super(s);
+		return 1;
+	}
+	hpfs_brelse4(&qbh);
+	end:
+	unlock_super(s);
+	return 0;
+}
+
+int hpfs_alloc_if_possible(struct super_block *s, secno sec)
+{
+	int r;
+	hpfs_lock_creation(s);
+	r = hpfs_alloc_if_possible_nolock(s, sec);
+	hpfs_unlock_creation(s);
+	return r;
+}
+
+/* Free sectors in bitmaps */
+
+void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
+{
+	struct quad_buffer_head qbh;
+	unsigned *bmp;
+	/*printk("2 - ");*/
+	if (!n) return;
+	if (sec < 0x12) {
+		hpfs_error(s, "Trying to free reserved sector %08x", sec);
+		return;
+	}
+	lock_super(s);
+	new_map:
+	if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
+		unlock_super(s);
+		return;
+	}	
+	new_tst:
+	if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
+		hpfs_error(s, "sector %08x not allocated", sec);
+		hpfs_brelse4(&qbh);
+		unlock_super(s);
+		return;
+	}
+	bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
+	if (!--n) {
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		unlock_super(s);
+		return;
+	}	
+	if (!(++sec & 0x3fff)) {
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		goto new_map;
+	}
+	goto new_tst;
+}
+
+/*
+ * Check if there are at least n free dnodes on the filesystem.
+ * Called before adding to dnode. If we run out of space while
+ * splitting dnodes, it would corrupt dnode tree.
+ */
+
+int hpfs_check_free_dnodes(struct super_block *s, int n)
+{
+	int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+	int b = s->s_hpfs_c_bitmap & 0x0fffffff;
+	int i, j;
+	unsigned *bmp;
+	struct quad_buffer_head qbh;
+	if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+		for (j = 0; j < 512; j++) {
+			unsigned k;
+			for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
+				hpfs_brelse4(&qbh);
+				return 0;
+			}
+		}
+	}
+	hpfs_brelse4(&qbh);
+	i = 0;
+	if (s->s_hpfs_c_bitmap != -1 ) {
+		bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
+		goto chk_bmp;
+	}
+	chk_next:
+	if (i == b) i++;
+	if (i >= n_bmps) return 1;
+	bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2");
+	chk_bmp:
+	if (bmp) {
+		for (j = 0; j < 512; j++) {
+			unsigned k;
+			for (k = 0xf; k; k <<= 4)
+				if ((bmp[j] & k) == k) {
+					/*printk("%08x,%08x\n",j,i);*/
+					if (!--n) {
+						hpfs_brelse4(&qbh);
+						return 0;
+					}
+				}
+		}
+		hpfs_brelse4(&qbh);
+	}
+	i++;
+	goto chk_next;
+}
+
+void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
+{
+	if (s->s_hpfs_chk) if (dno & 3) {
+		hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
+		return;
+	}
+	if (dno < s->s_hpfs_dirband_start ||
+	    dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+		hpfs_free_sectors(s, dno, 4);
+	} else {
+		struct quad_buffer_head qbh;
+		unsigned *bmp;
+		unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
+		lock_super(s);
+		if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+			unlock_super(s);
+			return;
+		}
+		bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		unlock_super(s);
+	}
+}
+
+struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
+			 dnode_secno *dno, struct quad_buffer_head *qbh,
+			 int lock)
+{
+	struct dnode *d;
+	if (!(*dno = alloc_in_dirband(s, near, lock)))
+		if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
+	if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
+		hpfs_free_dnode(s, *dno);
+		return NULL;
+	}
+	memset(d, 0, 2048);
+	d->magic = DNODE_MAGIC;
+	d->first_free = 52;
+	d->dirent[0] = 32;
+	d->dirent[2] = 8;
+	d->dirent[30] = 1;
+	d->dirent[31] = 255;
+	d->self = *dno;
+	return d;
+}
+
+struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno,
+			  struct buffer_head **bh)
+{
+	struct fnode *f;
+	if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
+	if (!(f = hpfs_get_sector(s, *fno, bh))) {
+		hpfs_free_sectors(s, *fno, 1);
+		return NULL;
+	}	
+	memset(f, 0, 512);
+	f->magic = FNODE_MAGIC;
+	f->ea_offs = 0xc4;
+	f->btree.n_free_nodes = 8;
+	f->btree.first_free = 8;
+	return f;
+}
+
+struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano,
+			  struct buffer_head **bh)
+{
+	struct anode *a;
+	if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
+	if (!(a = hpfs_get_sector(s, *ano, bh))) {
+		hpfs_free_sectors(s, *ano, 1);
+		return NULL;
+	}
+	memset(a, 0, 512);
+	a->magic = ANODE_MAGIC;
+	a->self = *ano;
+	a->btree.n_free_nodes = 40;
+	a->btree.n_used_nodes = 0;
+	a->btree.first_free = 8;
+	return a;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/anode.c linux/fs/hpfs/anode.c
--- v2.3.1/linux/fs/hpfs/anode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/anode.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,482 @@
+/*
+ *  linux/fs/hpfs/anode.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  handling HPFS anode tree that contains file allocation info
+ */
+
+#include "hpfs_fn.h"
+
+/* Find a sector in allocation tree */
+
+secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
+		   struct bplus_header *btree, unsigned sec,
+		   struct buffer_head *bh)
+{
+	anode_secno a = -1;
+	struct anode *anode;
+	int i;
+	int c1, c2 = 0;
+	go_down:
+	if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
+	if (btree->internal) {
+		for (i = 0; i < btree->n_used_nodes; i++)
+			if (btree->u.internal[i].file_secno > sec) {
+				a = btree->u.internal[i].down;
+				brelse(bh);
+				if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+				btree = &anode->btree;
+				goto go_down;
+			}
+		hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
+		brelse(bh);
+		return -1;
+	}
+	for (i = 0; i < btree->n_used_nodes; i++)
+		if (btree->u.external[i].file_secno <= sec &&
+		    btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
+			a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
+			if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
+				brelse(bh);
+				return -1;
+			}
+			if (inode) {
+				inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
+				inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
+				inode->i_hpfs_n_secs = btree->u.external[i].length;
+			}
+			brelse(bh);
+			return a;
+		}
+	hpfs_error(s, "sector %08x not found in external anode %08x", sec, a);
+	brelse(bh);
+	return -1;
+}
+
+/* Add a sector to tree */
+
+secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno)
+{
+	struct bplus_header *btree;
+	struct anode *anode = NULL, *ranode = NULL;
+	struct fnode *fnode;
+	anode_secno a, na = -1, ra, up = -1;
+	secno se;
+	struct buffer_head *bh, *bh1, *bh2;
+	int n;
+	unsigned fs;
+	int c1, c2 = 0;
+	if (fnod) {
+		if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
+		btree = &fnode->btree;
+	} else {
+		if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
+		btree = &anode->btree;
+	}
+	a = node;
+	go_down:
+	if ((n = btree->n_used_nodes - 1) < -!!fnod) {
+		hpfs_error(s, "anode %08x has no entries", a);
+		brelse(bh);
+		return -1;
+	}
+	if (btree->internal) {
+		a = btree->u.internal[n].down;
+		btree->u.internal[n].file_secno = -1;
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+		if (s->s_hpfs_chk)
+			if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
+		if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+		btree = &anode->btree;
+		goto go_down;
+	}
+	if (n >= 0) {
+		if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
+			hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
+				btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
+				fnod?'f':'a', node);
+			brelse(bh);
+			return -1;
+		}
+		if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
+			btree->u.external[n].length++;
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+			return se;
+		}
+	} else {
+		if (fsecno) {
+			hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno);
+			brelse(bh);
+			return -1;
+		}
+		se = node;
+	}	
+	if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
+		brelse(bh);
+		return -1;
+	}
+	fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
+	if (!btree->n_free_nodes) {
+		up = a != node ? anode->up : -1;
+		if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
+			brelse(bh);
+			hpfs_free_sectors(s, se, 1);
+			return -1;
+		}
+		if (a == node && fnod) {
+			anode->up = node;
+			anode->btree.fnode_parent = 1;
+			anode->btree.n_used_nodes = btree->n_used_nodes;
+			anode->btree.first_free = btree->first_free;
+			anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
+			memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
+			btree->internal = 1;
+			btree->n_free_nodes = 11;
+			btree->n_used_nodes = 1;
+			btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
+			btree->u.internal[0].file_secno = -1;
+			btree->u.internal[0].down = na;
+			mark_buffer_dirty(bh, 1);
+		} else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
+			brelse(bh);
+			brelse(bh1);
+			hpfs_free_sectors(s, se, 1);
+			hpfs_free_sectors(s, na, 1);
+			return -1;
+		}
+		brelse(bh);
+		bh = bh1;
+		btree = &anode->btree;
+	}
+	btree->n_free_nodes--; n = btree->n_used_nodes++;
+	btree->first_free += 12;
+	btree->u.external[n].disk_secno = se;
+	btree->u.external[n].file_secno = fs;
+	btree->u.external[n].length = 1;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	if ((a == node && fnod) || na == -1) return se;
+	c2 = 0;
+	while (up != -1) {
+		if (s->s_hpfs_chk)
+			if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
+		if (up != node || !fnod) {
+			if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
+			btree = &anode->btree;
+		} else {
+			if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
+			btree = &fnode->btree;
+		}
+		if (btree->n_free_nodes) {
+			btree->n_free_nodes--; n = btree->n_used_nodes++;
+			btree->first_free += 8;
+			btree->u.internal[n].file_secno = -1;
+			btree->u.internal[n].down = na;
+			btree->u.internal[n-1].file_secno = fs;
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+			brelse(bh2);
+			hpfs_free_sectors(s, ra, 1);
+			if ((anode = hpfs_map_anode(s, na, &bh))) {
+				anode->up = up;
+				anode->btree.fnode_parent = up == node && fnod;
+				mark_buffer_dirty(bh, 1);
+				brelse(bh);
+			}
+			return se;
+		}
+		up = up != node ? anode->up : -1;
+		btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
+		if (up == -1) anode->up = ra;
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+		a = na;
+		if ((anode = hpfs_alloc_anode(s, a, &na, &bh))) {
+			/*anode->up = up != -1 ? up : ra;*/
+			anode->btree.internal = 1;
+			anode->btree.n_used_nodes = 1;
+			anode->btree.n_free_nodes = 59;
+			anode->btree.first_free = 16;
+			anode->btree.u.internal[0].down = a;
+			anode->btree.u.internal[0].file_secno = -1;
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+			if ((anode = hpfs_map_anode(s, a, &bh))) {
+				anode->up = na;
+				mark_buffer_dirty(bh, 1);
+				brelse(bh);
+			}
+		} else na = a;
+	}
+	if ((anode = hpfs_map_anode(s, na, &bh))) {
+		anode->up = node;
+		if (fnod) anode->btree.fnode_parent = 1;
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+	}
+	if (!fnod) {
+		if (!(anode = hpfs_map_anode(s, node, &bh))) {
+			brelse(bh2);
+			return -1;
+		}
+		btree = &anode->btree;
+	} else {
+		if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
+			brelse(bh2);
+			return -1;
+		}
+		btree = &fnode->btree;
+	}
+	ranode->up = node;
+	memcpy(&ranode->btree, btree, btree->first_free);
+	if (fnod) ranode->btree.fnode_parent = 1;
+	ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
+	if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+		struct anode *unode;
+		if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
+			unode->up = ra;
+			unode->btree.fnode_parent = 0;
+			mark_buffer_dirty(bh1, 1);
+			brelse(bh1);
+		}
+	}
+	btree->internal = 1;
+	btree->n_free_nodes = fnod ? 10 : 58;
+	btree->n_used_nodes = 2;
+	btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
+	btree->u.internal[0].file_secno = fs;
+	btree->u.internal[0].down = ra;
+	btree->u.internal[1].file_secno = -1;
+	btree->u.internal[1].down = na;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	mark_buffer_dirty(bh2, 1);
+	brelse(bh2);
+	return se;
+}
+
+/*
+ * Remove allocation tree. Recursion would look much nicer but
+ * I want to avoid it because it can cause stack overflow.
+ */
+
+void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
+{
+	struct bplus_header *btree1 = btree;
+	struct anode *anode = NULL;
+	anode_secno ano = 0, oano;
+	struct buffer_head *bh;
+	int level = 0;
+	int pos = 0;
+	int i;
+	int c1, c2 = 0;
+	int d1, d2;
+	go_down:
+	d2 = 0;
+	while (btree1->internal) {
+		ano = btree1->u.internal[pos].down;
+		if (level) brelse(bh);
+		if (s->s_hpfs_chk)
+			if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
+				return;
+		anode = hpfs_map_anode(s, ano, &bh);
+		btree1 = &anode->btree;
+		level++;
+		pos = 0;
+	}
+	for (i = 0; i < btree1->n_used_nodes; i++)
+		hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
+	go_up:
+	if (!level) return;
+	if (s->s_hpfs_chk)
+		if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
+	hpfs_free_sectors(s, ano, 1);
+	oano = ano;
+	ano = anode->up;
+	brelse(bh);
+	if (--level) {
+		anode = hpfs_map_anode(s, ano, &bh);
+		btree1 = &anode->btree;
+	} else btree1 = btree;
+	for (i = 0; i < btree1->n_used_nodes; i++)
+		if (btree1->u.internal[i].down == oano)
+			if ((pos = i + 1) < btree1->n_used_nodes) goto go_down;
+			else goto go_up;
+	hpfs_error(s, "reference to anode %08x not found in anode %08x (probably bad up pointer)",
+		oano, level ? ano : -1);
+	if (level) brelse(bh);
+}
+
+/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
+
+static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
+{
+	struct anode *anode;
+	struct buffer_head *bh;
+	if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+	return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
+}
+
+int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
+	    unsigned len, char *buf)
+{
+	struct buffer_head *bh;
+	char *data;
+	secno sec;
+	unsigned l;
+	while (len) {
+		if (ano) {
+			if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+				return -1;
+		} else sec = a + (pos >> 9);
+		if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
+		if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+			return -1;
+		l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+		memcpy(buf, data + (pos & 0x1ff), l);
+		brelse(bh);
+		buf += l; pos += l; len -= l;
+	}
+	return 0;
+}
+
+int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
+	     unsigned len, char *buf)
+{
+	struct buffer_head *bh;
+	char *data;
+	secno sec;
+	unsigned l;
+	while (len) {
+		if (ano) {
+			if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+				return -1;
+		} else sec = a + (pos >> 9);
+		if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
+		if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+			return -1;
+		l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+		memcpy(data + (pos & 0x1ff), buf, l);
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+		buf += l; pos += l; len -= l;
+	}
+	return 0;
+}
+
+void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+	struct anode *anode;
+	struct buffer_head *bh;
+	if (ano) {
+		if (!(anode = hpfs_map_anode(s, a, &bh))) return;
+		hpfs_remove_btree(s, &anode->btree);
+		brelse(bh);
+		hpfs_free_sectors(s, a, 1);
+	} else hpfs_free_sectors(s, a, (len + 511) >> 9);
+}
+
+/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
+
+void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
+{
+	struct fnode *fnode;
+	struct anode *anode;
+	struct buffer_head *bh;
+	struct bplus_header *btree;
+	anode_secno node = f;
+	int i, j, nodes;
+	int c1, c2 = 0;
+	if (fno) {
+		if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
+		btree = &fnode->btree;
+	} else {
+		if (!(anode = hpfs_map_anode(s, f, &bh))) return;
+		btree = &anode->btree;
+	}
+	if (!secs) {
+		hpfs_remove_btree(s, btree);
+		if (fno) {
+			btree->n_free_nodes = 8;
+			btree->n_used_nodes = 0;
+			btree->first_free = 8;
+			btree->internal = 0;
+			mark_buffer_dirty(bh, 1);
+		} else hpfs_free_sectors(s, f, 1);
+		brelse(bh);
+		return;
+	}
+	while (btree->internal) {
+		nodes = btree->n_used_nodes + btree->n_free_nodes;
+		for (i = 0; i < btree->n_used_nodes; i++)
+			if (btree->u.internal[i].file_secno >= secs) goto f;
+		brelse(bh);
+		hpfs_error(s, "internal btree %08x doesn't end with -1", node);
+		return;
+		f:
+		for (j = i + 1; j < btree->n_used_nodes; j++)
+			hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
+		btree->n_used_nodes = i + 1;
+		btree->n_free_nodes = nodes - btree->n_used_nodes;
+		btree->first_free = 8 + 8 * btree->n_used_nodes;
+		mark_buffer_dirty(bh, 1);
+		if (btree->u.internal[i].file_secno == secs) {
+			brelse(bh);
+			return;
+		}
+		node = btree->u.internal[i].down;
+		brelse(bh);
+		if (s->s_hpfs_chk)
+			if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
+				return;
+		if (!(anode = hpfs_map_anode(s, node, &bh))) return;
+		btree = &anode->btree;
+	}	
+	nodes = btree->n_used_nodes + btree->n_free_nodes;
+	for (i = 0; i < btree->n_used_nodes; i++)
+		if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
+	brelse(bh);
+	return;
+	ff:
+	if (secs <= btree->u.external[i].file_secno) {
+		hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
+		if (i) i--;
+	}
+	else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
+		hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
+			btree->u.external[i].file_secno, btree->u.external[i].length
+			- secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
+		btree->u.external[i].length = secs - btree->u.external[i].file_secno;
+	}
+	for (j = i + 1; j < btree->n_used_nodes; j++)
+		hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
+	btree->n_used_nodes = i + 1;
+	btree->n_free_nodes = nodes - btree->n_used_nodes;
+	btree->first_free = 8 + 12 * btree->n_used_nodes;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+}
+
+/* Remove file or directory and it's eas - note that directory must
+   be empty when this is called. */
+
+void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
+{
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	struct extended_attribute *ea;
+	struct extended_attribute *ea_end;
+	if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
+	if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
+	else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
+	ea_end = fnode_end_ea(fnode);
+	for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+		if (ea->indirect)
+			hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+	hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
+	brelse(bh);
+	hpfs_free_sectors(s, fno, 1);
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/buffer.c linux/fs/hpfs/buffer.c
--- v2.3.1/linux/fs/hpfs/buffer.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/buffer.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,264 @@
+/*
+ *  linux/fs/hpfs/buffer.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  general buffer i/o
+ */
+
+#include "hpfs_fn.h"
+
+void hpfs_lock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+	printk("lock creation\n");
+#endif
+	while (s->s_hpfs_creation_de_lock) sleep_on(&s->s_hpfs_creation_de);
+	s->s_hpfs_creation_de_lock = 1;
+}
+
+void hpfs_unlock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+	printk("unlock creation\n");
+#endif
+	s->s_hpfs_creation_de_lock = 0;
+	wake_up(&s->s_hpfs_creation_de);
+}
+
+void hpfs_lock_iget(struct super_block *s, int mode)
+{
+#ifdef DEBUG_LOCKS
+	printk("lock iget\n");
+#endif
+	while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
+	s->s_hpfs_rd_inode = mode;
+}
+
+void hpfs_unlock_iget(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+	printk("unlock iget\n");
+#endif
+	s->s_hpfs_rd_inode = 0;
+	wake_up(&s->s_hpfs_iget_q);
+}
+
+void hpfs_lock_inode(struct inode *i)
+{
+	if (i) down(&i->i_hpfs_sem);
+}
+
+void hpfs_unlock_inode(struct inode *i)
+{
+	if (i) up(&i->i_hpfs_sem);
+}
+
+void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
+{
+	if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
+	if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+	if (i1->i_ino < i2->i_ino) {
+		down(&i1->i_hpfs_sem);
+		down(&i2->i_hpfs_sem);
+	} else if (i1->i_ino > i2->i_ino) {
+		down(&i2->i_hpfs_sem);
+		down(&i1->i_hpfs_sem);
+	} else down(&i1->i_hpfs_sem);
+}
+
+void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
+{
+	if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
+	if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+	if (i1->i_ino < i2->i_ino) {
+		up(&i2->i_hpfs_sem);
+		up(&i1->i_hpfs_sem);
+	} else if (i1->i_ino > i2->i_ino) {
+		up(&i1->i_hpfs_sem);
+		up(&i2->i_hpfs_sem);
+	} else up(&i1->i_hpfs_sem);
+}
+
+void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+	if (!i1) { hpfs_lock_2inodes(i2, i3); return; }
+	if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
+	if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
+	if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+		down(&i1->i_hpfs_sem);
+		hpfs_lock_2inodes(i2, i3);
+	} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+		down(&i2->i_hpfs_sem);
+		hpfs_lock_2inodes(i1, i3);
+	} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+		down(&i3->i_hpfs_sem);
+		hpfs_lock_2inodes(i1, i2);
+	} else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
+	else hpfs_lock_2inodes(i1, i3);
+}
+		
+void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+	if (!i1) { hpfs_unlock_2inodes(i2, i3); return; }
+	if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
+	if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
+	if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+		hpfs_unlock_2inodes(i2, i3);
+		up(&i1->i_hpfs_sem);
+	} else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+		hpfs_unlock_2inodes(i1, i3);
+		up(&i2->i_hpfs_sem);
+	} else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+		hpfs_unlock_2inodes(i1, i2);
+		up(&i3->i_hpfs_sem);
+	} else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
+	else hpfs_unlock_2inodes(i1, i3);
+}
+
+/* Map a sector into a buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
+		 int ahead)
+{
+	kdev_t dev = s->s_dev;
+	struct buffer_head *bh;
+
+	if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
+		*bhp = bh = bread(dev, secno, 512);
+	else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9);
+	if (bh != NULL)
+		return bh->b_data;
+	else {
+		printk("HPFS: hpfs_map_sector: read error\n");
+		return NULL;
+	}
+}
+
+/* Like hpfs_map_sector but don't read anything */
+
+void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp)
+{
+	struct buffer_head *bh;
+	/*return hpfs_map_sector(s, secno, bhp, 0);*/
+
+	if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+		mark_buffer_uptodate(bh, 1);
+		return bh->b_data;
+	} else {
+		printk("HPFS: hpfs_get_sector: getblk failed\n");
+		return NULL;
+	}
+}
+
+/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
+		   int ahead)
+{
+	kdev_t dev = s->s_dev;
+	struct buffer_head *bh;
+	char *data;
+
+	if (secno & 3) {
+		printk("HPFS: hpfs_map_4sectors: unaligned read\n");
+		return 0;
+	}
+
+	qbh->data = data = (char *)kmalloc(2048, GFP_KERNEL);
+	if (!data) {
+		printk("HPFS: hpfs_map_4sectors: out of memory\n");
+		goto bail;
+	}
+
+	if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
+		qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048);
+	else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9);
+	if (!bh)
+		goto bail0;
+	memcpy(data, bh->b_data, 512);
+
+	qbh->bh[1] = bh = bread(dev, secno + 1, 512);
+	if (!bh)
+		goto bail1;
+	memcpy(data + 512, bh->b_data, 512);
+
+	qbh->bh[2] = bh = bread(dev, secno + 2, 512);
+	if (!bh)
+		goto bail2;
+	memcpy(data + 2 * 512, bh->b_data, 512);
+
+	qbh->bh[3] = bh = bread(dev, secno + 3, 512);
+	if (!bh)
+		goto bail3;
+	memcpy(data + 3 * 512, bh->b_data, 512);
+
+	return data;
+
+ bail3:
+	brelse(qbh->bh[2]);
+ bail2:
+	brelse(qbh->bh[1]);
+ bail1:
+	brelse(qbh->bh[0]);
+ bail0:
+	kfree_s(data, 2048);
+	printk("HPFS: hpfs_map_4sectors: read error\n");
+ bail:
+	return NULL;
+}
+
+/* Don't read sectors */
+
+void *hpfs_get_4sectors(struct super_block *s, unsigned secno,
+                          struct quad_buffer_head *qbh)
+{
+	if (secno & 3) {
+		printk("HPFS: hpfs_get_4sectors: unaligned read\n");
+		return 0;
+	}
+
+	/*return hpfs_map_4sectors(s, secno, qbh, 0);*/
+	if (!(qbh->data = kmalloc(2048, GFP_KERNEL))) {
+		printk("HPFS: hpfs_get_4sectors: out of memory\n");
+		return NULL;
+	}
+	if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0;
+	if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1;
+	if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2;
+	if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3;
+	memcpy(qbh->data, qbh->bh[0]->b_data, 512);
+	memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512);
+	memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512);
+	memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512);
+	return qbh->data;
+
+	bail3:	brelse(qbh->bh[2]);
+	bail2:	brelse(qbh->bh[1]);
+	bail1:	brelse(qbh->bh[0]);
+	bail0:
+	return NULL;
+}
+	
+
+void hpfs_brelse4(struct quad_buffer_head *qbh)
+{
+	brelse(qbh->bh[3]);
+	brelse(qbh->bh[2]);
+	brelse(qbh->bh[1]);
+	brelse(qbh->bh[0]);
+	kfree_s(qbh->data, 2048);
+}	
+
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
+{
+	PRINTK(("hpfs_mark_4buffers_dirty\n"));
+	memcpy(qbh->bh[0]->b_data, qbh->data, 512);
+	memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
+	memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
+	memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512);
+	mark_buffer_dirty(qbh->bh[0],1);
+	mark_buffer_dirty(qbh->bh[1],1);
+	mark_buffer_dirty(qbh->bh[2],1);
+	mark_buffer_dirty(qbh->bh[3],1);
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/dentry.c linux/fs/hpfs/dentry.c
--- v2.3.1/linux/fs/hpfs/dentry.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/dentry.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,62 @@
+/*
+ *  linux/fs/hpfs/dentry.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  dcache operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+
+int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	unsigned long	 hash;
+	int		 i;
+	int l = qstr->len;
+
+	if (l == 1) if (qstr->name[0]=='.') goto x;
+	if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
+	if (hpfs_chk_name((char *)qstr->name,l))
+		/*return -ENAMETOOLONG;*/
+		return -ENOENT;
+	hpfs_adjust_length((char *)qstr->name, &l);
+	x:
+
+	hash = init_name_hash();
+	for (i = 0; i < l; i++)
+		hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
+	qstr->hash = end_name_hash(hash);
+
+	return 0;
+}
+
+int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	int al=a->len;
+	int bl=b->len;
+	hpfs_adjust_length((char *)a->name, &al);
+	hpfs_adjust_length((char *)b->name, &bl);
+	/* 'a' is the qstr of an already existing dentry, so the name
+	 * must be valid. 'b' must be validated first.
+	 */
+
+	if (hpfs_chk_name((char *)b->name, bl)) return 1;
+	if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
+	return 0;
+}
+
+struct dentry_operations hpfs_dentry_operations = {
+	NULL,			/* d_validate   */
+	hpfs_hash_dentry,	/* d_hash       */
+	hpfs_compare_dentry,	/* d_compare    */
+	NULL			/* d_delete     */
+};
+
+void hpfs_set_dentry_operations(struct dentry *dentry)
+{
+	dentry->d_op = &hpfs_dentry_operations;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/dir.c linux/fs/hpfs/dir.c
--- v2.3.1/linux/fs/hpfs/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/dir.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,282 @@
+/*
+ *  linux/fs/hpfs/dir.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  directory VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+int hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff)
+{
+	return -EISDIR;
+}
+
+int hpfs_dir_release(struct inode *inode, struct file *filp)
+{
+	hpfs_del_pos(inode, &filp->f_pos);
+	/*hpfs_write_if_changed(inode);*/
+	return 0;
+}
+
+int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	int lc;
+	long old_pos;
+	char *tempname;
+	int c1, c2 = 0;
+
+	if (!inode) return -EBADF;
+	if (!S_ISDIR(inode->i_mode)) return -EBADF;
+	if (inode->i_sb->s_hpfs_chk) {
+		if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
+			return -EFSERROR;
+		if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+			return -EFSERROR;
+	}
+	if (inode->i_sb->s_hpfs_chk >= 2) {
+		struct buffer_head *bh;
+		struct fnode *fno;
+		int e = 0;
+		if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh)))
+			return -EIOERROR;
+		if (!fno->dirflag) {
+			e = 1;
+			hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
+		}
+		if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+			e = 1;
+			hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+		}
+		brelse(bh);
+		if (e) return -EFSERROR;
+	}
+	lc = inode->i_sb->s_hpfs_lowercase;
+	if (filp->f_pos == -2) { /* diff -r requires this (note, that diff -r */
+		filp->f_pos = -3; /* also fails on msdos filesystem in 2.0) */
+		return 0;
+	}
+	if (filp->f_pos == -3) return -ENOENT;
+	
+	hpfs_lock_inode(inode);
+	
+	while (1) {
+		again:
+		/* This won't work when cycle is longer than number of dirents
+		   accepted by filldir, but what can I do?
+		   maybe killall -9 ls helps */
+		if (inode->i_sb->s_hpfs_chk)
+			if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
+				hpfs_unlock_inode(inode);
+				return -EFSERROR;
+			}
+		if (filp->f_pos == -2) {
+			hpfs_unlock_inode(inode);
+			return 0;
+		}
+		if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) {
+			printk("HPFS: warning: pos==%d\n",(int)filp->f_pos);
+			hpfs_unlock_inode(inode);
+			return 0;
+		}
+		if (filp->f_pos == 0) {
+			if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) {
+				hpfs_unlock_inode(inode);
+				return 0;
+			}
+			filp->f_pos = -1;
+		}
+		if (filp->f_pos == -1) {
+			if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) {
+				hpfs_unlock_inode(inode);
+				return 0;
+			}
+			filp->f_pos = 1;
+		}
+		if (filp->f_pos == 1) {
+			filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+			hpfs_add_pos(inode, &filp->f_pos);
+			filp->f_version = inode->i_version;
+		}
+			/*if (filp->f_version != inode->i_version) {
+				hpfs_unlock_inode(inode);
+				return -ENOENT;
+			}*/	
+			old_pos = filp->f_pos;
+			if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) {
+				hpfs_unlock_inode(inode);
+				return -EIOERROR;
+			}
+			if (de->first || de->last) {
+				if (inode->i_sb->s_hpfs_chk) {
+					if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
+					if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
+				}
+				hpfs_brelse4(&qbh);
+				goto again;
+			}
+			tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
+			if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode) < 0) {
+				filp->f_pos = old_pos;
+				if (tempname != (char *)de->name) kfree(tempname);
+				hpfs_brelse4(&qbh);
+				hpfs_unlock_inode(inode);
+				return 0;
+			}
+			if (tempname != (char *)de->name) kfree(tempname);
+			hpfs_brelse4(&qbh);
+	}
+}
+
+/*
+ * lookup.  Search the specified directory for the specified name, set
+ * *result to the corresponding inode.
+ *
+ * lookup uses the inode number to tell read_inode whether it is reading
+ * the inode of a directory or a file -- file ino's are odd, directory
+ * ino's are even.  read_inode avoids i/o for file inodes; everything
+ * needed is up here in the directory.  (And file fnodes are out in
+ * the boondocks.)
+ *
+ *    - M.P.: this is over, sometimes we've got to read file's fnode for eas
+ *	      inode numbers are just fnode sector numbers; iget lock is used
+ *	      to tell read_inode to read fnode or not.
+ */
+
+struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	struct inode *inode;
+	ino_t ino;
+
+	struct inode *result = NULL;
+	if (dir == 0) return -ENOENT;
+	hpfs_adjust_length((char *)name, &len);
+	hpfs_lock_inode(dir);
+	if (!S_ISDIR(dir->i_mode)) goto bail;
+	/*
+	 * Read in the directory entry. "." is there under the name ^A^A .
+	 * Always read the dir even for . and .. in case we need the dates.
+	 *
+	 * M.P.: No - we can't read '^A^A' for current directory. In some cases
+	 * the information under '^A^A' is incomplete (missing ea_size, bad
+	 * fnode number) - chkdsk ignores such errors and it doesn't seem to
+	 * matter under OS/2.
+	 */
+	if (name[0] == '.' && len == 1) {
+		result = dir;
+		dir->i_count++;
+		goto end;
+	} else if (name[0] == '.' && name[1] == '.' && len == 2) {
+		struct buffer_head *bh;
+		struct fnode *fnode;
+		if (dir->i_ino == dir->i_sb->s_hpfs_root) {
+			result = dir;
+			dir->i_count++;
+			goto end;
+		}
+		if (dir->i_hpfs_parent_dir == dir->i_sb->s_hpfs_root) {
+			result = dir->i_sb->s_root->d_inode;
+			result->i_count++;
+			goto end;
+		}
+		if (!(fnode = hpfs_map_fnode(dir->i_sb, dir->i_hpfs_parent_dir, &bh))) goto bail;
+		de = map_fnode_dirent(dir->i_sb, dir->i_hpfs_parent_dir, fnode, &qbh);
+		brelse(bh);
+	} else
+		de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh, NULL);
+
+	/*
+	 * This is not really a bailout, just means file not found.
+	 */
+
+	if (!de) {
+		result = NULL;
+		goto end;
+	}
+
+	/*
+	 * Get inode number, what we're after.
+	 */
+
+	ino = de->fnode;
+
+	/*
+	 * Go find or make an inode.
+	 */
+
+	hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
+	if (!(inode = iget(dir->i_sb, ino))) {
+		hpfs_unlock_iget(dir->i_sb);
+		hpfs_error(inode->i_sb, "hpfs_lookup: can't get inode");
+		goto bail1;
+	}
+	if (!de->directory) inode->i_hpfs_parent_dir = dir->i_ino;
+	hpfs_unlock_iget(dir->i_sb);
+
+	hpfs_decide_conv(inode, (char *)name, len);
+
+	if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
+		hpfs_error(inode->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
+		goto bail1;
+	}
+
+	/*
+	 * Fill in the info from the directory if this is a newly created
+	 * inode.
+	 */
+
+	if (!inode->i_ctime) {
+		if (!(inode->i_ctime = local_to_gmt(dir->i_sb, de->creation_date)))
+			inode->i_ctime = 1;
+		inode->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
+		inode->i_atime = local_to_gmt(dir->i_sb, de->read_date);
+		inode->i_hpfs_ea_size = de->ea_size;
+		if (!inode->i_hpfs_ea_mode && de->read_only)
+			inode->i_mode &= ~0222;
+		if (!de->directory) {
+			if (inode->i_size == -1) {
+				inode->i_size = de->file_size;
+			/*
+			 * i_blocks should count the fnode and any anodes.
+			 * We count 1 for the fnode and don't bother about
+			 * anodes -- the disk heads are on the directory band
+			 * and we want them to stay there.
+			 */
+				inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
+			}
+		}
+	}
+
+	hpfs_brelse4(&qbh);
+
+	/*
+	 * Made it.
+	 */
+
+	result = inode;
+	end:
+	hpfs_unlock_inode(dir);
+	hpfs_set_dentry_operations(dentry);
+	d_add(dentry, result);
+	return NULL;
+
+	/*
+	 * Didn't.
+	 */
+	bail1:
+	
+	hpfs_brelse4(&qbh);
+	
+	bail:
+
+	hpfs_unlock_inode(dir);
+	return ERR_PTR(-ENOENT);
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/dnode.c linux/fs/hpfs/dnode.c
--- v2.3.1/linux/fs/hpfs/dnode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/dnode.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,1078 @@
+/*
+ *  linux/fs/hpfs/dnode.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  handling directory dnode tree - adding, deleteing & searching for dirents
+ */
+
+#include "hpfs_fn.h"
+
+static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
+{
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end = dnode_end_de(d);
+	int i = 1;
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+		if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i;
+		i++;
+	}
+	printk("HPFS: get_pos: not_found\n");
+	return ((loff_t)d->self << 4) | (loff_t)1;
+}
+
+void hpfs_add_pos(struct inode *inode, loff_t *pos)
+{
+	int i = 0;
+	loff_t **ppos;
+	if (inode->i_hpfs_rddir_off)
+		for (; inode->i_hpfs_rddir_off[i]; i++)
+			if (inode->i_hpfs_rddir_off[i] == pos) return;
+	if (!(i&0x0f)) {
+		if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
+			printk("HPFS: out of memory for position list\n");
+			return;
+		}
+		if (inode->i_hpfs_rddir_off) {
+			memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
+			kfree(inode->i_hpfs_rddir_off);
+		}
+		inode->i_hpfs_rddir_off = ppos;
+	}
+	inode->i_hpfs_rddir_off[i] = pos;
+	inode->i_hpfs_rddir_off[i + 1] = NULL;
+}
+
+void hpfs_del_pos(struct inode *inode, loff_t *pos)
+{
+	loff_t **i, **j;
+	if (!inode->i_hpfs_rddir_off) goto not_f;
+	for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+	goto not_f;
+	fnd:
+	for (j = i + 1; *j; j++) ;
+	*i = *(j - 1);
+	*(j - 1) = NULL;
+	if (j - 1 == inode->i_hpfs_rddir_off) {
+		kfree(inode->i_hpfs_rddir_off);
+		inode->i_hpfs_rddir_off = NULL;
+	}
+	return;
+	not_f:
+	/*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/
+	return;
+}
+
+static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
+			 loff_t p1, loff_t p2)
+{
+	loff_t **i;
+	if (!inode->i_hpfs_rddir_off) return;
+	for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+	return;
+}
+
+void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t)
+{
+	if (*p == f) *p = t;
+}
+
+/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t)
+{
+	if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f);
+}*/
+
+void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c)
+{
+	if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+		int n = (*p & 0x3f) + c;
+		if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8);
+		else *p = (*p & ~0x3f) | n;
+	}
+}
+
+void hpfs_pos_del(loff_t *p, loff_t d, loff_t c)
+{
+	if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+		int n = (*p & 0x3f) - c;
+		if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8);
+		else *p = (*p & ~0x3f) | n;
+	}
+}
+
+static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d)
+{
+	struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL;
+	de_end = dnode_end_de(d);
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+		deee = dee; dee = de;
+	}	
+	return deee;
+}
+
+static struct hpfs_dirent *dnode_last_de(struct dnode *d)
+{
+	struct hpfs_dirent *de, *de_end, *dee = NULL;
+	de_end = dnode_end_de(d);
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+		dee = de;
+	}	
+	return dee;
+}
+
+static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr)
+{
+	struct hpfs_dirent *de;
+	if (!(de = dnode_last_de(d))) {
+		hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
+		return;
+	}
+	if (s->s_hpfs_chk) {
+		if (de->down) {
+			hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
+				d->self, de_down_pointer(de));
+			return;
+		}
+		if (de->length != 32) {
+			hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self);
+			return;
+		}
+	}
+	if (ptr) {
+		if ((d->first_free += 4) > 2048) {
+			hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self);
+			d->first_free -= 4;
+			return;
+		}
+		de->length = 36;
+		de->down = 1;
+		*(dnode_secno *)((char *)de + 32) = ptr;
+	}
+}
+
+/* Add an entry to dnode and don't care if it grows over 2048 bytes */
+
+struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name,
+				unsigned namelen, secno down_ptr)
+{
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end = dnode_end_de(d);
+	unsigned d_size = de_size(namelen, down_ptr);
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+		int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last);
+		if (!c) {
+			hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self);
+			return NULL;
+		}
+		if (c < 0) break;
+	}
+	memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
+	memset(de, 0, d_size);
+	if (down_ptr) {
+		*(int *)((char *)de + d_size - 4) = down_ptr;
+		de->down = 1;
+	}
+	de->length = d_size;
+	if (down_ptr) de->down = 1;
+	de->not_8x3 = hpfs_is_name_long(name, namelen);
+	de->namelen = namelen;
+	memcpy(de->name, name, namelen);
+	d->first_free += d_size;
+	return de;
+}
+
+/* Delete dirent and don't care about it's subtree */
+
+void hpfs_delete_de(struct super_block *s, struct dnode *d, struct hpfs_dirent *de)
+{
+	if (de->last) {
+		hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self);
+		return;
+	}
+	d->first_free -= de->length;
+	memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de);
+}
+
+static void fix_up_ptrs(struct super_block *s, struct dnode *d)
+{
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end = dnode_end_de(d);
+	dnode_secno dno = d->self;
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de))
+		if (de->down) {
+			struct quad_buffer_head qbh;
+			struct dnode *dd;
+			if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) {
+				if (dd->up != dno || dd->root_dnode) {
+					dd->up = dno;
+					dd->root_dnode = 0;
+					hpfs_mark_4buffers_dirty(&qbh);
+				}
+				hpfs_brelse4(&qbh);
+			}
+		}
+}
+
+/* Add an entry to dnode and do dnode splitting if required */
+
+int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, unsigned namelen,
+		      struct hpfs_dirent *new_de, dnode_secno down_ptr)
+{
+	struct quad_buffer_head qbh, qbh1, qbh2;
+	struct dnode *d, *ad, *rd, *nd = NULL;
+	dnode_secno adno, rdno;
+	struct hpfs_dirent *de;
+	struct hpfs_dirent nde;
+	char *nname;
+	int h;
+	int pos;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	int c1, c2 = 0;
+	if (!(nname = kmalloc(256, GFP_KERNEL))) {
+		printk("HPFS: out of memory, can't add to dnode\n");
+		return 1;
+	}
+	go_up:
+	if (namelen >= 256) {
+		hpfs_error(i->i_sb, "hpfs_add_to_dnode: namelen == %d", namelen);
+		if (nd) kfree(nd);
+		kfree(nname);
+		return 1;
+	}
+	if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) {
+		if (nd) kfree(nd);
+		kfree(nname);
+		return 1;
+	}
+	go_up_a:
+	if (i->i_sb->s_hpfs_chk)
+		if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
+			hpfs_brelse4(&qbh);
+			if (nd) kfree(nd);
+			kfree(nname);
+			return 1;
+		}
+	if (d->first_free + de_size(namelen, down_ptr) <= 2048) {
+		loff_t t;
+		copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de);
+		t = get_pos(d, de);
+		for_all_poss(i, hpfs_pos_ins, t, 1);
+		for_all_poss(i, hpfs_pos_subst, 4, t);
+		for_all_poss(i, hpfs_pos_subst, 5, t + 1);
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		if (nd) kfree(nd);
+		kfree(nname);
+		return 0;
+	}
+	if (!nd) if (!(nd = kmalloc(0x924, GFP_KERNEL))) {
+		/* 0x924 is a max size of dnode after adding a dirent with
+		   max name length. We alloc this only once. There must
+		   not be any error while splitting dnodes, otherwise the
+		   whole directory, not only file we're adding, would
+		   be lost. */
+		printk("HPFS: out of memory for dnode splitting\n");
+		hpfs_brelse4(&qbh);
+		kfree(nname);
+		return 1;
+	}	
+	memcpy(nd, d, d->first_free);
+	copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de);
+	for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1);
+	h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10;
+	if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) {
+		hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+		hpfs_brelse4(&qbh);
+		kfree(nd);
+		kfree(nname);
+		return 1;
+	}
+	i->i_size += 2048;
+	i->i_blocks += 4;
+	pos = 1;
+	for (de = dnode_first_de(nd); (char *)de_next_de(de) - (char *)nd < h; de = de_next_de(de)) {
+		copy_de(hpfs_add_de(i->i_sb, ad, de->name, de->namelen, de->down ? de_down_pointer(de) : 0), de);
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, ((loff_t)adno << 4) | pos);
+		pos++;
+	}
+	copy_de(new_de = &nde, de);
+	memcpy(name = nname, de->name, namelen = de->namelen);
+	for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4);
+	down_ptr = adno;
+	set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
+	de = de_next_de(de);
+	memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de);
+	nd->first_free -= (char *)de - (char *)nd - 20;
+	memcpy(d, nd, nd->first_free);
+	for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos);
+	fix_up_ptrs(i->i_sb, ad);
+	if (!d->root_dnode) {
+		dno = ad->up = d->up;
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		hpfs_mark_4buffers_dirty(&qbh1);
+		hpfs_brelse4(&qbh1);
+		goto go_up;
+	}
+	if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) {
+		hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+		hpfs_brelse4(&qbh);
+		hpfs_brelse4(&qbh1);
+		kfree(nd);
+		kfree(nname);
+		return 1;
+	}
+	i->i_size += 2048;
+	i->i_blocks += 4;
+	rd->root_dnode = 1;
+	rd->up = d->up;
+	if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) {
+		hpfs_free_dnode(i->i_sb, rdno);
+		hpfs_brelse4(&qbh);
+		hpfs_brelse4(&qbh1);
+		hpfs_brelse4(&qbh2);
+		kfree(nd);
+		kfree(nname);
+		return 1;
+	}
+	fnode->u.external[0].disk_secno = rdno;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	d->up = ad->up = i->i_hpfs_dno = rdno;
+	d->root_dnode = ad->root_dnode = 0;
+	hpfs_mark_4buffers_dirty(&qbh);
+	hpfs_brelse4(&qbh);
+	hpfs_mark_4buffers_dirty(&qbh1);
+	hpfs_brelse4(&qbh1);
+	qbh = qbh2;
+	set_last_pointer(i->i_sb, rd, dno);
+	dno = rdno;
+	d = rd;
+	goto go_up_a;
+}
+
+/*
+ * Add an entry to directory btree.
+ * I hate such crazy directory structure.
+ * It's easy to read but terrible to write.
+ * I wrote this directory code 4 times.
+ * I hope, now it's finally bug-free.
+ */
+
+int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
+		    struct hpfs_dirent *new_de, int cdepth)
+{
+	struct dnode *d;
+	struct hpfs_dirent *de, *de_end;
+	struct quad_buffer_head qbh;
+	dnode_secno dno;
+	int c;
+	int depth = cdepth;
+	int c1, c2 = 0;
+	dno = i->i_hpfs_dno;
+	down:
+	if (i->i_sb->s_hpfs_chk)
+		if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
+	if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
+	de_end = dnode_end_de(d);
+	for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+		if (!(c = hpfs_compare_names(i->i_sb, name, namelen, de->name, de->namelen, de->last))) {
+			hpfs_brelse4(&qbh);
+			return -1;
+		}	
+		if (c < 0) {
+			if (de->down) {
+				dno = de_down_pointer(de);
+				hpfs_brelse4(&qbh);
+				depth++;
+				goto down;
+			}
+			break;
+		}
+	}
+	hpfs_brelse4(&qbh);
+	if (!cdepth) hpfs_lock_creation(i->i_sb);
+	if (hpfs_check_free_dnodes(i->i_sb, depth + 2)) {
+		c = 1;
+		goto ret;
+	}	
+	i->i_version = ++event;
+	c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0);
+	ret:
+	if (!cdepth) hpfs_unlock_creation(i->i_sb);
+	return c;
+}
+
+/* 
+ * Find dirent with higher name in 'from' subtree and move it to 'to' dnode.
+ * Return the dnode we moved from (to be checked later if it's empty)
+ */
+
+static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
+{
+	dnode_secno dno, ddno;
+	dnode_secno chk_up = to;
+	struct dnode *dnode;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de, *nde;
+	int a;
+	loff_t t;
+	int c1, c2 = 0;
+	dno = from;
+	while (1) {
+		if (i->i_sb->s_hpfs_chk)
+			if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
+				return 0;
+		if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
+		if (i->i_sb->s_hpfs_chk) {
+			if (dnode->up != chk_up) {
+				hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
+					dno, chk_up, dnode->up);
+				hpfs_brelse4(&qbh);
+				return 0;
+			}
+			chk_up = dno;
+		}
+		if (!(de = dnode_last_de(dnode))) {
+			hpfs_error(i->i_sb, "move_to_top: dnode %08x has no last de", dno);
+			hpfs_brelse4(&qbh);
+			return 0;
+		}
+		if (!de->down) break;
+		dno = de_down_pointer(de);
+		hpfs_brelse4(&qbh);
+	}
+	while (!(de = dnode_pre_last_de(dnode))) {
+		dnode_secno up = dnode->up;
+		hpfs_brelse4(&qbh);
+		hpfs_free_dnode(i->i_sb, dno);
+		i->i_size -= 2048;
+		i->i_blocks -= 4;
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, 5);
+		if (up == to) return to;
+		if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return 0;
+		if (dnode->root_dnode) {
+			hpfs_error(i->i_sb, "move_to_top: got to root_dnode while moving from %08x to %08x", from, to);
+			hpfs_brelse4(&qbh);
+			return 0;
+		}
+		de = dnode_last_de(dnode);
+		if (!de || !de->down) {
+			hpfs_error(i->i_sb, "move_to_top: dnode %08x doesn't point down to %08x", up, dno);
+			hpfs_brelse4(&qbh);
+			return 0;
+		}
+		dnode->first_free -= 4;
+		de->length -= 4;
+		de->down = 0;
+		hpfs_mark_4buffers_dirty(&qbh);
+		dno = up;
+	}
+	t = get_pos(dnode, de);
+	for_all_poss(i, hpfs_pos_subst, t, 4);
+	for_all_poss(i, hpfs_pos_subst, t + 1, 5);
+	if (!(nde = kmalloc(de->length, GFP_KERNEL))) {
+		hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted");
+		hpfs_brelse4(&qbh);
+		return 0;
+	}
+	memcpy(nde, de, de->length);
+	ddno = de->down ? de_down_pointer(de) : 0;
+	hpfs_delete_de(i->i_sb, dnode, de);
+	set_last_pointer(i->i_sb, dnode, ddno);
+	hpfs_mark_4buffers_dirty(&qbh);
+	hpfs_brelse4(&qbh);
+	a = hpfs_add_to_dnode(i, to, nde->name, nde->namelen, nde, from);
+	kfree(nde);
+	if (a) return 0;
+	return dno;
+}
+
+/* 
+ * Check if a dnode is empty and delete it from the tree
+ * (chkdsk doesn't like empty dnodes)
+ */
+
+static void delete_empty_dnode(struct inode *i, dnode_secno dno)
+{
+	struct quad_buffer_head qbh;
+	struct dnode *dnode;
+	dnode_secno down, up, ndown;
+	int p;
+	struct hpfs_dirent *de;
+	int c1, c2 = 0;
+	try_it_again:
+	if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return;
+	if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return;
+	if (dnode->first_free > 56) goto end;
+	if (dnode->first_free == 52 || dnode->first_free == 56) {
+		struct hpfs_dirent *de_end;
+		int root = dnode->root_dnode;
+		up = dnode->up;
+		de = dnode_first_de(dnode);
+		down = de->down ? de_down_pointer(de) : 0;
+		if (i->i_sb->s_hpfs_chk) if (root && !down) {
+			hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
+			goto end;
+		}
+		hpfs_brelse4(&qbh);
+		hpfs_free_dnode(i->i_sb, dno);
+		i->i_size -= 2048;
+		i->i_blocks -= 4;
+		if (root) {
+			struct fnode *fnode;
+			struct buffer_head *bh;
+			struct dnode *d1;
+			struct quad_buffer_head qbh1;
+			if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
+				hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
+				return;
+			}
+			if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+				d1->up = up;
+				d1->root_dnode = 1;
+				hpfs_mark_4buffers_dirty(&qbh1);
+				hpfs_brelse4(&qbh1);
+			}
+			if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) {
+				fnode->u.external[0].disk_secno = down;
+				mark_buffer_dirty(bh, 1);
+				brelse(bh);
+			}
+			i->i_hpfs_dno = down;
+			for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) -2);
+			return;
+		}
+		if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return;
+		p = 1;
+		de_end = dnode_end_de(dnode);
+		for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de), p++)
+			if (de->down) if (de_down_pointer(de) == dno) goto fnd;
+		hpfs_error(i->i_sb, "delete_empty_dnode: pointer to dnode %08x not found in dnode %08x", dno, up);
+		goto end;
+		fnd:
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p);
+		if (!down) {
+			de->down = 0;
+			de->length -= 4;
+			dnode->first_free -= 4;
+			memmove(de_next_de(de), (char *)de_next_de(de) + 4,
+				(char *)dnode + dnode->first_free - (char *)de_next_de(de));
+		} else {
+			struct dnode *d1;
+			struct quad_buffer_head qbh1;
+			*(dnode_secno *) ((void *) de + de->length - 4) = down;
+			if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+				d1->up = up;
+				hpfs_mark_4buffers_dirty(&qbh1);
+				hpfs_brelse4(&qbh1);
+			}
+		}
+	} else {
+		hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free);
+		goto end;
+	}
+
+	/*{
+		static int cnt_t_ = 0;
+		if (cnt_t_++ & 1) goto endm;
+	}*/
+
+	if (!de->last) {
+		struct hpfs_dirent *de_next = de_next_de(de);
+		struct hpfs_dirent *de_cp;
+		struct dnode *d1;
+		struct quad_buffer_head qbh1;
+		if (!de_next->down) goto endm;
+		ndown = de_down_pointer(de_next);
+		if (!(de_cp = kmalloc(de->length, GFP_KERNEL))) {
+			printk("HPFS: out of memory for dtree balancing\n");
+			goto endm;
+		}
+		memcpy(de_cp, de, de->length);
+		hpfs_delete_de(i->i_sb, dnode, de);
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4);
+		for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1);
+		if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) {
+			d1->up = ndown;
+			hpfs_mark_4buffers_dirty(&qbh1);
+			hpfs_brelse4(&qbh1);
+		}
+		hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, de_cp->down ? de_down_pointer(de_cp) : 0);
+		/*printk("UP-TO-DNODE: %08x (ndown = %08x, down = %08x, dno = %08x)\n", up, ndown, down, dno);*/
+		dno = up;
+		kfree(de_cp);
+		goto try_it_again;
+	} else {
+		/*printk("HPFS: warning: not balancing tree\n");*/
+		struct hpfs_dirent *de_prev = dnode_pre_last_de(dnode);
+		struct hpfs_dirent *de_cp;
+		struct dnode *d1;
+		struct quad_buffer_head qbh1;
+		dnode_secno dlp;
+		if (!de_prev) {
+			hpfs_error(i->i_sb, "delete_empty_dnode: empty dnode %08x", up);
+			hpfs_mark_4buffers_dirty(&qbh);
+			hpfs_brelse4(&qbh);
+			dno = up;
+			goto try_it_again;
+		}
+		if (!de_prev->down) goto endm;
+		ndown = de_down_pointer(de_prev);
+		if ((d1 = hpfs_map_dnode(i->i_sb, ndown, &qbh1))) {
+			struct hpfs_dirent *del = dnode_last_de(d1);
+			dlp = del->down ? de_down_pointer(del) : 0;
+			if (!dlp && down) {
+				if (d1->first_free > 2044) {
+					if (i->i_sb->s_hpfs_chk >= 2) {
+						printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+						printk("HPFS: warning: terminating balancing operation\n");
+					}
+					hpfs_brelse4(&qbh1);
+					goto endm;
+				}
+				if (i->i_sb->s_hpfs_chk >= 2) {
+					printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+					printk("HPFS: warning: goin'on\n");
+				}
+				del->length += 4;
+				del->down = 1;
+				d1->first_free += 4;
+			}
+			if (dlp && !down) {
+				del->length -= 4;
+				del->down = 0;
+				d1->first_free -= 4;
+			} else if (down)
+				*(dnode_secno *) ((void *) del + del->length - 4) = down;
+		} else goto endm;
+		if (!(de_cp = kmalloc(de_prev->length, GFP_KERNEL))) {
+			printk("HPFS: out of memory for dtree balancing\n");
+			hpfs_brelse4(&qbh1);
+			goto endm;
+		}
+		hpfs_mark_4buffers_dirty(&qbh1);
+		hpfs_brelse4(&qbh1);
+		memcpy(de_cp, de_prev, de_prev->length);
+		hpfs_delete_de(i->i_sb, dnode, de_prev);
+		if (!de_prev->down) {
+			de_prev->length += 4;
+			de_prev->down = 1;
+			dnode->first_free += 4;
+		}
+		*(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown;
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
+		for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1));
+		if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) {
+			d1->up = ndown;
+			hpfs_mark_4buffers_dirty(&qbh1);
+			hpfs_brelse4(&qbh1);
+		}
+		hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, dlp);
+		dno = up;
+		kfree(de_cp);
+		goto try_it_again;
+	}
+	endm:
+	hpfs_mark_4buffers_dirty(&qbh);
+	end:
+	hpfs_brelse4(&qbh);
+}
+
+
+/* Delete dirent from directory */
+
+int hpfs_remove_dirent(struct inode *i, dnode_secno dno, struct hpfs_dirent *de,
+		       struct quad_buffer_head *qbh, int depth)
+{
+	struct dnode *dnode = qbh->data;
+	dnode_secno down = 0;
+	loff_t t;
+	if (de->first || de->last) {
+		hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno);
+		hpfs_brelse4(qbh);
+		return 1;
+	}
+	if (de->down) {
+		if ((down = de_down_pointer(de)) && depth) {
+			hpfs_lock_creation(i->i_sb);
+			if (hpfs_check_free_dnodes(i->i_sb, depth + 2)) {
+				hpfs_brelse4(qbh);
+				hpfs_unlock_creation(i->i_sb);
+				return 2;
+			}
+		}
+	}
+	i->i_version = ++event;
+	for_all_poss(i, hpfs_pos_del, (t = get_pos(dnode, de)) + 1, 1);
+	hpfs_delete_de(i->i_sb, dnode, de);
+	hpfs_mark_4buffers_dirty(qbh);
+	hpfs_brelse4(qbh);
+	if (down) {
+		dnode_secno a = move_to_top(i, down, dno);
+		for_all_poss(i, hpfs_pos_subst, 5, t);
+		if (depth) hpfs_unlock_creation(i->i_sb);
+		if (a) delete_empty_dnode(i, a);
+		return !a;
+	} else delete_empty_dnode(i, dno);
+	return 0;
+}
+
+void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
+		       int *n_subdirs, int *n_items)
+{
+	struct dnode *dnode;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	dnode_secno ptr, odno = 0;
+	int c1, c2 = 0;
+	int d1, d2 = 0;
+	go_down:
+	if (n_dnodes) (*n_dnodes)++;
+	if (s->s_hpfs_chk)
+		if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
+	ptr = 0;
+	go_up:
+	if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+	if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
+		hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
+	de = dnode_first_de(dnode);
+	if (ptr) while(1) {
+		if (de->down) if (de_down_pointer(de) == ptr) goto process_de;
+		if (de->last) {
+			hpfs_brelse4(&qbh);
+			hpfs_error(s, "hpfs_count_dnodes: pointer to dnode %08x not found in dnode %08x, got here from %08x",
+				ptr, dno, odno);
+			return;
+		}
+		de = de_next_de(de);
+	}
+	next_de:
+	if (de->down) {
+		odno = dno;
+		dno = de_down_pointer(de);
+		hpfs_brelse4(&qbh);
+		goto go_down;
+	}
+	process_de:
+	if (!de->first && !de->last && de->directory && n_subdirs) (*n_subdirs)++;
+	if (!de->first && !de->last && n_items) (*n_items)++;
+	if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de;
+	ptr = dno;
+	dno = dnode->up;
+	if (dnode->root_dnode) {
+		hpfs_brelse4(&qbh);
+		return;
+	}
+	hpfs_brelse4(&qbh);
+	if (s->s_hpfs_chk)
+		if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
+	odno = -1;
+	goto go_up;
+}
+
+static struct hpfs_dirent *map_nth_dirent(struct super_block *s, dnode_secno dno, int n,
+					  struct quad_buffer_head *qbh, struct dnode **dn)
+{
+	int i;
+	struct hpfs_dirent *de, *de_end;
+	struct dnode *dnode;
+	dnode = hpfs_map_dnode(s, dno, qbh);
+	if (!dnode) return NULL;
+	if (dn) *dn=dnode;
+	de = dnode_first_de(dnode);
+	de_end = dnode_end_de(dnode);
+	for (i = 1; de < de_end; i++, de = de_next_de(de)) {
+		if (i == n) {
+			return de;
+		}	
+		if (de->last) break;
+	}
+	hpfs_brelse4(qbh);
+	hpfs_error(s, "map_nth_dirent: n too high; dnode = %08x, requested %08x", dno, n);
+	return NULL;
+}
+
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno)
+{
+	struct quad_buffer_head qbh;
+	dnode_secno d = dno;
+	dnode_secno up = 0;
+	struct hpfs_dirent *de;
+	int c1, c2 = 0;
+
+	again:
+	if (s->s_hpfs_chk)
+		if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
+			return d;
+	if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
+	if (s->s_hpfs_chk)
+		if (up && ((struct dnode *)qbh.data)->up != up)
+			hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
+	if (!de->down) {
+		hpfs_brelse4(&qbh);
+		return d;
+	}
+	up = d;
+	d = de_down_pointer(de);
+	hpfs_brelse4(&qbh);
+	goto again;
+}
+
+struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
+				   struct quad_buffer_head *qbh)
+{
+	loff_t pos;
+	unsigned c;
+	dnode_secno dno;
+	struct hpfs_dirent *de, *d;
+	struct hpfs_dirent *up_de;
+	struct hpfs_dirent *end_up_de;
+	struct dnode *dnode;
+	struct dnode *up_dnode;
+	struct quad_buffer_head qbh0;
+
+	pos = *posp;
+	dno = pos >> 6 << 2;
+	pos &= 077;
+	if (!(de = map_nth_dirent(inode->i_sb, dno, pos, qbh, &dnode)))
+		goto bail;
+
+	/* Going to the next dirent */
+	if ((d = de_next_de(de)) < dnode_end_de(dnode)) {
+		if (!(++*posp & 077)) {
+			hpfs_error(inode->i_sb, "map_pos_dirent: pos crossed dnode boundary; pos = %08x", *posp);
+			goto bail;
+		}
+		/* We're going down the tree */
+		if (d->down) {
+			*posp = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, de_down_pointer(d)) << 4) + 1;
+		}
+	
+		return de;
+	}
+
+	/* Going up */
+	if (dnode->root_dnode) goto bail;
+
+	if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0)))
+		goto bail;
+
+	end_up_de = dnode_end_de(up_dnode);
+	c = 0;
+	for (up_de = dnode_first_de(up_dnode); up_de < end_up_de;
+	     up_de = de_next_de(up_de)) {
+		if (!(++c & 077)) hpfs_error(inode->i_sb,
+			"map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up);
+		if (up_de->down && de_down_pointer(up_de) == dno) {
+			*posp = ((loff_t) dnode->up << 4) + c;
+			hpfs_brelse4(&qbh0);
+			return de;
+		}
+	}
+	
+	hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x",
+		dno, dnode->up);
+	hpfs_brelse4(&qbh0);
+	
+	bail:
+	*posp = -2;
+	return de;
+}
+
+/* Find a dirent in tree */
+
+struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len,
+			       dnode_secno *dd, struct quad_buffer_head *qbh, int *depth)
+{
+	struct dnode *dnode;
+	struct hpfs_dirent *de;
+	struct hpfs_dirent *de_end;
+	int c1, c2 = 0;
+
+	if (depth) *depth = 0;
+
+	if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
+	again:
+	if (inode->i_sb->s_hpfs_chk)
+		if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
+	if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
+	
+	de_end = dnode_end_de(dnode);
+	for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de)) {
+		int t = hpfs_compare_names(inode->i_sb, name, len, de->name, de->namelen, de->last);
+		if (!t) {
+			if (dd) *dd = dno;
+			return de;
+		}
+		if (t < 0) {
+			if (de->down) {
+				dno = de_down_pointer(de);
+				hpfs_brelse4(qbh);
+				if (depth) (*depth)++;
+				goto again;
+			}
+		break;
+		}
+	}
+	hpfs_brelse4(qbh);
+	return NULL;
+}
+
+/*
+ * Remove empty directory. In normal cases it is only one dnode with two
+ * entries, but we must handle also such obscure cases when it's a tree
+ * of empty dnodes.
+ */
+
+void hpfs_remove_dtree(struct super_block *s, dnode_secno dno)
+{
+	struct quad_buffer_head qbh;
+	struct dnode *dnode;
+	struct hpfs_dirent *de;
+	dnode_secno d1, d2, rdno = dno;
+	while (1) {
+		if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+		de = dnode_first_de(dnode);
+		if (de->last) {
+			if (de->down) d1 = de_down_pointer(de);
+			else goto error;
+			hpfs_brelse4(&qbh);
+			hpfs_free_dnode(s, dno);
+			dno = d1;
+		} else break;
+	}
+	if (!de->first) goto error;
+	d1 = de->down ? de_down_pointer(de) : 0;
+	de = de_next_de(de);
+	if (!de->last) goto error;
+	d2 = de->down ? de_down_pointer(de) : 0;
+	hpfs_brelse4(&qbh);
+	hpfs_free_dnode(s, dno);
+	do {
+		while (d1) {
+			if (!(dnode = hpfs_map_dnode(s, dno = d1, &qbh))) return;
+			de = dnode_first_de(dnode);
+			if (!de->last) goto error;
+			d1 = de->down ? de_down_pointer(de) : 0;
+			hpfs_brelse4(&qbh);
+			hpfs_free_dnode(s, dno);
+		}
+		d1 = d2;
+		d2 = 0;
+	} while (d1);
+	return;
+	error:
+	hpfs_brelse4(&qbh);
+	hpfs_free_dnode(s, dno);
+	hpfs_error(s, "directory %08x is corrupted or not empty", rdno);
+}
+
+/* 
+ * Find dirent for specified fnode. Use truncated 15-char name in fnode as
+ * a help for searching.
+ */
+
+struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
+				     struct fnode *f, struct quad_buffer_head *qbh)
+{
+	char *name1;
+	char *name2;
+	int name1len, name2len;
+	struct dnode *d;
+	dnode_secno dno, downd;
+	struct fnode *upf;
+	struct buffer_head *bh;
+	struct hpfs_dirent *de, *de_end;
+	int c;
+	int c1, c2 = 0;
+	int d1, d2 = 0;
+	name1 = f->name;
+	if (!(name2 = kmalloc(256, GFP_KERNEL))) {
+		printk("HPFS: out of memory, can't map dirent\n");
+		return NULL;
+	}
+	if (f->len <= 15)
+		memcpy(name2, name1, name1len = name2len = f->len);
+	else {
+		memcpy(name2, name1, 15);
+		memset(name2 + 15, 0xff, 256 - 15);
+		/*name2[15] = 0xff;*/
+		name1len = 15; name2len = 256;
+	}
+	if (!(upf = hpfs_map_fnode(s, f->up, &bh))) {
+		kfree(name2);
+		return NULL;
+	}	
+	if (!upf->dirflag) {
+		brelse(bh);
+		hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up);
+		kfree(name2);
+		return NULL;
+	}
+	dno = upf->u.external[0].disk_secno;
+	brelse(bh);
+	go_down:
+	downd = 0;
+	go_up:
+	if (!(d = hpfs_map_dnode(s, dno, qbh))) {
+		kfree(name2);
+		return NULL;
+	}
+	de_end = dnode_end_de(d);
+	de = dnode_first_de(d);
+	if (downd) {
+		while (de < de_end) {
+			if (de->down) if (de_down_pointer(de) == downd) goto f;
+			de = de_next_de(de);
+		}
+		hpfs_error(s, "pointer to dnode %08x not found in dnode %08x", downd, dno);
+		hpfs_brelse4(qbh);
+		kfree(name2);
+		return NULL;
+	}
+	next_de:
+	if (de->fnode == fno) {
+		kfree(name2);
+		return de;
+	}
+	c = hpfs_compare_names(s, name1, name1len, de->name, de->namelen, de->last);
+	if (c < 0 && de->down) {
+		dno = de_down_pointer(de);
+		hpfs_brelse4(qbh);
+		if (s->s_hpfs_chk)
+			if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
+			kfree(name2);
+			return NULL;
+		}
+		goto go_down;
+	}
+	f:
+	if (de->fnode == fno) {
+		kfree(name2);
+		return de;
+	}
+	c = hpfs_compare_names(s, name2, name2len, de->name, de->namelen, de->last);
+	if (c < 0 && !de->last) goto not_found;
+	if ((de = de_next_de(de)) < de_end) goto next_de;
+	if (d->root_dnode) goto not_found;
+	downd = dno;
+	dno = d->up;
+	hpfs_brelse4(qbh);
+	if (s->s_hpfs_chk)
+		if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
+			kfree(name2);
+			return NULL;
+		}
+	goto go_up;
+	not_found:
+	hpfs_brelse4(qbh);
+	hpfs_error(s, "dirent for fnode %08x not found", fno);
+	kfree(name2);
+	return NULL;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/ea.c linux/fs/hpfs/ea.c
--- v2.3.1/linux/fs/hpfs/ea.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/ea.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,304 @@
+/*
+ *  linux/fs/hpfs/ea.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  handling extended attributes
+ */
+
+#include "hpfs_fn.h"
+
+/* Remove external extended attributes. ano specifies wheter a is a 
+   direct sector where eas start or an anode */
+
+void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+	unsigned pos = 0;
+	while (pos < len) {
+		char ex[4 + 255 + 1 + 8];
+		struct extended_attribute *ea = (struct extended_attribute *)ex;
+		if (pos + 4 > len) {
+			hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+				ano ? "anode" : "sectors", a, len);
+			return;
+		}
+		if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+		if (ea->indirect) {
+			if (ea->valuelen != 8) {
+				hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
+					ano ? "anode" : "sectors", a, pos);
+				return;
+			}
+			if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
+				return;
+			hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+		}
+		pos += ea->namelen + ea->valuelen + 5;
+	}
+	if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
+	else {
+		struct buffer_head *bh;
+		struct anode *anode;
+		if ((anode = hpfs_map_anode(s, a, &bh))) {
+			hpfs_remove_btree(s, &anode->btree);
+			brelse(bh);
+			hpfs_free_sectors(s, a, 1);
+		}
+	}
+}
+
+static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
+{
+	char *ret;
+	if (!(ret = kmalloc(size + 1, GFP_KERNEL))) {
+		printk("HPFS: out of memory for EA\n");
+		return NULL;
+	}
+	if (hpfs_ea_read(s, a, ano, 0, size, ret)) {
+		kfree(ret);
+		return NULL;
+	}
+	ret[size] = 0;
+	return ret;
+}
+
+static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
+			    int size)
+{
+	hpfs_ea_write(s, a, ano, 0, size, data);
+}
+
+/* Read an extended attribute named 'key' */
+
+char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size)
+{
+	char *ret;
+	unsigned pos;
+	int ano, len;
+	secno a;
+	struct extended_attribute *ea;
+	struct extended_attribute *ea_end = fnode_end_ea(fnode);
+	for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+		if (!strcmp(ea->name, key)) {
+			if (ea->indirect)
+				return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+			if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+				printk("HPFS: out of memory for EA\n");
+				return NULL;
+			}
+			memcpy(ret, ea_data(ea), ea->valuelen);
+			ret[ea->valuelen] = 0;
+			return ret;
+		}
+	a = fnode->ea_secno;
+	len = fnode->ea_size_l;
+	ano = fnode->ea_anode;
+	pos = 0;
+	while (pos < len) {
+		char ex[4 + 255 + 1 + 8];
+		ea = (struct extended_attribute *)ex;
+		if (pos + 4 > len) {
+			hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+				ano ? "anode" : "sectors", a, len);
+			return NULL;
+		}
+		if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
+		if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+			return NULL;
+		if (!strcmp(ea->name, key)) {
+			if (ea->indirect)
+				return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+			if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+				printk("HPFS: out of memory for EA\n");
+				return NULL;
+			}
+			if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
+				kfree(ret);
+				return NULL;
+			}
+			ret[ea->valuelen] = 0;
+			return ret;
+		}
+		pos += ea->namelen + ea->valuelen + 5;
+	}
+	return NULL;
+}
+
+/* 
+ * Update or create extended attribute 'key' with value 'data'. Note that
+ * when this ea exists, it MUST have the same size as size of data.
+ * This driver can't change sizes of eas ('cause I just don't need it).
+ */
+
+void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
+{
+	fnode_secno fno = inode->i_ino;
+	struct super_block *s = inode->i_sb;
+	unsigned pos;
+	int ano, len;
+	secno a;
+	unsigned char h[4];
+	struct extended_attribute *ea;
+	struct extended_attribute *ea_end = fnode_end_ea(fnode);
+	for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+		if (!strcmp(ea->name, key)) {
+			if (ea->indirect) {
+				if (ea_len(ea) == size)
+					set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+			} else if (ea->valuelen == size) {
+				memcpy(ea_data(ea), data, size);
+			}
+			return;
+		}
+	a = fnode->ea_secno;
+	len = fnode->ea_size_l;
+	ano = fnode->ea_anode;
+	pos = 0;
+	while (pos < len) {
+		char ex[4 + 255 + 1 + 8];
+		ea = (struct extended_attribute *)ex;
+		if (pos + 4 > len) {
+			hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+				ano ? "anode" : "sectors", a, len);
+			return;
+		}
+		if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+		if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+			return;
+		if (!strcmp(ea->name, key)) {
+			if (ea->indirect) {
+				if (ea_len(ea) == size)
+					set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+			}
+			else {
+				if (ea->valuelen == size)
+					hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
+			}
+			return;
+		}
+		pos += ea->namelen + ea->valuelen + 5;
+	}
+	if (!fnode->ea_size_s) {
+		/*if (fnode->ea_size_s) {
+			hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
+				inode->i_ino, fnode->ea_size_s);
+			return;
+		}*/
+		fnode->ea_offs = 0xc4;
+	}
+	if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->ea_size_s > 0x200) {
+		hpfs_error(s, "fnode %08x: ea_offs == %03x, ea_size_s == %03x",
+			inode->i_ino, fnode->ea_offs, fnode->ea_size_s);
+		return;
+	}
+	if ((fnode->ea_size_s || !fnode->ea_size_l) &&
+	     fnode->ea_offs + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
+		/* I'm not sure ... maybe we overwrite ACL here. I have no info
+		   on it right now :-( */
+		ea = fnode_end_ea(fnode);
+		*(char *)ea = 0;
+		ea->namelen = strlen(key);
+		ea->valuelen = size;
+		strcpy(ea->name, key);
+		memcpy(ea_data(ea), data, size);
+		fnode->ea_size_s += strlen(key) + size + 5;
+		goto ret;
+	}
+	/* Most the code here is 99.9993422% unused. I hope there are no bugs.
+	   But what .. HPFS.IFS has also bugs in ea management. */
+	if (fnode->ea_size_s && !fnode->ea_size_l) {
+		secno n;
+		struct buffer_head *bh;
+		char *data;
+		if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
+		if (!(data = hpfs_get_sector(s, n, &bh))) {
+			hpfs_free_sectors(s, n, 1);
+			return;
+		}
+		memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
+		fnode->ea_size_l = fnode->ea_size_s;
+		fnode->ea_size_s = 0;
+		fnode->ea_secno = n;
+		fnode->ea_anode = 0;
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+	}
+	pos = fnode->ea_size_l + 5 + strlen(key) + size;
+	len = (fnode->ea_size_l + 511) >> 9;
+	if (pos >= 30000) goto bail;
+	while (((pos + 511) >> 9) > len) {
+		if (!len) {
+			if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
+				goto bail;
+			fnode->ea_anode = 0;
+			len++;
+		}
+		else if (!fnode->ea_anode)
+			if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) len++;
+			else {
+				/* Aargh... don't know how to create ea anodes :-( */
+				/*struct buffer_head *bh;
+				struct anode *anode;
+				anode_secno a_s;
+				if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
+					goto bail;
+				anode->up = fno;
+				anode->btree.fnode_parent = 1;
+				anode->btree.n_free_nodes--;
+				anode->btree.n_used_nodes++;
+				anode->btree.first_free += 12;
+				anode->u.external[0].disk_secno = fnode->ea_secno;
+				anode->u.external[0].file_secno = 0;
+				anode->u.external[0].length = len;
+				mark_buffer_dirty(bh, 1);
+				brelse(bh);
+				fnode->ea_anode = 1;
+				fnode->ea_secno = a_s;*/
+				secno new_sec;
+				int i;
+				if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
+					goto bail;
+				for (i = 0; i < len; i++) {
+					struct buffer_head *bh1, *bh2;
+					void *b1, *b2;
+					if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
+						hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+						goto bail;
+					}
+					if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) {
+						brelse(bh1);
+						hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+						goto bail;
+					}
+					memcpy(b2, b1, 512);
+					brelse(bh1);
+					mark_buffer_dirty(bh2, 1);
+					brelse(bh2);
+				}
+				hpfs_free_sectors(s, fnode->ea_secno, len);
+				fnode->ea_secno = new_sec;
+				len = (pos + 511) >> 9;
+			}
+		if (fnode->ea_anode)
+			if (hpfs_add_sector_to_btree(s, fnode->ea_secno, 0, len) != -1)
+				len++;
+			else goto bail;
+	}
+	h[0] = 0;
+	h[1] = strlen(key);
+	h[2] = size & 0xff;
+	h[3] = size >> 8;
+	if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
+	if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
+	if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
+	fnode->ea_size_l = pos;
+	ret:
+	inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+	return;
+	bail:
+	if (fnode->ea_secno)
+		if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
+		else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
+	else fnode->ea_secno = fnode->ea_size_l = 0;
+}
+	
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/file.c linux/fs/hpfs/file.c
--- v2.3.1/linux/fs/hpfs/file.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/file.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,195 @@
+/*
+ *  linux/fs/hpfs/file.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  file VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+int hpfs_open(struct inode *i, struct file *f)
+{
+	hpfs_lock_inode(i);
+	hpfs_unlock_inode(i); /* make sure nobody is deleting the file */
+	if (!i->i_nlink) return -ENOENT;
+	return 0;
+}
+
+int hpfs_file_release(struct inode *inode, struct file *file)
+{
+	hpfs_write_if_changed(inode);
+	return 0;
+}
+
+int hpfs_file_fsync(struct file *file, struct dentry *dentry)
+{
+	/*return file_fsync(file, dentry);*/
+	return 0; /* Don't fsync :-) */
+}
+
+/*
+ * generic_file_read often calls bmap with non-existing sector,
+ * so we must ignore such errors.
+ */
+
+secno hpfs_bmap(struct inode *inode, unsigned file_secno)
+{
+	unsigned n, disk_secno;
+	struct fnode *fnode;
+	struct buffer_head *bh;
+	if (((inode->i_size + 511) >> 9) <= file_secno) return 0;
+	n = file_secno - inode->i_hpfs_file_sec;
+	if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+	if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
+	disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
+	if (disk_secno == -1) return 0;
+	if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
+	return disk_secno;
+}
+
+void hpfs_truncate(struct inode *i)
+{
+	if (IS_IMMUTABLE(i)) return /*-EPERM*/;
+	i->i_hpfs_n_secs = 0;
+	hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
+	i->i_blocks = 1 + ((i->i_size + 511) >> 9);
+	/*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+	hpfs_write_inode(i);
+}
+
+ssize_t hpfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	int i,j;
+	int a = generic_file_read(filp, buf, count, ppos);
+	if (inode->i_hpfs_conv != CONV_TEXT || a < 0) {
+		return a;
+	}	
+	for (i = 0, j = 0; i < a; i++) {
+		char c;
+		int error;
+		if ((error = get_user(c, buf + i))) return error;
+		if (c != '\r') {
+			if (i != j) put_user(c, buf + j);
+			j++;
+		}
+	}
+	return j;
+}
+
+ssize_t hpfs_file_write(struct file *filp, const char *buf, size_t count,
+			loff_t *ppos)
+{
+	struct inode *i = filp->f_dentry->d_inode;
+	int carry, error = 0;
+	const char *start = buf;
+	if (!i) return -EINVAL;
+	if (!S_ISREG(i->i_mode)) return -EINVAL;
+	if (IS_IMMUTABLE(i)) return -EPERM;
+	if (filp->f_flags & O_APPEND) *ppos = i->i_size;
+	if (count <= 0) return 0;
+	if ((unsigned)(*ppos+count) >= 0x80000000U || (unsigned)count >= 0x80000000U) return -EFBIG;
+	carry = 0;
+	while (count || carry) {
+		int ii, add = 0;
+		secno sec = 0; /* Go away, uninitialized variable warning */
+		int offset, size, written;
+		char ch;
+		struct buffer_head *bh;
+		char *data;
+		offset = *ppos & 0x1ff;
+		size = count > 0x200 - offset ? 0x200 - offset : count;
+		if ((*ppos >> 9) < ((i->i_size + 0x1ff) >> 9)) {
+			i->i_hpfs_n_secs = 0;
+			if (!(sec = hpfs_bmap(i, *ppos >> 9))) {
+				hpfs_error(i->i_sb, "bmap failed, file %08x, fsec %08x",
+					i->i_ino, *ppos >> 9);
+				error =- EFSERROR;
+				break;
+			}
+		} else for (ii = (i->i_size + 0x1ff) >> 9, add = 1; ii <= *ppos >> 9; ii++) {
+			if ((sec = hpfs_add_sector_to_btree(i->i_sb, i->i_ino, 1, ii)) == -1) {
+				hpfs_truncate(i);
+				return -ENOSPC;
+			}
+			if (*ppos != i->i_size)
+				if ((data = hpfs_get_sector(i->i_sb, sec, &bh))) {
+					memset(data, 0, 512);
+					mark_buffer_dirty(bh, 0);
+					brelse(bh);
+				}
+			i->i_size = 0x200 * ii + 1;
+			i->i_blocks++;
+			/*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+			if (i->i_sb->s_hpfs_chk >= 2) {
+				secno bsec;
+				bsec = hpfs_bmap(i, ii);
+				if (sec != bsec) {
+					hpfs_error(i->i_sb, "sec == %08x, bmap returns %08x", sec, bsec);
+					error = -EFSERROR;
+					break;
+				}
+			}	
+			PRINTK(("file_write: added %08x\n", sec));
+		}
+		if (!sec || sec == 15) {
+			hpfs_error(i->i_sb, "bmap returned empty sector");
+			error = -EFSERROR;
+			break;
+		}
+		if (i->i_sb->s_hpfs_chk)
+			if (hpfs_chk_sectors(i->i_sb, sec, 1, "data")) {
+				error = -EFSERROR;
+				break;
+			}
+		if ((!offset && size == 0x200) || add)
+			data = hpfs_get_sector(i->i_sb, sec, &bh);
+		else data = hpfs_map_sector(i->i_sb, sec, &bh, 0);
+		if (!data) {
+			error = -EIO;
+			break;
+		}
+		if (i->i_hpfs_conv != CONV_TEXT) {
+			memcpy_fromfs(data + offset, buf, written = size);
+			buf += size;
+		} else {
+			int left;
+			char *to;
+			/* LF->CR/LF conversion, stolen from fat fs */
+			written = left = 0x200 - offset;
+			to = (char *) bh->b_data + (*ppos & 0x1ff);
+			if (carry) {
+				*to++ = '\n';
+				left--;
+				carry = 0;
+			}
+			for (size = 0; size < count && left; size++) {
+				if ((error = get_user(ch, buf++))) break;
+				if (ch == '\n') {
+					*to++ = '\r';
+					left--;
+				}
+				if (!left) carry = 1;
+				else {
+					*to++ = ch;
+					left--;
+				}
+			}
+			written -= left;
+		}
+		update_vm_cache(i, *ppos, bh->b_data + (*ppos & 0x1ff), written);
+		*ppos += written;
+		if (*ppos > i->i_size) {
+			i->i_size = *ppos;
+			/*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+		}
+		mark_buffer_dirty(bh, 0);
+		brelse(bh);
+		count -= size;
+	}
+	if (start == buf) return error;
+	i->i_mtime = CURRENT_TIME;
+	/*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+	return buf - start;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/hpfs.h linux/fs/hpfs/hpfs.h
--- v2.3.1/linux/fs/hpfs/hpfs.h	Wed Jun 24 14:30:10 1998
+++ linux/fs/hpfs/hpfs.h	Thu May 13 23:48:20 1999
@@ -1,3 +1,11 @@
+/*
+ *  linux/fs/hpfs/hpfs.h
+ *
+ *  HPFS structures by Chris Smith, 1993
+ *
+ *  a little bit modified by Mikulas Patocka, 1998-1999
+ */
+
 /* The paper
 
      Duncan, Roy
@@ -24,6 +32,8 @@
 /* The boot block is very like a FAT boot block, except that the
    29h signature byte is 28h instead, and the ID string is "HPFS". */
 
+#define BB_MAGIC 0xaa55
+
 struct hpfs_boot_block
 {
   unsigned char jmp[3];
@@ -61,7 +71,12 @@
 {
   unsigned magic;			/* f995 e849 */
   unsigned magic1;			/* fa53 e9c5, more magic? */
-  unsigned huh202;			/* ?? 202 = N. of B. in 1.00390625 S.*/
+  /*unsigned huh202;*/			/* ?? 202 = N. of B. in 1.00390625 S.*/
+  char version;				/* version of a filesystem  usually 2 */
+  char funcversion;			/* functional version - oldest version
+  					   of filesystem that can understand
+					   this disk */
+  unsigned short int zero;		/* 0 */
   fnode_secno root;			/* fnode of root directory */
   secno n_sectors;			/* size of filesystem */
   unsigned n_badblocks;			/* number of bad blocks */
@@ -70,14 +85,14 @@
   secno badblocks;			/* bad block list */
   unsigned zero3;			/* 0 */
   time_t last_chkdsk;			/* date last checked, 0 if never */
-  unsigned zero4;			/* 0 */
+  /*unsigned zero4;*/			/* 0 */
+  time_t last_optimize;			/* date last optimized, 0 if never */
   secno n_dir_band;			/* number of sectors in dir band */
   secno dir_band_start;			/* first sector in dir band */
   secno dir_band_end;			/* last sector in dir band */
   secno dir_band_bitmap;		/* free space map, 1 dnode per bit */
-  unsigned zero5[8];			/* 0 */
-  secno scratch_dnodes;			/* ?? 8 preallocated sectors near dir
-					   band, 4-aligned. */
+  char volume_name[32];			/* not used */
+  secno user_id_table;			/* 8 preallocated sectors - user id */
   unsigned zero6[103];			/* 0 */
 };
 
@@ -94,9 +109,23 @@
   unsigned magic1;			/* fa52 29c5, more magic? */
 
   unsigned dirty: 1;			/* 0 clean, 1 "improperly stopped" */
-  unsigned flag1234: 4;			/* unknown flags */
+  /*unsigned flag1234: 4;*/		/* unknown flags */
+  unsigned sparedir_used: 1;		/* spare dirblks used */
+  unsigned hotfixes_used: 1;		/* hotfixes used */
+  unsigned bad_sector: 1;		/* bad sector, corrupted disk (???) */
+  unsigned bad_bitmap: 1;		/* bad bitmap */
   unsigned fast: 1;			/* partition was fast formatted */
-  unsigned flag6to31: 26;		/* unknown flags */
+  unsigned old_wrote: 1;		/* old version wrote to partion */
+  unsigned old_wrote_1: 1;		/* old version wrote to partion (?) */
+  unsigned install_dasd_limits: 1;	/* HPFS386 flags */
+  unsigned resynch_dasd_limits: 1;
+  unsigned dasd_limits_operational: 1;
+  unsigned multimedia_active: 1;
+  unsigned dce_acls_active: 1;
+  unsigned dasd_limits_dirty: 1;
+  unsigned flag67: 2;
+  unsigned char mm_contlgulty;
+  unsigned char unused;
 
   secno hotfix_map;			/* info about remapped bad sectors */
   unsigned n_spares_used;		/* number of hotfixes */
@@ -106,10 +135,14 @@
 					   follows in this block*/
   secno code_page_dir;			/* code page directory block */
   unsigned n_code_pages;		/* number of code pages */
-  unsigned large_numbers[2];		/* ?? */
-  unsigned zero1[15];
-  dnode_secno spare_dnodes[20];		/* emergency free dnode list */
-  unsigned zero2[81];			/* room for more? */
+  /*unsigned large_numbers[2];*/	/* ?? */
+  unsigned super_crc;			/* on HPFS386 and LAN Server this is
+  					   checksum of superblock, on normal
+					   OS/2 unused */
+  unsigned spare_crc;			/* on HPFS386 checksum of spareblock */
+  unsigned zero1[15];			/* unused */
+  dnode_secno spare_dnodes[100];	/* emergency free dnode list */
+  unsigned zero2[1];			/* room for more? */
 };
 
 /* The bad block list is 4 sectors long.  The first word must be zero,
@@ -221,7 +254,8 @@
   unsigned magic;			/* 77e4 0aae */
   unsigned first_free;			/* offset from start of dnode to
 					   first free dir entry */
-  unsigned increment_me;		/* some kind of activity counter?
+  unsigned root_dnode:1;		/* Is it root dnode? */
+  unsigned increment_me:31;		/* some kind of activity counter?
 					   Neither HPFS.IFS nor CHKDSK cares
 					   if you change this word */
   secno up;				/* (root dnode) directory's fnode
@@ -233,12 +267,12 @@
 struct hpfs_dirent {
   unsigned short length;		/* offset to next dirent */
   unsigned first: 1;			/* set on phony ^A^A (".") entry */
-  unsigned flag1: 1;
+  unsigned has_acl: 1;
   unsigned down: 1;			/* down pointer present (after name) */
   unsigned last: 1;			/* set on phony \377 entry */
-  unsigned flag4: 1;
-  unsigned flag5: 1;
-  unsigned flag6: 1;
+  unsigned has_ea: 1;			/* entry has EA */
+  unsigned has_xtd_perm: 1;		/* has extended perm list (???) */
+  unsigned has_explicit_acl: 1;
   unsigned has_needea: 1;		/* ?? some EA has NEEDEA set
 					   I have no idea why this is
 					   interesting in a dir entry */
@@ -256,7 +290,8 @@
   time_t read_date;			/* atime */
   time_t creation_date;			/* ctime */
   unsigned ea_size;			/* total EA length, bytes */
-  unsigned char zero1;
+  unsigned char no_of_acls : 3;		/* number of ACL's */
+  unsigned char reserver : 5;
   unsigned char ix;			/* code page index (of filename), see
 					   struct code_page_data */
   unsigned char namelen, name[1];	/* file name */
@@ -265,34 +300,6 @@
 			  precedes next dirent, which is on a word boundary. */
 };
 
-/* The b-tree down pointer from a dir entry */
-
-static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
-{
-  return *(dnode_secno *) ((void *) de + de->length - 4);
-}
-
-/* The first dir entry in a dnode */
-
-static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
-{
-  return (void *) dnode->dirent;
-}
-
-/* The end+1 of the dir entries */
-
-static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
-{
-  return (void *) dnode + dnode->first_free;
-}
-
-/* The dir entry after dir entry de */
-
-static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
-{
-  return (void *) de + de->length;
-}
-
 
 /* B+ tree: allocation info in fnodes and anodes */
 
@@ -320,7 +327,7 @@
 
 struct bplus_header
 {
-  unsigned flag0: 1;
+  unsigned hbff: 1;	/* high bit of first free entry offset */
   unsigned flag1: 1;
   unsigned flag2: 1;
   unsigned flag3: 1;
@@ -332,7 +339,7 @@
 					   may be a chkdsk glitch or may mean
 					   this bit is irrelevant in fnodes,
 					   or this interpretation is all wet */
-  unsigned flag6: 1;
+  unsigned binary_search: 1;		/* suggest binary search (unused) */
   unsigned internal: 1;			/* 1 -> (internal) tree of anodes
 					   0 -> (leaf) list of extents */
   unsigned char fill[3];
@@ -348,21 +355,26 @@
   } u;
 };
 
-/* fnode: root of allocation b+ tree, and EAs */
+/* fnode: root of allocation b+ tree, and EA's */
 
 /* Every file and every directory has one fnode, pointed to by the directory
-   entry and pointing to the file's sectors or directory's root dnode.  EAs
-   are also stored here, and there are said to be ACLs somewhere here too. */
+   entry and pointing to the file's sectors or directory's root dnode.  EA's
+   are also stored here, and there are said to be ACL's somewhere here too. */
 
 #define FNODE_MAGIC 0xf7e40aae
 
 struct fnode
 {
   unsigned magic;			/* f7e4 0aae */
-  unsigned zero1[2];
+  unsigned zero1[2];			/* read history */
   unsigned char len, name[15];		/* true length, truncated name */
   fnode_secno up;			/* pointer to file's directory fnode */
-  unsigned zero2[3];
+  /*unsigned zero2[3];*/
+  secno acl_size_l;
+  secno acl_secno;
+  unsigned short acl_size_s;
+  char acl_anode;
+  char zero2;				/* history bit count */
   unsigned ea_size_l;			/* length of disk-resident ea's */
   secno ea_secno;			/* first sector of disk-resident ea's*/
   unsigned short ea_size_s;		/* length of fnode-resident ea's */
@@ -392,12 +404,16 @@
   } u;
 
   unsigned file_size;			/* file length, bytes */
-  unsigned n_needea;			/* number of EAs with NEEDEA set */
-  unsigned zero4[4];
+  unsigned n_needea;			/* number of EA's with NEEDEA set */
+  char user_id[16];			/* unused */
   unsigned ea_offs;			/* offset from start of fnode
 					   to first fnode-resident ea */
-  unsigned zero5[2];
-  unsigned char ea[316];		/* zero or more EAs, packed together
+  char dasd_limit_treshhold;
+  char dasd_limit_delta;
+  unsigned dasd_limit;
+  unsigned dasd_usage;
+  /*unsigned zero5[2];*/
+  unsigned char ea[316];		/* zero or more EA's, packed together
 					   with no alignment padding.
 					   (Do not use this name, get here
 					   via fnode + ea_offs. I think.) */
@@ -453,8 +469,9 @@
   unsigned needea: 1;			/* required ea */
   unsigned char namelen;		/* length of name, bytes */
   unsigned short valuelen;		/* length of value, bytes */
+  unsigned char name[0];
   /*
-    unsigned char name[namelen];	ASCII attrib name
+    unsigned char name[namelen];	ascii attrib name
     unsigned char nul;			terminating '\0', not counted
     unsigned char value[valuelen];	value, arbitrary
       if this.indirect, valuelen is 8 and the value is
@@ -464,34 +481,6 @@
         which points to the value.
   */
 };
-
-static inline unsigned char *ea_name (struct extended_attribute *ea)
-{
-  return (void *) ea + sizeof *ea;
-}
-
-static inline unsigned char *ea_value (struct extended_attribute *ea)
-{
-  return (void *) ea + sizeof *ea + ea->namelen + 1;
-}
-
-static inline struct extended_attribute *
-    ea_next_ea (struct extended_attribute *ea)
-{
-  return (void *) ea + sizeof *ea + ea->namelen + 1 + ea->valuelen;
-}
-
-static inline unsigned ea_indirect_length (struct extended_attribute *ea)
-{
-  unsigned *v = (void *) ea_value (ea);
-  return v[0];
-}
-
-static inline secno ea_indirect_secno (struct extended_attribute *ea)
-{
-  unsigned *v = (void *) ea_value (ea);
-  return v[1];
-}
 
 /*
    Local Variables:
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/hpfs_caps.c linux/fs/hpfs/hpfs_caps.c
--- v2.3.1/linux/fs/hpfs/hpfs_caps.c	Wed Nov  8 02:34:15 1995
+++ linux/fs/hpfs/hpfs_caps.c	Wed Dec 31 16:00:00 1969
@@ -1,171 +0,0 @@
-/* Capitalization rules for HPFS */
-
-/* In OS/2, HPFS filenames preserve upper and lower case letter distinctions
-   but filename matching ignores case.  That is, creating a file "Foo"
-   actually creates a file named "Foo" which can be looked up as "Foo",
-   "foo", or "FOO", among other possibilities.
-
-   Also, HPFS is internationalized -- a table giving the uppercase
-   equivalent of every character is stored in the filesystem, so that
-   any national character set may be used.  If several different
-   national character sets are in use, several tables are stored
-   in the filesystem.
-
-   It would be perfectly reasonable for Linux HPFS to act as a Unix
-   filesystem and match "Foo" only if asked for "Foo" exactly.  But
-   the sort order of HPFS directories is case-insensitive, so Linux
-   still has to know the capitalization rules used by OS/2.  Because
-   of this, it turns out to be more natural for us to be case-insensitive
-   than not.
-
-   Currently the standard character set used by Linux is Latin-1.
-   Work is underway to permit people to use UTF-8 instead, therefore
-   all code that depends on the character set is segregated here.
-
-   (It would be wonderful if Linux HPFS could be independent of what
-   character set is in use on the Linux side, but because of the
-   necessary case folding this is impossible.)
-
-   There is a map from Latin-1 into code page 850 for every printing
-   character in Latin-1.  The NLS documentation of OS/2 shows that
-   everybody has 850 available unless they don't have Western latin
-   chars available at all (so fitting them to Linux without Unicode
-   is a doomed exercise).
-
-   It is not clear exactly how HPFS.IFS handles the situation when
-   multiple code pages are in use.  Experiments show that
-
-   - tables on the disk give uppercasing rules for the installed code pages
-
-   - each directory entry is tagged with what code page was current
-     when that name was created
-
-   - doing just CHCP, without changing what's on the disk in any way,
-     can change what DIR reports, and what name a case-folded match
-     will match.
-
-   This means, I think, that HPFS.IFS operates in the current code
-   page, without regard to the uppercasing information recorded in
-   the tables on the disk.  It does record the uppercasing rules
-   it used, perhaps for CHKDSK, but it does not appear to use them
-   itself.
-
-   So: Linux, a Latin-1 system, will operate in code page 850.  We
-   recode between 850 and Latin-1 when dealing with the names actually
-   on the disk.  We don't use the uppercasing tables either.
-
-   In a hypothetical UTF-8 implementation, one reasonable way to
-   proceed that matches OS/2 (for least surprise) is: do case
-   translation in UTF-8, and recode to/from one of the code pages
-   available on the mounted filesystem.  Reject as invalid any name
-   containing chars that can't be represented on disk by one of the
-   code pages OS/2 is using.  Recoding from on-disk names to UTF-8
-   could use the code page tags, though this is not what OS/2 does. */
-
-
-static const unsigned char tb_cp850_to_latin1[128] =
-{
-  199, 252, 233, 226, 228, 224, 229, 231,
-  234, 235, 232, 239, 238, 236, 196, 197,
-  201, 230, 198, 244, 246, 242, 251, 249,
-  255, 214, 220, 248, 163, 216, 215, 159,
-  225, 237, 243, 250, 241, 209, 170, 186,
-  191, 174, 172, 189, 188, 161, 171, 187,
-  155, 156, 157, 144, 151, 193, 194, 192,
-  169, 135, 128, 131, 133, 162, 165, 147,
-  148, 153, 152, 150, 145, 154, 227, 195,
-  132, 130, 137, 136, 134, 129, 138, 164,
-  240, 208, 202, 203, 200, 158, 205, 206,
-  207, 149, 146, 141, 140, 166, 204, 139,
-  211, 223, 212, 210, 245, 213, 181, 254,
-  222, 218, 219, 217, 253, 221, 175, 180,
-  173, 177, 143, 190, 182, 167, 247, 184,
-  176, 168, 183, 185, 179, 178, 142, 160,
-};
-
-#if 0
-static const unsigned char tb_latin1_to_cp850[128] =
-{
-  186, 205, 201, 187, 200, 188, 204, 185,
-  203, 202, 206, 223, 220, 219, 254, 242,
-  179, 196, 218, 191, 192, 217, 195, 180,
-  194, 193, 197, 176, 177, 178, 213, 159,
-  255, 173, 189, 156, 207, 190, 221, 245,
-  249, 184, 166, 174, 170, 240, 169, 238,
-  248, 241, 253, 252, 239, 230, 244, 250,
-  247, 251, 167, 175, 172, 171, 243, 168,
-  183, 181, 182, 199, 142, 143, 146, 128,
-  212, 144, 210, 211, 222, 214, 215, 216,
-  209, 165, 227, 224, 226, 229, 153, 158,
-  157, 235, 233, 234, 154, 237, 232, 225,
-  133, 160, 131, 198, 132, 134, 145, 135,
-  138, 130, 136, 137, 141, 161, 140, 139,
-  208, 164, 149, 162, 147, 228, 148, 246,
-  155, 151, 163, 150, 129, 236, 231, 152,
-};
-#endif
-
-#define A_GRAVE 0300
-#define THORN	0336   
-#define MULTIPLY 0327
-#define a_grave 0340
-#define thorn	0376
-#define divide	0367
-
-static inline unsigned latin1_upcase (unsigned c)
-{
-  if (c - 'a' <= 'z' - 'a'
-      || (c - a_grave <= thorn - a_grave
-	  && c != divide))
-    return c - 'a' + 'A';
-  else
-    return c;
-}
-
-static inline unsigned latin1_downcase (unsigned c)
-{
-  if (c - 'A' <= 'Z' - 'A'
-      || (c - A_GRAVE <= THORN - A_GRAVE
-	  && c != MULTIPLY))
-    return c + 'a' - 'A';
-  else
-    return c;
-}
-
-#if 0
-static inline unsigned latin1_to_cp850 (unsigned c)
-{
-  if ((signed) c - 128 >= 0)
-    return tb_latin1_to_cp850[c - 128];
-  else
-    return c;
-}
-#endif
-
-static inline unsigned cp850_to_latin1 (unsigned c)
-{
-  if ((signed) c - 128 >= 0)
-    return tb_cp850_to_latin1[c - 128];
-  else
-    return c;
-}
-
-unsigned hpfs_char_to_upper_linux (unsigned c)
-{
-  return latin1_upcase (cp850_to_latin1 (c));
-}
-
-unsigned linux_char_to_upper_linux (unsigned c)
-{
-  return latin1_upcase (c);
-}
-
-unsigned hpfs_char_to_lower_linux (unsigned c)
-{
-  return latin1_downcase (cp850_to_latin1 (c));
-}
-
-unsigned hpfs_char_to_linux (unsigned c)
-{
-  return cp850_to_latin1 (c);
-}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/hpfs_caps.h linux/fs/hpfs/hpfs_caps.h
--- v2.3.1/linux/fs/hpfs/hpfs_caps.h	Sun Feb  5 04:44:32 1995
+++ linux/fs/hpfs/hpfs_caps.h	Wed Dec 31 16:00:00 1969
@@ -1,4 +0,0 @@
-unsigned hpfs_char_to_linux (unsigned c);
-unsigned hpfs_char_to_lower_linux (unsigned c);
-unsigned hpfs_char_to_upper_linux (unsigned c);
-unsigned linux_char_to_upper_linux (unsigned c);
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/hpfs_fn.h linux/fs/hpfs/hpfs_fn.h
--- v2.3.1/linux/fs/hpfs/hpfs_fn.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/hpfs_fn.h	Thu May 13 23:48:20 1999
@@ -0,0 +1,314 @@
+/*
+ *  linux/fs/hpfs/hpfs_fn.h
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  function headers
+ */
+
+//#define DBG
+//#define DEBUG_LOCKS
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <stdarg.h>
+
+#include "hpfs.h"
+
+#define memcpy_tofs memcpy
+#define memcpy_fromfs memcpy
+
+#define EIOERROR  EIO
+#define EFSERROR  EPERM
+#define EMEMERROR ENOMEM
+
+#define ANODE_ALLOC_FWD	512
+#define FNODE_ALLOC_FWD	0
+#define ALLOC_FWD_MIN	16
+#define ALLOC_FWD_MAX	512
+#define ALLOC_M		1
+#define FNODE_RD_AHEAD	16
+#define ANODE_RD_AHEAD	16
+#define DNODE_RD_AHEAD	4
+
+#define CHKCOND(x,y) if (!(x)) printk y
+
+#ifdef DBG
+#define PRINTK(x) printk x
+#else
+#undef PRINTK
+#define PRINTK(x)
+#endif
+
+typedef void nonconst; /* What this is for ? */
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+extern inline time_t local_to_gmt(struct super_block *s, time_t t)
+{
+	extern struct timezone sys_tz;
+	return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0) +s->s_hpfs_timeshift;
+}
+
+extern inline time_t gmt_to_local(struct super_block *s, time_t t)
+{
+	extern struct timezone sys_tz;
+	return t - sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0) -s->s_hpfs_timeshift;
+}
+
+/*
+ * conv= options
+ */
+
+#define CONV_BINARY 0			/* no conversion */
+#define CONV_TEXT 1			/* crlf->newline */
+#define CONV_AUTO 2			/* decide based on file contents */
+
+/* Four 512-byte buffers and the 2k block obtained by concatenating them */
+
+struct quad_buffer_head {
+	struct buffer_head *bh[4];
+	void *data;
+};
+
+/* The b-tree down pointer from a dir entry */
+
+extern inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+{
+  CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
+  return *(dnode_secno *) ((void *) de + de->length - 4);
+}
+
+/* The first dir entry in a dnode */
+
+extern inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+{
+  return (void *) dnode->dirent;
+}
+
+/* The end+1 of the dir entries */
+
+extern inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+{
+  CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
+  return (void *) dnode + dnode->first_free;
+}
+
+/* The dir entry after dir entry de */
+
+extern inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+{
+  CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
+  return (void *) de + de->length;
+}
+
+extern inline struct extended_attribute *fnode_ea(struct fnode *fnode)
+{
+	return (struct extended_attribute *)((char *)fnode + fnode->ea_offs);
+}
+
+extern inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
+{
+	return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->ea_size_s);
+}
+
+extern inline struct extended_attribute *next_ea(struct extended_attribute *ea)
+{
+	return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
+}
+
+extern inline secno ea_sec(struct extended_attribute *ea)
+{
+	return *(secno *)((char *)ea + 9 + ea->namelen);
+}
+
+extern inline secno ea_len(struct extended_attribute *ea)
+{
+	return *(secno *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline char *ea_data(struct extended_attribute *ea)
+{
+	return (char *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline unsigned de_size(int namelen, secno down_ptr)
+{
+	return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0);
+}
+
+extern inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
+{
+	int a = dst->down;
+	int n = dst->not_8x3;
+	if (!dst || !src) return;
+	memcpy((char *)dst + 2, (char *)src + 2, 28);
+	dst->down = a;
+	dst->not_8x3 = n;
+}
+
+extern inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
+{
+	int i;
+	if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
+	if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
+	for (i = 1; i < n; i++)
+		if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
+			return i + 1;
+	return 0;
+}
+
+/* alloc.c */
+
+int hpfs_chk_sectors(struct super_block *, secno, int, char *);
+secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
+int hpfs_alloc_if_possible_nolock(struct super_block *, secno);
+int hpfs_alloc_if_possible(struct super_block *, secno);
+void hpfs_free_sectors(struct super_block *, secno, unsigned);
+int hpfs_check_free_dnodes(struct super_block *, int);
+void hpfs_free_dnode(struct super_block *, secno);
+struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
+struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
+struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
+
+/* anode.c */
+
+secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *);
+secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_btree(struct super_block *, struct bplus_header *);
+int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
+int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
+void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
+void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
+
+/* buffer.c */
+
+void hpfs_lock_creation(struct super_block *);
+void hpfs_unlock_creation(struct super_block *);
+void hpfs_lock_iget(struct super_block *, int);
+void hpfs_unlock_iget(struct super_block *);
+void hpfs_lock_inode(struct inode *);
+void hpfs_unlock_inode(struct inode *);
+void hpfs_lock_2inodes(struct inode *, struct inode *);
+void hpfs_unlock_2inodes(struct inode *, struct inode *);
+void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *);
+void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *);
+void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
+void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
+void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
+void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *);
+void hpfs_brelse4(struct quad_buffer_head *);
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *);
+
+/* dentry.c */
+
+void hpfs_set_dentry_operations(struct dentry *);
+
+/* dir.c */
+
+int hpfs_dir_read(struct file *, char *, size_t, loff_t *);
+int hpfs_dir_release(struct inode *, struct file *);
+int hpfs_readdir(struct file *, void *, filldir_t);
+struct dentry *hpfs_lookup(struct inode *, struct dentry *);
+
+/* dnode.c */
+
+void hpfs_add_pos(struct inode *, loff_t *);
+void hpfs_del_pos(struct inode *, loff_t *);
+struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
+void hpfs_delete_de(struct super_block *, struct dnode *, struct hpfs_dirent *);
+int hpfs_add_to_dnode(struct inode *, dnode_secno, unsigned char *, unsigned, struct hpfs_dirent *, dnode_secno);
+int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
+int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
+void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
+struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
+struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *, int *depth);
+void hpfs_remove_dtree(struct super_block *, dnode_secno);
+struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
+
+/* ea.c */
+
+void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
+char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
+void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
+
+/* file.c */
+
+int hpfs_file_release(struct inode *, struct file *);
+int hpfs_open(struct inode *, struct file *);
+int hpfs_file_fsync(struct file *, struct dentry *);
+secno hpfs_bmap(struct inode *, unsigned);
+void hpfs_truncate(struct inode *);
+ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *);
+ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *);
+
+/* inode.c */
+
+void hpfs_read_inode(struct inode *);
+void hpfs_write_inode_ea(struct inode *, struct fnode *);
+void hpfs_write_inode(struct inode *);
+void hpfs_write_inode_nolock(struct inode *);
+int hpfs_notify_change(struct dentry *, struct iattr *);
+void hpfs_write_if_changed(struct inode *);
+void hpfs_delete_inode(struct inode *);
+
+/* map.c */
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
+unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
+char *hpfs_load_code_page(struct super_block *, secno);
+secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
+struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
+
+/* mmap.c */
+
+int hpfs_mmap(struct file *, struct vm_area_struct *);
+
+/* name.c */
+
+unsigned char hpfs_upcase(unsigned char *, unsigned char);
+int hpfs_chk_name(unsigned char *, unsigned);
+char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
+int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
+int hpfs_is_name_long(unsigned char *, unsigned);
+void hpfs_adjust_length(unsigned char *, unsigned *);
+void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
+
+/* namei.c */
+
+int hpfs_mkdir(struct inode *, struct dentry *, int);
+int hpfs_create(struct inode *, struct dentry *, int);
+int hpfs_mknod(struct inode *, struct dentry *, int, int);
+int hpfs_symlink(struct inode *, struct dentry *, const char *);
+int hpfs_unlink(struct inode *, struct dentry *);
+int hpfs_rmdir(struct inode *, struct dentry *);
+int hpfs_readlink(struct dentry *, char *, int);
+struct dentry *hpfs_follow_link(struct dentry *, struct dentry *, unsigned int);
+int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/* super.c */
+
+void hpfs_error(struct super_block *, char *, ...);
+int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
+int hpfs_remount_fs(struct super_block *, int *, char *);
+void hpfs_put_super(struct super_block *);
+int hpfs_statfs(struct super_block *, struct statfs *, int);
+struct super_block *hpfs_read_super(struct super_block *, void *, int);
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
--- v2.3.1/linux/fs/hpfs/hpfs_fs.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/hpfs/hpfs_fs.c	Wed Dec 31 16:00:00 1969
@@ -1,1759 +0,0 @@
-/*
- *  linux/fs/hpfs/hpfs_fs.c
- *  read-only HPFS
- *  version 1.0
- *
- *  Chris Smith 1993
- *
- *  Sources & references:
- *   Duncan, _Design ... of HPFS_, MSJ 4(5)   (C) 1989 Microsoft Corp
- *   linux/fs/minix  Copyright (C) 1991, 1992, 1993  Linus Torvalds
- *   linux/fs/msdos  Written 1992, 1993 by Werner Almesberger
- *   linux/fs/isofs  Copyright (C) 1991  Eric Youngdale
- */
-
-#include <linux/module.h>
-
-#include <linux/fs.h>
-#include <linux/hpfs_fs.h>
-#include <linux/errno.h>
-#include <linux/malloc.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/locks.h>
-#include <linux/stat.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <asm/bitops.h>
-#include <asm/uaccess.h>
-
-#include "hpfs.h"
-#include "hpfs_caps.h"
-
-/*
- * HPFS is a mixture of 512-byte blocks and 2048-byte blocks.  The 2k blocks
- * are used for directories and bitmaps.  For bmap to work, we must run the
- * file system with 512-byte blocks.  The 2k blocks are assembled in buffers
- * obtained from kmalloc.
- *
- * For a file's i-number we use the sector number of its fnode, coded.
- * (Directory ino's are even, file ino's are odd, and ino >> 1 is the
- * sector address of the fnode.  This is a hack to allow lookup() to
- * tell read_inode() whether it is necessary to read the fnode.)
- *
- * The map_xxx routines all read something into a buffer and return a
- * pointer somewhere in the buffer.  The caller must do the brelse.
- * The other routines are balanced.
- *
- * For details on the data structures see hpfs.h and the Duncan paper.
- *
- * Overview
- *
- * [ The names of these data structures, except fnode, are not Microsoft's
- * or IBM's.  I don't know what names they use.  The semantics described
- * here are those of this implementation, and any coincidence between it
- * and real HPFS is to be hoped for but not guaranteed by me, and
- * certainly not guaranteed by MS or IBM.  Who know nothing about this. ]
- *
- * [ Also, the following will make little sense if you haven't read the
- * Duncan paper, which is excellent. ]
- *
- * HPFS is a tree.  There are 3 kinds of nodes.  A directory is a tree
- * of dnodes, and a file's allocation info is a tree of sector runs
- * stored in fnodes and anodes.
- *
- * The top pointer is in the super block, it points to the fnode of the
- * root directory.
- *
- * The root directory -- all directories -- gives file names, dates &c,
- * and fnode addresses.  If the directory fits in one dnode, that's it,
- * otherwise the top dnode points to other dnodes, forming a tree.  A
- * dnode tree (one directory) might look like
- *
- *     ((a b c) d (e f g) h (i j) k l (m n o p))
- *
- * The subtrees appear between the files.  Each dir entry contains, along
- * with the name and fnode, a dnode pointer to the subtree that precedes it
- * (if there is one; a flag tells that).  The first entry in every directory
- * is ^A^A, the "." entry for the directory itself.  The last entry in every
- * dnode is \377, a fake entry whose only valid fields are the bit marking
- * it last and the down pointer to the subtree preceding it, if any.
- *
- * The "value" field of directory entries is an fnode address.  The fnode
- * tells where the sectors of the file are.  The fnode for a subdirectory
- * contains one pointer, to the root dnode of the subdirectory.  The fnode
- * for a data file contains, in effect, a tiny anode.  (Most of the space
- * in fnodes is for extended attributes.)
- *
- * anodes and the anode part of fnodes are trees of extents.  An extent
- * is a (length, disk address) pair, labeled with the file address being
- * mapped.  E.g.,
- *
- *     (0: 3@1000  3: 1@2000  4: 2@10)
- *
- * means the file:disk sector map (0:1000 1:1001 2:1002 3:2000 4:10 5:11).
- *
- * There is space for 8 file:len@disk triples in an fnode, or for 40 in an
- * anode.  If this is insufficient, subtrees are used, as in
- *
- *  (6: (0: 3@1000  3: 1@2000  4: 2@10)  12: (6: 3@8000  9: 1@9000  10: 2@20))
- *
- * The label on a subtree is the first address *after* that tree.  The
- * subtrees are always anodes.  The label:subtree pairs require only
- * two words each, so non-leaf subtrees have a different format; there
- * is room for 12 label:subtree pairs in an fnode, or 60 in an anode.
- *
- * Within a directory, each dnode contains a pointer up to its parent
- * dnode.  The root dnode points up to the directory's fnode.
- *
- * Each fnode contains a pointer to the directory that contains it
- * (to the fnode of the directory).  So this pointer in a directory
- * fnode is "..".
- *
- * On the disk, dnodes are all together in the center of the partition,
- * and HPFS even manages to put all the dnodes for a single directory
- * together, generally.  fnodes are out with the data.  anodes are seldom
- * seen -- in fact noncontiguous files are seldom seen.  I think this is
- * partly the open() call that lets programs specify the length of an
- * output file when they know it, and partly because HPFS.IFS really is
- * very good at resisting fragmentation.
- */
-
-/* notation */
-
-#define little_ushort(x) (*(unsigned short *) &(x))
-typedef void nonconst;
-
-/* super block ops */
-
-static void hpfs_read_inode(struct inode *);
-static void hpfs_put_super(struct super_block *);
-static int hpfs_statfs(struct super_block *, struct statfs *, int);
-static int hpfs_remount_fs(struct super_block *, int *, char *);
-
-static const struct super_operations hpfs_sops =
-{
-	hpfs_read_inode,		/* read_inode */
-	NULL,				/* write_inode */
-	NULL,				/* put_inode */
-	NULL,				/* delete_inode */
-	NULL,				/* notify_change */
-	hpfs_put_super,			/* put_super */
-	NULL,				/* write_super */
-	hpfs_statfs,			/* statfs */
-	hpfs_remount_fs,		/* remount_fs */
-};
-
-/* file ops */
-
-static ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *);
-static secno hpfs_bmap(struct inode *, unsigned);
-
-static const struct file_operations hpfs_file_ops =
-{
-	NULL,				/* lseek - default */
-	hpfs_file_read,			/* read */
-	NULL,				/* write */
-	NULL,				/* readdir - bad */
-	NULL,				/* poll - default */
-	NULL,				/* ioctl - default */
-	generic_file_mmap,		/* mmap */
-	NULL,				/* no special open is needed */
-	NULL,				/* flush */
-	NULL,				/* release */
-	file_fsync,			/* fsync */
-};
-
-static const struct inode_operations hpfs_file_iops =
-{
-	(nonconst *) & hpfs_file_ops,	/* default file operations */
-	NULL,				/* create */
-	NULL,				/* lookup */
-	NULL,				/* link */
-	NULL,				/* unlink */
-	NULL,				/* symlink */
-	NULL,				/* mkdir */
-	NULL,				/* rmdir */
-	NULL,				/* mknod */
-	NULL,				/* rename */
-	NULL,				/* readlink */
-	NULL,				/* follow_link */
-	generic_readpage,		/* readpage */
-	NULL,				/* writepage */
-	(int (*)(struct inode *, int))
-	&hpfs_bmap,			/* bmap */
-	NULL,				/* truncate */
-	NULL,				/* permission */
-};
-
-/* directory ops */
-
-static ssize_t hpfs_dir_read(struct file *filp, char *buf, 
-			     size_t count, loff_t *ppos);
-static int hpfs_readdir(struct file *filp,
-			void *dirent, filldir_t filldir);
-static struct dentry *hpfs_lookup(struct inode *, struct dentry *);
-
-static const struct file_operations hpfs_dir_ops =
-{
-	NULL,				/* lseek - default */
-	hpfs_dir_read,			/* read */
-	NULL,				/* write - bad */
-	hpfs_readdir,			/* readdir */
-	NULL,				/* poll - default */
-	NULL,				/* ioctl - default */
-	NULL,				/* mmap */
-	NULL,				/* no special open code */
-	NULL,				/* flush */
-	NULL,				/* no special release code */
-	file_fsync,			/* fsync */
-};
-
-static const struct inode_operations hpfs_dir_iops =
-{
-	(nonconst *) & hpfs_dir_ops,	/* default directory file ops */
-	NULL,				/* create */
-	hpfs_lookup,			/* lookup */
-	NULL,				/* link */
-	NULL,				/* unlink */
-	NULL,				/* symlink */
-	NULL,				/* mkdir */
-	NULL,				/* rmdir */
-	NULL,				/* mknod */
-	NULL,				/* rename */
-	NULL,				/* readlink */
-	NULL,				/* readpage */
-	NULL,				/* writepage */
-	NULL,				/* bmap */
-	NULL,				/* truncate */
-	NULL,				/* permission */
-};
-
-/* Four 512-byte buffers and the 2k block obtained by concatenating them */
-
-struct quad_buffer_head {
-	struct buffer_head *bh[4];
-	void *data;
-};
-
-/* forwards */
-
-static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
-		      int *lowercase, int *conv, int *nocheck);
-static int check_warn(int not_ok,
-		      const char *p1, const char *p2, const char *p3);
-static int zerop(void *addr, unsigned len);
-static void count_dnodes(struct inode *inode, dnode_secno dno,
-			 unsigned *n_dnodes, unsigned *n_subdirs);
-static unsigned count_bitmap(struct super_block *s);
-static unsigned count_one_bitmap(kdev_t dev, secno secno);
-static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
-			  secno file_secno, struct buffer_head **bhp);
-static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
-				    const unsigned char *name, unsigned len,
-				      struct quad_buffer_head *qbh);
-static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
-					  struct quad_buffer_head *qbh);
-static dnode_secno dir_subdno(struct inode *inode, unsigned pos);
-static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
-					  int n,
-					  struct quad_buffer_head *qbh);
-static unsigned choose_conv(unsigned char *p, unsigned len);
-static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
-			     unsigned len);
-static dnode_secno fnode_dno(kdev_t dev, ino_t ino);
-static struct fnode *map_fnode(kdev_t dev, ino_t ino,
-			       struct buffer_head **bhp);
-static struct anode *map_anode(kdev_t dev, unsigned secno,
-			       struct buffer_head **bhp);
-static struct dnode *map_dnode(kdev_t dev, unsigned secno,
-			       struct quad_buffer_head *qbh);
-static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp);
-static void *map_4sectors(kdev_t dev, unsigned secno,
-			  struct quad_buffer_head *qbh);
-static void brelse4(struct quad_buffer_head *qbh);
-
-/*
- * make inode number for a file
- */
-
-static inline ino_t file_ino(fnode_secno secno)
-{
-	return secno << 1 | 1;
-}
-
-/*
- * make inode number for a directory
- */
-
-static inline ino_t dir_ino(fnode_secno secno)
-{
-	return secno << 1;
-}
-
-/*
- * get fnode address from an inode number
- */
-
-static inline fnode_secno ino_secno(ino_t ino)
-{
-	return ino >> 1;
-}
-
-/*
- * test for directory's inode number
- */
-
-static inline int ino_is_dir(ino_t ino)
-{
-	return (ino & 1) == 0;
-}
-
-/*
- * conv= options
- */
-
-#define CONV_BINARY 0			/* no conversion */
-#define CONV_TEXT 1			/* crlf->newline */
-#define CONV_AUTO 2			/* decide based on file contents */
-
-/*
- * local time (HPFS) to GMT (Unix)
- */
-
-static inline time_t local_to_gmt(time_t t)
-{
-	extern struct timezone sys_tz;
-	return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0);
-}
-
-/* super block ops */
-
-/*
- * mount.  This gets one thing, the root directory inode.  It does a
- * bunch of guessed-at consistency checks.
- */
-
-struct super_block *hpfs_read_super(struct super_block *s,
-				    void *options, int silent)
-{
-	struct hpfs_boot_block *bootblock;
-	struct hpfs_super_block *superblock;
-	struct hpfs_spare_block *spareblock;
-	struct hpfs_dirent *de = NULL;
-	struct buffer_head *bh0, *bh1, *bh2;
-	struct quad_buffer_head qbh;
-	dnode_secno root_dno;
-	kdev_t dev;
-	uid_t uid;
-	gid_t gid;
-	umode_t umask;
-	int lowercase;
-	int conv;
-	int dubious;
-	int nocheck;
-
-	MOD_INC_USE_COUNT;
-
-	/*
-	 * Get the mount options
-	 */
-
-	if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
-				 &nocheck)) {
-		printk("HPFS: syntax error in mount options.  Not mounted.\n");
-		s->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-
-	/*
-	 * Fill in the super block struct
-	 */
-
-	lock_super(s);
-	dev = s->s_dev;
-	set_blocksize(dev, 512);
-
-	/*
-	 * fetch sectors 0, 16, 17
-	 */
-
-	bootblock = map_sector(dev, 0, &bh0);
-	if (!bootblock)
-		goto bail;
-
-	superblock = map_sector(dev, 16, &bh1);
-	if (!superblock)
-		goto bail0;
-
-	spareblock = map_sector(dev, 17, &bh2);
-	if (!spareblock)
-		goto bail1;
-
-	/*
-	 * Check that this fs looks enough like a known one that we can find
-	 * and read the root directory.
-	 */
-
-	if (bootblock->magic != 0xaa55
-	    || superblock->magic != SB_MAGIC
-	    || spareblock->magic != SP_MAGIC
-	    || bootblock->sig_28h != 0x28
-	    || memcmp(&bootblock->sig_hpfs, "HPFS    ", 8)
-	    || little_ushort(bootblock->bytes_per_sector) != 512) {
-		printk("HPFS: hpfs_read_super: Not HPFS\n");
-		goto bail2;
-	}
-
-	/*
-	 * Check for inconsistencies -- possibly wrong guesses here, possibly
-	 * filesystem problems.
-	 */
-
-	dubious = 0;
-
-	dubious |= check_warn(spareblock->dirty != 0,
-		       "`Improperly stopped'", "flag is set", "run CHKDSK");
-	dubious |= check_warn(spareblock->n_spares_used != 0,
-			      "Spare blocks", "may be in use", "run CHKDSK");
-
-	/*
-	 * Above errors mean we could get wrong answers if we proceed,
-	 * so don't
-	 */
-
-	if (dubious && !nocheck)
-		goto bail2;
-
-	dubious |= check_warn((spareblock->n_dnode_spares !=
-			       spareblock->n_dnode_spares_free),
-			      "Spare dnodes", "may be in use", "run CHKDSK");
-	dubious |= check_warn(superblock->zero1 != 0,
-			      "#1", "unknown word nonzero", "investigate");
-	dubious |= check_warn(superblock->zero3 != 0,
-			      "#3", "unknown word nonzero", "investigate");
-	dubious |= check_warn(superblock->zero4 != 0,
-			      "#4", "unknown word nonzero", "investigate");
-	dubious |= check_warn(!zerop(superblock->zero5,
-				     sizeof superblock->zero5),
-			      "#5", "unknown word nonzero", "investigate");
-	dubious |= check_warn(!zerop(superblock->zero6,
-				     sizeof superblock->zero6),
-			      "#6", "unknown word nonzero", "investigate");
-
-	if (dubious)
-		printk("HPFS: Proceeding, but operation may be unreliable\n");
-
-	/*
-	 * set fs read only
-	 */
-
-	s->s_flags |= MS_RDONLY;
-
-	/*
-	 * fill in standard stuff
-	 */
-
-	s->s_magic = HPFS_SUPER_MAGIC;
-	s->s_blocksize = 512;
-	s->s_blocksize_bits = 9;
-	s->s_op = (struct super_operations *) &hpfs_sops;
-
-	/*
-	 * fill in hpfs stuff
-	 */
-
-	s->s_hpfs_root = dir_ino(superblock->root);
-	s->s_hpfs_fs_size = superblock->n_sectors;
-	s->s_hpfs_dirband_size = superblock->n_dir_band / 4;
-	s->s_hpfs_dmap = superblock->dir_band_bitmap;
-	s->s_hpfs_bitmaps = superblock->bitmaps;
-	s->s_hpfs_uid = uid;
-	s->s_hpfs_gid = gid;
-	s->s_hpfs_mode = 0777 & ~umask;
-	s->s_hpfs_n_free = -1;
-	s->s_hpfs_n_free_dnodes = -1;
-	s->s_hpfs_lowercase = lowercase;
-	s->s_hpfs_conv = conv;
-
-	/*
-	 * done with the low blocks
-	 */
-
-	brelse(bh2);
-	brelse(bh1);
-	brelse(bh0);
-
-	/*
-	 * all set.  try it out.
-	 */
-
-	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
-	unlock_super(s);
-
-	if (!s->s_root) {
-		printk("HPFS: hpfs_read_super: inode get failed\n");
-		s->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-
-	/*
-	 * find the root directory's . pointer & finish filling in the inode
-	 */
-
-	root_dno = fnode_dno(dev, s->s_hpfs_root);
-	if (root_dno)
-		de = map_dirent(s->s_root->d_inode, root_dno,
-				"\001\001", 2, &qbh);
-	if (!root_dno || !de) {
-		printk("HPFS: "
-		       "hpfs_read_super: root dir isn't in the root dir\n");
-		s->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return 0;
-	}
-
-	s->s_root->d_inode->i_atime = local_to_gmt(de->read_date);
-	s->s_root->d_inode->i_mtime = local_to_gmt(de->write_date);
-	s->s_root->d_inode->i_ctime = local_to_gmt(de->creation_date);
-
-	brelse4(&qbh);
-	return s;
-
- bail2:
-	brelse(bh2);
- bail1:
-	brelse(bh1);
- bail0:
-	brelse(bh0);
- bail:
-	s->s_dev = 0;
-	unlock_super(s);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-static int check_warn(int not_ok,
-		      const char *p1, const char *p2, const char *p3)
-{
-	if (not_ok)
-		printk("HPFS: %s %s. Please %s\n", p1, p2, p3);
-	return not_ok;
-}
-
-static int zerop(void *addr, unsigned len)
-{
-	unsigned char *p = addr;
-	return p[0] == 0 && memcmp(p, p + 1, len - 1) == 0;
-}
-
-/*
- * A tiny parser for option strings, stolen from dosfs.
- */
-
-static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
-		      int *lowercase, int *conv, int *nocheck)
-{
-	char *p, *rhs;
-
-	*uid = current->uid;
-	*gid = current->gid;
-	*umask = current->fs->umask;
-	*lowercase = 1;
-	*conv = CONV_BINARY;
-        *nocheck = 0;
-
-	if (!opts)
-		return 1;
-
-	for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
-		if ((rhs = strchr(p, '=')) != 0)
-			*rhs++ = '\0';
-		if (!strcmp(p, "uid")) {
-			if (!rhs || !*rhs)
-				return 0;
-			*uid = simple_strtoul(rhs, &rhs, 0);
-			if (*rhs)
-				return 0;
-		}
-		else if (!strcmp(p, "gid")) {
-			if (!rhs || !*rhs)
-				return 0;
-			*gid = simple_strtoul(rhs, &rhs, 0);
-			if (*rhs)
-				return 0;
-		}
-		else if (!strcmp(p, "umask")) {
-			if (!rhs || !*rhs)
-				return 0;
-			*umask = simple_strtoul(rhs, &rhs, 8);
-			if (*rhs)
-				return 0;
-		}
-		else if (!strcmp(p, "case")) {
-			if (!strcmp(rhs, "lower"))
-				*lowercase = 1;
-			else if (!strcmp(rhs, "asis"))
-				*lowercase = 0;
-			else
-				return 0;
-		}
-		else if (!strcmp(p, "conv")) {
-			if (!strcmp(rhs, "binary"))
-				*conv = CONV_BINARY;
-			else if (!strcmp(rhs, "text"))
-				*conv = CONV_TEXT;
-			else if (!strcmp(rhs, "auto"))
-				*conv = CONV_AUTO;
-			else
-				return 0;
-		}
-		else if (!strcmp(p,"nocheck"))
-			*nocheck=1;
-		else
-			return 1;
-	}
-
-	return 1;
-}
-
-/*
- * read_inode.  This is called with exclusive access to a new inode that
- * has only (i_dev,i_ino) set.  It is responsible for filling in the rest.
- * We leave the dates blank, to be filled in from the dir entry.
- *
- * NOTE that there must be no sleeping from the return in this routine
- * until lookup() finishes filling in the inode, otherwise the partly
- * completed inode would be visible during the sleep.
- *
- * It is done in this strange and sinful way because the alternative
- * is to read the fnode, find the dir pointer in it, read that fnode
- * to get the dnode pointer, search through that whole directory for
- * the ino we're reading, and get the dates.  It works that way, but
- * ls sounds like fsck.
- */
-
-static void hpfs_read_inode(struct inode *inode)
-{
-	struct super_block *s = inode->i_sb;
-
-	/* be ready to bail out */
-
-	inode->i_op = 0;
-	inode->i_mode = 0;
-
-	if (inode->i_ino == 0
-	    || ino_secno(inode->i_ino) >= inode->i_sb->s_hpfs_fs_size) {
-		printk("HPFS: read_inode: bad ino\n");
-		return;
-	}
-
-	/*
-	 * canned stuff
-	 */
-
-	inode->i_uid = s->s_hpfs_uid;
-	inode->i_gid = s->s_hpfs_gid;
-	inode->i_mode = s->s_hpfs_mode;
-	inode->i_hpfs_conv = s->s_hpfs_conv;
-
-	inode->i_hpfs_dno = 0;
-	inode->i_hpfs_n_secs = 0;
-	inode->i_hpfs_file_sec = 0;
-	inode->i_hpfs_disk_sec = 0;
-	inode->i_hpfs_dpos = 0;
-	inode->i_hpfs_dsubdno = 0;
-
-	/*
-	 * figure out whether we are looking at a directory or a file
-	 */
-
-	if (ino_is_dir(inode->i_ino))
-		inode->i_mode |= S_IFDIR;
-	else {
-		inode->i_mode |= S_IFREG;
-		inode->i_mode &= ~0111;
-	}
-
-	/*
-	 * these fields must be filled in from the dir entry, which we don't
-	 * have but lookup does.  It will fill them in before letting the
-	 * inode out of its grasp.
-	 */
-
-	inode->i_atime = 0;
-	inode->i_mtime = 0;
-	inode->i_ctime = 0;
-	inode->i_size = 0;
-
-	/*
-	 * fill in the rest
-	 */
-
-	if (S_ISREG(inode->i_mode)) {
-
-		inode->i_op = (struct inode_operations *) &hpfs_file_iops;
-		inode->i_nlink = 1;
-		inode->i_blksize = 512;
-
-	}
-	else {
-		unsigned n_dnodes, n_subdirs;
-		struct buffer_head *bh0;
-		struct fnode *fnode = map_fnode(inode->i_dev,
-						inode->i_ino, &bh0);
-
-		if (!fnode) {
-			printk("HPFS: read_inode: no fnode\n");
-			inode->i_mode = 0;
-			return;
-		}
-
-		inode->i_hpfs_parent_dir = dir_ino(fnode->up);
-		inode->i_hpfs_dno = fnode->u.external[0].disk_secno;
-
-		brelse(bh0);
-
-		n_dnodes = n_subdirs = 0;
-		count_dnodes(inode, inode->i_hpfs_dno, &n_dnodes, &n_subdirs);
-
-		inode->i_op = (struct inode_operations *) &hpfs_dir_iops;
-		inode->i_blksize = 512;	/* 2048 here confuses ls & du & ... */
-		inode->i_blocks = 4 * n_dnodes;
-		inode->i_size = 512 * inode->i_blocks;
-		inode->i_nlink = 2 + n_subdirs;
-	}
-}
-
-/*
- * unmount.
- */
-
-static void hpfs_put_super(struct super_block *s)
-{
-	MOD_DEC_USE_COUNT;
-}
-
-/*
- * statfs.  For free inode counts we report the count of dnodes in the
- * directory band -- not exactly right but pretty analogous.
- */
-
-static int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
-{
-	struct statfs tmp;
-
-	/*
-	 * count the bits in the bitmaps, unless we already have
-	 */
-	if (s->s_hpfs_n_free == -1) {
-		s->s_hpfs_n_free = count_bitmap(s);
-		s->s_hpfs_n_free_dnodes =
-		    count_one_bitmap(s->s_dev, s->s_hpfs_dmap);
-	}
-
-	/*
-	 * fill in the user statfs struct
-	 */
-	tmp.f_type = s->s_magic;
-	tmp.f_bsize = 512;
-	tmp.f_blocks = s->s_hpfs_fs_size;
-	tmp.f_bfree = s->s_hpfs_n_free;
-	tmp.f_bavail = s->s_hpfs_n_free;
-	tmp.f_files = s->s_hpfs_dirband_size;
-	tmp.f_ffree = s->s_hpfs_n_free_dnodes;
-	tmp.f_namelen = 254;
-
-	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
-}
-
-/*
- * remount.  Don't let read only be turned off.
- */
-
-static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
-{
-	if (!(*flags & MS_RDONLY))
-		return -EINVAL;
-	return 0;
-}
-
-/*
- * count the dnodes in a directory, and the subdirs.
- */
-
-static void count_dnodes(struct inode *inode, dnode_secno dno,
-			 unsigned *n_dnodes, unsigned *n_subdirs)
-{
-	struct quad_buffer_head qbh;
-	struct dnode *dnode;
-	struct hpfs_dirent *de;
-	struct hpfs_dirent *de_end;
-
-	dnode = map_dnode(inode->i_dev, dno, &qbh);
-	if (!dnode)
-		return;
-	de = dnode_first_de(dnode);
-	de_end = dnode_end_de(dnode);
-
-	(*n_dnodes)++;
-
-	for (; de < de_end; de = de_next_de(de)) {
-		if (de->down)
-			count_dnodes(inode, de_down_pointer(de),
-				     n_dnodes, n_subdirs);
-		if (de->directory && !de->first)
-			(*n_subdirs)++;
-		if (de->last || de->length == 0)
-			break;
-	}
-
-	brelse4(&qbh);
-}
-
-/*
- * count the bits in the free space bit maps
- */
-
-static unsigned count_bitmap(struct super_block *s)
-{
-	unsigned n, count, n_bands;
-	secno *bitmaps;
-	struct quad_buffer_head qbh;
-
-	/*
-	 * there is one bit map for each 16384 sectors
-	 */
-	n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
-
-	/*
-	 * their locations are given in an array pointed to by the super
-	 * block
-	 */
-	bitmaps = map_4sectors(s->s_dev, s->s_hpfs_bitmaps, &qbh);
-	if (!bitmaps)
-		return 0;
-
-	count = 0;
-
-	/*
-	 * map each one and count the free sectors
-	 */
-	for (n = 0; n < n_bands; n++)
-		if (bitmaps[n] == 0)
-			printk("HPFS: bit map pointer missing\n");
-		else
-			count += count_one_bitmap(s->s_dev, bitmaps[n]);
-
-	brelse4(&qbh);
-	return count;
-}
-
-/*
- * Read in one bit map, count the bits, return the count.
- */
-
-static unsigned count_one_bitmap(kdev_t dev, secno secno)
-{
-	struct quad_buffer_head qbh;
-	char *bits;
-	unsigned i, count;
-
-	bits = map_4sectors(dev, secno, &qbh);
-	if (!bits)
-		return 0;
-
-	count = 0;
-
-	for (i = 0; i < 8 * 2048; i++)
-		count += (test_bit(i, bits) != 0);
-	brelse4(&qbh);
-
-	return count;
-}
-
-/* file ops */
-
-/*
- * read.  Read the bytes, put them in buf, return the count.
- */
-
-static ssize_t hpfs_file_read(struct file *filp, char *buf,
-			      size_t count, loff_t *ppos)
-{
-	struct inode *inode = filp->f_dentry->d_inode;
-	size_t q, r, n, n0;
-	struct buffer_head *bh;
-	char *block;
-	char *start;
-
-	if (inode == 0 || !S_ISREG(inode->i_mode))
-		return -EINVAL;
-
-	/*
-	 * truncate count at EOF
-	 */
-	if (count > inode->i_size - (off_t) *ppos)
-		count = inode->i_size - *ppos;
-
-	start = buf;
-	while (count > 0) {
-		/*
-		 * get file sector number, offset in sector, length to end of
-		 * sector
-		 */
-		q = *ppos >> 9;
-		r = *ppos & 511;
-		n = 512 - r;
-
-		/*
-		 * get length to copy to user buffer
-		 */
-		if (n > count)
-			n = count;
-
-		/*
-		 * read the sector, copy to user
-		 */
-		block = map_sector(inode->i_dev, hpfs_bmap(inode, q), &bh);
-		if (!block)
-			return -EIO;
-
-		/*
-		 * but first decide if it has \r\n, if the mount option said
-		 * to do that
-		 */
-		if (inode->i_hpfs_conv == CONV_AUTO)
-			inode->i_hpfs_conv = choose_conv(block + r, n);
-
-		if (inode->i_hpfs_conv == CONV_BINARY) {
-			/*
-			 * regular copy, output length is same as input
-			 * length
-			 */
-			copy_to_user(buf, block + r, n);
-			n0 = n;
-		}
-		else {
-			/*
-			 * squeeze out \r, output length varies
-			 */
-			n0 = convcpy_tofs(buf, block + r, n);
-			if (count > inode->i_size - (off_t) *ppos - n + n0)
-				count = inode->i_size - *ppos - n + n0;
-		}
-
-		brelse(bh);
-
-		/*
-		 * advance input n bytes, output n0 bytes
-		 */
-		*ppos += n;
-		buf += n0;
-		count -= n0;
-	}
-
-	return buf - start;
-}
-
-/*
- * This routine implements conv=auto.  Return CONV_BINARY or CONV_TEXT.
- */
-
-static unsigned choose_conv(unsigned char *p, unsigned len)
-{
-	unsigned tvote, bvote;
-	unsigned c;
-
-	tvote = bvote = 0;
-
-	while (len--) {
-		c = *p++;
-		if (c < ' ') {
-			if (c == '\r' && len && *p == '\n')
-				tvote += 10;
-			else if (c == '\t' || c == '\n');
-			else
-				bvote += 5;
-		} else if (c < '\177')
-			tvote++;
-		else
-			bvote += 5;
-	}
-
-	if (tvote > bvote)
-		return CONV_TEXT;
-	else
-		return CONV_BINARY;
-}
-
-/*
- * This routine implements conv=text.  :s/crlf/nl/
- */
-
-static unsigned convcpy_tofs(unsigned char *out, unsigned char *in,
-			     unsigned len)
-{
-	unsigned char *start = out;
-
-	while (len--) {
-		unsigned c = *in++;
-		if (c == '\r' && (len == 0 || *in == '\n'));
-		else
-			put_user(c, out++);
-	}
-
-	return out - start;
-}
-
-/*
- * Return the disk sector number containing a file sector.
- */
-
-static secno hpfs_bmap(struct inode *inode, unsigned file_secno)
-{
-	unsigned n, disk_secno;
-	struct fnode *fnode;
-	struct buffer_head *bh;
-
-	/*
-	 * There is one sector run cached in the inode. See if the sector is
-	 * in it.
-	 */
-
-	n = file_secno - inode->i_hpfs_file_sec;
-	if (n < inode->i_hpfs_n_secs)
-		return inode->i_hpfs_disk_sec + n;
-
-	/*
-	 * No, read the fnode and go find the sector.
-	 */
-
-	else {
-		fnode = map_fnode(inode->i_dev, inode->i_ino, &bh);
-		if (!fnode)
-			return 0;
-		disk_secno = bplus_lookup(inode, &fnode->btree,
-					  file_secno, &bh);
-		brelse(bh);
-		return disk_secno;
-	}
-}
-
-/*
- * Search allocation tree *b for the given file sector number and return
- * the disk sector number.  Buffer *bhp has the tree in it, and can be
- * reused for subtrees when access to *b is no longer needed.
- * *bhp is busy on entry and exit.
- */
-
-static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
-			  secno file_secno, struct buffer_head **bhp)
-{
-	int i;
-
-	/*
-	 * A leaf-level tree gives a list of sector runs.  Find the one
-	 * containing the file sector we want, cache the map info in the
-	 * inode for later, and return the corresponding disk sector.
-	 */
-
-	if (!b->internal) {
-		struct bplus_leaf_node *n = b->u.external;
-		for (i = 0; i < b->n_used_nodes; i++) {
-			unsigned t = file_secno - n[i].file_secno;
-			if (t < n[i].length) {
-				inode->i_hpfs_file_sec = n[i].file_secno;
-				inode->i_hpfs_disk_sec = n[i].disk_secno;
-				inode->i_hpfs_n_secs = n[i].length;
-				return n[i].disk_secno + t;
-			}
-		}
-	}
-
-	/*
-	 * A non-leaf tree gives a list of subtrees.  Find the one containing
-	 * the file sector we want, read it in, and recurse to search it.
-	 */
-
-	else {
-		struct bplus_internal_node *n = b->u.internal;
-		for (i = 0; i < b->n_used_nodes; i++) {
-			if (file_secno < n[i].file_secno) {
-				struct anode *anode;
-				anode_secno ano = n[i].down;
-				brelse(*bhp);
-				anode = map_anode(inode->i_dev, ano, bhp);
-				if (!anode)
-					break;
-				return bplus_lookup(inode, &anode->btree,
-						    file_secno, bhp);
-			}
-		}
-	}
-
-	/*
-	 * If we get here there was a hole in the file.  As far as I know we
-	 * never do get here, but falling off the end would be indelicate. So
-	 * return a pointer to a handy all-zero sector.  This is not a
-	 * reasonable way to handle files with holes if they really do
-	 * happen.
-	 */
-
-	printk("HPFS: bplus_lookup: sector not found\n");
-	return 15;
-}
-
-/* directory ops */
-
-/*
- * lookup.  Search the specified directory for the specified name, set
- * *result to the corresponding inode.
- *
- * lookup uses the inode number to tell read_inode whether it is reading
- * the inode of a directory or a file -- file ino's are odd, directory
- * ino's are even.  read_inode avoids i/o for file inodes; everything
- * needed is up here in the directory.  (And file fnodes are out in
- * the boondocks.)
- */
-
-static struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
-{
-	const char *name = dentry->d_name.name;
-	int len = dentry->d_name.len;
-	struct hpfs_dirent *de;
-	struct inode *inode;
-	ino_t ino;
-	int retval;
-	struct quad_buffer_head qbh;
-
-	/*
-	 * Read in the directory entry. "." is there under the name ^A^A .
-	 * Always read the dir even for . and .. in case we need the dates.
-	 */
-
-	if (name[0] == '.' && len == 1)
-		de = map_dirent(dir, dir->i_hpfs_dno, "\001\001", 2, &qbh);
-	else if (name[0] == '.' && name[1] == '.' && len == 2)
-		de = map_dirent(dir,
-				fnode_dno(dir->i_dev, dir->i_hpfs_parent_dir),
-				"\001\001", 2, &qbh);
-	else
-		de = map_dirent(dir, dir->i_hpfs_dno, name, len, &qbh);
-
-	/*
-	 * This is not really a bailout, just means file not found.
-	 */
-
-	if (!de) {
-		d_add(dentry, NULL);
-		retval = 0;
-		goto out;
-	}
-
-	/*
-	 * Get inode number, what we're after.
-	 */
-
-	if (de->directory)
-		ino = dir_ino(de->fnode);
-	else
-		ino = file_ino(de->fnode);
-
-	/*
-	 * Go find or make an inode.
-	 */
-
-	retval = -EACCES;
-	if (!(inode = iget(dir->i_sb, ino)))
-		goto free4;
-
-	/*
-	 * Fill in the info from the directory if this is a newly created
-	 * inode.
-	 */
-
-	if (!inode->i_atime) {
-		inode->i_atime = local_to_gmt(de->read_date);
-		inode->i_mtime = local_to_gmt(de->write_date);
-		inode->i_ctime = local_to_gmt(de->creation_date);
-		if (de->read_only)
-			inode->i_mode &= ~0222;
-		if (!de->directory) {
-			inode->i_size = de->file_size;
-			/*
-			 * i_blocks should count the fnode and any anodes.
-			 * We count 1 for the fnode and don't bother about
-			 * anodes -- the disk heads are on the directory band
-			 * and we want them to stay there.
-			 */
-			inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
-		}
-	}
-
-	d_add(dentry, inode);
-	retval = 0;
-
-      free4:
-	brelse4(&qbh);
-
-      out:
-	return ERR_PTR(retval);
-}
-
-/*
- * Compare two counted strings ignoring case.
- * HPFS directory order sorts letters as if they're upper case.
- */
-
-static inline int memcasecmp(const unsigned char *s1, const unsigned char *s2,
-			     unsigned n)
-{
-	int t;
-
-	if (n != 0)
-		do {
-			unsigned c1 = linux_char_to_upper_linux (*s1++);
-			unsigned c2 = hpfs_char_to_upper_linux (*s2++);
-			if ((t = c1 - c2) != 0)
-				return t;
-		} while (--n != 0);
-
-	return 0;
-}
-
-/*
- * Search a directory for the given name, return a pointer to its dir entry
- * and a pointer to the buffer containing it.
- */
-
-static struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno,
-				      const unsigned char *name, unsigned len,
-				      struct quad_buffer_head *qbh)
-{
-	struct dnode *dnode;
-	struct hpfs_dirent *de;
-	struct hpfs_dirent *de_end;
-	int t, l;
-
-	/*
-	 * read the dnode at the root of our subtree
-	 */
-	dnode = map_dnode(inode->i_dev, dno, qbh);
-	if (!dnode)
-		return 0;
-
-	/*
-	 * get pointers to start and end+1 of dir entries
-	 */
-	de = dnode_first_de(dnode);
-	de_end = dnode_end_de(dnode);
-
-	/*
-	 * look through the entries for the name we're after
-	 */
-	for ( ; de < de_end; de = de_next_de(de)) {
-
-		/*
-		 * compare names
-		 */
-		l = len < de->namelen ? len : de->namelen;
-		t = memcasecmp(name, de->name, l);
-
-		/*
-		 * initial substring matches, compare lengths
-		 */
-		if (t == 0) {
-			t = len - de->namelen;
-			/* bingo */
-			if (t == 0)
-				return de;
-		}
-
-		/*
-		 * wanted name .lt. dir name => not present.
-		 */
-		if (t < 0) {
-			/*
-			 * if there is a subtree, search it.
-			 */
-			if (de->down) {
-				dnode_secno sub_dno = de_down_pointer(de);
-				brelse4(qbh);
-				return map_dirent(inode, sub_dno,
-						  name, len, qbh);
-			}
-			else
-				break;
-		}
-
-		/*
-		 * de->last is set on the last name in the dnode (it's always
-		 * a "\377" pseudo entry).  de->length == 0 means we're about
-		 * to infinite loop. This test does nothing in a well-formed
-		 * dnode.
-		 */
-		if (de->last || de->length == 0)
-			break;
-	}
-
-	/*
-	 * name not found.
-	 */
-	brelse4(qbh);
-	return 0;
-}
-
-/*
- * readdir.  Return exactly 1 dirent.  (I tried and tried, but currently
- * the interface with libc just does not permit more than 1.  If it gets
- * fixed, throw this out and just walk the tree and write records into
- * the user buffer.)
- *
- * [ we now can handle multiple dirents, although the current libc doesn't
- *   use that. The way hpfs does this is pretty strange, as we need to do
- *   the name translation etc before calling "filldir()". This is untested,
- *   as I don't have any hpfs partitions to test against.   Linus ]
- *
- * We keep track of our position in the dnode tree with a sort of
- * dewey-decimal record of subtree locations.  Like so:
- *
- *   (1 (1.1 1.2 1.3) 2 3 (3.1 (3.1.1 3.1.2) 3.2 3.3 (3.3.1)) 4)
- *
- * Subtrees appear after their file, out of lexical order,
- * which would be before their file.  It's easier.
- *
- * A directory can't hold more than 56 files, so 6 bits are used for
- * position numbers.  If the tree is so deep that the position encoding
- * doesn't fit, I'm sure something absolutely fascinating happens.
- *
- * The actual sequence of f_pos values is
- *     0 => .   -1 => ..   1 1.1 ... 8.9 9 => files  -2 => eof
- *
- * The directory inode caches one position-to-dnode correspondence so
- * we won't have to repeatedly scan the top levels of the tree.
- */
-
-/*
- * Translate the given name: Blam it to lowercase if the mount option said to.
- */
-
-static void translate_hpfs_name(const unsigned char * from, int len, char * to, int lowercase)
-{
-	while (len > 0) {
-		unsigned t = *from;
-		len--;
-		if (lowercase)
-			t = hpfs_char_to_lower_linux (t);
-		else
-			t = hpfs_char_to_linux (t);
-		*to = t;
-		from++;
-		to++;
-	}
-}
-
-static int hpfs_readdir(struct file *filp, void * dirent,
-	filldir_t filldir)
-{
-	struct quad_buffer_head qbh;
-	struct hpfs_dirent *de;
-	int namelen, lc;
-	ino_t ino;
-	char * tempname;
-	long old_pos;
-	struct inode *inode = filp->f_dentry->d_inode;
-
-	tempname = (char *) __get_free_page(GFP_KERNEL);
-	if (!tempname)
-		return -ENOMEM;
-
-	lc = inode->i_sb->s_hpfs_lowercase;
-	switch ((long) filp->f_pos) {
-	case -2:
-		break;
-
-	case 0:
-		if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
-			break;
-		filp->f_pos = -1;
-		/* fall through */
-
-	case -1:
-		if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0)
-			break;
-		filp->f_pos = 1;
-		/* fall through */
-
-	default:
-		for (;;) {
-			old_pos = filp->f_pos;
-			de = map_pos_dirent(inode, &filp->f_pos, &qbh);
-			if (!de) {
-				filp->f_pos = -2;
-				break;
-			}
-			namelen = de->namelen;
-			translate_hpfs_name(de->name, namelen, tempname, lc);
-			if (de->directory)
-				ino = dir_ino(de->fnode);
-			else
-				ino = file_ino(de->fnode);
-			brelse4(&qbh);
-			if (filldir(dirent, tempname, namelen, old_pos, ino) < 0) {
-				filp->f_pos = old_pos;
-				break;
-			}
-		}
-	}
-	free_page((unsigned long) tempname);
-	return 0;
-}
-
-/*
- * Map the dir entry at subtree coordinates given by *posp, and
- * increment *posp to point to the following dir entry.
- */
-
-static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
-					  struct quad_buffer_head *qbh)
-{
-	unsigned pos, q, r;
-	dnode_secno dno;
-	struct hpfs_dirent *de;
-
-	/*
-	 * Get the position code and split off the rightmost index r
-	 */
-
-	pos = *posp;
-	q = pos >> 6;
-	r = pos & 077;
-
-	/*
-	 * Get the sector address of the dnode
-	 * pointed to by the leading part q
-	 */
-
-	dno = dir_subdno(inode, q);
-	if (!dno)
-		return 0;
-
-	/*
-	 * Get the entry at index r in dnode q
-	 */
-
-	de = map_nth_dirent(inode->i_dev, dno, r, qbh);
-
-	/*
-	 * If none, we're out of files in this dnode.  Ascend.
-	 */
-
-	if (!de) {
-		if (q == 0)
-			return 0;
-		*posp = q + 1;
-		return map_pos_dirent(inode, posp, qbh);
-	}
-
-	/*
-	 * If a subtree is here, descend.
-	 */
-
-	if (de->down)
-		*posp = pos << 6 | 1;
-	else
-		*posp = pos + 1;
-
-	/*
-	 * Don't return the ^A^A and \377 entries.
-	 */
-
-	if (de->first || de->last) {
-		brelse4(qbh);
-		return map_pos_dirent(inode, posp, qbh);
-	}
-	else
-		return de;
-}
-
-/*
- * Return the address of the dnode with subtree coordinates given by pos.
- */
-
-static dnode_secno dir_subdno(struct inode *inode, unsigned pos)
-{
-	struct hpfs_dirent *de;
-	struct quad_buffer_head qbh;
-
-	/*
-	 * 0 is the root dnode
-	 */
-
-	if (pos == 0)
-		return inode->i_hpfs_dno;
-
-	/*
-	 * we have one pos->dnode translation cached in the inode
-	 */
-
-	else if (pos == inode->i_hpfs_dpos)
-		return inode->i_hpfs_dsubdno;
-
-	/*
-	 * otherwise go look
-	 */
-
-	else {
-		unsigned q = pos >> 6;
-		unsigned r = pos & 077;
-		dnode_secno dno;
-
-		/*
-		 * dnode at position q
-		 */
-		dno = dir_subdno(inode, q);
-		if (dno == 0)
-			return 0;
-
-		/*
-		 * entry at index r
-		 */
-		de = map_nth_dirent(inode->i_dev, dno, r, &qbh);
-		if (!de || !de->down)
-			return 0;
-
-		/*
-		 * get the dnode down pointer
-		 */
-		dno = de_down_pointer(de);
-		brelse4(&qbh);
-
-		/*
-		 * cache it for next time
-		 */
-		inode->i_hpfs_dpos = pos;
-		inode->i_hpfs_dsubdno = dno;
-		return dno;
-	}
-}
-
-/*
- * Return the dir entry at index n in dnode dno, or 0 if there isn't one
- */
-
-static struct hpfs_dirent *map_nth_dirent(kdev_t dev, dnode_secno dno,
-					  int n,
-					  struct quad_buffer_head *qbh)
-{
-	int i;
-	struct hpfs_dirent *de, *de_end;
-	struct dnode *dnode = map_dnode(dev, dno, qbh);
-
-	de = dnode_first_de(dnode);
-	de_end = dnode_end_de(dnode);
-
-	for (i = 1; de < de_end; i++, de = de_next_de(de)) {
-		if (i == n)
-			return de;
-		if (de->last || de->length == 0)
-			break;
-	}
-
-	brelse4(qbh);
-	return 0;
-}
-
-static ssize_t hpfs_dir_read(struct file *filp, char *buf,
-			     size_t count, loff_t *ppos)
-{
-	return -EISDIR;
-}
-
-/* Return the dnode pointer in a directory fnode */
-
-static dnode_secno fnode_dno(kdev_t dev, ino_t ino)
-{
-	struct buffer_head *bh;
-	struct fnode *fnode;
-	dnode_secno dno;
-
-	fnode = map_fnode(dev, ino, &bh);
-	if (!fnode)
-		return 0;
-
-	dno = fnode->u.external[0].disk_secno;
-	brelse(bh);
-	return dno;
-}
-
-/* Map an fnode into a buffer and return pointers to it and to the buffer. */
-
-static struct fnode *map_fnode(kdev_t dev, ino_t ino, struct buffer_head **bhp)
-{
-	struct fnode *fnode;
-
-	if (ino == 0) {
-		printk("HPFS: missing fnode\n");
-		return 0;
-	}
-
-	fnode = map_sector(dev, ino_secno(ino), bhp);
-	if (fnode)
-		if (fnode->magic != FNODE_MAGIC) {
-			printk("HPFS: map_fnode: bad fnode pointer\n");
-			brelse(*bhp);
-			return 0;
-		}
-	return fnode;
-}
-
-/* Map an anode into a buffer and return pointers to it and to the buffer. */
-
-static struct anode *map_anode(kdev_t dev, unsigned secno,
-			       struct buffer_head **bhp)
-{
-	struct anode *anode;
-
-	if (secno == 0) {
-		printk("HPFS: missing anode\n");
-		return 0;
-	}
-
-	anode = map_sector(dev, secno, bhp);
-	if (anode)
-		if (anode->magic != ANODE_MAGIC || anode->self != secno) {
-			printk("HPFS: map_anode: bad anode pointer\n");
-			brelse(*bhp);
-			return 0;
-		}
-	return anode;
-}
-
-/* Map a dnode into a buffer and return pointers to it and to the buffer. */
-
-static struct dnode *map_dnode(kdev_t dev, unsigned secno,
-			       struct quad_buffer_head *qbh)
-{
-	struct dnode *dnode;
-
-	if (secno == 0) {
-		printk("HPFS: missing dnode\n");
-		return 0;
-	}
-
-	dnode = map_4sectors(dev, secno, qbh);
-	if (dnode)
-		if (dnode->magic != DNODE_MAGIC || dnode->self != secno) {
-			printk("HPFS: map_dnode: bad dnode pointer\n");
-			brelse4(qbh);
-			return 0;
-		}
-	return dnode;
-}
-
-/* Map a sector into a buffer and return pointers to it and to the buffer. */
-
-static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp)
-{
-	struct buffer_head *bh;
-
-	if ((*bhp = bh = bread(dev, secno, 512)) != 0)
-		return bh->b_data;
-	else {
-		printk("HPFS: map_sector: read error\n");
-		return 0;
-	}
-}
-
-/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
-
-static void *map_4sectors(kdev_t dev, unsigned secno,
-			  struct quad_buffer_head *qbh)
-{
-	struct buffer_head *bh;
-	char *data;
-
-	if (secno & 3) {
-		printk("HPFS: map_4sectors: unaligned read\n");
-		return 0;
-	}
-
-	qbh->data = data = kmalloc(2048, GFP_KERNEL);
-	if (!data)
-		goto bail;
-
-	qbh->bh[0] = bh = bread(dev, secno, 512);
-	if (!bh)
-		goto bail0;
-	memcpy(data, bh->b_data, 512);
-
-	qbh->bh[1] = bh = bread(dev, secno + 1, 512);
-	if (!bh)
-		goto bail1;
-	memcpy(data + 512, bh->b_data, 512);
-
-	qbh->bh[2] = bh = bread(dev, secno + 2, 512);
-	if (!bh)
-		goto bail2;
-	memcpy(data + 2 * 512, bh->b_data, 512);
-
-	qbh->bh[3] = bh = bread(dev, secno + 3, 512);
-	if (!bh)
-		goto bail3;
-	memcpy(data + 3 * 512, bh->b_data, 512);
-
-	return data;
-
- bail3:
-	brelse(qbh->bh[2]);
- bail2:
-	brelse(qbh->bh[1]);
- bail1:
-	brelse(qbh->bh[0]);
- bail0:
-	kfree_s(data, 2048);
- bail:
-	printk("HPFS: map_4sectors: read error\n");
-	return 0;
-}
-
-/* Deallocate a 4-buffer block */
-
-static void brelse4(struct quad_buffer_head *qbh)
-{
-	brelse(qbh->bh[3]);
-	brelse(qbh->bh[2]);
-	brelse(qbh->bh[1]);
-	brelse(qbh->bh[0]);
-	kfree_s(qbh->data, 2048);
-}
-
-static struct file_system_type hpfs_fs_type = {
-	"hpfs", 
-	FS_REQUIRES_DEV,
-        hpfs_read_super,
-	NULL
-};
-
-__initfunc(int init_hpfs_fs(void))
-{
-        return register_filesystem(&hpfs_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int init_module(void)
-{
-	return init_hpfs_fs();
-}
-
-void cleanup_module(void)
-{
-        unregister_filesystem(&hpfs_fs_type);
-}
-
-#endif
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/inode.c linux/fs/hpfs/inode.c
--- v2.3.1/linux/fs/hpfs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/inode.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,380 @@
+/*
+ *  linux/fs/hpfs/inode.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  inode VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+static const struct file_operations hpfs_file_ops =
+{
+	NULL,				/* lseek - default */
+	hpfs_file_read,			/* read */
+	hpfs_file_write,		/* write */
+	NULL,				/* readdir - bad */
+	NULL,				/* poll - default */
+	NULL,				/* ioctl - default */
+	generic_file_mmap/*hpfs_mmap*/,	/* mmap */
+	hpfs_open,			/* open */
+	NULL,				/* flush */
+	hpfs_file_release,		/* release */
+	hpfs_file_fsync,		/* fsync */
+	NULL,				/* fasync */
+	NULL,				/* check_media_change */
+	NULL,				/* revalidate */
+	NULL,				/* lock */
+};
+
+static const struct inode_operations hpfs_file_iops =
+{
+	(nonconst *) & hpfs_file_ops,	/* default file operations */
+	NULL,				/* create */
+	NULL,				/* lookup */
+	NULL,				/* link */
+	NULL,				/* unlink */
+	NULL,				/* symlink */
+	NULL,				/* mkdir */
+	NULL,				/* rmdir */
+	NULL,				/* mknod */
+	NULL,				/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	generic_readpage,		/* readpage */
+	NULL,				/* writepage */
+	(int (*)(struct inode *, int))
+	&hpfs_bmap,			/* bmap */
+	&hpfs_truncate,			/* truncate */
+	NULL,				/* permission */
+	NULL,				/* smap */
+	NULL,				/* updatepage */
+	NULL,				/* revalidate */
+};
+
+static const struct file_operations hpfs_dir_ops =
+{
+	NULL,				/* lseek - default */
+	hpfs_dir_read,			/* read */
+	NULL,				/* write - bad */
+	hpfs_readdir,			/* readdir */
+	NULL,				/* poll - default */
+	NULL,				/* ioctl - default */
+	NULL,				/* mmap */
+	hpfs_open,			/* open */
+	NULL,				/* flush */
+	hpfs_dir_release,		/* no special release code */
+	hpfs_file_fsync,		/* fsync */
+	NULL,				/* fasync */
+	NULL,				/* check_media_change */
+	NULL,				/* revalidate */
+	NULL,				/* lock */
+};
+
+static const struct inode_operations hpfs_dir_iops =
+{
+	(nonconst *) & hpfs_dir_ops,	/* default directory file ops */
+	hpfs_create,			/* create */
+	hpfs_lookup,			/* lookup */
+	NULL,				/* link */
+	hpfs_unlink,			/* unlink */
+	hpfs_symlink,			/* symlink */
+	hpfs_mkdir,			/* mkdir */
+	hpfs_rmdir,			/* rmdir */
+	hpfs_mknod,			/* mknod */
+	hpfs_rename,			/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL,				/* permission */
+	NULL,				/* smap */
+	NULL,				/* updatepage */
+	NULL,				/* revalidate */
+};
+
+const struct inode_operations hpfs_symlink_iops =
+{
+	NULL,				/* default file operations */
+	NULL,				/* create */
+	NULL,				/* lookup */
+	NULL,				/* link */
+	NULL,				/* unlink */
+	NULL,				/* symlink */
+	NULL,				/* mkdir */
+	NULL,				/* rmdir */
+	NULL,				/* mknod */
+	NULL,				/* rename */
+	hpfs_readlink,			/* readlink */
+	hpfs_follow_link,		/* follow_link */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL,				/* permission */
+	NULL,				/* smap */
+	NULL,				/* updatepage */
+	NULL,				/* revalidate */
+};
+
+
+void hpfs_read_inode(struct inode *i)
+{
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	struct super_block *sb = i->i_sb;
+	unsigned char *ea;
+	int ea_size;
+	i->i_op = 0;
+	init_MUTEX(&i->i_hpfs_sem);
+	i->i_uid = sb->s_hpfs_uid;
+	i->i_gid = sb->s_hpfs_gid;
+	i->i_mode = sb->s_hpfs_mode;
+	i->i_hpfs_conv = sb->s_hpfs_conv;
+	i->i_blksize = 512;
+	i->i_size = -1;
+	i->i_blocks = -1;
+	
+	i->i_hpfs_dno = 0;
+	i->i_hpfs_n_secs = 0;
+	i->i_hpfs_file_sec = 0;
+	i->i_hpfs_disk_sec = 0;
+	i->i_hpfs_dpos = 0;
+	i->i_hpfs_dsubdno = 0;
+	i->i_hpfs_ea_mode = 0;
+	i->i_hpfs_ea_uid = 0;
+	i->i_hpfs_ea_gid = 0;
+	i->i_hpfs_ea_size = 0;
+	i->i_version = ++event;
+
+	i->i_hpfs_rddir_off = NULL;
+	i->i_hpfs_dirty = 0;
+
+	i->i_atime = 0;
+	i->i_mtime = 0;
+	i->i_ctime = 0;
+
+	if (!i->i_sb->s_hpfs_rd_inode)
+		hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
+	if (i->i_sb->s_hpfs_rd_inode == 2) {
+		i->i_mode |= S_IFREG;
+		i->i_mode &= ~0111;
+		i->i_op = (struct inode_operations *) &hpfs_file_iops;
+		i->i_nlink = 1;
+		return;
+	}
+	if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
+		/*i->i_mode |= S_IFREG;
+		i->i_mode &= ~0111;
+		i->i_op = (struct inode_operations *) &hpfs_file_iops;
+		i->i_nlink = 0;*/
+		make_bad_inode(i);
+		return;
+	}
+	if (i->i_sb->s_hpfs_eas) {
+		if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
+			if (ea_size == 2) {
+				i->i_uid = ea[0] + (ea[1] << 8);
+				i->i_hpfs_ea_uid = 1;
+			}
+			kfree(ea);
+		}
+		if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
+			if (ea_size == 2) {
+				i->i_gid = ea[0] + (ea[1] << 8);
+				i->i_hpfs_ea_gid = 1;
+			}
+			kfree(ea);
+		}
+		if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
+			kfree(ea);
+			i->i_mode = S_IFLNK | 0777;
+			i->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+			i->i_nlink = 1;
+			i->i_size = ea_size;
+			i->i_blocks = 1;
+			brelse(bh);
+			return;
+		}
+		if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
+			if (ea_size == 2) {
+				i->i_mode = ea[0] + (ea[1] << 8);
+				i->i_hpfs_ea_mode = 1;
+			}
+			kfree(ea);
+			if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
+				if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
+					if (ea_size == 4)
+						i->i_rdev = to_kdev_t(ea[0] + (ea[1] << 8) + (ea[2] << 16) + (ea[3] << 24));
+					kfree(ea);
+				}
+			}
+			if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode) || S_ISFIFO(i->i_mode) || S_ISSOCK(i->i_mode)) {
+				brelse(bh);
+				i->i_nlink = 1;
+				i->i_size = 0;
+				i->i_blocks = 1;
+				i->i_op = NULL;
+				if (S_ISBLK(i->i_mode)) i->i_op = (struct inode_operations *) &blkdev_inode_operations;
+				if (S_ISCHR(i->i_mode)) i->i_op = (struct inode_operations *) &chrdev_inode_operations;
+				if (S_ISFIFO(i->i_mode)) init_fifo(i);
+				return;
+			}
+		}
+	}
+	if (fnode->dirflag) {
+		unsigned n_dnodes, n_subdirs;
+		i->i_mode |= S_IFDIR;
+		i->i_op = (struct inode_operations *) &hpfs_dir_iops;
+		i->i_hpfs_parent_dir = fnode->up;
+		i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+		if (sb->s_hpfs_chk >= 2) {
+			struct buffer_head *bh0;
+			if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+		}
+		n_dnodes = 0; n_subdirs = 0;
+		hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+		i->i_blocks = 4 * n_dnodes;
+		i->i_size = 2048 * n_dnodes;
+		i->i_nlink = 2 + n_subdirs;
+	} else {
+		i->i_mode |= S_IFREG;
+		if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+		i->i_op = (struct inode_operations *) &hpfs_file_iops;
+		i->i_nlink = 1;
+		i->i_size = fnode->file_size;
+		i->i_blocks = ((i->i_size + 511) >> 9) + 1;
+	}
+	brelse(bh);
+}
+
+void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
+{
+	if (fnode->acl_size_l || fnode->acl_size_s) {
+		/* Some unknown structures like ACL may be in fnode,
+		   we'd better not overwrite them */
+		hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
+	} else if (i->i_sb->s_hpfs_eas >= 2) {
+		unsigned char ea[4];
+		if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+			ea[0] = i->i_uid & 0xff;
+			ea[1] = i->i_uid >> 8;
+			hpfs_set_ea(i, fnode, "UID", ea, 2);
+			i->i_hpfs_ea_uid = 1;
+		}
+		if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+			ea[0] = i->i_gid & 0xff;
+			ea[1] = i->i_gid >> 8;
+			hpfs_set_ea(i, fnode, "GID", ea, 2);
+			i->i_hpfs_ea_gid = 1;
+		}
+		if (!S_ISLNK(i->i_mode))
+			if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
+			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
+			  && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
+			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+				ea[0] = i->i_mode & 0xff;
+				ea[1] = i->i_mode >> 8;
+				hpfs_set_ea(i, fnode, "MODE", ea, 2);
+				i->i_hpfs_ea_mode = 1;
+			}
+		if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
+			int d = kdev_t_to_nr(i->i_rdev);
+			ea[0] = d & 0xff;
+			ea[1] = (d >> 8) & 0xff;
+			ea[2] = (d >> 16) & 0xff;
+			ea[3] = d >> 24;
+			hpfs_set_ea(i, fnode, "DEV", ea, 4);
+		}
+	}
+}
+
+void hpfs_write_inode(struct inode *i)
+{
+	struct inode *parent;
+	if (!i->i_nlink) return;
+	if (i->i_ino == i->i_sb->s_hpfs_root) return;
+	if (i->i_hpfs_rddir_off && !i->i_count) {
+		if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
+		kfree(i->i_hpfs_rddir_off);
+		i->i_hpfs_rddir_off = NULL;
+	}
+	i->i_hpfs_dirty = 0;
+	hpfs_lock_iget(i->i_sb, 1);
+	parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+	hpfs_unlock_iget(i->i_sb);
+	hpfs_lock_inode(parent);
+	hpfs_write_inode_nolock(i);
+	hpfs_unlock_inode(parent);
+	iput(parent);
+}
+
+void hpfs_write_inode_nolock(struct inode *i)
+{
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	if (i->i_ino == i->i_sb->s_hpfs_root) return;
+	if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
+	if (i->i_ino != i->i_sb->s_hpfs_root) {
+		if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
+			brelse(bh);
+			return;
+		}
+	} else de = NULL;
+	if (S_ISREG(i->i_mode)) {
+		fnode->file_size = de->file_size = i->i_size;
+	} else if (S_ISDIR(i->i_mode)) {
+		fnode->file_size = de->file_size = 0;
+	}
+	hpfs_write_inode_ea(i, fnode);
+	if (de) {
+		de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+		de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+		de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+		de->read_only = !(i->i_mode & 0222);
+		de->ea_size = i->i_hpfs_ea_size;
+		hpfs_mark_4buffers_dirty(&qbh);
+		hpfs_brelse4(&qbh);
+	}
+	if (S_ISDIR(i->i_mode)) {
+		if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh, NULL))) {
+			de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+			de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+			de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+			de->read_only = !(i->i_mode & 0222);
+			de->ea_size = /*i->i_hpfs_ea_size*/0;
+			de->file_size = 0;
+			hpfs_mark_4buffers_dirty(&qbh);
+			hpfs_brelse4(&qbh);
+		} else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino);
+	}
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+}
+
+int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL;
+	if ((error = inode_change_ok(inode, attr))) return error;
+	inode_setattr(inode, attr);
+	hpfs_write_inode(inode);
+	return 0;
+}
+
+void hpfs_write_if_changed(struct inode *inode)
+{
+	if (inode->i_hpfs_dirty) {
+		hpfs_write_inode(inode);
+	}
+}
+
+void hpfs_delete_inode(struct inode *inode)
+{
+	hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/map.c linux/fs/hpfs/map.c
--- v2.3.1/linux/fs/hpfs/map.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/map.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,268 @@
+/*
+ *  linux/fs/hpfs/map.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  mapping structures to memory with some minimal checks
+ */
+
+#include "hpfs_fn.h"
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
+{
+	return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
+}
+
+unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
+			 struct quad_buffer_head *qbh, char *id)
+{
+	secno sec;
+	if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
+		hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
+		return NULL;
+	}
+	sec = s->s_hpfs_bmp_dir[bmp_block];
+	if (!sec || sec > s->s_hpfs_fs_size-4) {
+		hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
+		return NULL;
+	}
+	return hpfs_map_4sectors(s, sec, qbh, 4);
+}
+
+/*
+ * Load first code page into kernel memory, return pointer to 256-byte array,
+ * first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are
+ * lowercasing table
+ */
+
+char *hpfs_load_code_page(struct super_block *s, secno cps)
+{
+	struct buffer_head *bh;
+	secno cpds;
+	unsigned cpi;
+	unsigned char *ptr;
+	unsigned char *cp_table;
+	int i;
+	struct code_page_data *cpd;
+	struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
+	if (!cp) return NULL;
+	if (cp->magic != CP_DIR_MAGIC) {
+		printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
+		brelse(bh);
+		return NULL;
+	}
+	if (!cp->n_code_pages) {
+		printk("HPFS: n_code_pages == 0\n");
+		brelse(bh);
+		return NULL;
+	}
+	cpds = cp->array[0].code_page_data;
+	cpi = cp->array[0].index;
+	brelse(bh);
+	
+	if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
+	if ((unsigned)cpd->offs[cpi] > 0x178) {
+		printk("HPFS: Code page index out of sector\n");
+		brelse(bh);
+		return NULL;
+	}
+	ptr = (char *)cpd + cpd->offs[cpi] + 6;
+	if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
+		printk("HPFS: out of memory for code page table\n");
+		brelse(bh);
+		return NULL;
+	}
+	memcpy(cp_table, ptr, 128);
+	brelse(bh);
+
+	/* Try to build lowercasing table from uppercasing one */
+
+	for (i=128; i<256; i++) cp_table[i]=i;
+	for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128)
+		cp_table[cp_table[i-128]] = i;
+	
+	return cp_table;
+}
+
+secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
+{
+	struct buffer_head *bh;
+	int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
+	int i;
+	secno *b;
+	if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
+		printk("HPFS: can't allocate memory for bitmap directory\n");
+		return NULL;
+	}	
+	for (i=0;i<n;i++) {
+		secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
+		if (!d) {
+			kfree(b);
+			return NULL;
+		}
+		memcpy((char *)b + 512 * i, d, 512);
+		brelse(bh);
+	}
+	return b;
+}
+
+/*
+ * Load fnode to memory
+ */
+
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
+{
+	struct fnode *fnode;
+	if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
+		return NULL;
+	}
+	if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
+		if (s->s_hpfs_chk) {
+			struct extended_attribute *ea;
+			struct extended_attribute *ea_end;
+			if (fnode->magic != FNODE_MAGIC) {
+				hpfs_error(s, "bad magic on fnode %08x", ino);
+				goto bail;
+			}
+			if (!fnode->dirflag) {
+				if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
+				    (fnode->btree.internal ? 12 : 8)) {
+					hpfs_error(s, "bad number of nodes in fnode %08x", ino);
+					goto bail;
+				}
+				if (fnode->btree.first_free !=
+				    8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
+					hpfs_error(s, "bad first_free pointer in fnode %08x", ino);
+					goto bail;
+				}
+			}
+			if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
+			   (signed int)fnode->ea_offs + fnode->ea_size_s > 0x200)) {
+				hpfs_error(s, "bad EA info in fnode %08x: ea_offs == %04x ea_size_s == %04x",
+					ino, fnode->ea_offs, fnode->ea_size_s);
+				goto bail;
+			}
+			ea = fnode_ea(fnode);
+			ea_end = fnode_end_ea(fnode);
+			while (ea != ea_end) {
+				if (ea > ea_end) {
+					hpfs_error(s, "bad EA in fnode %08x", ino);
+					goto bail;
+				}
+				ea = next_ea(ea);
+			}
+		}
+	}
+	return fnode;
+	bail:
+	brelse(*bhp);
+	return NULL;
+}
+
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
+{
+	struct anode *anode;
+	if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
+	if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
+		if (s->s_hpfs_chk) {
+			if (anode->magic != ANODE_MAGIC || anode->self != ano) {
+				hpfs_error(s, "bad magic on anode %08x", ano);
+				goto bail;
+			}
+			if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
+			    (anode->btree.internal ? 60 : 40)) {
+				hpfs_error(s, "bad number of nodes in anode %08x", ano);
+				goto bail;
+			}
+			if (anode->btree.first_free !=
+			    8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
+				hpfs_error(s, "bad first_free pointer in anode %08x", ano);
+				goto bail;
+			}
+		}
+	return anode;
+	bail:
+	brelse(*bhp);
+	return NULL;
+}
+
+/*
+ * Load dnode to memory and do some checks
+ */
+
+struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
+			     struct quad_buffer_head *qbh)
+{
+	struct dnode *dnode;
+	if (s->s_hpfs_chk) {
+		if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
+		if (secno & 3) {
+			hpfs_error(s, "dnode %08x not byte-aligned", secno);
+			return NULL;
+		}	
+	}
+	if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
+		if (s->s_hpfs_chk) {
+			unsigned p, pp = 0;
+			unsigned char *d = (char *)dnode;
+			int b = 0;
+			if (dnode->magic != DNODE_MAGIC) {
+				hpfs_error(s, "bad magic on dnode %08x", secno);
+				goto bail;
+			}
+			if (dnode->self != secno)
+				hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
+			/* Check dirents - bad dirents would cause infinite
+			   loops or shooting to memory */
+			if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
+				hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
+				goto bail;
+			}
+			for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
+				struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
+				if (de->length > 292 || (de->length < 32) || (de->length & 3)) {
+					hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+					goto bail;
+				}
+				if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
+					hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+					goto bail;
+				}
+				if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
+				if (de->down) if (de_down_pointer(de) < 0x10) {
+					hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+					goto bail;
+				}
+				pp = p;
+				
+			}
+			if (p != dnode->first_free) {
+				hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
+				goto bail;
+			}
+			if (d[pp + 30] != 1 || d[pp + 31] != 255) {
+				hpfs_error(s, "dnode %08x does not end with \\377 entry", secno);
+				goto bail;
+			}
+			if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno);
+		}
+	return dnode;
+	bail:
+	hpfs_brelse4(qbh);
+	return NULL;
+}
+
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino)
+{
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	dnode_secno dno;
+
+	fnode = hpfs_map_fnode(s, ino, &bh);
+	if (!fnode)
+		return 0;
+
+	dno = fnode->u.external[0].disk_secno;
+	brelse(bh);
+	return dno;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/mmap.c linux/fs/hpfs/mmap.c
--- v2.3.1/linux/fs/hpfs/mmap.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/mmap.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,128 @@
+/*
+ *  linux/fs/hpfs/mmap.c
+ *
+ *  taken from fat filesystem
+ *
+ *  Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ *  Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+ *
+ *  Modified for HPFS by Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz)
+ *
+ *  mmap handling for hpfs filesystem
+ *	(generic_file_mmap may be used only on filesystems that keep zeros
+ *	 in last file sector beyond end)
+ */
+
+/*
+ * generic_file_mmap doesn't erase the space beyond file end in last sector. :-(
+ * Hpfs doesn't keep zeros in last sector. This causes problems with kernel
+ * mkdep.c and probably other programs. Additionally this could be a security
+ * hole - some interesting data, like pieces of /etc/shadow could be found
+ * beyond file end.
+ *
+ * So, I can't use generic mmap. mmap from fat filesystem looks good, so I used
+ * it.
+ *
+ * BTW. fat uses generic mmap on normal disks. Doesn't it also have above bugs?
+ * I don't think Msdos erases space in last sector.
+ *
+ * If you fix generic_file_mmap, you can remove this file and use it.
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Fill in the supplied page for mmap
+ */
+
+static unsigned long hpfs_file_mmap_nopage(
+	struct vm_area_struct * area,
+	unsigned long address,
+	int error_code)
+{
+	/*struct inode * inode = area->vm_inode;*/
+	struct inode * inode = area->vm_file->f_dentry->d_inode;
+	unsigned long page;
+	unsigned int clear;
+	loff_t pos;
+	long gap;	/* distance from eof to pos */
+
+	page = __get_free_page(GFP_KERNEL);
+	if (!page)
+		return page;
+	address &= PAGE_MASK;
+	pos = address - area->vm_start + area->vm_offset;
+
+	clear = 0;
+	gap = inode->i_size - pos;
+	if (gap <= 0){
+		/* mmaping beyond end of file */
+		clear = PAGE_SIZE;
+	}else{
+		int cur_read;
+		int need_read;
+		/*struct file *filp = area->vm_file;*/
+		struct file filp;
+		if (gap < PAGE_SIZE){
+			clear = PAGE_SIZE - gap;
+		}
+		filp.f_reada = 0;
+		filp.f_pos = pos;
+		filp.f_dentry=area->vm_file->f_dentry;
+		need_read = PAGE_SIZE - clear;
+		{
+			mm_segment_t cur_fs = get_fs();
+			set_fs (KERNEL_DS);
+			cur_read = generic_file_read (&filp,(char*)page
+				,need_read,&pos);
+			set_fs (cur_fs);
+		}
+		if (cur_read != need_read){
+			hpfs_error(inode->i_sb, "Error while reading an mmap file %08x", inode->i_ino);
+		}
+	}
+	if (clear > 0){
+		memset ((char*)page+PAGE_SIZE-clear,0,clear);
+	}
+	return page;
+}
+
+struct vm_operations_struct hpfs_file_mmap = {
+	NULL,			/* open */
+	NULL,			/* close */
+	NULL,			/* unmap */
+	NULL,			/* protect */
+	NULL,			/* sync */
+	NULL,			/* advise */
+	hpfs_file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	NULL,			/* swapout */
+	NULL,			/* swapin */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int hpfs_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct inode *inode = file->f_dentry->d_inode;
+	/*printk("start mmap\n");*/
+	if (vma->vm_flags & VM_SHARED)	/* only PAGE_COW or read-only supported now */
+		return -EINVAL;
+	if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	/*if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+	}*/
+
+	vma->vm_file = file;
+	/*inode->i_count++;*/
+	file->f_count++;
+	vma->vm_ops = &hpfs_file_mmap;
+	/*printk("end mmap\n");*/
+	return 0;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/name.c linux/fs/hpfs/name.c
--- v2.3.1/linux/fs/hpfs/name.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/name.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,141 @@
+/*
+ *  linux/fs/hpfs/name.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  operations with filenames
+ */
+
+#include "hpfs_fn.h"
+
+char *text_postfix[]={
+".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
+".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
+".RC", ".TEX", ".TXT", ".Y", ""};
+
+char *text_prefix[]={
+"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
+"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
+
+void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
+{
+	int i;
+	if (inode->i_hpfs_conv != CONV_AUTO) return;
+	for (i = 0; *text_postfix[i]; i++) {
+		int l = strlen(text_postfix[i]);
+		if (l <= len)
+			if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
+				goto text;
+	}
+	for (i = 0; *text_prefix[i]; i++) {
+		int l = strlen(text_prefix[i]);
+		if (l <= len)
+			if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
+				goto text;
+	}
+	inode->i_hpfs_conv = CONV_BINARY;
+	return;
+	text:
+	inode->i_hpfs_conv = CONV_TEXT;
+	return;
+}
+
+static inline int not_allowed_char(unsigned char c)
+{
+	return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
+	      c=='>' || c=='?' || c=='\\' || c=='|';
+}
+
+static inline int no_dos_char(unsigned char c)
+{	/* Characters that are allowed in HPFS but not in DOS */
+	return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']';
+}
+
+static inline unsigned char upcase(unsigned char *dir, unsigned char a)
+{
+	if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a;
+	if (!dir) return a;
+	return dir[a-128];
+}
+
+unsigned char hpfs_upcase(unsigned char *dir, unsigned char a)
+{
+	return upcase(dir, a);
+}
+
+static inline unsigned char locase(unsigned char *dir, unsigned char a)
+{
+	if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a;
+	if (!dir) return a;
+	return dir[a];
+}
+
+int hpfs_chk_name(unsigned char *name, unsigned len)
+{
+	int i;
+	if (!len || len > 254) return 1;
+	for (i = 0; i < len; i++) if (not_allowed_char(name[i])) return 1;
+	if (len == 1) if (name[0] == '.') return 1;
+	if (len == 2) if (name[0] == '.' && name[1] == '.') return 1;
+	return 0;
+}
+
+char *hpfs_translate_name(struct super_block *s, unsigned char *from,
+			  unsigned len, int lc, int lng)
+{
+	char *to;
+	int i;
+	if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
+		printk("HPFS: Long name flag mismatch - name ");
+		for (i=0; i<len; i++) printk("%c", from[i]);
+		printk(" misidentified as %s.\n", lng ? "short" : "long");
+		printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n");
+	}
+	if (!lc) return from;
+	if (!(to = kmalloc(len, GFP_KERNEL))) {
+		printk("HPFS: can't allocate memory for name conversion buffer\n");
+		return from;
+	}
+	for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
+	return to;
+}
+
+int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
+		  unsigned char *n2, unsigned l2, int last)
+{
+	unsigned l = l1 < l2 ? l1 : l2;
+	unsigned i;
+	if (last) return -1;
+	for (i = 0; i < l; i++) {
+		unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
+		unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
+		if (c1 < c2) return -1;
+		if (c1 > c2) return 1;
+	}
+	if (l1 < l2) return -1;
+	if (l1 > l2) return 1;
+	return 0;
+}
+
+int hpfs_is_name_long(unsigned char *name, unsigned len)
+{
+	int i,j;
+	for (i = 0; i < len && name[i] != '.'; i++)
+		if (no_dos_char(name[i])) return 1;
+	if (!i || i > 8) return 1;
+	if (i == len) return 0;
+	for (j = i + 1; j < len; j++)
+		if (name[j] == '.' || no_dos_char(name[i])) return 1;
+	return j - i > 4;
+}
+
+/* OS/2 clears dots and spaces at the end of file name, so we have to */
+
+void hpfs_adjust_length(unsigned char *name, unsigned *len)
+{
+	if (!*len) return;
+	if (*len == 1 && name[0] == '.') return;
+	if (*len == 2 && name[0] == '.' && name[1] == '.') return;
+	while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' '))
+		(*len)--;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/namei.c linux/fs/hpfs/namei.c
--- v2.3.1/linux/fs/hpfs/namei.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/namei.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,588 @@
+/*
+ *  linux/fs/hpfs/namei.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  adding & removing files & directories
+ */
+
+#include "hpfs_fn.h"
+
+int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct quad_buffer_head qbh0;
+	struct buffer_head *bh;
+	struct hpfs_dirent *de;
+	struct fnode *fnode;
+	struct dnode *dnode;
+	struct inode *result;
+	fnode_secno fno;
+	dnode_secno dno;
+	int r;
+	struct hpfs_dirent dee;
+	if (!dir) return -ENOENT;
+	if (!S_ISDIR(dir->i_mode)) {
+		return -ENOTDIR;
+	}
+	hpfs_adjust_length((char *)name, &len);
+	if (hpfs_chk_name((char *)name, len)) {
+		return -ENAMETOOLONG;
+	}	
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
+	memset(&dee, 0, sizeof dee);
+	dee.directory = 1;
+	if (!(mode & 0222)) dee.read_only = 1;
+	/*dee.archive = 0;*/
+	dee.hidden = name[0] == '.';
+	dee.fnode = fno;
+	dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+	hpfs_lock_inode(dir);
+	r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+	if (r == 1) goto bail2;
+	if (r == -1) {
+		brelse(bh);
+		hpfs_brelse4(&qbh0);
+		hpfs_free_sectors(dir->i_sb, fno, 1);
+		hpfs_free_dnode(dir->i_sb, dno);
+		hpfs_unlock_inode(dir);
+		return -EEXIST;
+	}
+	fnode->len = len;
+	memcpy(fnode->name, name, len > 15 ? 15 : len);
+	fnode->up = dir->i_ino;
+	fnode->dirflag = 1;
+	fnode->btree.n_free_nodes = 7;
+	fnode->btree.n_used_nodes = 1;
+	fnode->btree.first_free = 0x14;
+	fnode->u.external[0].disk_secno = dno;
+	fnode->u.external[0].file_secno = -1;
+	dnode->root_dnode = 1;
+	dnode->up = fno;
+	de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
+	de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+	if (!(mode & 0222)) de->read_only = 1;
+	de->first = de->directory = 1;
+	/*de->hidden = de->system = 0;*/
+	de->fnode = fno;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	hpfs_mark_4buffers_dirty(&qbh0);
+	hpfs_brelse4(&qbh0);
+	dir->i_nlink++;
+	hpfs_lock_iget(dir->i_sb, 1);
+	if ((result = iget(dir->i_sb, fno))) {
+		result->i_hpfs_parent_dir = dir->i_ino;
+		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+		result->i_hpfs_ea_size = 0;
+		if (dee.read_only) result->i_mode &= ~0222;
+		if (result->i_uid != current->fsuid ||
+		    result->i_gid != current->fsgid ||
+		    result->i_mode != (mode | S_IFDIR)) {
+			result->i_uid = current->fsuid;
+			result->i_gid = current->fsgid;
+			result->i_mode = mode | S_IFDIR;
+			hpfs_write_inode_nolock(result);
+		}
+		d_instantiate(dentry, result);
+	}
+	hpfs_unlock_iget(dir->i_sb);
+	hpfs_unlock_inode(dir);
+	return 0;
+	bail2:
+	hpfs_brelse4(&qbh0);
+	hpfs_free_dnode(dir->i_sb, dno);
+	hpfs_unlock_inode(dir);
+	bail1:
+	brelse(bh);
+	hpfs_free_sectors(dir->i_sb, fno, 1);
+	bail:
+	return -ENOSPC;
+}
+
+int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct inode *result;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	fnode_secno fno;
+	int r;
+	struct hpfs_dirent dee;
+	result = NULL;
+	if (!dir) return -ENOENT;
+	if (!S_ISDIR(dir->i_mode)) {
+		return -ENOTDIR;
+	}
+	if (!S_ISREG(mode)) {
+		return -EINVAL;
+	}
+	hpfs_adjust_length((char *)name, &len);
+	if (hpfs_chk_name((char *)name, len)) {
+		return -ENAMETOOLONG;
+	}	
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	memset(&dee, 0, sizeof dee);
+	if (!(mode & 0222)) dee.read_only = 1;
+	dee.archive = 1;
+	dee.hidden = name[0] == '.';
+	dee.fnode = fno;
+	dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+	hpfs_lock_inode(dir);
+	r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+	if (r == 1) goto bail1;
+	if (r == -1) {
+		brelse(bh);
+		hpfs_free_sectors(dir->i_sb, fno, 1);
+		hpfs_unlock_inode(dir);
+		return -EEXIST;
+	}
+	fnode->len = len;
+	memcpy(fnode->name, name, len > 15 ? 15 : len);
+	fnode->up = dir->i_ino;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	hpfs_lock_iget(dir->i_sb, 2);
+	if ((result = iget(dir->i_sb, fno))) {
+		hpfs_decide_conv(result, (char *)name, len);
+		result->i_hpfs_parent_dir = dir->i_ino;
+		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+		result->i_hpfs_ea_size = 0;
+		if (dee.read_only) result->i_mode &= ~0222;
+		if (result->i_blocks == -1) result->i_blocks = 1;
+		if (result->i_size == -1) result->i_size = 0;
+		if (result->i_uid != current->fsuid ||
+		    result->i_gid != current->fsgid ||
+		    result->i_mode != (mode | S_IFREG)) {
+			result->i_uid = current->fsuid;
+			result->i_gid = current->fsgid;
+			result->i_mode = mode | S_IFREG;
+			hpfs_write_inode_nolock(result);
+		}
+		d_instantiate(dentry, result);
+	}
+	hpfs_unlock_iget(dir->i_sb);
+	hpfs_unlock_inode(dir);
+	return 0;
+	bail1:
+	brelse(bh);
+	hpfs_free_sectors(dir->i_sb, fno, 1);
+	hpfs_unlock_inode(dir);
+	bail:
+	return -ENOSPC;
+}
+
+int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	fnode_secno fno;
+	int r;
+	struct hpfs_dirent dee;
+	struct inode *result = NULL;
+	if (!dir) return -ENOENT;
+	if (dir->i_sb->s_hpfs_eas < 2) {
+		/*iput(dir);*/
+		return -EPERM;
+	}
+	if (!S_ISDIR(dir->i_mode)) {
+		return -ENOTDIR;
+	}
+	if (!S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISFIFO(mode) && !S_ISSOCK(mode)) {
+		return -EINVAL;
+	}
+	hpfs_adjust_length((char *)name, &len);
+	if (hpfs_chk_name((char *)name, len)) {
+		return -ENAMETOOLONG;
+	}	
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	memset(&dee, 0, sizeof dee);
+	if (!(mode & 0222)) dee.read_only = 1;
+	dee.archive = 1;
+	dee.hidden = name[0] == '.';
+	dee.fnode = fno;
+	dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+	hpfs_lock_inode(dir);
+	r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+	if (r == 1) goto bail1;
+	if (r == -1) {
+		brelse(bh);
+		hpfs_free_sectors(dir->i_sb, fno, 1);
+		hpfs_unlock_inode(dir);
+		return -EEXIST;
+	}
+	fnode->len = len;
+	memcpy(fnode->name, name, len > 15 ? 15 : len);
+	fnode->up = dir->i_ino;
+	mark_buffer_dirty(bh, 1);
+	hpfs_lock_iget(dir->i_sb, 2);
+	if ((result = iget(dir->i_sb, fno))) {
+		result->i_hpfs_parent_dir = dir->i_ino;
+		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+		result->i_hpfs_ea_size = 0;
+		/*if (result->i_blocks == -1) result->i_blocks = 1;
+		if (result->i_size == -1) result->i_size = 0;*/
+		result->i_mode = mode;
+		result->i_uid = current->fsuid;
+		result->i_gid = current->fsgid;
+		if (!S_ISFIFO(mode)) result->i_rdev = to_kdev_t(rdev);
+		result->i_nlink = 1;
+		result->i_size = 0;
+		result->i_blocks = 1;
+		result->i_op = NULL;
+		if (S_ISBLK(result->i_mode)) result->i_op = (struct inode_operations *) &blkdev_inode_operations;
+		if (S_ISCHR(result->i_mode)) result->i_op = (struct inode_operations *) &chrdev_inode_operations;
+		if (S_ISFIFO(result->i_mode)) init_fifo(result);
+		hpfs_write_inode_nolock(result);
+		d_instantiate(dentry, result);
+	}
+	hpfs_unlock_iget(dir->i_sb);
+	hpfs_unlock_inode(dir);
+	brelse(bh);
+	return 0;
+	bail1:
+	brelse(bh);
+	hpfs_free_sectors(dir->i_sb, fno, 1);
+	hpfs_unlock_inode(dir);
+	bail:
+	return -ENOSPC;
+}
+
+extern const struct inode_operations hpfs_symlink_iops;
+
+int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	fnode_secno fno;
+	int r;
+	struct hpfs_dirent dee;
+	struct inode *result;
+	if (!dir) return -ENOENT;
+	if (dir->i_sb->s_hpfs_eas < 2) {
+		return -EPERM;
+	}
+	if (!S_ISDIR(dir->i_mode)) {
+		return -ENOTDIR;
+	}
+	hpfs_adjust_length((char *)name, &len);
+	if (hpfs_chk_name((char *)name, len)) {
+		return -ENAMETOOLONG;
+	}
+	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+	memset(&dee, 0, sizeof dee);
+	dee.archive = 1;
+	dee.hidden = name[0] == '.';
+	dee.fnode = fno;
+	dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+	hpfs_lock_inode(dir);
+	r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+	if (r == 1) goto bail1;
+	if (r == -1) {
+		brelse(bh);
+		hpfs_free_sectors(dir->i_sb, fno, 1);
+		hpfs_unlock_inode(dir);
+		return -EEXIST;
+	}
+	fnode->len = len;
+	memcpy(fnode->name, name, len > 15 ? 15 : len);
+	fnode->up = dir->i_ino;
+	mark_buffer_dirty(bh, 1);
+	brelse(bh);
+	hpfs_lock_iget(dir->i_sb, 2);
+	if ((result = iget(dir->i_sb, fno))) {
+		result->i_hpfs_parent_dir = dir->i_ino;
+		result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+		result->i_hpfs_ea_size = 0;
+		/*if (result->i_blocks == -1) result->i_blocks = 1;
+		if (result->i_size == -1) result->i_size = 0;*/
+		result->i_mode = S_IFLNK | 0777;
+		result->i_uid = current->fsuid;
+		result->i_gid = current->fsgid;
+		result->i_blocks = 1;
+		result->i_size = strlen(symlink);
+		result->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+		if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) {
+			hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+		}
+		hpfs_write_inode_nolock(result);
+		d_instantiate(dentry, result);
+	}
+	hpfs_unlock_iget(dir->i_sb);
+	hpfs_unlock_inode(dir);
+	return 0;
+	bail1:
+	brelse(bh);
+	hpfs_free_sectors(dir->i_sb, fno, 1);
+	hpfs_unlock_inode(dir);
+	bail:
+	return -ENOSPC;
+}
+
+int hpfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	struct inode *inode = dentry->d_inode;
+	dnode_secno dno;
+	fnode_secno fno;
+	int depth, r;
+	hpfs_adjust_length((char *)name, &len);
+	hpfs_lock_2inodes(dir, inode);
+	if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh, &depth))) {
+		hpfs_unlock_2inodes(dir, inode);
+		return -ENOENT;
+	}
+	if (de->first) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -EPERM;
+	}
+	if (de->directory) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -EISDIR;
+	}
+	fno = de->fnode;
+	if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, depth)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent");
+	if (r != 2) {
+		inode->i_nlink--;
+		hpfs_unlock_2inodes(dir, inode);
+		d_delete(dentry);
+	} else hpfs_unlock_2inodes(dir, inode);
+	return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	struct quad_buffer_head qbh;
+	struct hpfs_dirent *de;
+	struct inode *inode = dentry->d_inode;
+	dnode_secno dno;
+	fnode_secno fno;
+	int n_items = 0;
+	int depth, r;
+	hpfs_adjust_length((char *)name, &len);
+	hpfs_lock_2inodes(dir, inode);
+	if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh, &depth))) {
+		hpfs_unlock_2inodes(dir, inode);
+		return -ENOENT;
+	}	
+	if (de->first) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -EPERM;
+	}
+	if (!de->directory) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -ENOTDIR;
+	}
+	/*shrink_dcache_parent(dentry);
+	if (dentry->d_count > 1) {*/
+	if (!list_empty(&dentry->d_hash)) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -EBUSY;
+	}
+	hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+	if (n_items) {
+		hpfs_brelse4(&qbh);
+		hpfs_unlock_2inodes(dir, inode);
+		return -ENOTEMPTY;
+	}
+	fno = de->fnode;
+	if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, depth)) == 1)
+		hpfs_error(dir->i_sb, "there was error when removing dirent");
+	if (r != 2) {
+		dir->i_nlink--;
+		inode->i_nlink = 0;
+		hpfs_unlock_2inodes(dir, inode);
+		d_delete(dentry);
+	} else hpfs_unlock_2inodes(dir, inode);
+	return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_readlink(struct dentry *dentry, char *buf, int len)
+{
+	struct inode *i = dentry->d_inode;
+	struct fnode *fnode;
+	struct buffer_head *bh;
+	char *symlink;
+	int slen;
+	if (!S_ISLNK(i->i_mode)) {
+		return -EINVAL;
+	}
+	if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+		return -EIO;
+	}
+	if (!(symlink = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &slen))) {
+		brelse(bh);
+		return -EFSERROR;
+	}
+	brelse(bh);
+	if (slen > len) slen = len;
+	memcpy_tofs(buf, symlink, slen);
+	kfree(symlink);
+	return slen;
+}
+
+struct dentry *hpfs_follow_link(struct dentry *dinode, struct dentry *ddir,
+				unsigned int follow)
+{
+	struct inode *inode = dinode->d_inode;
+	char *link;
+	int len;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) {
+		dput(dinode);
+		return ERR_PTR(-EIO);
+	}
+	if (!(link = hpfs_get_ea(inode->i_sb, fnode, "SYMLINK", &len))) {
+		brelse(bh);
+		dput(dinode);
+		return ERR_PTR(-EIO);
+	}
+	brelse(bh);
+	UPDATE_ATIME(inode);
+	ddir = lookup_dentry(link, ddir, follow);
+	kfree(link);
+	return ddir;
+}
+
+int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		struct inode *new_dir, struct dentry *new_dentry)
+{
+	char *old_name = (char *)old_dentry->d_name.name;
+	int old_len = old_dentry->d_name.len;
+	char *new_name = (char *)new_dentry->d_name.name;
+	int new_len = new_dentry->d_name.len;
+	struct inode *i;
+	struct inode *new_inode = new_dentry->d_inode;
+	struct quad_buffer_head qbh, qbh1;
+	struct hpfs_dirent *dep, *nde;
+	struct hpfs_dirent de;
+	dnode_secno dno;
+	int depth, r;
+	int err = 0;
+	struct buffer_head *bh;
+	struct fnode *fnode;
+	hpfs_adjust_length((char *)old_name, &old_len);
+	hpfs_adjust_length((char *)new_name, &new_len);
+	if (hpfs_chk_name((char *)new_name, new_len) || hpfs_chk_name((char *)old_name, old_len)) {
+		err = -ENAMETOOLONG;
+		goto end2;
+	}
+	if (!(i = old_dentry->d_inode)) {
+		hpfs_error(old_dir->i_sb, "hpfs_rename: grrr, could not get inode");
+		err = -ENOENT;
+		goto end2;
+	}
+	if (i->i_sb != old_dir->i_sb || i->i_sb != new_dir->i_sb) {
+		err = -EINVAL; /* Do not allow to move mount points */
+		goto end1_;
+	}
+
+	hpfs_lock_3inodes(old_dir, new_dir, i);
+	
+	if (is_subdir(new_dentry, old_dentry) || (new_inode && S_ISDIR(new_inode->i_mode))) {
+		err = -EINVAL;
+		goto end1;
+	}
+
+	if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh, &depth))) {
+		hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
+		err = -ENOENT;
+		goto end1;
+	}
+	copy_de(&de, dep);
+	de.hidden = new_name[0] == '.';
+
+	if (new_inode) {
+		hpfs_brelse4(&qbh);
+		if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1, NULL))) {
+			int r;
+			if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, depth)) != 2) {
+				if (!(nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1, NULL))) {
+					hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent #2");
+					goto end1;
+				}
+				new_inode->i_nlink = 0;
+				copy_de(nde, &de);
+				memcpy(nde->name, new_name, new_len);
+				hpfs_mark_4buffers_dirty(&qbh1);
+				hpfs_brelse4(&qbh1);
+				goto end;
+			}
+			err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+			goto end1;
+		}
+		hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
+		err = -EFSERROR;
+		goto end1;
+	}
+
+	if (new_dir == old_dir) hpfs_brelse4(&qbh);
+
+	hpfs_lock_creation(i->i_sb);
+	if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, depth + 2))) {
+		hpfs_unlock_creation(i->i_sb);
+		if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
+		err = r == 1 ? -ENOSPC : -EFSERROR;
+		if (new_dir != old_dir) hpfs_brelse4(&qbh);
+		goto end1;
+	}
+	
+	if (new_dir == old_dir)
+		if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh, &depth))) {
+			hpfs_unlock_creation(i->i_sb);
+			hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
+			err = -ENOENT;
+			goto end1;
+		}
+
+	if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
+		hpfs_unlock_creation(i->i_sb);
+		hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
+		err = r == 2 ? -ENOSPC : -EFSERROR;
+		goto end1;
+	}
+	hpfs_unlock_creation(i->i_sb);
+	
+	end:
+	i->i_hpfs_parent_dir = new_dir->i_ino;
+	if (S_ISDIR(i->i_mode)) {
+		new_dir->i_nlink++;
+		old_dir->i_nlink--;
+	}
+	if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+		fnode->up = new_dir->i_ino;
+		fnode->len = new_len;
+		memcpy(fnode->name, new_name, new_len>15?15:new_len);
+		if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+	}
+	d_move(old_dentry, new_dentry);
+	i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+	hpfs_decide_conv(i, (char *)new_name, new_len);
+	end1:
+	hpfs_unlock_3inodes(old_dir, new_dir, i);
+	end1_:
+	end2:
+	return err;
+}
diff -u --recursive --new-file v2.3.1/linux/fs/hpfs/super.c linux/fs/hpfs/super.c
--- v2.3.1/linux/fs/hpfs/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hpfs/super.c	Thu May 13 23:48:20 1999
@@ -0,0 +1,595 @@
+/*
+ *  linux/fs/hpfs/super.c
+ *
+ *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ *  mouning, unmounting, error handling
+ */
+
+#include "hpfs_fn.h"
+#include <linux/module.h>
+
+/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
+
+static void mark_dirty(struct super_block *s)
+{
+	if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
+		struct buffer_head *bh;
+		struct hpfs_spare_block *sb;
+		if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+			sb->dirty = 1;
+			sb->old_wrote = 0;
+			mark_buffer_dirty(bh, 1);
+			brelse(bh);
+		}
+	}
+}
+
+/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
+   were errors) */
+
+static void unmark_dirty(struct super_block *s)
+{
+	struct buffer_head *bh;
+	struct hpfs_spare_block *sb;
+	if (s->s_flags & MS_RDONLY) return;
+	if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+		sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
+		sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
+		mark_buffer_dirty(bh, 1);
+		brelse(bh);
+	}
+}
+
+/* Filesystem error... */
+
+#define ERR_BUF_SIZE 1024
+
+void hpfs_error(struct super_block *s, char *m,...)
+{
+	char *buf;
+	va_list l;
+	va_start(l, m);
+	if (!(buf = kmalloc(ERR_BUF_SIZE, GFP_KERNEL)))
+		printk("HPFS: No memory for error message '%s'\n",m);
+	else if (vsprintf(buf, m, l) >= ERR_BUF_SIZE)
+		printk("HPFS: Grrrr... Kernel memory corrupted ... going on, but it'll crash very soon :-(\n");
+	printk("HPFS: filesystem error: ");
+	if (buf) printk("%s", buf);
+	else printk("%s\n",m);
+	if (!s->s_hpfs_was_error) {
+		if (s->s_hpfs_err == 2) {
+			printk("; crashing the system because you wanted it\n");
+			mark_dirty(s);
+			panic("HPFS panic");
+		} else if (s->s_hpfs_err == 1) {
+			if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
+			else {
+				printk("; remounting read-only\n");
+				mark_dirty(s);
+				s->s_flags |= MS_RDONLY;
+			}
+		} else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
+		else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
+	} else printk("\n");
+	if (buf) kfree(buf);
+	s->s_hpfs_was_error = 1;
+}
+
+/* 
+ * A little trick to detect cycles in many hpfs structures and don't let the
+ * kernel crash on corrupted filesystem. When first called, set c2 to 0.
+ *
+ * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
+ * nested each in other, chkdsk locked up happilly.
+ */
+
+int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
+		char *msg)
+{
+	if (*c2 && *c1 == key) {
+		hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
+		return 1;
+	}
+	(*c2)++;
+	if (!((*c2 - 1) & *c2)) *c1 = key;
+	return 0;
+}
+
+void hpfs_put_super(struct super_block *s)
+{
+	if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+	if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+	unmark_dirty(s);
+	s->s_dev = 0;
+	MOD_DEC_USE_COUNT;
+}
+
+static unsigned count_one_bitmap(struct super_block *s, secno secno)
+{
+	struct quad_buffer_head qbh;
+	unsigned *bits;
+	unsigned i, count;
+	if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0;
+	count = 0;
+	for (i = 0; i < 2048 / sizeof(unsigned); i++) {
+		unsigned b; 
+		for (b = bits[i]; b; b>>=1) count += b & 1;
+	}
+	hpfs_brelse4(&qbh);
+	return count;
+}
+
+static unsigned count_bitmaps(struct super_block *s)
+{
+	unsigned n, count, n_bands;
+	n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+	count = 0;
+	for (n = 0; n < n_bands; n++)
+		count += count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
+	return count;
+}
+
+int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+{
+	struct statfs tmp;
+	/*if (s->s_hpfs_n_free == -1) {*/
+		s->s_hpfs_n_free = count_bitmaps(s);
+		s->s_hpfs_n_free_dnodes = count_one_bitmap(s, s->s_hpfs_dmap);
+	/*}*/
+	tmp.f_type = s->s_magic;
+	tmp.f_bsize = 512;
+	tmp.f_blocks = s->s_hpfs_fs_size;
+	tmp.f_bfree = s->s_hpfs_n_free;
+	tmp.f_bavail = s->s_hpfs_n_free;
+	tmp.f_files = s->s_hpfs_dirband_size / 4;
+	tmp.f_ffree = s->s_hpfs_n_free_dnodes;
+	tmp.f_namelen = 254;
+	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+/* Super operations */
+
+static const struct super_operations hpfs_sops =
+{
+        hpfs_read_inode,		/* read_inode */
+        NULL,				/* write_inode */
+	NULL,				/* put_inode */
+	hpfs_delete_inode,		/* delete inode */
+        hpfs_notify_change,		/* notify_change */
+        hpfs_put_super,			/* put_super */
+        NULL,				/* write_super */
+        hpfs_statfs,			/* statfs */
+        hpfs_remount_fs,		/* remount_fs */
+	NULL,				/* clear inode */
+	NULL,				/* umount_begin */
+};
+
+/*
+ * A tiny parser for option strings, stolen from dosfs.
+ *
+ * Stolen again from read-only hpfs.
+ */
+
+int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+	       int *lowercase, int *conv, int *eas, int *chk, int *errs,
+	       int *chkdsk, int *timeshift)
+{
+	char *p, *rhs;
+
+	if (!opts)
+		return 1;
+
+	/*printk("Parsing opts: '%s'\n",opts);*/
+
+	for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
+		if ((rhs = strchr(p, '=')) != 0)
+			*rhs++ = '\0';
+		if (!strcmp(p, "help")) return 2;
+		if (!strcmp(p, "uid")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*uid = simple_strtoul(rhs, &rhs, 0);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "gid")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*gid = simple_strtoul(rhs, &rhs, 0);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "umask")) {
+			if (!rhs || !*rhs)
+				return 0;
+			*umask = simple_strtoul(rhs, &rhs, 8);
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "timeshift")) {
+			int m = 1;
+			if (!rhs || !*rhs)
+				return 0;
+			if (*rhs == '-') m = -1;
+			if (*rhs == '+' || *rhs == '-') rhs++;
+			*timeshift = simple_strtoul(rhs, &rhs, 0) * m;
+			if (*rhs)
+				return 0;
+		}
+		else if (!strcmp(p, "case")) {
+			if (!strcmp(rhs, "lower"))
+				*lowercase = 1;
+			else if (!strcmp(rhs, "asis"))
+				*lowercase = 0;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "conv")) {
+			if (!strcmp(rhs, "binary"))
+				*conv = CONV_BINARY;
+			else if (!strcmp(rhs, "text"))
+				*conv = CONV_TEXT;
+			else if (!strcmp(rhs, "auto"))
+				*conv = CONV_AUTO;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "check")) {
+			if (!strcmp(rhs, "none"))
+				*chk = 0;
+			else if (!strcmp(rhs, "normal"))
+				*chk = 1;
+			else if (!strcmp(rhs, "strict"))
+				*chk = 2;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "errors")) {
+			if (!strcmp(rhs, "continue"))
+				*errs = 0;
+			else if (!strcmp(rhs, "remount-ro"))
+				*errs = 1;
+			else if (!strcmp(rhs, "panic"))
+				*errs = 2;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "eas")) {
+			if (!strcmp(rhs, "no"))
+				*eas = 0;
+			else if (!strcmp(rhs, "ro"))
+				*eas = 1;
+			else if (!strcmp(rhs, "rw"))
+				*eas = 2;
+			else
+				return 0;
+		}
+		else if (!strcmp(p, "chkdsk")) {
+			if (!strcmp(rhs, "no"))
+				*chkdsk = 0;
+			else if (!strcmp(rhs, "errors"))
+				*chkdsk = 1;
+			else if (!strcmp(rhs, "always"))
+				*chkdsk = 2;
+			else
+				return 0;
+		}
+		else
+			return 0;
+	}
+	return 1;
+}
+
+static inline void hpfs_help()
+{
+	printk("\n\
+HPFS filesystem options:\n\
+      help              do not mount and display this text\n\
+      uid=xxx           set uid of files that don't have uid specified in eas\n\
+      gid=xxx           set gid of files that don't have gid specified in eas\n\
+      umask=xxx         set mode of files that don't have mode specified in eas\n\
+      case=lower        lowercase all files\n\
+      case=asis         do not lowercase files (default)\n\
+      conv=binary       do not convert CR/LF -> LF (default)\n\
+      conv=auto         convert only files with known text extensions\n\
+      conv=text         convert all files\n\
+      check=none        no fs checks - kernel may crash on corrupted filesystem\n\
+      check=normal      do some checks - it should not crash (default)\n\
+      check=strict      do extra time-consuming checks, used for debugging\n\
+      errors=continue   continue on errors\n\
+      errors=remount-ro remount read-only if errors found (default)\n\
+      errors=panic      panic on errors\n\
+      chkdsk=no         do not mark fs for chkdsking even if there were errors\n\
+      chkdsk=errors     mark fs dirty if errors found (default)\n\
+      chkdsk=always     always mark fs dirty - used for debugging\n\
+      eas=no            ignore extended attributes\n\
+      eas=ro            read but do not write extended attributes\n\
+      eas=rw            r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
+      timeshift=nnn	add nnn seconds to file times\n\
+\n");
+}
+
+int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
+{
+	uid_t uid;
+	gid_t gid;
+	umode_t umask;
+	int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+	int o;
+	
+	*flags |= MS_NOATIME;
+	
+	uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
+	umask = 0777 & ~s->s_hpfs_mode;
+	lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
+	eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
+	errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
+
+	if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
+	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
+		printk("HPFS: bad mount options.\n");
+	    	return 1;
+	}
+	if (o == 2) {
+		hpfs_help();
+		return 1;
+	}
+	if (timeshift != s->s_hpfs_timeshift) {
+		printk("HPFS: timeshift can't be changed using remount.\n");
+		return 1;
+	}
+
+	unmark_dirty(s);
+
+	s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
+	s->s_hpfs_mode = 0777 & ~umask;
+	s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
+	s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
+	s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
+
+	if (!(*flags & MS_RDONLY)) mark_dirty(s);
+
+	return 0;
+}
+
+struct super_block *hpfs_read_super(struct super_block *s, void *options,
+				    int silent)
+{
+	kdev_t dev;
+	struct buffer_head *bh0, *bh1, *bh2;
+	struct hpfs_boot_block *bootblock;
+	struct hpfs_super_block *superblock;
+	struct hpfs_spare_block *spareblock;
+
+	uid_t uid;
+	gid_t gid;
+	umode_t umask;
+	int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+
+	dnode_secno root_dno;
+	struct hpfs_dirent *de = NULL;
+	struct quad_buffer_head qbh;
+
+	int o;
+
+	MOD_INC_USE_COUNT;
+
+	s->s_hpfs_bmp_dir = NULL;
+	s->s_hpfs_cp_table = NULL;
+
+	s->s_hpfs_creation_de_lock = s->s_hpfs_rd_inode = 0;
+	s->s_hpfs_creation_de = s->s_hpfs_iget_q = NULL;
+
+	uid = current->uid;
+	gid = current->gid;
+	umask = current->fs->umask;
+	lowercase = 0;
+	conv = CONV_BINARY;
+	eas = 2;
+	chk = 1;
+	errs = 1;
+	chkdsk = 1;
+	timeshift = 0;
+
+	if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
+		printk("HPFS: bad mount options.\n");
+		goto bail0;
+	}
+	if (o==2) {
+		hpfs_help();
+		goto bail0;
+	}
+
+	/*s->s_hpfs_mounting = 1;*/
+	lock_super(s);
+	dev = s->s_dev;
+	set_blocksize(dev, 512);
+	s->s_hpfs_fs_size = -1;
+	if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
+	if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
+	if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
+
+	/* Check magics */
+	if (/*bootblock->magic != BB_MAGIC
+	    ||*/ superblock->magic != SB_MAGIC
+	    || spareblock->magic != SP_MAGIC) {
+		if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
+		goto bail4;
+	}
+
+	/* Check version */
+	if (!(s->s_flags & MS_RDONLY) &&
+	      superblock->funcversion != 2 && superblock->funcversion != 3) {
+		printk("HPFS: Bad version %d,%d. Mount readonly to go around\n",
+			(int)superblock->version, (int)superblock->funcversion);
+		printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
+		goto bail4;
+	}
+
+	s->s_flags |= MS_NOATIME;
+
+	/* Fill superblock stuff */
+	s->s_magic = HPFS_SUPER_MAGIC;
+	s->s_blocksize = 512;
+	s->s_blocksize_bits = 9;
+	s->s_op = (struct super_operations *) &hpfs_sops;
+
+	s->s_hpfs_root = superblock->root;
+	s->s_hpfs_fs_size = superblock->n_sectors;
+	s->s_hpfs_bitmaps = superblock->bitmaps;
+	s->s_hpfs_dirband_start = superblock->dir_band_start;
+	s->s_hpfs_dirband_size = superblock->n_dir_band;
+	s->s_hpfs_dmap = superblock->dir_band_bitmap;
+	s->s_hpfs_uid = uid;
+	s->s_hpfs_gid = gid;
+	s->s_hpfs_mode = 0777 & ~umask;
+	s->s_hpfs_n_free = -1;
+	s->s_hpfs_n_free_dnodes = -1;
+	s->s_hpfs_lowercase = lowercase;
+	s->s_hpfs_conv = conv;
+	s->s_hpfs_eas = eas;
+	s->s_hpfs_chk = chk;
+	s->s_hpfs_chkdsk = chkdsk;
+	s->s_hpfs_err = errs;
+	s->s_hpfs_timeshift = timeshift;
+	s->s_hpfs_was_error = 0;
+	s->s_hpfs_cp_table = NULL;
+	s->s_hpfs_c_bitmap = -1;
+	
+	/* Load bitmap directory */
+	if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+		goto bail4;
+	
+	/* Check for general fs errors*/
+	if (spareblock->dirty && !spareblock->old_wrote) {
+		if (errs == 2) {
+			printk("HPFS: Improperly stopped, not mounted\n");
+			goto bail4;
+		}
+		hpfs_error(s, "improperly stopped");
+	}
+
+	if (!(s->s_flags & MS_RDONLY)) {
+		spareblock->dirty = 1;
+		spareblock->old_wrote = 0;
+		mark_buffer_dirty(bh2, 1);
+	}
+
+	if (spareblock->hotfixes_used || spareblock->n_spares_used) {
+		if (errs >= 2) {
+			printk("HPFS: Hotfixes not supported here, try chkdsk\n");
+			mark_dirty(s);
+			goto bail4;
+		}
+		hpfs_error(s, "hotfixes not supported here, try chkdsk");
+		if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
+		else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
+	}
+	if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
+		if (errs >= 2) {
+			printk("HPFS: Spare dnodes used, try chkdsk\n");
+			mark_dirty(s);
+			goto bail4;
+		}
+		hpfs_error(s, "warning: spare dnodes used, try chkdsk");
+		if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
+	}
+	if (chk) {
+		unsigned a;
+		if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
+		    superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
+			hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
+				superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
+			goto bail4;
+		}
+		a = s->s_hpfs_dirband_size;
+		s->s_hpfs_dirband_size = 0;
+		if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
+		    hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
+		    hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
+			mark_dirty(s);
+			goto bail4;
+		}
+		s->s_hpfs_dirband_size = a;
+	} else printk("HPFS: You really don't want any checks? You are crazy...\n");
+
+	/* Load code page table */
+	if (spareblock->n_code_pages)
+		if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+			printk("HPFS: Warning: code page support is disabled\n");
+
+	brelse(bh2);
+	brelse(bh1);
+	brelse(bh0);
+
+	hpfs_lock_iget(s, 1);
+	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
+	hpfs_unlock_iget(s);
+	unlock_super(s);
+	if (!s->s_root || !s->s_root->d_inode) {
+		printk("HPFS: iget failed. Why???\n");
+		goto bail0;
+	}
+	hpfs_set_dentry_operations(s->s_root);
+
+	/*
+	 * find the root directory's . pointer & finish filling in the inode
+	 */
+
+	root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
+	if (root_dno)
+		de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh, NULL);
+	if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
+	else {
+		s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
+		s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
+		s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
+		s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
+		s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+		if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
+		if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
+	}
+	if (de) hpfs_brelse4(&qbh);
+
+	return s;
+
+bail4:	brelse(bh2);
+bail3:	brelse(bh1);
+bail2:	brelse(bh0);
+bail1:	unlock_super(s);
+bail0:	s->s_dev = 0;
+	if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+	if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+struct file_system_type hpfs_fs_type = {
+	"hpfs", FS_REQUIRES_DEV, hpfs_read_super, NULL
+};
+
+int init_hpfs_fs(void)
+{
+	return register_filesystem(&hpfs_fs_type);
+}
+
+#ifdef MODULE
+
+/*int register_symtab_from(struct symbol_table *, long *);*/
+
+int init_module(void)
+{
+	/*int status;
+	if (!(status = init_hpfs_fs())) register_symtab(NULL);
+	return status;*/
+	return init_hpfs_fs();
+}
+
+void cleanup_module(void)
+{
+	unregister_filesystem(&hpfs_fs_type);
+}
+
+#endif
diff -u --recursive --new-file v2.3.1/linux/fs/inode.c linux/fs/inode.c
--- v2.3.1/linux/fs/inode.c	Fri May 14 18:55:24 1999
+++ linux/fs/inode.c	Thu May 13 23:18:20 1999
@@ -296,6 +296,7 @@
 			INIT_LIST_HEAD(&inode->i_hash);
 			list_del(&inode->i_list);
 			list_add(&inode->i_list, dispose);
+			inode->i_state |= I_FREEING;
 			continue;
 		}
 		busy = 1;
@@ -356,6 +357,7 @@
 		list_del(&INODE(tmp)->i_hash);
 		INIT_LIST_HEAD(&INODE(tmp)->i_hash);
 		list_add(tmp, freeable);
+		list_entry(tmp, struct inode, i_list)->i_state = I_FREEING;
 		found = 1;
 	}
 
@@ -639,6 +641,43 @@
 	return tmp & HASH_MASK;
 }
 
+/* Yeah, I know about quadratic hash. Maybe, later. */
+ino_t iunique(struct super_block *sb, ino_t max_reserved)
+{
+	static ino_t counter = 0;
+	struct inode *inode;
+	struct list_head * head;
+	ino_t res;
+	spin_lock(&inode_lock);
+retry:
+	if (counter > max_reserved) {
+		head = inode_hashtable + hash(sb,counter);
+		inode = find_inode(sb, res = counter++, head);
+		if (!inode) {
+			spin_unlock(&inode_lock);
+			return res;
+		}
+		inode->i_count--; /* compensate find_inode() */
+	} else {
+		counter = max_reserved + 1;
+	}
+	goto retry;
+	
+}
+
+struct inode *igrab(struct inode *inode)
+{
+	spin_lock(&inode_lock);
+	if (inode->i_state & I_FREEING)
+		inode = NULL;
+	else
+		inode->i_count++;
+	spin_unlock(&inode_lock);
+	if (inode)
+		wait_on_inode(inode);
+	return inode;
+}
+
 struct inode *iget(struct super_block *sb, unsigned long ino)
 {
 	struct list_head * head = inode_hashtable + hash(sb,ino);
@@ -692,6 +731,7 @@
 				INIT_LIST_HEAD(&inode->i_hash);
 				list_del(&inode->i_list);
 				INIT_LIST_HEAD(&inode->i_list);
+				inode->i_state|=I_FREEING;
 				if (op && op->delete_inode) {
 					void (*delete)(struct inode *) = op->delete_inode;
 					spin_unlock(&inode_lock);
@@ -702,6 +742,7 @@
 			if (list_empty(&inode->i_hash)) {
 				list_del(&inode->i_list);
 				INIT_LIST_HEAD(&inode->i_list);
+				inode->i_state|=I_FREEING;
 				spin_unlock(&inode_lock);
 				clear_inode(inode);
 				spin_lock(&inode_lock);
diff -u --recursive --new-file v2.3.1/linux/fs/msdos/msdosfs_syms.c linux/fs/msdos/msdosfs_syms.c
--- v2.3.1/linux/fs/msdos/msdosfs_syms.c	Fri Feb 20 18:28:23 1998
+++ linux/fs/msdos/msdosfs_syms.c	Thu May 13 23:18:20 1999
@@ -20,11 +20,9 @@
 EXPORT_SYMBOL(msdos_create);
 EXPORT_SYMBOL(msdos_lookup);
 EXPORT_SYMBOL(msdos_mkdir);
-EXPORT_SYMBOL(msdos_read_inode);
 EXPORT_SYMBOL(msdos_rename);
 EXPORT_SYMBOL(msdos_rmdir);
 EXPORT_SYMBOL(msdos_unlink);
-EXPORT_SYMBOL(msdos_unlink_umsdos);
 EXPORT_SYMBOL(msdos_read_super);
 EXPORT_SYMBOL(msdos_put_super);
 
diff -u --recursive --new-file v2.3.1/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.3.1/linux/fs/msdos/namei.c	Fri Apr 23 21:20:38 1999
+++ linux/fs/msdos/namei.c	Thu May 13 23:18:20 1999
@@ -3,6 +3,7 @@
  *
  *  Written 1992,1993 by Werner Almesberger
  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *  Rewritten for constant inumbers 1999 by Al Viro
  */
 
 
@@ -43,43 +44,24 @@
 static char bad_if_strict[] = "+=,; ";
 #endif
 
+/* Must die */
 void msdos_put_super(struct super_block *sb)
 {
 	fat_put_super(sb);
-	MOD_DEC_USE_COUNT;
 }
 
-struct super_operations msdos_sops = { 
-	msdos_read_inode,
-	fat_write_inode,
-	fat_put_inode,
-	fat_delete_inode,
-	fat_notify_change,
-	msdos_put_super,
-	NULL, /* added in 0.96c */
-	fat_statfs,
-	NULL
-};
-
 /***** Formats an MS-DOS file name. Rejects invalid names. */
 static int msdos_format_name(char conv,const char *name,int len,
-	char *res,int dot_dirs,char dotsOK)
+	char *res,char dotsOK)
 	/* conv is relaxed/normal/strict, name is proposed name,
 	 * len is the length of the proposed name, res is the result name,
-	 * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
+	 * dotsOK is if hidden files get dots.
 	 */
 {
 	char *walk;
 	const char **reserved;
 	unsigned char c;
 	int space;
-
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
-		if (!dot_dirs) return -EEXIST;
-		memset(res+1,' ',10);
-		while (len--) *res++ = '.';
-		return 0;
-	}
 	if (name[0] == '.') {  /* dotfile because . and .. already done */
 		if (!dotsOK) return -EINVAL;
 		/* Get rid of dot - test for it elsewhere */
@@ -142,32 +124,31 @@
 	return 0;
 }
 
-
 /***** Locates a directory entry.  Uses unformatted name. */
 static int msdos_find(struct inode *dir,const char *name,int len,
     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
 {
 	int res;
 	char dotsOK;
-	char scantype;
 	char msdos_name[MSDOS_NAME];
 
 	dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
 	res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-				name,len, msdos_name,1,dotsOK);
+				name,len, msdos_name,dotsOK);
 	if (res < 0)
 		return -ENOENT;
-	if((name[0]=='.') && dotsOK){
-	    switch(len){
-		case  0: panic("Empty name in msdos_find!");
-		case  1: scantype = SCAN_ANY;				break;
-		case  2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
-		default: scantype = SCAN_HID;
-	    }
-	} else {
-	    scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
+	res = fat_scan(dir,msdos_name,bh,de,ino);
+	if (!res && dotsOK) {
+		if (name[0]=='.') {
+			if (!((*de)->attr & ATTR_HIDDEN))
+				res = -ENOENT;
+		} else {
+			if ((*de)->attr & ATTR_HIDDEN)
+				res = -ENOENT;
+		}
 	}
-	return fat_scan(dir,msdos_name,bh,de,ino,scantype);
+	return res;
+
 }
 
 /*
@@ -183,7 +164,7 @@
 	char msdos_name[MSDOS_NAME];
 	
 	error = msdos_format_name(options->name_check, qstr->name, qstr->len,
-					msdos_name, 1, options->dotsOK);
+					msdos_name, options->dotsOK);
 	if (!error)
 		qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
 	return 0;
@@ -200,11 +181,11 @@
 	char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
 
 	error = msdos_format_name(options->name_check, a->name, a->len,
-					a_msdos_name, 1, options->dotsOK);
+					a_msdos_name, options->dotsOK);
 	if (error)
 		goto old_compare;
 	error = msdos_format_name(options->name_check, b->name, b->len,
-					b_msdos_name, 1, options->dotsOK);
+					b_msdos_name, options->dotsOK);
 	if (error)
 		goto old_compare;
 	error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
@@ -228,26 +209,9 @@
 	NULL
 };
 
-struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
-{
-	struct super_block *res;
-
-	MOD_INC_USE_COUNT;
-
-	MSDOS_SB(sb)->options.isvfat = 0;
-	sb->s_op = &msdos_sops;
-	res = fat_read_super(sb, data, silent);
-	if (res == NULL)
-		goto out_fail;
-	sb->s_root->d_op = &msdos_dentry_operations;
-	return res;
-
-out_fail:
-	sb->s_dev = 0;
-	MOD_DEC_USE_COUNT;
-	return NULL;
-}
-
+/*
+ * AV. Wrappers for FAT sb operations. Is it wise?
+ */
 
 /***** Get inode using directory and name */
 struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
@@ -255,7 +219,7 @@
 	struct super_block *sb = dir->i_sb;
 	struct inode *inode = NULL;
 	struct msdos_dir_entry *de;
-	struct buffer_head *bh;
+	struct buffer_head *bh = NULL;
 	int ino,res;
 	
 	PRINTK (("msdos_lookup\n"));
@@ -269,76 +233,52 @@
 		goto add;
 	if (res < 0)
 		goto out;
-	if (bh)
-		fat_brelse(sb, bh);
-
-	/* try to get the inode */
-	res = -EACCES;
-	inode = iget(sb, ino);
-	if (!inode)
-		goto out;
-	if (!inode->i_sb ||
-	   (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
-		printk(KERN_WARNING "msdos_lookup: foreign inode??\n");
-	}
-	/* mkdir in progress? */
-	if (MSDOS_I(inode)->i_busy) {
-		printk(KERN_WARNING "msdos_lookup: %s/%s busy\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
-		iput(inode);
+	inode = fat_build_inode(sb, de, ino, &res);
+	if (res)
 		goto out;
-	}
 add:
 	d_add(dentry, inode);
 	res = 0;
 out:
+	if (bh)
+		fat_brelse(sb, bh);
 	return ERR_PTR(res);
 }
 
-
 /***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,
-    int is_dir, int is_hid, struct inode **result)
+static int msdos_add_entry(struct inode *dir, const char *name,
+			   struct buffer_head **bh,
+			   struct msdos_dir_entry **de,
+			   int *ino,
+			   int is_dir, int is_hid)
 {
 	struct super_block *sb = dir->i_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	int res,ino;
+	int res;
 
-	*result = NULL;
-	if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
-		if (res != -ENOENT) return res;
-		if ((dir->i_ino == MSDOS_ROOT_INO) &&
-		    (MSDOS_SB(sb)->fat_bits != 32))
-			return -ENOSPC;
-		if ((res = fat_add_cluster(dir)) < 0) return res;
-		if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
-	}
+	if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
+		return res;
 	/*
 	 * XXX all times should be set by caller upon successful completion.
 	 */
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(dir);
-	memcpy(de->name,name,MSDOS_NAME);
-	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
-	de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
-	de->start = 0;
-	de->starthi = 0;
-	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
-	de->size = 0;
-	fat_mark_buffer_dirty(sb, bh, 1);
-
-	if ((*result = iget(dir->i_sb,ino)) != NULL)
-		msdos_read_inode(*result);
-	fat_brelse(sb, bh);
-	if (!*result) return -EIO;
-	(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
-	    CURRENT_TIME;
-	mark_inode_dirty(*result);
+	memcpy((*de)->name,name,MSDOS_NAME);
+	(*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+	if (is_hid)
+		(*de)->attr |= ATTR_HIDDEN;
+	(*de)->start = 0;
+	(*de)->starthi = 0;
+	fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+	(*de)->size = 0;
+	fat_mark_buffer_dirty(sb, *bh, 1);
 	return 0;
 }
 
-/***** Create a file or directory */
+/*
+ * AV. Huh??? It's exported. Oughtta check usage.
+ */
+
+/***** Create a file */
 int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
 {
 	struct super_block *sb = dir->i_sb;
@@ -350,79 +290,27 @@
 
 	res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
 				dentry->d_name.name,dentry->d_name.len,
-				msdos_name,0,
-				MSDOS_SB(sb)->options.dotsOK);
+				msdos_name, MSDOS_SB(sb)->options.dotsOK);
 	if (res < 0)
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
-	fat_lock_creation();
-	/* Scan for existing file twice, so that creating a file fails
-	 * with -EINVAL if the other (dotfile/nondotfile) exists.
-	 * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
-	 */
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
-		fat_unlock_creation();
-		fat_brelse(sb, bh);
-		return is_hid ? -EEXIST : -EINVAL;
- 	}
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
-		fat_unlock_creation();
+	/* Have to do it due to foo vs. .foo conflicts */
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
 		fat_brelse(sb, bh);
-		return is_hid ? -EINVAL : -EEXIST;
+		return -EINVAL;
  	}
-	res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
-				 &inode);
-	fat_unlock_creation();
-	if (!res)
-		d_instantiate(dentry, inode);
-	return res;
-}
-
-
-#ifdef DEBUG
-
-static void dump_fat(struct super_block *sb,int start)
-{
-	printk("[");
-	while (start) {
-		printk("%d ",start);
-        	start = fat_access(sb,start,-1);
-		if (!start) {
-			printk("ERROR");
-			break;
-		}
-		if (start == -1) break;
-	}
-	printk("]\n");
-}
-
-#endif
-
-/***** See if directory is empty */
-static int msdos_empty(struct inode *dir)
-{
-	loff_t pos;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	int result = 0;
-
-	pos = 0;
-	bh = NULL;
-	while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
-		/* Ignore vfat longname entries */
-		if (de->attr == ATTR_EXT)
-			continue;
-		if (!IS_FREE(de->name) && 
-		    strncmp(de->name,MSDOS_DOT   , MSDOS_NAME) &&
-		    strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
-			result = -ENOTEMPTY;
-			break;
-		}
-	}
-	if (bh)
-		fat_brelse(dir->i_sb, bh);
-
-	return result;
+	inode = NULL;
+	res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
+	if (res)
+		return res;
+	inode = fat_build_inode(dir->i_sb, de, ino, &res);
+	fat_brelse(sb, 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 */
@@ -446,23 +334,19 @@
 	res = -EBUSY;
 	if (!list_empty(&dentry->d_hash))
 		goto rmdir_done;
-	res = msdos_empty(inode);
+	res = fat_dir_empty(inode);
 	if (res)
 		goto rmdir_done;
 
+	de->name[0] = DELETED_FLAG;
+	fat_mark_buffer_dirty(sb, bh, 1);
+	fat_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);
-	/*
-	 * Do the d_delete before any blocking operations.
-	 * We must make a negative dentry, as the FAT code
-	 * apparently relies on the inode being iput().
-	 */
 	d_delete(dentry);
-	de->name[0] = DELETED_FLAG;
-	fat_mark_buffer_dirty(sb, bh, 1);
 	res = 0;
 
 rmdir_done:
@@ -476,77 +360,94 @@
 	struct super_block *sb = dir->i_sb;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	struct inode *inode,*dot;
-	int ino,res,is_hid;
+	struct inode *inode;
+	int res,is_hid;
 	char msdos_name[MSDOS_NAME];
+	struct buffer_head *bh1;
+	struct msdos_dir_entry *de1;
+	int ino;
 
 	res = msdos_format_name(MSDOS_SB(sb)->options.name_check,
 				dentry->d_name.name,dentry->d_name.len,
-				msdos_name,0,
-				MSDOS_SB(sb)->options.dotsOK);
+				msdos_name,MSDOS_SB(sb)->options.dotsOK);
 	if (res < 0)
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
-	fat_lock_creation();
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0)
+	/* foo vs .foo situation */
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino) >= 0)
 		goto out_exist;
 
-	res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
-	if (res < 0)
+	res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
+	if (res)
 		goto out_unlock;
+	inode = fat_build_inode(dir->i_sb, de, ino, &res);
+	if (!inode) {
+		fat_brelse(sb, bh);
+		goto out_unlock;
+	}
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	res = 0;
 
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 
-	if ((res = fat_add_cluster(inode)) < 0)
+	if (!(bh1 = fat_add_cluster1(inode))) {
+		res = -ENOSPC;
 		goto mkdir_error;
-	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
-	MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
-	MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart;
-	dot->i_nlink = inode->i_nlink;
-	mark_inode_dirty(dot);
-	iput(dot);
+	}
+	fat_brelse(sb, bh);
+	de1 = (struct msdos_dir_entry *)bh1->b_data;
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(inode);
 
-	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
-		goto mkdir_error;
-	dot->i_size = dir->i_size;
-	MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
-	MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
-	dot->i_nlink = dir->i_nlink;
-	mark_inode_dirty(dot);
-	iput(dot);
+	memcpy(de1->name,MSDOS_DOT,MSDOS_NAME);
+	de1->attr = ATTR_DIR;
+	de1->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
+	de1->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
+	fat_date_unix2dos(inode->i_mtime,&de1->time,&de1->date);
+	de1->size = 0;
+	de1->time = CT_LE_W(de1->time);
+	de1->date = CT_LE_W(de1->date);
+	de1++;
+	memcpy(de1->name,MSDOS_DOTDOT,MSDOS_NAME);
+	de1->attr = ATTR_DIR;
+	de1->start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+	de1->starthi = CT_LE_W(MSDOS_I(dir)->i_logstart >> 16);
+	fat_date_unix2dos(dir->i_mtime,&de1->time,&de1->date);
+	de1->size = 0;
+	de1->time = CT_LE_W(de1->time);
+	de1->date = CT_LE_W(de1->date);
+	fat_mark_buffer_dirty(sb, bh1, 1);
+	fat_brelse(sb, bh1);
 	d_instantiate(dentry, inode);
 	res = 0;
 
 out_unlock:
-	fat_unlock_creation();
 	return res;
 
 mkdir_error:
 	printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
-	bh = NULL;
-	fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY);
 	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);
-	iput(inode);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
 	fat_brelse(sb, bh);
+	fat_detach(inode);
+	iput(inode);
 	goto out_unlock;
 
 out_exist:
 	fat_brelse(sb, bh);
-	res = -EEXIST;
+	res = -EINVAL;
 	goto out_unlock;
 }
 
 /***** Unlink a file */
-static int msdos_unlinkx( struct inode *dir, struct dentry *dentry, int nospc)
+int msdos_unlink( struct inode *dir, struct dentry *dentry)
 {
 	struct super_block *sb = dir->i_sb;
 	struct inode *inode = dentry->d_inode;
@@ -559,39 +460,21 @@
 			&bh, &de, &ino);
 	if (res < 0)
 		goto unlink_done;
-	res = -EPERM;
-	if (!S_ISREG(inode->i_mode) && nospc)
-		goto unlink_done;
-	/* N.B. check for busy files? */
 
+	de->name[0] = DELETED_FLAG;
+	fat_mark_buffer_dirty(sb, bh, 1);
+	fat_detach(inode);
+	fat_brelse(sb, bh);
 	inode->i_nlink = 0;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	MSDOS_I(inode)->i_busy = 1;
 	mark_inode_dirty(inode);
 	mark_inode_dirty(dir);
 	d_delete(dentry);	/* This also frees the inode */
-	de->name[0] = DELETED_FLAG;
-	fat_mark_buffer_dirty(sb, bh, 1);
 	res = 0;
 unlink_done:
-	fat_brelse(sb, bh);
 	return res;
 }
 
-/***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,struct dentry *dentry)
-{
-	return msdos_unlinkx (dir,dentry,1);
-}
-
-/***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
-{
-	return msdos_unlinkx (dir,dentry,0);
-}
-
-/* Now we could merge it with msdos_rename_same. Later */
-/***** Rename across directories - a nonphysical move */
 static int do_msdos_rename(struct inode *old_dir, char *old_name,
     struct dentry *old_dentry,
     struct inode *new_dir,char *new_name, struct dentry *new_dentry,
@@ -599,157 +482,97 @@
     struct msdos_dir_entry *old_de, int old_ino, int is_hid)
 {
 	struct super_block *sb = old_dir->i_sb;
-	struct buffer_head *new_bh,*free_bh,*dotdot_bh;
-	struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
-	struct inode *old_inode,*new_inode,*dotdot_inode;
-	int new_ino,free_ino,dotdot_ino;
-	int error, exists;
+	struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
+	struct msdos_dir_entry *new_de,*dotdot_de;
+	struct inode *old_inode,*new_inode;
+	int new_ino,dotdot_ino;
+	int error;
+	int is_dir;
 
 	old_inode = old_dentry->d_inode;
-	if (old_dir==new_dir && !strncmp(old_name, new_name, MSDOS_NAME))
-		goto set_hid;
-	error = -ENOENT;
-	if (*(unsigned char *) old_de->name == DELETED_FLAG)
-		goto out;
+	new_inode = new_dentry->d_inode;
+	is_dir = S_ISDIR(old_inode->i_mode);
 
-	/* find free spot */
-	if (new_dir!=old_dir)
-		while ((error = fat_scan(new_dir, NULL, &free_bh, &free_de,
-					&free_ino, SCAN_ANY)) < 0) {
-			if (error != -ENOENT)
-				goto out;
-			error = fat_add_cluster(new_dir);
+	if (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode)
+		goto degenerate_case;
+	if (is_dir) {
+		if (new_inode) {
+			error = fat_dir_empty(new_inode);
 			if (error)
 				goto out;
 		}
-
-	exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-	if (exists) {  /* Trash the old file! */
-		error = -EIO;
-		new_inode = new_dentry->d_inode;
-		/* Make sure it really exists ... */
-		if (!new_inode) {
-			printk(KERN_ERR
-				"msdos_rename: %s/%s inode NULL, ino=%d\n",
-				new_dentry->d_parent->d_name.name,
-				new_dentry->d_name.name, new_ino);
-			d_drop(new_dentry);
-			goto out_new;
-		}
- 		error = -EPERM;
-		if ((old_de->attr & ATTR_SYS))
-			goto out_new;
-
-		if (S_ISDIR(new_inode->i_mode)) {
-			error = msdos_empty(new_inode);
-			if (error)
-				goto out_new;
-			new_dir->i_nlink--;
-			mark_inode_dirty(new_dir);
-		}
-		new_inode->i_nlink = 0;
-		MSDOS_I(new_inode)->i_busy = 1;
-		mark_inode_dirty(new_inode);
-
-		new_de->name[0] = DELETED_FLAG;
-		fat_mark_buffer_dirty(sb, new_bh, 1);
-		fat_brelse(sb, new_bh);
-	}
-
-	if (old_dir==new_dir) {
-		memcpy(old_de->name, new_name, MSDOS_NAME);
-set_hid:
-		old_de->attr = is_hid
-			? (old_de->attr | ATTR_HIDDEN)
-			: (old_de->attr &~ ATTR_HIDDEN);
-		fat_mark_buffer_dirty(sb, old_bh, 1);
-		MSDOS_I(old_inode)->i_attrs = is_hid
-			? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
-			: (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
-		error = 0;
-		goto out;
-	}
-
-	/* Get the dotdot inode if we'll need it ... */
-	dotdot_bh = NULL;
-	dotdot_inode = NULL;
-	if (S_ISDIR(old_inode->i_mode)) {
 		error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
-				&dotdot_de, &dotdot_ino, SCAN_ANY);
+				&dotdot_de, &dotdot_ino);
 		if (error < 0) {
 			printk(KERN_WARNING
 				"MSDOS: %s/%s, get dotdot failed, ret=%d\n",
 				old_dentry->d_parent->d_name.name,
 				old_dentry->d_name.name, error);
-			goto rename_done;
+			goto out;
 		}
-		error = -EIO;
-		dotdot_inode = iget(sb, dotdot_ino);
-		if (!dotdot_inode)
-			goto out_dotdot;
 	}
+	if (!new_bh) {
+		error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
+					&new_ino, is_dir, is_hid);
+		if (error)
+			goto out;
+	}
+	new_dir->i_version = ++event;
 
-	/*
-	 * Potential race here. It will go away when we'll switch to
-	 * sane inumbers (along with a frigging lot of other races).
-	 */
-
-	/* set new entry */
-	memcpy(free_de, old_de, sizeof(struct msdos_dir_entry));
-	memcpy(free_de->name, new_name, MSDOS_NAME);
-	free_de->attr = is_hid
-		? (free_de->attr|ATTR_HIDDEN)
-		: (free_de->attr&~ATTR_HIDDEN);
-
-	/*
-	 * Now the tricky part. We need to change i_ino. icache ignores
-	 * i_ino for unhashed inodes, so we'll remove inode from hash,
-	 * change what we want to change and reinsert it back. NB: we
-	 * don't have to invalidate FAT cache here - all we need is to
-	 * flip i_ino in relevant cache entries. Later.
-	 */
-	remove_inode_hash(old_inode);
+	/* There we go */
 
-	fat_cache_inval_inode(old_inode);
-	old_inode->i_version = ++event;
-	MSDOS_I(old_inode)->i_binary =
-		fat_is_binary(MSDOS_SB(sb)->options.conversion, free_de->ext);
-	old_inode->i_ino = free_ino;
-	fat_mark_buffer_dirty(sb, free_bh, 1);
+	if (new_inode)
+		fat_detach(new_inode);
 	old_de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, old_bh, 1);
-
-	insert_inode_hash(old_inode);
-
-	/* a directory? */
+	fat_detach(old_inode);
+	fat_attach(old_inode, new_ino);
+	if (is_hid)
+		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+	else
+		MSDOS_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);
+	if (new_inode) {
+		new_inode->i_nlink--;
+		new_inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(new_inode);
+	}
 	if (dotdot_bh) {
-		MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
-		MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
 		dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
 		dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
-		old_dir->i_nlink--;
-		new_dir->i_nlink++;
-		/* no need to mark them dirty */
-		dotdot_inode->i_nlink = new_dir->i_nlink;
-		mark_inode_dirty(dotdot_inode);
-		iput(dotdot_inode);
 		fat_mark_buffer_dirty(sb, dotdot_bh, 1);
-		fat_brelse(sb, 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;
-
-rename_done:
-	fat_brelse(sb, free_bh);
 out:
+	fat_brelse(sb, new_bh);
+	fat_brelse(sb, dotdot_bh);
 	return error;
 
-out_dotdot:
-	fat_brelse(sb, dotdot_bh);
-	goto rename_done;
-out_new:
-	fat_brelse(sb, new_bh);
-	goto rename_done;
+degenerate_case:
+	error = -EINVAL;
+	if (new_de!=old_de)
+		goto out;
+	if (is_hid)
+		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+	else
+		MSDOS_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);
+	return 0;
 }
 
 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
@@ -765,27 +588,24 @@
 
 	error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
 				old_dentry->d_name.name, old_dentry->d_name.len,
-				old_msdos_name, 1,MSDOS_SB(sb)->options.dotsOK);
+				old_msdos_name,MSDOS_SB(sb)->options.dotsOK);
 	if (error < 0)
 		goto rename_done;
 	error = msdos_format_name(MSDOS_SB(sb)->options.name_check,
 				new_dentry->d_name.name, new_dentry->d_name.len,
-				new_msdos_name, 0,MSDOS_SB(sb)->options.dotsOK);
+				new_msdos_name,MSDOS_SB(sb)->options.dotsOK);
 	if (error < 0)
 		goto rename_done;
 
 	is_hid  = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
 	old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
-	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de,
-			&old_ino, old_hid?SCAN_HID:SCAN_NOTHID);
+	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_ino);
 	if (error < 0)
 		goto rename_done;
 
-	fat_lock_creation();
 	error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
 				new_dir, new_msdos_name, new_dentry,
 				old_bh, old_de, (ino_t)old_ino, is_hid);
-	fat_unlock_creation();
 	fat_brelse(sb, old_bh);
 
 rename_done:
@@ -817,10 +637,29 @@
 	NULL,                   /* revalidate */
 };
 
+static void msdos_put_super_callback(struct super_block *sb)
+{
+	MOD_DEC_USE_COUNT;
+}
 
-void msdos_read_inode(struct inode *inode)
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
 {
-	fat_read_inode(inode, &msdos_dir_inode_operations);
+	struct super_block *res;
+
+	MOD_INC_USE_COUNT;
+
+	MSDOS_SB(sb)->options.isvfat = 0;
+	res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
+	if (res == NULL)
+		goto out_fail;
+	MSDOS_SB(sb)->put_super_callback=msdos_put_super_callback;
+	sb->s_root->d_op = &msdos_dentry_operations;
+	return res;
+
+out_fail:
+	sb->s_dev = 0;
+	MOD_DEC_USE_COUNT;
+	return NULL;
 }
 
 
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.3.1/linux/fs/ncpfs/dir.c	Fri May 14 18:55:25 1999
+++ linux/fs/ncpfs/dir.c	Fri May 14 12:43:00 1999
@@ -38,8 +38,7 @@
 static int c_seen_eof;
 static int c_last_returned_index;
 static struct ncp_dirent *c_entry = NULL;
-static int c_lock = 0;
-static DECLARE_WAIT_QUEUE_HEAD(c_wait);
+static DECLARE_MUTEX(c_sem);
 
 static int ncp_read_volume_list(struct ncp_server *, int, int,
 					struct ncp_dirent *);
@@ -230,15 +229,12 @@
 
 static inline void ncp_lock_dircache(void)
 {
-	while (c_lock)
-		sleep_on(&c_wait);
-	c_lock = 1;
+	down(&c_sem);
 }
 
 static inline void ncp_unlock_dircache(void)
 {
-	c_lock = 0;
-	wake_up(&c_wait);
+	up(&c_sem);
 }
 
 
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/file.c linux/fs/ncpfs/file.c
--- v2.3.1/linux/fs/ncpfs/file.c	Mon Aug 24 13:02:44 1998
+++ linux/fs/ncpfs/file.c	Fri May 14 12:43:00 1999
@@ -100,6 +100,8 @@
 	size_t already_read = 0;
 	off_t pos;
 	int bufsize, error;
+	void* freepage;
+	int freelen;
 
 	DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -135,16 +137,24 @@
 
 	bufsize = NCP_SERVER(inode)->buffer_size;
 
+	error = -EIO;
+	freelen = ncp_read_bounce_size(bufsize);
+	freepage = kmalloc(freelen, GFP_NFS);
+	if (!freepage)
+		goto out;
+	error = 0;
 	/* First read in as much as possible for each bufsize. */
 	while (already_read < count) {
 		int read_this_time;
 		int to_read = min(bufsize - (pos % bufsize),
 				  count - already_read);
 
-		error = ncp_read(NCP_SERVER(inode),
+		error = ncp_read_bounce(NCP_SERVER(inode),
 			 	NCP_FINFO(inode)->file_handle,
-				pos, to_read, buf, &read_this_time);
+				pos, to_read, buf, &read_this_time, 
+				freepage, freelen);
 		if (error) {
+			kfree(freepage);
 			error = -EIO;	/* This is not exact, i know.. */
 			goto out;
 		}
@@ -156,6 +166,7 @@
 			break;
 		}
 	}
+	kfree(freepage);
 
 	file->f_pos = pos;
 
@@ -177,6 +188,7 @@
 	size_t already_written = 0;
 	off_t pos;
 	int bufsize, errno;
+	void* bouncebuffer;
 
 	DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -210,14 +222,23 @@
 
 	already_written = 0;
 
+	bouncebuffer = kmalloc(bufsize, GFP_NFS);
+	if (!bouncebuffer)
+		return -EIO;	/* -ENOMEM */
 	while (already_written < count) {
 		int written_this_time;
 		int to_write = min(bufsize - (pos % bufsize),
 				   count - already_written);
 
-		if (ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
-			  pos, to_write, buf, &written_this_time) != 0) {
-			return -EIO;
+		if (copy_from_user(bouncebuffer, buf, to_write)) {
+			errno = -EFAULT;
+			break;
+		}
+		if (ncp_write_kernel(NCP_SERVER(inode), 
+		    NCP_FINFO(inode)->file_handle,
+		    pos, to_write, buf, &written_this_time) != 0) {
+			errno = -EIO;
+			break;
 		}
 		pos += written_this_time;
 		buf += written_this_time;
@@ -227,7 +248,7 @@
 			break;
 		}
 	}
-
+	kfree(bouncebuffer);
 	inode->i_mtime = inode->i_atime = CURRENT_TIME;
 	
 	file->f_pos = pos;
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.3.1/linux/fs/ncpfs/inode.c	Fri May 14 18:55:25 1999
+++ linux/fs/ncpfs/inode.c	Fri May 14 12:43:00 1999
@@ -346,26 +346,27 @@
 						   GFP_KERNEL);
 	if (server == NULL)
 		goto out_no_server;
+	memset(server, 0, sizeof(*server));
 	NCP_SBP(sb) = server;
 
 	server->ncp_filp = ncp_filp;
-	server->lock = 0;
-	init_waitqueue_head(&server->wait);
+/*	server->lock = 0;	*/
+	init_MUTEX(&server->sem);
 	server->packet = NULL;
-	server->buffer_size = 0;
-	server->conn_status = 0;
-	server->root_dentry = NULL;
-	server->root_setuped = 0;
+/*	server->buffer_size = 0;	*/
+/*	server->conn_status = 0;	*/
+/*	server->root_dentry = NULL;	*/
+/*	server->root_setuped = 0;	*/
 #ifdef CONFIG_NCPFS_PACKET_SIGNING
-	server->sign_wanted = 0;
-	server->sign_active = 0;
+/*	server->sign_wanted = 0;	*/
+/*	server->sign_active = 0;	*/
 #endif
 	server->auth.auth_type = NCP_AUTH_NONE;
-	server->auth.object_name_len = 0;
-	server->auth.object_name = NULL;
-	server->auth.object_type = 0;
-	server->priv.len = 0;
-	server->priv.data = NULL;
+/*	server->auth.object_name_len = 0;	*/
+/*	server->auth.object_name = NULL;	*/
+/*	server->auth.object_type = 0;		*/
+/*	server->priv.len = 0;			*/
+/*	server->priv.data = NULL;		*/
 
 	server->m = *data;
 	/* Althought anything producing this is buggy, it happens
@@ -687,7 +688,7 @@
 		if ((result = ncp_make_open(inode, O_RDWR)) < 0) {
 			return -EACCES;
 		}
-		ncp_write(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+		ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
 			  attr->ia_size, 0, "", &written);
 
 		/* According to ndir, the changes only take effect after
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v2.3.1/linux/fs/ncpfs/ioctl.c	Tue Apr 20 15:17:20 1999
+++ linux/fs/ncpfs/ioctl.c	Fri May 14 12:43:00 1999
@@ -33,6 +33,7 @@
 	int result;
 	struct ncp_ioctl_request request;
 	struct ncp_fs_info info;
+	char* bouncebuffer;
 
 #ifdef NCP_IOC_GETMOUNTUID_INT
 	/* remove after ncpfs-2.0.13/2.2.0 gets released */
@@ -57,12 +58,9 @@
 		    && (current->uid != server->m.mounted_uid)) {
 			return -EACCES;
 		}
-		if ((result = verify_area(VERIFY_READ, (char *) arg,
-					  sizeof(request))) != 0) {
-			return result;
-		}
-		copy_from_user(&request, (struct ncp_ioctl_request *) arg,
-			       sizeof(request));
+		if (copy_from_user(&request, (struct ncp_ioctl_request *) arg,
+			       sizeof(request)))
+			return -EFAULT;
 
 		if ((request.function > 255)
 		    || (request.size >
@@ -73,6 +71,13 @@
 					  NCP_PACKET_SIZE)) != 0) {
 			return result;
 		}
+		bouncebuffer = kmalloc(NCP_PACKET_SIZE, GFP_NFS);
+		if (!bouncebuffer)
+			return -ENOMEM;
+		if (copy_from_user(bouncebuffer, request.data, request.size)) {
+			kfree(bouncebuffer);
+			return -EFAULT;
+		}
 		ncp_lock_server(server);
 
 		/* FIXME: We hack around in the server's structures
@@ -80,17 +85,22 @@
 
 		server->has_subfunction = 0;
 		server->current_size = request.size;
-		copy_from_user(server->packet, request.data, request.size);
-
-		ncp_request(server, request.function);
-
-		DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n",
-			server->reply_size);
-		copy_to_user(request.data, server->packet, server->reply_size);
+		memcpy(server->packet, bouncebuffer, request.size);
 
+		result = ncp_request2(server, request.function, 
+			bouncebuffer, NCP_PACKET_SIZE);
+		if (result)
+			result = -EIO;
+		else
+			result = server->reply_size;
 		ncp_unlock_server(server);
-
-		return server->reply_size;
+		DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n",
+			result);
+		if (result >= 0)
+			if (copy_to_user(request.data, bouncebuffer, result))
+				result = -EFAULT;
+		kfree(bouncebuffer);
+		return result;
 
 	case NCP_IOC_CONN_LOGGED_IN:
 
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c
--- v2.3.1/linux/fs/ncpfs/mmap.c	Sun Mar  7 10:40:33 1999
+++ linux/fs/ncpfs/mmap.c	Fri May 14 12:43:00 1999
@@ -37,11 +37,10 @@
 	struct dentry *dentry = file->f_dentry;
 	struct inode *inode = dentry->d_inode;
 	unsigned long page;
-	unsigned int clear;
-	unsigned long tmp;
+	unsigned int already_read;
+	unsigned int count;
 	int bufsize;
 	int pos;
-	mm_segment_t fs;
 
 	page = __get_free_page(GFP_KERNEL);
 	if (!page)
@@ -49,35 +48,24 @@
 	address &= PAGE_MASK;
 	pos = address - area->vm_start + area->vm_offset;
 
-	clear = 0;
+	count = PAGE_SIZE;
 	if (address + PAGE_SIZE > area->vm_end) {
-		clear = address + PAGE_SIZE - area->vm_end;
+		count = area->vm_end - address;
 	}
 	/* what we can read in one go */
 	bufsize = NCP_SERVER(inode)->buffer_size;
 
-	fs = get_fs();
-	set_fs(get_ds());
-
-	if (ncp_make_open(inode, O_RDONLY) < 0) {
-		clear = PAGE_SIZE;
-	} else {
-		int already_read = 0;
-		int count = PAGE_SIZE - clear;
-		int to_read;
-
+	already_read = 0;
+	if (ncp_make_open(inode, O_RDONLY) >= 0) {
 		while (already_read < count) {
 			int read_this_time;
+			int to_read;
 
-			if ((pos % bufsize) != 0) {
-				to_read = bufsize - (pos % bufsize);
-			} else {
-				to_read = bufsize;
-			}
+			to_read = bufsize - (pos % bufsize);
 
 			to_read = min(to_read, count - already_read);
 
-			if (ncp_read(NCP_SERVER(inode),
+			if (ncp_read_kernel(NCP_SERVER(inode),
 				     NCP_FINFO(inode)->file_handle,
 				     pos, to_read,
 				     (char *) (page + already_read),
@@ -94,12 +82,9 @@
 
 	}
 
-	set_fs(fs);
-
-	tmp = page + PAGE_SIZE;
-	while (clear--) {
-		*(char *) --tmp = 0;
-	}
+	if (already_read < PAGE_SIZE)
+		memset((char*)(page + already_read), 0, 
+		       PAGE_SIZE - already_read);
 	return page;
 }
 
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c
--- v2.3.1/linux/fs/ncpfs/ncplib_kernel.c	Tue Apr 20 15:17:20 1999
+++ linux/fs/ncpfs/ncplib_kernel.c	Fri May 14 12:43:00 1999
@@ -754,7 +754,7 @@
 
 /* We have to transfer to/from user space */
 int
-ncp_read(struct ncp_server *server, const char *file_id,
+ncp_read_kernel(struct ncp_server *server, const char *file_id,
 	     __u32 offset, __u16 to_read, char *target, int *bytes_read)
 {
 	char *source;
@@ -772,18 +772,27 @@
 	*bytes_read = ntohs(ncp_reply_word(server, 0));
 	source = ncp_reply_data(server, 2 + (offset & 1));
 
-	result = -EFAULT;
-	if (!copy_to_user(target, source, *bytes_read))
-		result = 0;
+	memcpy(target, source, *bytes_read);
 out:
 	ncp_unlock_server(server);
 	return result;
 }
 
+/* There is a problem... egrep and some other silly tools do:
+	x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
+	read(<ncpfs fd>, x, 32768);
+   Now copying read result by copy_to_user causes pagefault. This pagefault
+   could not be handled because of server was locked due to read. So we have
+   to use temporary buffer. So ncp_unlock_server must be done before
+   copy_to_user (and for write, copy_from_user must be done before 
+   ncp_init_request... same applies for send raw packet ioctl). Because of
+   file is normally read in bigger chunks, caller provides kmalloced 
+   (vmalloced) chunk of memory with size >= to_read...
+ */
 int
-ncp_write(struct ncp_server *server, const char *file_id,
-	      __u32 offset, __u16 to_write,
-		const char *source, int *bytes_written)
+ncp_read_bounce(struct ncp_server *server, const char *file_id,
+	 __u32 offset, __u16 to_read, char *target, int *bytes_read,
+	 void* bounce, __u32 bufsize)
 {
 	int result;
 
@@ -791,46 +800,47 @@
 	ncp_add_byte(server, 0);
 	ncp_add_mem(server, file_id, 6);
 	ncp_add_dword(server, htonl(offset));
-	ncp_add_word(server, htons(to_write));
-	ncp_add_mem_fromfs(server, source, to_write);
-
-	if ((result = ncp_request(server, 73)) != 0)
-		goto out;
-	*bytes_written = to_write;
-	result = 0;
-out:
+	ncp_add_word(server, htons(to_read));
+	result = ncp_request2(server, 72, bounce, bufsize);
 	ncp_unlock_server(server);
+	if (!result) {
+		int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce + 
+			  sizeof(struct ncp_reply_header))));
+		result = -EIO;
+		if (len <= to_read) {
+			char* source;
+
+			source = (char*)bounce + 
+			         sizeof(struct ncp_reply_header) + 2 + 
+			         (offset & 1);
+			*bytes_read = len;
+			result = 0;
+			if (copy_to_user(target, source, len))
+				result = -EFAULT;
+		}
+	}
 	return result;
 }
 
-#ifdef CONFIG_NCPFS_EXTRAS
-int
-ncp_read_kernel(struct ncp_server *server, const char *file_id,
-		__u32 offset, __u16 to_read, char *target, int *bytes_read) {
-	int error;
-	mm_segment_t old_fs;
-	
-	old_fs = get_fs();
-	set_fs(get_ds());
-	error = ncp_read(server, file_id, offset, to_read, target, bytes_read);
-	set_fs(old_fs);
-	return error;
-}
-
 int
 ncp_write_kernel(struct ncp_server *server, const char *file_id,
 		 __u32 offset, __u16 to_write,
-		 const char *source, int *bytes_written) {
-	int error;
-	mm_segment_t old_fs;
+		 const char *source, int *bytes_written)
+{
+	int result;
+
+	ncp_init_request(server);
+	ncp_add_byte(server, 0);
+	ncp_add_mem(server, file_id, 6);
+	ncp_add_dword(server, htonl(offset));
+	ncp_add_word(server, htons(to_write));
+	ncp_add_mem(server, source, to_write);
 	
-	old_fs = get_fs();
-	set_fs(get_ds());
-	error = ncp_write(server, file_id, offset, to_write, source, bytes_written);
-	set_fs(old_fs);
-	return error;
+	if ((result = ncp_request(server, 73)) == 0)
+		*bytes_written = to_write;
+	ncp_unlock_server(server);
+	return result;
 }
-#endif
 
 #ifdef CONFIG_NCPFS_IOCTL_LOCKING
 int
@@ -876,4 +886,5 @@
 	return 0;
 }
 #endif	/* CONFIG_NCPFS_IOCTL_LOCKING */
+
 
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h
--- v2.3.1/linux/fs/ncpfs/ncplib_kernel.h	Tue Apr 20 15:17:20 1999
+++ linux/fs/ncpfs/ncplib_kernel.h	Fri May 14 12:43:00 1999
@@ -32,20 +32,24 @@
 #include <linux/ncp_fs.h>
 #include <linux/ncp_fs_sb.h>
 
+#define NCP_MIN_SYMLINK_SIZE	8
+#define NCP_MAX_SYMLINK_SIZE	512
+
 int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
 int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
   			  int options, int *ret_size, int *ret_options);
 int ncp_get_volume_info_with_number(struct ncp_server *, int,
 				struct ncp_volume_info *);
 int ncp_close_file(struct ncp_server *, const char *);
-int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *);
-int ncp_write(struct ncp_server *, const char *, __u32, __u16,
-		const char *, int *);
-#ifdef CONFIG_NCPFS_EXTRAS
-int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *);
+static inline int ncp_read_bounce_size(__u32 size) {
+	return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8;
+};
+int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16, 
+		char *, int *, void* bounce, __u32 bouncelen);
+int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, 
+		char *, int *);
 int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
 		const char *, int *);
-#endif
 
 int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
 		struct nw_info_struct *target);
diff -u --recursive --new-file v2.3.1/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v2.3.1/linux/fs/ncpfs/sock.c	Fri Mar 26 13:57:41 1999
+++ linux/fs/ncpfs/sock.c	Fri May 14 12:43:00 1999
@@ -83,7 +83,8 @@
 
 #define NCP_SLACK_SPACE 1024
 
-static int do_ncp_rpc_call(struct ncp_server *server, int size)
+static int do_ncp_rpc_call(struct ncp_server *server, int size,
+		struct ncp_reply_header* reply_buf, int max_reply_size)
 {
 	struct file *file;
 	struct inode *inode;
@@ -276,7 +277,7 @@
 	 * we have the correct reply, so read into the correct place and
 	 * return it
 	 */
-	result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT);
+	result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT);
 	if (result < 0) {
 		printk(KERN_WARNING "NCP: notice message: result=%d\n", result);
 	} else if (result < sizeof(struct ncp_reply_header)) {
@@ -299,7 +300,8 @@
  * We need the server to be locked here, so check!
  */
 
-static int ncp_do_request(struct ncp_server *server, int size)
+static int ncp_do_request(struct ncp_server *server, int size,
+		void* reply, int max_reply_size)
 {
 	int result;
 
@@ -316,7 +318,7 @@
 		sign_packet(server, &size);
 	}
 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
-	result = do_ncp_rpc_call(server, size);
+	result = do_ncp_rpc_call(server, size, reply, max_reply_size);
 
 	DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result);
 
@@ -332,10 +334,11 @@
  * received. It assumes that server->current_size contains the ncp
  * request size
  */
-int ncp_request(struct ncp_server *server, int function)
+int ncp_request2(struct ncp_server *server, int function, 
+		void* rpl, int size)
 {
 	struct ncp_request_header *h;
-	struct ncp_reply_header *reply;
+	struct ncp_reply_header* reply = rpl;
 	int request_size = server->current_size
 			 - sizeof(struct ncp_request_header);
 	int result;
@@ -357,12 +360,11 @@
 	h->task = 2; /* (current->pid) & 0xff; */
 	h->function = function;
 
-	result = ncp_do_request(server, request_size + sizeof(*h));
+	result = ncp_do_request(server, request_size + sizeof(*h), reply, size);
 	if (result < 0) {
 		DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result);
 		goto out;
 	}
-	reply = (struct ncp_reply_header *) (server->packet);
 	server->completion = reply->completion_code;
 	server->conn_status = reply->connection_state;
 	server->reply_size = result;
@@ -393,7 +395,7 @@
 	h->task		= 2; /* see above */
 	h->function	= 0;
 
-	result = ncp_do_request(server, sizeof(*h));
+	result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
 	if (result < 0)
 		goto out;
 	server->sequence = 0;
@@ -417,7 +419,7 @@
 	h->task		= 2; /* see above */
 	h->function	= 0;
 
-	return ncp_do_request(server, sizeof(*h));
+	return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
 }
 
 void ncp_lock_server(struct ncp_server *server)
@@ -428,16 +430,18 @@
 		DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n");
 	}
 #endif
-	while (server->lock)
-		sleep_on(&server->wait);
+	down(&server->sem);
+	if (server->lock)
+		printk(KERN_WARNING "ncp_lock_server: was locked!\n");
 	server->lock = 1;
 }
 
 void ncp_unlock_server(struct ncp_server *server)
 {
-	if (server->lock != 1) {
+	if (!server->lock) {
 		printk(KERN_WARNING "ncp_unlock_server: was not locked!\n");
+		return;
 	}
 	server->lock = 0;
-	wake_up(&server->wait);
+	up(&server->sem);
 }
diff -u --recursive --new-file v2.3.1/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c
--- v2.3.1/linux/fs/nfsd/nfsfh.c	Sat Mar 20 12:28:14 1999
+++ linux/fs/nfsd/nfsfh.c	Thu May 13 23:18:21 1999
@@ -407,6 +407,9 @@
 	sb = get_super(dev);
 	if (!sb)
 		goto out_page;
+	result = ERR_PTR(-ENOSYS);
+	if (!sb->s_op->read_inode)	/* No working iget(), e.g. FAT */
+		goto out_page;
 	root = dget(sb->s_root);
 	root_ino = root->d_inode->i_ino; /* usually 2 */
 
diff -u --recursive --new-file v2.3.1/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.3.1/linux/fs/smbfs/inode.c	Fri May 14 18:55:26 1999
+++ linux/fs/smbfs/inode.c	Thu May 13 17:16:22 1999
@@ -88,7 +88,7 @@
 		result->i_op = &smb_dir_inode_operations;
 	else
 		result->i_op = NULL;
-	insert_inode_hash(result)
+	insert_inode_hash(result);
 	return result;
 }
 
diff -u --recursive --new-file v2.3.1/linux/fs/umsdos/README-WIP.txt linux/fs/umsdos/README-WIP.txt
--- v2.3.1/linux/fs/umsdos/README-WIP.txt	Sun Nov 29 11:43:13 1998
+++ linux/fs/umsdos/README-WIP.txt	Thu May 13 23:18:21 1999
@@ -14,7 +14,7 @@
 Legend: those lines marked with '+' on the beggining of line indicates it
 passed all of my tests, and performed perfect in all of them.
 
-Current status (981129) - UMSDOS dentry-pre 0.84:
+Current status (990202) - UMSDOS 0.85:
 
 (1) pure MSDOS (no --linux-.--- EMD file):
 
@@ -56,7 +56,7 @@
 
 WRITE:
 + create symlink		- works
-- create hardlink		- works, but see portability WARNING below
+- create hardlink		- works
 + create file			- works
 + create special file		- works
 + write to file			- works
@@ -90,24 +90,23 @@
 contains hardlinks will break them.
 
 Note: (about pseudoroot) If you are currently trying to use UMSDOS as root
-partition (with linux installed in c:\linux) it will boot, but there are
+partition (with linux installed in c:\linux) it will boot, but there may be
 some problems. Volunteers ready to test pseudoroot are needed (preferably
-ones with working backups or unimportant data). There are problems with
-different interpretation of hard links in normal in pseudo-root modes,
-resulting is 'silent delete' of them sometimes. Also, '/DOS' pseudo
+ones with working backups or unimportant data).  For example, '/DOS' pseudo
 directory is only partially re-implemented and buggy. It works most of the
 time, though. Update: should work ok in 0.84, although it still does not
 work correctly in combination with initrd featere. Working on this!
 
-Warning: (about creating hardlinks in pseudoroot mode) - hardlinks created in
-pseudoroot mode are not compatibile with 'normal' hardlinks, and vice versa.
-That is because harlink which is /foo in pseudoroot mode, becomes
-/linux/foo in normal mode. I'm thinking about this one. However, since most
-people either always use pseudoroot, or always use normal umsdos filesystem,
-this is no showstopper.
+Note: (about creating hardlinks in pseudoroot mode) - hardlinks created in
+pseudoroot mode are now again compatibile with 'normal' hardlinks, and vice
+versa. Thanks to Sorin Iordachescu <sorin@rodae.ro> for providing fix.
 
-Warning: (about hardlinks) - modifying hardlinks (esp. if there are in
+Warning: (about hardlinks) - modifying hardlinks (esp. if they are in
 different directories) are currently somewhat broken, I'm working on it.
+Problem seems to be that code uses and updates EMD of directory where 'real
+hardlink' is stored, not EMD of directory where our pseudo-hardlink is
+located! I'm looking for ideas how to work around this in clean way, since
+without it modifying hardlinks in any but most simple ways is broken!
 
 ------------------------------------------------------------------------------
 
diff -u --recursive --new-file v2.3.1/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.3.1/linux/fs/umsdos/dir.c	Sat May  8 17:56:37 1999
+++ linux/fs/umsdos/dir.c	Thu May 13 23:18:21 1999
@@ -670,10 +670,20 @@
 	/* N.B. not safe -- fix this soon! */
 	current->fs->root = dentry->d_sb->s_root;
 	path = d_path(dentry, buffer, len);
+
+	if (*path == '/')
+		path++; /* skip leading '/' */
+
+	if (old_root->d_inode == pseudo_root)
+	{
+		*(path-1) = '/';
+		path -= (UMSDOS_PSDROOT_LEN+1);
+		memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN);
+	}
+
 	current->fs->root = old_root;
 	return path;
 }
-	
 
 /*
  * Return the dentry which points to a pseudo-hardlink.
@@ -718,7 +728,14 @@
 	/* start at root dentry */
 	dentry_dst = dget(base);
 	path[len] = '\0';
-	pt = path + 1; /* skip leading '/' */
+	
+	pt = path;
+	if (*path == '/')
+		pt++; /* skip leading '/' */
+	
+	if (base->d_inode == pseudo_root)
+		pt += (UMSDOS_PSDROOT_LEN + 1);
+	
 	while (1) {
 		struct dentry *dir = dentry_dst, *demd;
 		char *start = pt;
@@ -741,6 +758,7 @@
 dir->d_parent->d_name.name, dir->d_name.name, start, real);
 #endif
 		dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
 		if (real)
 			d_drop(dir);
 		dput (dir);
diff -u --recursive --new-file v2.3.1/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.3.1/linux/fs/umsdos/inode.c	Fri May 14 18:55:27 1999
+++ linux/fs/umsdos/inode.c	Thu May 13 23:18:21 1999
@@ -60,8 +60,8 @@
 			" Notify jacques@solucorp.qc.ca\n");
 	}
 
-	inode->u.umsdos_i.i_patched = 0;
-	fat_put_inode (inode);
+	if (inode->i_count == 1)
+		inode->u.umsdos_i.i_patched = 0;
 }
 
 
@@ -169,29 +169,6 @@
 }
 
 
-/*
- * Load an inode from disk.
- */
-/* #Specification: Inode / post initialisation
- * To completely initialise an inode, we need access to the owner
- * directory, so we can locate more info in the EMD file. This is
- * not available the first time the inode is accessed, so we use
- * a value in the inode to tell if it has been finally initialised.
- * 
- * New inodes are obtained by the lookup and create routines, and
- * each of these must ensure that the inode gets patched.
- */
-void UMSDOS_read_inode (struct inode *inode)
-{
-	Printk ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
-		inode, inode->i_ino));
-	msdos_read_inode (inode);
-
-	/* inode needs patching */
-	inode->u.umsdos_i.i_patched = 0;
-}
-
-
 int umsdos_notify_change_locked(struct dentry *, struct iattr *);
 /*
  * lock the parent dir before starting ...
@@ -337,7 +314,7 @@
 
 static struct super_operations umsdos_sops =
 {
-	UMSDOS_read_inode,	/* read_inode */
+	NULL,			/* read_inode */
 	UMSDOS_write_inode,	/* write_inode */
 	UMSDOS_put_inode,	/* put_inode */
 	fat_delete_inode,	/* delete_inode */
@@ -345,7 +322,8 @@
 	UMSDOS_put_super,	/* put_super */
 	NULL,			/* write_super */
 	fat_statfs,		/* statfs */
-	NULL			/* remount_fs */
+	NULL,			/* remount_fs */
+	fat_clear_inode,	/* clear_inode */
 };
 
 /*
@@ -367,7 +345,7 @@
 	if (!res)
 		goto out_fail;
 
-	printk (KERN_INFO "UMSDOS dentry-pre 0.84 "
+	printk (KERN_INFO "UMSDOS 0.85 "
 		"(compatibility level %d.%d, fast msdos)\n", 
 		UMSDOS_VERSION, UMSDOS_RELEASE);
 
@@ -412,16 +390,20 @@
 /*
  * Check for an alternate root if we're the root device.
  */
+
+extern kdev_t ROOT_DEV;
 static struct dentry *check_pseudo_root(struct super_block *sb)
 {
 	struct dentry *root, *init;
 
 	/*
 	 * Check whether we're mounted as the root device.
-	 * If so, this should be the only superblock.
+	 * must check like this, because we can be used with initrd
 	 */
-	if (sb->s_list.next->next != &sb->s_list)
+		
+	if (sb->s_dev != ROOT_DEV)
 		goto out_noroot;
+		
 printk("check_pseudo_root: mounted as root\n");
 
 	root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0); 
diff -u --recursive --new-file v2.3.1/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.3.1/linux/fs/vfat/namei.c	Sat May  8 17:56:37 1999
+++ linux/fs/vfat/namei.c	Thu May 13 23:18:21 1999
@@ -49,24 +49,6 @@
 # define CHECK_STACK check_stack(__FILE__, __LINE__)
 #endif
 
-struct vfat_find_info {
-	const char *name;
-	int len;
-	int new_filename;
-	int found;
-	int is_long;
-	off_t offset;
-	off_t short_offset;
-	int long_slots;
-	ino_t ino;
-	int posix;
-	int anycase;
-};
-
-void vfat_read_inode(struct inode *inode);
-static int vfat_valid_shortname(const char *,int, int, int);
-static int vfat_format_name(const char *, int, char *, int, int);
-static int vfat_valid_longname(const char *, int, int, int);
 static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
 static int vfat_hash(struct dentry *parent, struct qstr *qstr);
 static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
@@ -100,9 +82,8 @@
 	}
 };
 
-void vfat_put_super(struct super_block *sb)
+static void vfat_put_super_callback(struct super_block *sb)
 {
-	fat_put_super(sb);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -115,18 +96,6 @@
 	return 0;
 }
 
-static struct super_operations vfat_sops = { 
-	vfat_read_inode,
-	fat_write_inode,
-	fat_put_inode,
-	fat_delete_inode,
-	fat_notify_change,
-	vfat_put_super,
-	NULL, /* write_super */
-	fat_statfs,
-	NULL  /* remount */
-};
-
 static int simple_getbool(char *s, int *setval)
 {
 	if (s) {
@@ -283,40 +252,6 @@
 	return 1;
 }
 
-struct super_block *vfat_read_super(struct super_block *sb,void *data,
-				    int silent)
-{
-	struct super_block *res;
-  
-	MOD_INC_USE_COUNT;
-	
-	MSDOS_SB(sb)->options.isvfat = 1;
-
-	sb->s_op = &vfat_sops;
-	res = fat_read_super(sb, data, silent);
-	if (res == NULL) {
-		sb->s_dev = 0;
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-
-	if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
-		MOD_DEC_USE_COUNT;
-	} else {
-		MSDOS_SB(sb)->options.dotsOK = 0;
-		if (MSDOS_SB(sb)->options.posixfs) {
-			MSDOS_SB(sb)->options.name_check = 's';
-		}
-		if (MSDOS_SB(sb)->options.name_check != 's') {
-			sb->s_root->d_op = &vfat_dentry_ops[0];
-		} else {
-			sb->s_root->d_op = &vfat_dentry_ops[2];
-		}
-	}
-
-	return res;
-}
-
 #ifdef DEBUG
 
 static void
@@ -394,26 +329,17 @@
 static char bad_chars[] = "*?<>|\":/\\";
 static char replace_chars[] = "[];,+=";
 
-static int vfat_find(struct inode *dir,struct qstr* name,
-		      int new_filename,int is_dir,
-		      struct vfat_slot_info *sinfo_out);
-
 /* Checks the validity of a long MS-DOS filename */
 /* Returns negative number on error, 0 for a normal
  * return, and 1 for . or .. */
 
-static int vfat_valid_longname(const char *name, int len, int dot_dirs,
-			       int xlate)
+static int vfat_valid_longname(const char *name, int len, int xlate)
 {
 	const char **reserved, *walk;
 	unsigned char c;
 	int i, baselen;
 
 	if (IS_FREE(name)) return -EINVAL;
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
-		if (!dot_dirs) return -EEXIST;
-		return 1;
-	}
 
 	if (len && name[len-1] == ' ') return -EINVAL;
 	if (len >= 256) return -EINVAL;
@@ -443,19 +369,13 @@
 	return 0;
 }
 
-static int vfat_valid_shortname(const char *name,int len,
-				int dot_dirs, int utf8)
+static int vfat_valid_shortname(const char *name,int len,int utf8)
 {
 	const char *walk;
 	unsigned char c;
 	int space;
 	int baselen;
 
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
-		if (!dot_dirs) return -EEXIST;
-		return 1;
-	}
-
 	space = 1; /* disallow names starting with a dot */
 	c = 0;
 	for (walk = name; len && walk-name < 8;) {
@@ -499,34 +419,21 @@
 {
 	struct msdos_dir_entry *de;
 	struct buffer_head *bh = NULL;
-	loff_t pos = 0;
+	int ino,res;
 
-	while(fat_get_entry(dir, &pos, &bh, &de) >= 0) {
-		if (de->attr == ATTR_EXT)
-			continue;
-		if (memcmp(de->name,name,MSDOS_NAME))
-			continue;
-		fat_brelse(dir->i_sb,bh);
-		return 0;
-	}
-	fat_brelse(dir->i_sb,bh);
-	return -ENOENT;
+	res=fat_scan(dir,name,&bh,&de,&ino);
+	fat_brelse(dir->i_sb, bh);
+	if (res<0)
+		return -ENOENT;
+	return 0;
 }
 
-static int vfat_format_name(const char *name,int len,char *res,
-			    int dot_dirs,int utf8)
+static int vfat_format_name(const char *name,int len,char *res,int utf8)
 {
 	char *walk;
 	unsigned char c;
 	int space;
 
-	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
-		if (!dot_dirs) return -EEXIST;
-		memset(res+1,' ',10);
-		while (len--) *res++ = '.';
-		return 0;
-	}
-
 	space = 1; /* disallow names starting with a dot */
 	for (walk = res; len-- && (c=*name++)!='.' ; walk++) {
 		if (walk-res == 8) return -EINVAL;
@@ -588,7 +495,7 @@
 		for (i = 0, p = msdos_name, ip = name; ; i++, p++, ip++) {
 			if (i == len) {
 				if (vfat_format_name(msdos_name,
-						len, name_res, 1, utf8) < 0)
+						len, name_res, utf8) < 0)
 					break;
 				PRINTK3(("vfat_create_shortname 1\n"));
 				if (vfat_find_form(dir, name_res) < 0)
@@ -732,60 +639,6 @@
 	return 0;
 }
 
-static loff_t vfat_find_free_slots(struct inode *dir,int slots)
-{
-	struct super_block *sb = dir->i_sb;
-	loff_t offset, curr;
-	struct msdos_dir_entry *de;
-	struct buffer_head *bh;
-	struct inode *inode;
-	int ino;
-	int row;
-	int done;
-	int res;
-	int added;
-
-	PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots));
-	offset = curr = 0;
-	bh = NULL;
-	row = 0;
-	ino = fat_get_entry(dir,&curr,&bh,&de);
-
-	for (added = 0; added < 2; added++) {
-		while (ino > -1) {
-			done = IS_FREE(de->name);
-			if (done) {
-				inode = iget(sb,ino);
-				if (inode) {
-					/* Directory slots of busy deleted files aren't available yet. */
-					done = !MSDOS_I(inode)->i_busy;
-					/* PRINTK3(("inode %d still busy\n", ino)); */
-					iput(inode);
-				}
-			}
-			if (done) {
-				row++;
-				if (row == slots) {
-					fat_brelse(sb, bh);
-					/* printk("----- Free offset at %d\n", offset); */
-					return offset;
-				}
-			} else {
-				row = 0;
-				offset = curr;
-			}
-			ino = fat_get_entry(dir,&curr,&bh,&de);
-		}
-
-		if ((dir->i_ino == MSDOS_ROOT_INO) &&
-		    (MSDOS_SB(sb)->fat_bits != 32)) 
-			return -ENOSPC;
-		if ((res = fat_add_cluster(dir)) < 0) return res;
-		ino = fat_get_entry(dir,&curr,&bh,&de);
-	}
-	return -ENOSPC;
-}
-
 /* Translate a string, including coded sequences into Unicode */
 static int
 xlate_to_uni(const char *name, int len, char *outname, int *outlen,
@@ -889,249 +742,129 @@
 	PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots));
 
 	for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
-		int end, j;
-
-		PRINTK3(("vfat_fill_long_slots 4\n"));
 		ps->id = slot;
 		ps->attr = ATTR_EXT;
 		ps->reserved = 0;
 		ps->alias_checksum = cksum;
 		ps->start = 0;
-		PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
 		offset = (slot - 1) * 26;
 		ip = &uniname[offset];
-		j = offset;
-		end = 0;
-		for (i = 0; i < 10; i += 2) {
-			ps->name0_4[i] = *ip++;
-			ps->name0_4[i+1] = *ip++;
-		}
-		PRINTK3(("vfat_fill_long_slots 6\n"));
-		for (i = 0; i < 12; i += 2) {
-			ps->name5_10[i] = *ip++;
-			ps->name5_10[i+1] = *ip++;
-		}
-		PRINTK3(("vfat_fill_long_slots 7\n"));
-		for (i = 0; i < 4; i += 2) {
-			ps->name11_12[i] = *ip++;
-			ps->name11_12[i+1] = *ip++;
-		}
+		memcpy(ps->name0_4, ip, 10);
+		memcpy(ps->name5_10, ip+10, 12);
+		memcpy(ps->name11_12, ip+22, 4);
 	}
-	PRINTK3(("vfat_fill_long_slots 8\n"));
 	ds[0].id |= 0x40;
 
 	de = (struct msdos_dir_entry *) ps;
 	PRINTK3(("vfat_fill_long_slots 9\n"));
 	strncpy(de->name, msdos_name, MSDOS_NAME);
+	(*slots)++;
 
 	free_page(page);
 	return 0;
 }
-		
+
+/* We can't get "." or ".." here - VFS takes care of those cases */
+
 static int vfat_build_slots(struct inode *dir,const char *name,int len,
-     struct msdos_dir_slot *ds, int *slots, int *is_long)
+     struct msdos_dir_slot *ds, int *slots)
 {
 	struct msdos_dir_entry *de;
 	char msdos_name[MSDOS_NAME];
 	int res, xlate, utf8;
 	struct nls_table *nls;
 
-	PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
 	de = (struct msdos_dir_entry *) ds;
 	xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
 	utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
 	nls = MSDOS_SB(dir->i_sb)->nls_io;
 
 	*slots = 1;
-	*is_long = 0;
-	if (len == 1 && name[0] == '.') {
-		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
-	} else if (len == 2 && name[0] == '.' && name[1] == '.') {
-		strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
-	} else {
-		PRINTK3(("vfat_build_slots 4\n"));
-		res = vfat_valid_longname(name, len, 1, xlate);
-		if (res < 0) {
-			return res;
-		}
-		res = vfat_valid_shortname(name, len, 1, utf8);
-		if (res > -1) {
-			PRINTK3(("vfat_build_slots 5a\n"));
-			res = vfat_format_name(name, len, de->name, 1, utf8);
-			PRINTK3(("vfat_build_slots 5b\n"));
-		} else {
-			res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
-			if (res < 0) {
-				return res;
-			}
-
-			*is_long = 1;
-
-			return vfat_fill_long_slots(ds, name, len, msdos_name,
-						    slots, xlate, utf8, nls);
-		}
-	}
-	return 0;
-}
-
-static int vfat_readdir_cb(
-	filldir_t filldir,
-	void * buf,
-	const char * name,
-	int name_len,
-	int is_long,
-	off_t offset,
-	off_t short_offset,
-	int long_slots,
-	ino_t ino)
-{
-	struct vfat_find_info *vf = (struct vfat_find_info *) buf;
-	const char *s1, *s2;
-	int i;
-
-#ifdef DEBUG
-	if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
-			  vf->name, vf->len, name, name_len);
-#endif
-
-	if (vf->len != name_len) {
+	res = vfat_valid_longname(name, len, xlate);
+	if (res < 0)
+		return res;
+	if (vfat_valid_shortname(name, len, utf8) >= 0) {
+		vfat_format_name(name, len, de->name, utf8);
 		return 0;
 	}
-
-	s1 = name; s2 = vf->name;
-	for (i = 0; i < name_len; i++) {
-		if (vf->anycase || (vf->new_filename && !vf->posix)) {
-			if (tolower(*s1) != tolower(*s2))
-				return 0;
-		} else {
-			if (*s1 != *s2)
-				return 0;
-		}
-		s1++; s2++;
-	}
-	vf->found = 1;
-	vf->is_long = is_long;
-	vf->offset = (offset == 2) ? 0 : offset;
-	vf->short_offset = (short_offset == 2) ? 0 : short_offset;
-	vf->long_slots = long_slots;
-	vf->ino = ino;
-	return -1;
+	res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
+	if (res < 0)
+		return res;
+	return vfat_fill_long_slots(ds, name, len, msdos_name, slots, xlate,
+					utf8, nls);
 }
 
-static int vfat_find(struct inode *dir,struct qstr* qname,
-    int new_filename,int is_dir,struct vfat_slot_info *sinfo_out)
+static int vfat_add_entry(struct inode *dir,struct qstr* qname,
+	int is_dir,struct vfat_slot_info *sinfo_out,
+	struct buffer_head **bh, struct msdos_dir_entry **de)
 {
 	struct super_block *sb = dir->i_sb;
-	struct vfat_find_info vf;
-	struct file fil;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
 	struct msdos_dir_slot *ps;
 	loff_t offset;
 	struct msdos_dir_slot *ds;
-	int is_long;
 	int slots, slot;
 	int res;
-
-	PRINTK2(("Entering vfat_find\n"));
+	struct msdos_dir_entry *de1;
+	struct buffer_head *bh1;
+	int ino;
+	int len;
+	loff_t dummy;
 
 	ds = (struct msdos_dir_slot *)
 	    kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
 	if (ds == NULL) return -ENOMEM;
 
-	fil.f_pos = 0;
-	vf.name = qname->name;
-	vf.len = qname->len;
-	while (vf.len && vf.name[vf.len-1] == '.') {
-		vf.len--;
-	}
-	vf.new_filename = new_filename;
-	vf.found = 0;
-	vf.posix = MSDOS_SB(sb)->options.posixfs;
-	vf.anycase = (MSDOS_SB(sb)->options.name_check != 's');
-	res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,1,0);
-	PRINTK3(("vfat_find: Debug 1\n"));
-	if (res < 0) goto cleanup;
-	if (vf.found) {
-		if (new_filename) {
-			res = -EEXIST;
-			goto cleanup;
-		}
-		sinfo_out->longname_offset = vf.offset;
-		sinfo_out->shortname_offset = vf.short_offset;
-		sinfo_out->is_long = vf.is_long;
-		sinfo_out->long_slots = vf.long_slots;
-		sinfo_out->total_slots = vf.long_slots + 1;
-		sinfo_out->ino = vf.ino;
-
-		PRINTK3(("vfat_find: Debug 2\n"));
-		res = 0;
+	len = qname->len;
+	while (len && qname->name[len-1] == '.')
+		len--;
+	res = fat_search_long(dir, qname->name, len,
+			(MSDOS_SB(sb)->options.name_check != 's') ||
+			!MSDOS_SB(sb)->options.posixfs,
+			&dummy, &dummy);
+	if (res > 0) /* found */
+		res = -EEXIST;
+	if (res)
 		goto cleanup;
-	}
 
-	PRINTK3(("vfat_find: Debug 3\n"));
-	if (!new_filename) {
-		res = -ENOENT;
-		goto cleanup;
-	}
-	
-	res = vfat_build_slots(dir, qname->name, vf.len, ds, 
-			       &slots, &is_long);
-	/* Here we either have is_long and slots>=0 or slots==1 */
+	res = vfat_build_slots(dir, qname->name, len, ds, &slots);
 	if (res < 0) goto cleanup;
 
-	de = (struct msdos_dir_entry *) ds;
-
-	bh = NULL;
-
-	PRINTK3(("vfat_find: create file 1\n"));
-	if (is_long) slots++;
-	offset = vfat_find_free_slots(dir, slots);
+	offset = fat_add_entries(dir, slots, &bh1, &de1, &ino);
 	if (offset < 0) {
 		res = offset;
 		goto cleanup;
 	}
+	fat_brelse(sb, bh1);
 
-	PRINTK3(("vfat_find: create file 2\n"));
 	/* Now create the new entry */
-	bh = NULL;
+	*bh = NULL;
 	for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
-		PRINTK3(("vfat_find: create file 3, slot=%d\n",slot));
-		sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
-		if (sinfo_out->ino < 0) {
-			PRINTK3(("vfat_find: problem\n"));
-			res = sinfo_out->ino;
+		if (fat_get_entry(dir,&offset,bh,de, &sinfo_out->ino) < 0) {
+			res = -EIO;
 			goto cleanup;
 		}
-		memcpy(de, ps, sizeof(struct msdos_dir_slot));
-		fat_mark_buffer_dirty(sb, bh, 1);
+		memcpy(*de, ps, sizeof(struct msdos_dir_slot));
+		fat_mark_buffer_dirty(sb, *bh, 1);
 	}
 
-	PRINTK3(("vfat_find: create file 4\n"));
 	dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
 	mark_inode_dirty(dir);
 
-	PRINTK3(("vfat_find: create file 5\n"));
+	fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+	(*de)->ctime_ms = 0;
+	(*de)->ctime = (*de)->time;
+	(*de)->adate = (*de)->cdate = (*de)->date;
+	(*de)->start = 0;
+	(*de)->starthi = 0;
+	(*de)->size = 0;
+	(*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+	(*de)->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
 
-	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
-	de->ctime_ms = 0;
-	de->ctime = de->time;
-	de->adate = de->cdate = de->date;
-	de->start = 0;
-	de->starthi = 0;
-	de->size = 0;
-	de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
-	de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
 
-
-	fat_mark_buffer_dirty(sb, bh, 1);
-	fat_brelse(sb, bh);
+	fat_mark_buffer_dirty(sb, *bh, 1);
 
 	/* slots can't be less than 1 */
-	sinfo_out->is_long = (slots > 1);
 	sinfo_out->long_slots = slots - 1;
-	sinfo_out->total_slots = slots;
-	sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
 	sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
 	res = 0;
 
@@ -1140,6 +873,29 @@
 	return res;
 }
 
+static int vfat_find(struct inode *dir,struct qstr* qname,
+	struct vfat_slot_info *sinfo, struct buffer_head **last_bh,
+	struct msdos_dir_entry **last_de)
+{
+	struct super_block *sb = dir->i_sb;
+	loff_t offset;
+	int res,len;
+
+	len = qname->len;
+	while (len && qname->name[len-1] == '.') 
+		len--;
+	res = fat_search_long(dir, qname->name, len,
+			(MSDOS_SB(sb)->options.name_check != 's'),
+			&offset,&sinfo->longname_offset);
+	if (res>0) {
+		sinfo->long_slots = res-1;
+		if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->ino)>=0)
+			return 0;
+		res = -EIO;
+	} 
+	return res ? res : -ENOENT;
+}
+
 /* Find a hashed dentry for inode; NULL if there are none */
 static struct dentry *find_alias(struct inode *inode)
 {
@@ -1162,8 +918,10 @@
 {
 	int res;
 	struct vfat_slot_info sinfo;
-	struct inode *result;
+	struct inode *inode;
 	struct dentry *alias;
+	struct buffer_head *bh = NULL;
+	struct msdos_dir_entry *de;
 	int table;
 	
 	PRINTK2(("vfat_lookup: name=%s, len=%d\n", 
@@ -1172,317 +930,160 @@
 	table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
 	dentry->d_op = &vfat_dentry_ops[table];
 
-	result = NULL;
-	if ((res = vfat_find(dir,&dentry->d_name,0,0,&sinfo)) < 0) {
-		result = NULL;
-		table++;
-		goto error;
-	}
-	PRINTK3(("vfat_lookup 4.5\n"));
-	if (!(result = iget(dir->i_sb,sinfo.ino)))
-		return ERR_PTR(-EACCES);
-	PRINTK3(("vfat_lookup 5\n"));
-	if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
-		iput(result);
-		result = NULL;
+	inode = NULL;
+	res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+	if (res < 0) {
 		table++;
 		goto error;
 	}
-	alias = find_alias(result);
+	inode = fat_build_inode(dir->i_sb, de, sinfo.ino, &res);
+	fat_brelse(dir->i_sb, bh);
+	if (res)
+		return ERR_PTR(res);
+	alias = find_alias(inode);
 	if (alias) {
 		if (d_invalidate(alias)==0)
 			dput(alias);
 		else {
-			iput(result);
+			iput(inode);
 			return alias;
 		}
+		
 	}
-	PRINTK3(("vfat_lookup 6\n"));
 error:
 	dentry->d_op = &vfat_dentry_ops[table];
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
-	d_add(dentry,result);
-	return 0;
+	d_add(dentry,inode);
+	return NULL;
 }
 
-
-static int vfat_create_entry(struct inode *dir,struct qstr* qname,
-    int is_dir, struct inode **result)
+int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
 {
 	struct super_block *sb = dir->i_sb;
-	int res,ino;
-	loff_t offset;
-	struct buffer_head *bh;
+	struct inode *inode = NULL;
+	struct buffer_head *bh = NULL;
 	struct msdos_dir_entry *de;
 	struct vfat_slot_info sinfo;
+	int res;
 
-	*result=0;
-	PRINTK1(("vfat_create_entry: Entering\n"));
-	res = vfat_find(dir, qname, 1, is_dir, &sinfo);
-	if (res < 0) {
+	res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);
+	if (res < 0)
 		return res;
-	}
-
-	offset = sinfo.shortname_offset;
-
-	PRINTK3(("vfat_create_entry 2\n"));
-	bh = NULL;
-	ino = fat_get_entry(dir, &offset, &bh, &de);
-	if (ino < 0) {
-		PRINTK3(("vfat_mkdir problem\n"));
-		if (bh)
-			fat_brelse(sb, bh);
-		return ino;
-	}
-	PRINTK3(("vfat_create_entry 3\n"));
-
-	if ((*result = iget(dir->i_sb,ino)) != NULL)
-		vfat_read_inode(*result);
+	inode = fat_build_inode(sb, de, sinfo.ino, &res);
 	fat_brelse(sb, bh);
-	if (!*result)
-		return -EIO;
-	(*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
-	    CURRENT_TIME;
-	mark_inode_dirty(*result);
-	(*result)->i_version = ++event;
+	if (!inode)
+		return res;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	inode->i_version = ++event;
 	dir->i_version = event;
-
-	return 0;
-}
-
-int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
-{
-	int res;
-	struct inode *result;
-
-	result=NULL;
-	fat_lock_creation();
-	res = vfat_create_entry(dir,&dentry->d_name,0,&result);
-	fat_unlock_creation();
-	if (res < 0) {
-		PRINTK3(("vfat_create: unable to get new entry\n"));
-	} else {
-		dentry->d_time = dentry->d_parent->d_inode->i_version;
-		d_instantiate(dentry,result);
-	}
-	return res;
-}
-
-static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
-     struct buffer_head *bh,
-     struct msdos_dir_entry *de,int ino,const char *name, int isdot)
-{
-	struct super_block *sb = dir->i_sb;
-	struct inode *dot;
-
-	PRINTK2(("vfat_create_a_dotdir: Entering\n"));
-
-	/*
-	 * XXX all times should be set by caller upon successful completion.
-	 */
-	dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	mark_inode_dirty(dir);
-	memcpy(de->name,name,MSDOS_NAME);
-	de->lcase = 0;
-	de->attr = ATTR_DIR;
-	de->start = 0;
-	de->starthi = 0;
-	fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
-	de->ctime_ms = 0;
-	de->ctime = de->time;
-	de->adate = de->cdate = de->date;
-	de->size = 0;
-	fat_mark_buffer_dirty(sb, bh, 1);
-	dot = iget(dir->i_sb,ino);
-	if (!dot)
-		return -EIO;
-	vfat_read_inode(dot);
-	dot->i_mtime = dot->i_atime = CURRENT_TIME;
-	mark_inode_dirty(dot);
-	if (isdot) {
-		dot->i_size = dir->i_size;
-		MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
-		MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
-		dot->i_nlink = dir->i_nlink;
-	} else {
-		dot->i_size = parent->i_size;
-		MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
-		MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart;
-		dot->i_nlink = parent->i_nlink;
-	}
-
-	iput(dot);
-
-	PRINTK3(("vfat_create_a_dotdir 2\n"));
+	dentry->d_time = dentry->d_parent->d_inode->i_version;
+	d_instantiate(dentry,inode);
 	return 0;
 }
 
 static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
 {
 	struct super_block *sb = dir->i_sb;
-	int res;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
-	loff_t offset;
+	__u16 date, time;
 
-	PRINTK2(("vfat_create_dotdirs: Entering\n"));
-	if ((res = fat_add_cluster(dir)) < 0) return res;
-
-	PRINTK3(("vfat_create_dotdirs 2\n"));
-	offset = 0;
-	bh = NULL;
-	if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
-	
-	PRINTK3(("vfat_create_dotdirs 3\n"));
-	res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
-	PRINTK3(("vfat_create_dotdirs 4\n"));
-	if (res < 0) {
-		fat_brelse(sb, bh);
-		return res;
-	}
-	PRINTK3(("vfat_create_dotdirs 5\n"));
-
-	if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
-		fat_brelse(sb, bh);
-		return res;
-	}
-	PRINTK3(("vfat_create_dotdirs 6\n"));
-
-	res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
-	PRINTK3(("vfat_create_dotdirs 7\n"));
+	if ((bh = fat_add_cluster1(dir)) == NULL) return -ENOSPC;
+	/* zeroed out, so... */
+	fat_date_unix2dos(dir->i_mtime,&time,&date);
+	de = (struct msdos_dir_entry*)&bh->b_data[0];
+	memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
+	memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
+	de[0].attr = de[1].attr = ATTR_DIR;
+	de[0].ctime = de[0].time = de[1].ctime = de[1].time = CT_LE_W(time);
+	de[0].adate = de[0].cdate = de[0].date = de[1].adate =
+		de[1].cdate = de[1].date = CT_LE_W(date);
+	de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+	de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
+	de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
+	de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
+	fat_mark_buffer_dirty(sb, bh, 1);
 	fat_brelse(sb, bh);
+	dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(dir);
 
-	return res;
-}
-
-/***** See if directory is empty */
-static int vfat_empty(struct inode *dir)
-{
-	struct super_block *sb = dir->i_sb;
-	loff_t pos;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-
-	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
-		pos = 0;
-		bh = NULL;
-		while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
-			/* Skip extended filename entries */
-			if (de->attr == ATTR_EXT) continue;
-
-			if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
-			    MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
-			    MSDOS_NAME)) {
-				fat_brelse(sb, bh);
-				return -ENOTEMPTY;
-			}
-		}
-		if (bh)
-			fat_brelse(sb, bh);
-	}
 	return 0;
 }
 
-static void vfat_free_ino(struct inode *dir,struct buffer_head *bh,
-     struct msdos_dir_entry *de,struct inode* victim)
+static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
+     struct buffer_head *bh, struct msdos_dir_entry *de)
 {
 	struct super_block *sb = dir->i_sb;
-	victim->i_nlink = 0;
-	victim->i_mtime = dir->i_mtime = CURRENT_TIME;
-	victim->i_atime = dir->i_atime = CURRENT_TIME;
+	loff_t offset;
+	int i,ino;
+
+	/* remove the shortname */
+	dir->i_mtime = CURRENT_TIME;
+	dir->i_atime = CURRENT_TIME;
 	dir->i_version = ++event;
-	MSDOS_I(victim)->i_busy = 1;
 	mark_inode_dirty(dir);
-	mark_inode_dirty(victim);
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
-}
-
-static int vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
-     struct inode* victim)
-{
-	struct super_block *sb = dir->i_sb;
-	loff_t offset;
-	struct buffer_head *bh=NULL;
-	struct msdos_dir_entry *de;
-	int res, i;
-
-	/* remove the shortname */
-	offset = sinfo->shortname_offset;
-	res = fat_get_entry(dir, &offset, &bh, &de);
-	if (res < 0) return res;
-	vfat_free_ino(dir,bh,de,victim);
 	/* remove the longname */
-	offset = sinfo->longname_offset;
+	offset = sinfo->longname_offset; de = NULL;
 	for (i = sinfo->long_slots; i > 0; --i) {
-		if (fat_get_entry(dir, &offset, &bh, &de) < 0)
+		if (fat_get_entry(dir, &offset, &bh, &de, &ino) < 0)
 			continue;
 		de->name[0] = DELETED_FLAG;
 		de->attr = 0;
 		fat_mark_buffer_dirty(sb, bh, 1);
 	}
 	if (bh) fat_brelse(sb, bh);
-	return 0;
 }
 
-static int vfat_rmdirx(struct inode *dir,struct dentry* dentry)
+int vfat_rmdir(struct inode *dir,struct dentry* dentry)
 {
 	int res;
 	struct vfat_slot_info sinfo;
+	struct buffer_head *bh = NULL;
+	struct msdos_dir_entry *de;
 
-	PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry));
-	res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
+	if (!list_empty(&dentry->d_hash))
+		return -EBUSY;
 
-	if (res >= 0 && sinfo.total_slots > 0) {
-		if (!list_empty(&dentry->d_hash))
-			return -EBUSY;
-		res = vfat_empty(dentry->d_inode);
-		if (res)
-			return res;
-		
-		res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
-		if (res >= 0) {
-			dir->i_nlink--;
-			res = 0;
-		}
-	}
-	return res;
-}
-
-/***** Remove a directory */
-int vfat_rmdir(struct inode *dir,struct dentry* dentry)
-{
-	int res;
-	PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
+	res = fat_dir_empty(dentry->d_inode);
+	if (res)
+		return res;
 
-	res = -EBUSY;
-	if (list_empty(&dentry->d_hash)) {
-		res = vfat_rmdirx(dir, dentry);
-		/* If that went OK all aliases are already dropped */
-	}
-	return res;
+	res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);
+	if (res<0)
+		return res;
+	dentry->d_inode->i_nlink = 0;
+	dentry->d_inode->i_mtime = CURRENT_TIME;
+	dentry->d_inode->i_atime = CURRENT_TIME;
+	fat_detach(dentry->d_inode);
+	mark_inode_dirty(dentry->d_inode);
+	/* releases bh */
+	vfat_remove_entry(dir,&sinfo,bh,de);
+	dir->i_nlink--;
+	return 0;
 }
 
-static int vfat_unlinkx(
-	struct inode *dir,
-	struct dentry* dentry,
-	int nospc)	/* Flag special file ? */
+int vfat_unlink(struct inode *dir, struct dentry* dentry)
 {
 	int res;
 	struct vfat_slot_info sinfo;
+	struct buffer_head *bh = NULL;
+	struct msdos_dir_entry *de;
 
-	PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
-	res = vfat_find(dir,&dentry->d_name,0,0,&sinfo);
-
-	if (res >= 0 && sinfo.total_slots > 0) {
-		if (!S_ISREG(dentry->d_inode->i_mode) && nospc) {
-			return -EPERM;
-		}
-		res = vfat_remove_entry(dir,&sinfo,dentry->d_inode);
-		if (res > 0) {
-			res = 0;
-		}
-	}
+	PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name));
+	res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+	if (res < 0)
+		return res;
+	dentry->d_inode->i_nlink = 0;
+	dentry->d_inode->i_mtime = CURRENT_TIME;
+	dentry->d_inode->i_atime = CURRENT_TIME;
+	fat_detach(dentry->d_inode);
+	mark_inode_dirty(dentry->d_inode);
+	/* releases bh */
+	vfat_remove_entry(dir,&sinfo,bh,de);
+	d_delete(dentry);
 
 	return res;
 }
@@ -1490,64 +1091,46 @@
 
 int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
 {
-	struct inode *inode;
+	struct super_block *sb = dir->i_sb;
+	struct inode *inode = NULL;
 	struct vfat_slot_info sinfo;
+	struct buffer_head *bh = NULL;
+	struct msdos_dir_entry *de;
 	int res;
 
-	PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
-	fat_lock_creation();
-	if ((res = vfat_create_entry(dir,&dentry->d_name,1,&inode)) < 0) {
-		fat_unlock_creation();
+	res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);
+	if (res < 0)
 		return res;
-	}
-
+	inode = fat_build_inode(sb, de, sinfo.ino, &res);
+	if (!inode)
+		goto out;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	inode->i_version = ++event;
+	dir->i_version = event;
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 
 	res = vfat_create_dotdirs(inode, dir);
 	if (res < 0)
 		goto mkdir_failed;
-	fat_unlock_creation();
 	dentry->d_time = dentry->d_parent->d_inode->i_version;
 	d_instantiate(dentry,inode);
+out:
+	fat_brelse(sb, bh);
 	return res;
 
 mkdir_failed:
-	fat_unlock_creation();
-	if (vfat_find(dir,&dentry->d_name,0,0,&sinfo) < 0)
-		goto mkdir_panic;
-	if (vfat_remove_entry(dir, &sinfo, inode) < 0)
-		goto mkdir_panic;
+	inode->i_nlink = 0;
+	inode->i_mtime = CURRENT_TIME;
+	inode->i_atime = CURRENT_TIME;
+	fat_detach(inode);
+	mark_inode_dirty(inode);
+	/* releases bh */
+	vfat_remove_entry(dir,&sinfo,bh,de);
 	iput(inode);
 	dir->i_nlink--;
 	return res;
-
-mkdir_panic:
-	dir->i_version = ++event;
-	fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
-	return res;
-}
-
-/***** Unlink, as called for msdosfs */
-int vfat_unlink(struct inode *dir,struct dentry* dentry)
-{
-	int res;
-
-	PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry, dentry->d_inode));
-	res = vfat_unlinkx (dir,dentry,1);
-	if (res >= 0)
-		d_delete(dentry);
-	return res;
-}
-
-/***** Unlink, as called for uvfatfs */
-int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry)
-{
-	int res;
-
-	res = vfat_unlinkx (dir,dentry,0);
-	iput(dir);
-	return res;
 }
  
 int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
@@ -1555,148 +1138,86 @@
 {
 	struct super_block *sb = old_dir->i_sb;
 	struct buffer_head *old_bh,*new_bh,*dotdot_bh;
-	struct msdos_dir_entry *old_de,*dotdot_de;
-	loff_t old_offset,new_offset,old_longname_offset;
-	int old_slots,old_ino,new_ino,dotdot_ino;
-	struct inode *old_inode, *new_inode, *dotdot_inode;
-	int res, is_dir, i;
-	int locked = 0;
-	struct vfat_slot_info sinfo;
+	struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+	int dotdot_ino;
+	struct inode *old_inode, *new_inode;
+	int res, is_dir;
+	struct vfat_slot_info old_sinfo,sinfo;
 
-	PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
-		 old_dentry, old_dentry->d_inode, old_dentry->d_inode->i_ino,
-		 new_dentry, new_dentry->d_inode,
-		 new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0));
-
-	old_bh = new_bh = NULL;
-	old_inode = new_inode = NULL;
-	res = vfat_find(old_dir,&old_dentry->d_name,0,0,&sinfo);
+	old_bh = new_bh = dotdot_bh = NULL;
+	old_inode = old_dentry->d_inode;
+	new_inode = new_dentry->d_inode;
+	res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);
 	PRINTK3(("vfat_rename 2\n"));
 	if (res < 0) goto rename_done;
 
-	old_slots = sinfo.total_slots;
-	old_longname_offset = sinfo.longname_offset;
-	old_offset = sinfo.shortname_offset;
-	old_ino = sinfo.ino;
-	res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
-	PRINTK3(("vfat_rename 3\n"));
-	if (res < 0) goto rename_done;
-
-	res = -ENOENT;
-	old_inode = old_dentry->d_inode;
 	is_dir = S_ISDIR(old_inode->i_mode);
 
-	fat_lock_creation(); locked = 1;
+	if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+				&dotdot_de,&dotdot_ino)) < 0)
+		goto rename_done;
 
 	if (new_dentry->d_inode) {
-		/*
-		 * OK, we have to remove the target. We should do it so
-		 * that nobody might go and find it negative. Actually we
-		 * should give warranties wrt preserving target over the
-		 * possible crash, but that's another story. We can't
-		 * get here with the target unhashed, so the directory entry
-		 * must exist.
-		 */
-
-		new_inode = new_dentry->d_inode;
-		res = vfat_find(new_dir,&new_dentry->d_name,0,is_dir,&sinfo);
-		if (res < 0 || new_inode->i_ino != sinfo.ino) {
+		res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,
+				&new_de);
+		if (res < 0 || MSDOS_I(new_inode)->i_location != sinfo.ino) {
 			/* WTF??? Cry and fail. */
 			printk(KERN_WARNING "vfat_rename: fs corrupted\n");
 			goto rename_done;
 		}
 
 		if (is_dir) {
-			res = vfat_empty(new_inode);
+			res = fat_dir_empty(new_inode);
 			if (res)
 				goto rename_done;
 		}
-		res = vfat_remove_entry(new_dir,&sinfo,new_inode);
-		if (res)
-			goto rename_done;
-
-		if (is_dir)
-			new_dir->i_nlink--;
+		fat_detach(new_inode);
+	} else {
+		res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,
+					&new_bh,&new_de);
+		if (res < 0) goto rename_done;
 	}
 
-	/* Serious lossage here. FAT uses braindead inode numbers scheme,
-	 * so we can't simply cannibalize the entry. It means that we have
-	 * no warranties that crash here will not make target disappear
-	 * after reboot. Lose, lose. Nothing to do with that until we'll
-	 * separate the functions of i_ino: it serves both as a search key
-	 * in icache and as a part of stat output. It would kill all the
-	 * 'busy' stuff on the spot. Later.
-	 */
-
-	res = vfat_find(new_dir,&new_dentry->d_name,1,is_dir,&sinfo);
-
-	if (res < 0) goto rename_done;
-
-	new_offset = sinfo.shortname_offset;
-	new_ino = sinfo.ino;
-
-	/* XXX: take care of other owners */
+	new_dir->i_version = ++event;
 
-	remove_inode_hash(old_inode);
-	fat_cache_inval_inode(old_inode);
-	old_inode->i_ino = new_ino;
-	old_inode->i_version = ++event;
-	insert_inode_hash(old_inode);
+	/* releases old_bh */
+	vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);
+	old_bh=NULL;
+	fat_detach(old_inode);
+	fat_attach(old_inode, sinfo.ino);
 	mark_inode_dirty(old_inode);
 
 	old_dir->i_version = ++event;
-	new_dir->i_version = ++event;
-
-	/* remove the old entry */
-	for (i = old_slots; i > 0; --i) {
-		res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
-		if (res < 0) {
-			printk("vfat_rename: problem 1\n");
-			continue;
-		}
-		old_de->name[0] = DELETED_FLAG;
-		old_de->attr = 0;
-		fat_mark_buffer_dirty(sb, old_bh, 1);
+	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	mark_inode_dirty(old_dir);
+	if (new_inode) {
+		new_inode->i_nlink--;
+		new_inode->i_ctime=CURRENT_TIME;
 	}
 
 	if (is_dir) {
-		if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
-		    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
-		if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
-			fat_brelse(sb, dotdot_bh);
-			res = -EIO;
-			goto rename_done;
-		}
-		MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
-		MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
-		dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
-		dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
-		mark_inode_dirty(dotdot_inode);
+		int start = MSDOS_I(new_dir)->i_logstart;
+		dotdot_de->start = CT_LE_W(start);
+		dotdot_de->starthi = CT_LE_W(start>>16);
 		fat_mark_buffer_dirty(sb, dotdot_bh, 1);
 		old_dir->i_nlink--;
-		new_dir->i_nlink++;
-		/* no need to mark them dirty */
-		dotdot_inode->i_nlink = new_dir->i_nlink;
-		iput(dotdot_inode);
-		fat_brelse(sb, dotdot_bh);
+		if (new_inode) {
+			new_inode->i_nlink--;
+		} else {
+			new_dir->i_nlink++;
+			mark_inode_dirty(new_dir);
+		}
 	}
 
-	if (res >= 0)
-		res = 0;
-
 rename_done:
-	if (locked)
-		fat_unlock_creation();
-	if (old_bh)
-		fat_brelse(sb, old_bh);
-	if (new_bh)
-		fat_brelse(sb, new_bh);
+	fat_brelse(sb, dotdot_bh);
+	fat_brelse(sb, old_bh);
+	fat_brelse(sb, new_bh);
 	return res;
 
 }
 
 
-
 /* Public inode operations for the VFAT fs */
 struct inode_operations vfat_dir_inode_operations = {
 	&fat_dir_operations,	/* default directory file-ops */
@@ -1718,10 +1239,38 @@
 	NULL			/* permission */
 };
 
-
-void vfat_read_inode(struct inode *inode)
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+				    int silent)
 {
-	fat_read_inode(inode, &vfat_dir_inode_operations);
+	struct super_block *res;
+  
+	MOD_INC_USE_COUNT;
+	
+	MSDOS_SB(sb)->options.isvfat = 1;
+
+	res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations);
+	if (res == NULL) {
+		sb->s_dev = 0;
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+
+	if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
+		MOD_DEC_USE_COUNT;
+	} else {
+		MSDOS_SB(sb)->put_super_callback=vfat_put_super_callback;
+		MSDOS_SB(sb)->options.dotsOK = 0;
+		if (MSDOS_SB(sb)->options.posixfs) {
+			MSDOS_SB(sb)->options.name_check = 's';
+		}
+		if (MSDOS_SB(sb)->options.name_check != 's') {
+			sb->s_root->d_op = &vfat_dentry_ops[0];
+		} else {
+			sb->s_root->d_op = &vfat_dentry_ops[2];
+		}
+	}
+
+	return res;
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.3.1/linux/fs/vfat/vfatfs_syms.c linux/fs/vfat/vfatfs_syms.c
--- v2.3.1/linux/fs/vfat/vfatfs_syms.c	Thu Oct 23 14:00:15 1997
+++ linux/fs/vfat/vfatfs_syms.c	Thu May 13 23:18:21 1999
@@ -21,13 +21,10 @@
 
 EXPORT_SYMBOL(vfat_create);
 EXPORT_SYMBOL(vfat_unlink);
-EXPORT_SYMBOL(vfat_unlink_uvfat);
 EXPORT_SYMBOL(vfat_mkdir);
 EXPORT_SYMBOL(vfat_rmdir);
 EXPORT_SYMBOL(vfat_rename);
-EXPORT_SYMBOL(vfat_put_super);
 EXPORT_SYMBOL(vfat_read_super);
-EXPORT_SYMBOL(vfat_read_inode);
 EXPORT_SYMBOL(vfat_lookup);
 
 int init_vfat_fs(void)
diff -u --recursive --new-file v2.3.1/linux/include/asm-alpha/bitops.h linux/include/asm-alpha/bitops.h
--- v2.3.1/linux/include/asm-alpha/bitops.h	Sun Sep  6 10:34:33 1998
+++ linux/include/asm-alpha/bitops.h	Fri May 14 12:41:23 1999
@@ -172,7 +172,10 @@
 
 extern inline unsigned long ffz(unsigned long word)
 {
-#ifdef __alpha_cix__
+#if 0 && defined(__alpha_cix__)
+	/* Swine architects -- a year after they publish v3 of the
+	   handbook, in the 21264 data sheet they quietly change CIX
+	   to FIX and remove the spiffy counting instructions.  */
 	/* Whee.  EV6 can calculate it directly.  */
 	unsigned long result;
 	__asm__("ctlz %1,%0" : "=r"(result) : "r"(~word));
@@ -208,7 +211,10 @@
  * of bits set) of a N-bit word
  */
 
-#ifdef __alpha_cix__
+#if 0 && defined(__alpha_cix__)
+/* Swine architects -- a year after they publish v3 of the handbook, in
+   the 21264 data sheet they quietly change CIX to FIX and remove the
+   spiffy counting instructions.  */
 /* Whee.  EV6 can calculate it directly.  */
 extern __inline__ unsigned long hweight64(unsigned long w)
 {
diff -u --recursive --new-file v2.3.1/linux/include/asm-alpha/init.h linux/include/asm-alpha/init.h
--- v2.3.1/linux/include/asm-alpha/init.h	Tue Dec 29 13:56:15 1998
+++ linux/include/asm-alpha/init.h	Fri May 14 12:41:23 1999
@@ -12,6 +12,6 @@
 #define __FINIT		.previous
 #define __INITDATA	.section	.data.init,"a"
 
-#define __cacheline_aligned __attribute__((__aligned__(L1_CACHE_BYTES)))
+#define __cacheline_aligned __attribute__((__aligned__(32)))
 
 #endif
diff -u --recursive --new-file v2.3.1/linux/include/asm-alpha/irq.h linux/include/asm-alpha/irq.h
--- v2.3.1/linux/include/asm-alpha/irq.h	Sun Jan 10 09:59:59 1999
+++ linux/include/asm-alpha/irq.h	Fri May 14 12:41:23 1999
@@ -92,7 +92,11 @@
 }
 
 extern void disable_irq(unsigned int);
+extern void disable_irq_nosync(unsigned int);
 extern void enable_irq(unsigned int);
+
+extern void irq_enter(int cpu, int irq);
+extern void irq_exit(int cpu, int irq);
 
 struct pt_regs;
 extern void (*perf_irq)(unsigned long, struct pt_regs *);
diff -u --recursive --new-file v2.3.1/linux/include/asm-alpha/smp.h linux/include/asm-alpha/smp.h
--- v2.3.1/linux/include/asm-alpha/smp.h	Tue Dec 29 13:56:15 1998
+++ linux/include/asm-alpha/smp.h	Fri May 14 12:41:23 1999
@@ -4,22 +4,30 @@
 #ifdef __SMP__
 
 #include <linux/tasks.h>
+#include <asm/init.h>
 #include <asm/pal.h>
 
 struct cpuinfo_alpha {
 	unsigned long loops_per_sec;
-	unsigned int next;
 	unsigned long *pgd_cache;
 	unsigned long *pte_cache;
 	unsigned long pgtable_cache_sz;
 	unsigned long ipi_count;
-} __attribute__((aligned(32)));
+	unsigned long prof_multiplier;
+	unsigned long prof_counter;
+} __cacheline_aligned;
 
 extern struct cpuinfo_alpha cpu_data[NR_CPUS];
 
 #define PROC_CHANGE_PENALTY     20
 
-extern __volatile__ int cpu_number_map[NR_CPUS];
+/* Map from cpu id to sequential logical cpu number.  This will only
+   not be idempotent when cpus failed to come on-line.  */
+extern int cpu_number_map[NR_CPUS];
+
+/* The reverse map from sequential logical cpu number to cpu id.  */
+extern int __cpu_logical_map[NR_CPUS];
+#define cpu_logical_map(cpu)  __cpu_logical_map[cpu]
 
 /* HACK: Cabrio WHAMI return value is bogus if more than 8 bits used.. :-( */
 
@@ -35,7 +43,6 @@
 }
 
 #define smp_processor_id()	(current->processor)
-#define cpu_logical_map(cpu)	(cpu)
 
 #endif /* __SMP__ */
 
diff -u --recursive --new-file v2.3.1/linux/include/asm-alpha/system.h linux/include/asm-alpha/system.h
--- v2.3.1/linux/include/asm-alpha/system.h	Tue May 11 09:55:45 1999
+++ linux/include/asm-alpha/system.h	Fri May 14 12:41:23 1999
@@ -147,6 +147,13 @@
 #endif
 #endif
 
+enum amask_enum {
+	AMASK_BWX = (1UL << 0),
+	AMASK_FIX = (1UL << 1),
+	AMASK_MAX = (1UL << 8),
+	AMASK_PRECISE_TRAP = (1UL << 9),
+};
+
 #define amask(mask)						\
 ({ unsigned long __amask, __input = (mask);			\
    __asm__ ("amask %1,%0" : "=r"(__amask) : "rI"(__input));	\
diff -u --recursive --new-file v2.3.1/linux/include/linux/efs_dir.h linux/include/linux/efs_dir.h
--- v2.3.1/linux/include/linux/efs_dir.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/efs_dir.h	Thu May 13 23:50:15 1999
@@ -0,0 +1,42 @@
+/*
+ * efs_dir.h
+ *
+ * Copyright (c) 1999 Al Smith
+ */
+
+#ifndef __EFS_DIR_H__
+#define __EFS_DIR_H__
+
+#define EFS_DIRBSIZE_BITS	EFS_BLOCKSIZE_BITS
+#define EFS_DIRBSIZE		(1 << EFS_DIRBSIZE_BITS)
+
+struct efs_dentry {
+	unsigned int	inode;
+	unsigned char	namelen;
+	char		name[3];
+};
+
+#define EFS_DENTSIZE	(sizeof(struct efs_dentry) - 3 + 1)
+#define EFS_MAXNAMELEN  ((1 << (sizeof(char) * 8)) - 1)
+
+#define EFS_DIRBLK_HEADERSIZE	4
+#define EFS_DIRBLK_MAGIC	0xbeef	/* moo */
+
+struct efs_dir {
+	unsigned short	magic;
+	unsigned char	firstused;
+	unsigned char	slots;
+
+	unsigned char	space[EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE];
+};
+
+#define EFS_MAXENTS \
+	((EFS_DIRBSIZE - EFS_DIRBLK_HEADERSIZE) / \
+	 (EFS_DENTSIZE + sizeof(char)))
+
+#define EFS_SLOTAT(dir, slot) EFS_REALOFF((dir)->space[slot])
+
+#define EFS_REALOFF(offset) ((offset << 1))
+
+#endif /* __EFS_DIR_H__ */
+
diff -u --recursive --new-file v2.3.1/linux/include/linux/efs_fs.h linux/include/linux/efs_fs.h
--- v2.3.1/linux/include/linux/efs_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/efs_fs.h	Fri May 14 18:46:37 1999
@@ -0,0 +1,67 @@
+/*
+ * efs_fs.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#ifndef __EFS_FS_H__
+#define __EFS_FS_H__
+
+#define EFS_VERSION "1.0a"
+
+static const char cprt[] = "EFS: "EFS_VERSION" - (c) 1999 Al Smith <Al.Smith@aeschi.ch.eu.org>";
+
+#include <asm/uaccess.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#if LINUX_VERSION_CODE < 0x20200
+#error This code is only for linux-2.2 and later.
+#endif
+
+/* 1 block is 512 bytes */
+#define	EFS_BLOCKSIZE_BITS	9
+#define	EFS_BLOCKSIZE		(1 << EFS_BLOCKSIZE_BITS)
+
+#include <linux/efs_fs_i.h>
+#include <linux/efs_dir.h>
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifdef _EFS_USE_GENERIC
+#define INODE_INFO(i) (struct efs_inode_info *)	&((i)->u.generic_ip)
+#define SUPER_INFO(s) (struct efs_sb_info *)	&((s)->u.generic_sbp)
+#else
+#define INODE_INFO(i)				&((i)->u.efs_i)
+#define SUPER_INFO(s)				&((s)->u.efs_sb)
+#endif
+
+extern struct inode_operations efs_dir_inode_operations;
+extern struct inode_operations efs_file_inode_operations;
+extern struct inode_operations efs_symlink_inode_operations;
+
+extern int init_module(void);
+extern void cleanup_module(void);
+extern struct super_block *efs_read_super(struct super_block *, void *, int);
+extern void efs_put_super(struct super_block *);
+extern int efs_statfs(struct super_block *, struct statfs *, int);
+
+extern void efs_read_inode(struct inode *);
+extern efs_block_t efs_map_block(struct inode *, efs_block_t);
+
+extern struct dentry *efs_lookup(struct inode *, struct dentry *);
+extern int efs_bmap(struct inode *, int);
+
+extern int init_efs_fs(void);
+
+#endif /* __EFS_FS_H__ */
+
diff -u --recursive --new-file v2.3.1/linux/include/linux/efs_fs_i.h linux/include/linux/efs_fs_i.h
--- v2.3.1/linux/include/linux/efs_fs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/efs_fs_i.h	Thu May 13 23:50:15 1999
@@ -0,0 +1,67 @@
+/*
+ * efs_fs_i.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1988 Silicon Graphics
+ */
+
+#ifndef	__EFS_FS_I_H__
+#define	__EFS_FS_I_H__
+
+typedef	int32_t		efs_block_t;
+typedef uint32_t	efs_ino_t;
+
+#define	EFS_DIRECTEXTENTS	12
+
+/*
+ * layout of an extent, in memory and on disk. 8 bytes exactly.
+ */
+typedef union extent_u {
+	unsigned char raw[8];
+	struct extent_s {
+		unsigned int	ex_magic:8;	/* magic # (zero) */
+		unsigned int	ex_bn:24;	/* basic block */
+		unsigned int	ex_length:8;	/* numblocks in this extent */
+		unsigned int	ex_offset:24;	/* logical offset into file */
+	} cooked;
+} efs_extent;
+
+typedef struct edevs {
+	short		odev;
+	unsigned int	ndev;
+} efs_devs;
+
+/*
+ * extent based filesystem inode as it appears on disk.  The efs inode
+ * is exactly 128 bytes long.
+ */
+struct	efs_dinode {
+	u_short		di_mode;	/* mode and type of file */
+	short		di_nlink;	/* number of links to file */
+	u_short		di_uid;		/* owner's user id */
+	u_short		di_gid;		/* owner's group id */
+	int32_t		di_size;	/* number of bytes in file */
+	int32_t		di_atime;	/* time last accessed */
+	int32_t		di_mtime;	/* time last modified */
+	int32_t		di_ctime;	/* time created */
+	uint32_t	di_gen;		/* generation number */
+	short		di_numextents;	/* # of extents */
+	u_char		di_version;	/* version of inode */
+	u_char		di_spare;	/* spare - used by AFS */
+	union di_addr {
+		efs_extent	di_extents[EFS_DIRECTEXTENTS];
+		efs_devs	di_dev;	/* device for IFCHR/IFBLK */
+	} di_u;
+};
+
+/* efs inode storage in memory */
+struct efs_inode_info {
+	int		numextents;
+	int		lastextent;
+
+	efs_extent	extents[EFS_DIRECTEXTENTS];
+};
+
+#endif	/* __EFS_FS_I_H__ */
+
diff -u --recursive --new-file v2.3.1/linux/include/linux/efs_fs_sb.h linux/include/linux/efs_fs_sb.h
--- v2.3.1/linux/include/linux/efs_fs_sb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/efs_fs_sb.h	Thu May 13 23:50:15 1999
@@ -0,0 +1,62 @@
+/*
+ * efs_fs_sb.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1988 Silicon Graphics
+ */
+
+#ifndef __EFS_FS_SB_H__
+#define __EFS_FS_SB_H__
+
+/* statfs() magic number for EFS */
+#define EFS_SUPER_MAGIC	0x414A53
+
+/* EFS superblock magic numbers */
+#define EFS_MAGIC	0x072959
+#define EFS_NEWMAGIC	0x07295a
+
+#define IS_EFS_MAGIC(x)	((x == EFS_MAGIC) || (x == EFS_NEWMAGIC))
+
+#define EFS_SUPER		1
+#define EFS_ROOTINODE		2
+
+/* efs superblock on disk */
+struct efs_super {
+	int32_t		fs_size;        /* size of filesystem, in sectors */
+	int32_t		fs_firstcg;     /* bb offset to first cg */
+	int32_t		fs_cgfsize;     /* size of cylinder group in bb's */
+	short		fs_cgisize;     /* bb's of inodes per cylinder group */
+	short		fs_sectors;     /* sectors per track */
+	short		fs_heads;       /* heads per cylinder */
+	short		fs_ncg;         /* # of cylinder groups in filesystem */
+	short		fs_dirty;       /* fs needs to be fsck'd */
+	int32_t		fs_time;        /* last super-block update */
+	int32_t		fs_magic;       /* magic number */
+	char		fs_fname[6];    /* file system name */
+	char		fs_fpack[6];    /* file system pack name */
+	int32_t		fs_bmsize;      /* size of bitmap in bytes */
+	int32_t		fs_tfree;       /* total free data blocks */
+	int32_t		fs_tinode;      /* total free inodes */
+	int32_t		fs_bmblock;     /* bitmap location. */
+	int32_t		fs_replsb;      /* Location of replicated superblock. */
+	int32_t		fs_lastialloc;  /* last allocated inode */
+	char		fs_spare[20];   /* space for expansion - MUST BE ZERO */
+	int32_t		fs_checksum;    /* checksum of volume portion of fs */
+};
+
+/* efs superblock information in memory */
+struct efs_sb_info {
+	int32_t	fs_magic;	/* superblock magic number */
+	int32_t	fs_start;	/* first block of filesystem */
+	int32_t	first_block;	/* first data block in filesystem */
+	int32_t	total_blocks;	/* total number of blocks in filesystem */
+	int32_t	group_size;	/* # of blocks a group consists of */ 
+	int32_t	data_free;	/* # of free data blocks */
+	int32_t	inode_free;	/* # of free inodes */
+	short	inode_blocks;	/* # of blocks used for inodes in every grp */
+	short	total_groups;	/* # of groups */
+};
+
+#endif /* __EFS_FS_SB_H__ */
+
diff -u --recursive --new-file v2.3.1/linux/include/linux/efs_vh.h linux/include/linux/efs_vh.h
--- v2.3.1/linux/include/linux/efs_vh.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/efs_vh.h	Thu May 13 23:50:15 1999
@@ -0,0 +1,70 @@
+/*
+ * efs_vh.h
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from IRIX header files (c) 1985 MIPS Computer Systems, Inc.
+ */
+
+#ifndef __EFS_VH_H__
+#define __EFS_VH_H__
+
+#define VHMAGIC		0xbe5a941	/* volume header magic number */
+#define NPARTAB		16		/* 16 unix partitions */
+#define NVDIR		15		/* max of 15 directory entries */
+#define BFNAMESIZE	16		/* max 16 chars in boot file name */
+#define VDNAMESIZE	8
+
+struct volume_directory {
+	char	vd_name[VDNAMESIZE];	/* name */
+	int	vd_lbn;			/* logical block number */
+	int	vd_nbytes;		/* file length in bytes */
+};
+
+struct partition_table {	/* one per logical partition */
+	int	pt_nblks;	/* # of logical blks in partition */
+	int	pt_firstlbn;	/* first lbn of partition */
+	int	pt_type;	/* use of partition */
+};
+
+struct volume_header {
+	int	vh_magic;			/* identifies volume header */
+	short	vh_rootpt;			/* root partition number */
+	short	vh_swappt;			/* swap partition number */
+	char	vh_bootfile[BFNAMESIZE];	/* name of file to boot */
+	char	pad[48];			/* device param space */
+	struct volume_directory vh_vd[NVDIR];	/* other vol hdr contents */
+	struct partition_table  vh_pt[NPARTAB];	/* device partition layout */
+	int	vh_csum;			/* volume header checksum */
+	int	vh_fill;			/* fill out to 512 bytes */
+};
+
+/* partition type sysv is used for EFS format CD-ROM partitions */
+#define SGI_SYSV	0x05
+#define SGI_EFS		0x07
+#define IS_EFS(x)	(((x) == SGI_EFS) || ((x) == SGI_SYSV))
+
+struct pt_types {
+	int	pt_type;
+	char	*pt_name;
+} sgi_pt_types[] = {
+	{0x00,		"SGI vh"},
+	{0x01,		"SGI trkrepl"},
+	{0x02,		"SGI secrepl"},
+	{0x03,		"SGI raw"},
+	{0x04,		"SGI bsd"},
+	{SGI_SYSV,	"SGI sysv"},
+	{0x06,		"SGI vol"},
+	{SGI_EFS,	"SGI efs"},
+	{0x08,		"SGI lv"},
+	{0x09,		"SGI rlv"},
+	{0x0A,		"SGI xfs"},
+	{0x0B,		"SGI xfslog"},
+	{0x0C,		"SGI xlv"},
+	{0x82,		"Linux swap"},
+	{0x83,		"Linux native"},
+	{0,		NULL}
+};
+
+#endif /* __EFS_VH_H__ */
+
diff -u --recursive --new-file v2.3.1/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.3.1/linux/include/linux/fs.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/fs.h	Fri May 14 18:44:59 1999
@@ -269,6 +269,7 @@
 #include <linux/sysv_fs_i.h>
 #include <linux/affs_fs_i.h>
 #include <linux/ufs_fs_i.h>
+#include <linux/efs_fs_i.h>
 #include <linux/coda_fs_i.h>
 #include <linux/romfs_fs_i.h>
 #include <linux/smb_fs_i.h>
@@ -381,6 +382,7 @@
 		struct sysv_inode_info		sysv_i;
 		struct affs_inode_info		affs_i;
 		struct ufs_inode_info		ufs_i;
+		struct efs_inode_info		efs_i;	   
 		struct romfs_inode_info		romfs_i;
 		struct coda_inode_info		coda_i;
 		struct smb_inode_info		smbfs_i;
@@ -505,6 +507,7 @@
 #include <linux/sysv_fs_sb.h>
 #include <linux/affs_fs_sb.h>
 #include <linux/ufs_fs_sb.h>
+#include <linux/efs_fs_sb.h>
 #include <linux/romfs_fs_sb.h>
 #include <linux/smb_fs_sb.h>
 #include <linux/hfs_fs_sb.h>
@@ -547,6 +550,7 @@
 		struct sysv_sb_info	sysv_sb;
 		struct affs_sb_info	affs_sb;
 		struct ufs_sb_info	ufs_sb;
+		struct efs_sb_info	efs_sb;	   
 		struct romfs_sb_info	romfs_sb;
 		struct smb_sb_info	smbfs_sb;
 		struct hfs_sb_info	hfs_sb;
@@ -830,6 +834,8 @@
 #define lnamei(pathname)	__namei(pathname, 0)
 
 extern void iput(struct inode *);
+extern struct inode * igrab(struct inode *inode);
+extern ino_t iunique(struct super_block *, ino_t);
 extern struct inode * iget(struct super_block *, unsigned long);
 extern void clear_inode(struct inode *);
 extern struct inode * get_empty_inode(void);
diff -u --recursive --new-file v2.3.1/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.3.1/linux/include/linux/genhd.h	Tue May 11 13:03:59 1999
+++ linux/include/linux/genhd.h	Fri May 14 18:44:58 1999
@@ -22,7 +22,7 @@
 #define CONFIG_SUN_PARTITION 1
 #endif
 
-#if defined(CONFIG_SGI)
+#if defined(CONFIG_SGI) || defined(CONFIG_SGI_DISKLABEL)
 #define CONFIG_SGI_PARTITION 1
 #endif
 
diff -u --recursive --new-file v2.3.1/linux/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h
--- v2.3.1/linux/include/linux/hpfs_fs_i.h	Wed Dec  1 04:44:15 1993
+++ linux/include/linux/hpfs_fs_i.h	Fri May 14 18:30:46 1999
@@ -1,7 +1,17 @@
 #ifndef _HPFS_FS_I
 #define _HPFS_FS_I
 
+#if ANALWARNINGS
+#warning Fix the FIFO stuff!
+#warning Fix the FIFO stuff!
+#warning Fix the FIFO stuff!
+#endif
+
 struct hpfs_inode_info {
+	union {				    /* Linux sometimes destroys this structure */
+		struct pipe_inode_info bla; /* due to a bug. Linus doesn't want to fix */
+		struct socket ble;	    /* it so I had to write this workaround :-) */
+	} dummy;
 	ino_t i_parent_dir;	/* (directories) gives fnode of parent dir */
 	unsigned i_dno;		/* (directories) root dnode */
 	unsigned i_dpos;	/* (directories) temp for readdir */
@@ -9,7 +19,14 @@
 	unsigned i_file_sec;	/* (files) minimalist cache of alloc info */
 	unsigned i_disk_sec;	/* (files) minimalist cache of alloc info */
 	unsigned i_n_secs;	/* (files) minimalist cache of alloc info */
+	unsigned i_ea_size;	/* size of extended attributes */
 	unsigned i_conv : 2;	/* (files) crlf->newline hackery */
+	unsigned i_ea_mode : 1;	/* file's permission is stored in ea */
+	unsigned i_ea_uid : 1;	/* file's uid is stored in ea */
+	unsigned i_ea_gid : 1;	/* file's gid is stored in ea */
+	unsigned i_dirty : 1;
+	struct semaphore i_sem;	/* semaphore */
+	loff_t **i_rddir_off;
 };
 
 #define i_hpfs_dno u.hpfs_i.i_dno
@@ -19,6 +36,15 @@
 #define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
 #define i_hpfs_dpos u.hpfs_i.i_dpos
 #define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
+#define i_hpfs_ea_size u.hpfs_i.i_ea_size
 #define i_hpfs_conv u.hpfs_i.i_conv
+#define i_hpfs_ea_mode u.hpfs_i.i_ea_mode
+#define i_hpfs_ea_uid u.hpfs_i.i_ea_uid
+#define i_hpfs_ea_gid u.hpfs_i.i_ea_gid
+/*#define i_hpfs_lock u.hpfs_i.i_lock*/
+/*#define i_hpfs_queue u.hpfs_i.i_queue*/
+#define i_hpfs_sem u.hpfs_i.i_sem
+#define i_hpfs_rddir_off u.hpfs_i.i_rddir_off
+#define i_hpfs_dirty u.hpfs_i.i_dirty
 
 #endif
diff -u --recursive --new-file v2.3.1/linux/include/linux/hpfs_fs_sb.h linux/include/linux/hpfs_fs_sb.h
--- v2.3.1/linux/include/linux/hpfs_fs_sb.h	Wed Dec  1 04:44:15 1993
+++ linux/include/linux/hpfs_fs_sb.h	Thu May 13 23:48:20 1999
@@ -5,6 +5,7 @@
 	ino_t sb_root;			/* inode number of root dir */
 	unsigned sb_fs_size;		/* file system size, sectors */
 	unsigned sb_bitmaps;		/* sector number of bitmap list */
+	unsigned sb_dirband_start;	/* directory band start sector */
 	unsigned sb_dirband_size;	/* directory band size, dnodes */
 	unsigned sb_dmap;		/* sector number of dnode bit map */
 	unsigned sb_n_free;		/* free blocks for statfs, or -1 */
@@ -12,13 +13,34 @@
 	uid_t sb_uid;			/* uid from mount options */
 	gid_t sb_gid;			/* gid from mount options */
 	umode_t sb_mode;		/* mode from mount options */
-	unsigned sb_lowercase : 1;	/* downcase filenames hackery */
 	unsigned sb_conv : 2;		/* crlf->newline hackery */
+	unsigned sb_eas : 2;		/* eas: 0-ignore, 1-ro, 2-rw */
+	unsigned sb_err : 2;		/* on errs: 0-cont, 1-ro, 2-panic */
+	unsigned sb_chk : 2;		/* checks: 0-no, 1-normal, 2-strict */
+	unsigned sb_lowercase : 1;	/* downcase filenames hackery */
+	unsigned sb_was_error : 1;	/* there was an error, set dirty flag */
+	unsigned sb_chkdsk : 2;		/* chkdsk: 0-no, 1-on errs, 2-allways */
+	unsigned sb_rd_fnode : 2;	/* read fnode 0-no 1-dirs 2-all */
+	unsigned sb_rd_inode : 2;	/* lookup tells read_inode: 1-read fnode
+					   2-don't read fnode, file
+					   3-don't read fnode, direcotry */
+	struct wait_queue *sb_iget_q;
+	unsigned char *sb_cp_table;	/* code page tables: */
+					/* 	128 bytes uppercasing table & */
+					/*	128 bytes lowercasing table */
+	unsigned *sb_bmp_dir;		/* main bitmap directory */
+	unsigned sb_c_bitmap;		/* current bitmap */
+	struct wait_queue *sb_creation_de;/* when creating dirents, nobody else
+					   can alloc blocks */
+	unsigned sb_creation_de_lock : 1;
+	/*unsigned sb_mounting : 1;*/
+	int sb_timeshift;
 };
 
 #define s_hpfs_root u.hpfs_sb.sb_root
 #define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
 #define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
+#define s_hpfs_dirband_start u.hpfs_sb.sb_dirband_start
 #define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
 #define s_hpfs_dmap u.hpfs_sb.sb_dmap
 #define s_hpfs_uid u.hpfs_sb.sb_uid
@@ -28,5 +50,20 @@
 #define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
 #define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
 #define s_hpfs_conv u.hpfs_sb.sb_conv
+#define s_hpfs_eas u.hpfs_sb.sb_eas
+#define s_hpfs_err u.hpfs_sb.sb_err
+#define s_hpfs_chk u.hpfs_sb.sb_chk
+#define s_hpfs_was_error u.hpfs_sb.sb_was_error
+#define s_hpfs_chkdsk u.hpfs_sb.sb_chkdsk
+#define s_hpfs_rd_fnode u.hpfs_sb.sb_rd_fnode
+#define s_hpfs_rd_inode u.hpfs_sb.sb_rd_inode
+#define s_hpfs_cp_table u.hpfs_sb.sb_cp_table
+#define s_hpfs_bmp_dir u.hpfs_sb.sb_bmp_dir
+#define s_hpfs_c_bitmap u.hpfs_sb.sb_c_bitmap
+#define s_hpfs_creation_de u.hpfs_sb.sb_creation_de
+#define s_hpfs_creation_de_lock u.hpfs_sb.sb_creation_de_lock
+#define s_hpfs_iget_q u.hpfs_sb.sb_iget_q
+/*#define s_hpfs_mounting u.hpfs_sb.sb_mounting*/
+#define s_hpfs_timeshift u.hpfs_sb.sb_timeshift
 
 #endif
diff -u --recursive --new-file v2.3.1/linux/include/linux/major.h linux/include/linux/major.h
--- v2.3.1/linux/include/linux/major.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/major.h	Thu May 13 23:50:15 1999
@@ -89,8 +89,11 @@
 #define SCSI_DISK6_MAJOR	70
 #define SCSI_DISK7_MAJOR	71
 
+
 #define SPECIALIX_NORMAL_MAJOR 75
 #define SPECIALIX_CALLOUT_MAJOR 76
+
+#define I2O_MAJOR		80	/* 80->87 */
 
 #define IDE6_MAJOR	88
 #define IDE7_MAJOR	89
diff -u --recursive --new-file v2.3.1/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.3.1/linux/include/linux/msdos_fs.h	Tue May 11 13:05:30 1999
+++ linux/include/linux/msdos_fs.h	Fri May 14 18:46:35 1999
@@ -183,12 +183,9 @@
 
 #ifdef __KERNEL__
 
-typedef int (*fat_filldir_t)(filldir_t filldir, void *, const char *,
-			     int, int, off_t, off_t, int, ino_t);
-
 struct fat_cache {
 	kdev_t device; /* device number. 0 means unused. */
-	int ino; /* inode number. */
+	int start_cluster; /* first cluster of the chain. */
 	int file_cluster; /* cluster number in the file. */
 	int disk_cluster; /* cluster number on disk. */
 	struct fat_cache *next; /* next cache entry */
@@ -199,15 +196,29 @@
 extern void lock_fat(struct super_block *sb);
 extern void unlock_fat(struct super_block *sb);
 extern int fat_add_cluster(struct inode *inode);
+extern struct buffer_head *fat_add_cluster1(struct inode *inode);
 extern int date_dos2unix(__u16 time, __u16 date);
 extern void fat_fs_panic(struct super_block *s,const char *msg);
 extern void fat_lock_creation(void);
 extern void fat_unlock_creation(void);
 extern void fat_date_unix2dos(int unix_date,__u16 *time, __u16 *date);
-extern int fat_get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
-			 struct msdos_dir_entry **de);
+extern int fat__get_entry(struct inode *dir,loff_t *pos,struct buffer_head **bh,
+			 struct msdos_dir_entry **de,int *ino);
+static __inline__ int fat_get_entry(struct inode *dir,loff_t *pos,
+		struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
+{
+	/* Fast stuff first */
+	if (*bh && *de &&
+	    	(*de - (struct msdos_dir_entry *)(*bh)->b_data) < MSDOS_DPB-1) {
+		*pos += sizeof(struct msdos_dir_entry);
+		(*de)++;
+		(*ino)++;
+		return 0;
+	}
+	return fat__get_entry(dir,pos,bh,de,ino);
+}
 extern int fat_scan(struct inode *dir,const char *name,struct buffer_head **res_bh,
-		    struct msdos_dir_entry **res_de,int *ino,char scantype);
+		    struct msdos_dir_entry **res_de,int *ino);
 extern int fat_parent_ino(struct inode *dir,int locked);
 extern int fat_subdirs(struct inode *dir);
 void fat_clusters_flush(struct super_block *sb);
@@ -224,26 +235,32 @@
 int fat_get_cluster(struct inode *inode,int cluster);
 
 /* inode.c */
+extern void fat_hash_init(void);
 extern int fat_bmap(struct inode *inode,int block);
 extern int fat_notify_change(struct dentry *, struct iattr *);
-extern void fat_put_inode(struct inode *inode);
+extern void fat_clear_inode(struct inode *inode);
 extern void fat_delete_inode(struct inode *inode);
 extern void fat_put_super(struct super_block *sb);
-extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
-extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
+extern void fat_attach(struct inode *inode, int ino);
+extern void fat_detach(struct inode *inode);
+extern struct inode *fat_iget(struct super_block*,int);
+extern struct inode *fat_build_inode(struct super_block*,struct msdos_dir_entry*,int,int*);
+extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent, struct inode_operations *dir_ops);
 extern void msdos_put_super(struct super_block *sb);
 extern int fat_statfs(struct super_block *sb,struct statfs *buf, int);
 extern void fat_write_inode(struct inode *inode);
 
 /* dir.c */
 extern struct file_operations fat_dir_operations;
-extern int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
-			fat_filldir_t fat_filldir, filldir_t filldir,
-			int shortnames, int longnames, int both);
+extern int fat_search_long(struct inode *dir, const char *name, int len,
+			   int anycase, loff_t *spos, loff_t *lpos);
 extern int fat_readdir(struct file *filp,
 		       void *dirent, filldir_t);
 extern int fat_dir_ioctl(struct inode * inode, struct file * filp,
 			 unsigned int cmd, unsigned long arg);
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+		  struct msdos_dir_entry **de, int *ino);
+int fat_dir_empty(struct inode *dir);
 
 /* file.c */
 extern struct inode_operations fat_file_inode_operations;
@@ -276,7 +293,6 @@
 extern int msdos_rmdir(struct inode *dir,struct dentry *dentry);
 extern int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode);
 extern int msdos_unlink(struct inode *dir,struct dentry *dentry);
-extern int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry);
 extern int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
 			struct inode *new_dir,struct dentry *new_dentry);
 
@@ -303,12 +319,10 @@
 /* vfat/namei.c - these are for dmsdos */
 extern int vfat_create(struct inode *dir,struct dentry *dentry,int mode);
 extern int vfat_unlink(struct inode *dir,struct dentry *dentry);
-extern int vfat_unlink_uvfat(struct inode *dir,struct dentry *dentry);
 extern int vfat_mkdir(struct inode *dir,struct dentry *dentry,int mode);
 extern int vfat_rmdir(struct inode *dir,struct dentry *dentry);
 extern int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
 		       struct inode *new_dir,struct dentry *new_dentry);
-extern void vfat_put_super(struct super_block *sb);
 extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
 					   int silent);
 extern void vfat_read_inode(struct inode *inode);
diff -u --recursive --new-file v2.3.1/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h
--- v2.3.1/linux/include/linux/msdos_fs_i.h	Wed Jun 24 14:30:11 1998
+++ linux/include/linux/msdos_fs_i.h	Thu May 13 23:18:21 1999
@@ -32,6 +32,9 @@
 	int i_busy;	/* file is either deleted but still open, or
 			   inconsistent (mkdir) */
 	int i_binary;	/* file contains non-text data */
+	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 -u --recursive --new-file v2.3.1/linux/include/linux/msdos_fs_sb.h linux/include/linux/msdos_fs_sb.h
--- v2.3.1/linux/include/linux/msdos_fs_sb.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/msdos_fs_sb.h	Thu May 13 23:18:21 1999
@@ -51,7 +51,9 @@
 	struct nls_table *nls_disk;  /* Codepage used on disk */
 	struct nls_table *nls_io;    /* Charset used for input and display */
 	struct cvf_format* cvf_format;
-	void* private_data;	
+	void *dir_ops;		     /* Opaque; default directory operations */
+	void (*put_super_callback)(struct super_block *);
+	void *private_data;	
 };
 
 #endif
diff -u --recursive --new-file v2.3.1/linux/include/linux/ncp.h linux/include/linux/ncp.h
--- v2.3.1/linux/include/linux/ncp.h	Tue May 11 13:05:32 1999
+++ linux/include/linux/ncp.h	Fri May 14 18:46:37 1999
@@ -72,9 +72,6 @@
 #define aDELETEINHIBIT	 (ntohl(1L<<(18-8)))
 #define aDONTCOMPRESS	 (nothl(1L<<(27-24)))
 
-#define NCP_MIN_SYMLINK_SIZE	8
-#define NCP_MAX_SYMLINK_SIZE	512
-
 #define AR_READ      (ntohs(0x0100))
 #define AR_WRITE     (ntohs(0x0200))
 #define AR_EXCLUSIVE (ntohs(0x2000))
diff -u --recursive --new-file v2.3.1/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v2.3.1/linux/include/linux/ncp_fs.h	Tue May 11 13:05:50 1999
+++ linux/include/linux/ncp_fs.h	Fri May 14 18:46:52 1999
@@ -248,7 +248,11 @@
 int ncp_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
 /* linux/fs/ncpfs/sock.c */
-int ncp_request(struct ncp_server *server, int function);
+int ncp_request2(struct ncp_server *server, int function,
+	void* reply, int max_reply_size);
+static int inline ncp_request(struct ncp_server *server, int function) {
+	return ncp_request2(server, function, server->packet, server->packet_size);
+}
 int ncp_connect(struct ncp_server *server);
 int ncp_disconnect(struct ncp_server *server);
 void ncp_lock_server(struct ncp_server *server);
diff -u --recursive --new-file v2.3.1/linux/include/linux/ncp_fs_sb.h linux/include/linux/ncp_fs_sb.h
--- v2.3.1/linux/include/linux/ncp_fs_sb.h	Fri May 14 18:55:29 1999
+++ linux/include/linux/ncp_fs_sb.h	Fri May 14 18:46:52 1999
@@ -8,6 +8,7 @@
 #ifndef _NCP_FS_SB
 #define _NCP_FS_SB
 
+#include <asm/semaphore.h>
 #include <linux/ncp_mount.h>
 #include <linux/types.h>
 
@@ -44,7 +45,7 @@
 				   receive replies */
 
 	int lock;		/* To prevent mismatch in protocols. */
-	wait_queue_head_t wait;
+	struct semaphore sem;
 
 	int current_size;	/* for packet preparation */
 	int has_subfunction;
diff -u --recursive --new-file v2.3.1/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.3.1/linux/include/linux/proc_fs.h	Tue May 11 13:04:26 1999
+++ linux/include/linux/proc_fs.h	Fri May 14 18:45:33 1999
@@ -204,8 +204,9 @@
 	PROC_SCSI_53C94,
 	PROC_SCSI_PLUTO,
 	PROC_SCSI_INI9100U,
- 	PROC_SCSI_INIA100,
+	PROC_SCSI_INIA100,
 	PROC_SCSI_FCAL,
+	PROC_SCSI_I2O,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
diff -u --recursive --new-file v2.3.1/linux/include/video/fbcon-vga-planes.h linux/include/video/fbcon-vga-planes.h
--- v2.3.1/linux/include/video/fbcon-vga-planes.h	Wed Dec 31 16:00:00 1969
+++ linux/include/video/fbcon-vga-planes.h	Thu May 13 23:48:20 1999
@@ -0,0 +1,37 @@
+/*
+ *  FBcon low-level driver for VGA 4-plane modes
+ */
+
+#ifndef _VIDEO_FBCON_VGA_PLANES_H
+#define _VIDEO_FBCON_VGA_PLANES_H
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#if defined(CONFIG_FBCON_VGA_PLANES) || defined(CONFIG_FBCON_VGA_PLANES_MODULE)
+#define FBCON_HAS_VGA_PLANES
+#endif
+#else
+#if defined(CONFIG_FBCON_VGA_PLANES)
+#define FBCON_HAS_VGA_PLANES
+#endif
+#endif
+
+extern struct display_switch fbcon_vga_planes;
+extern struct display_switch fbcon_ega_planes;
+extern void fbcon_vga_planes_setup(struct display *p);
+extern void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx,
+				   int height, int width);
+extern void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy,
+				   int sx, int height, int width);
+extern void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c,
+				  int yy, int xx);
+extern void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c,
+				  int yy, int xx);
+extern void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p,
+				   const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p,
+				   const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_vga_planes_revc(struct display *p, int xx, int yy);
+
+#endif /* _VIDEO_FBCON_VGA_PLANES_H */
diff -u --recursive --new-file v2.3.1/linux/init/main.c linux/init/main.c
--- v2.3.1/linux/init/main.c	Fri May 14 18:55:30 1999
+++ linux/init/main.c	Thu May 13 23:18:21 1999
@@ -1332,21 +1332,6 @@
 	/* Mount the root filesystem.. */
 	mount_root();
 
-#ifdef CONFIG_UMSDOS_FS
-	{
-		/*
-			When mounting a umsdos fs as root, we detect
-			the pseudo_root (/linux) and initialise it here.
-			pseudo_root is defined in fs/umsdos/inode.c
-		*/
-		extern struct inode *pseudo_root;
-		if (pseudo_root != NULL){
-			current->fs->root = pseudo_root->i_sb->s_root;
-			current->fs->pwd  = pseudo_root->i_sb->s_root;
-		}
-	}
-#endif
-
 #ifdef CONFIG_BLK_DEV_INITRD
 	root_mountflags = real_root_mountflags;
 	if (mount_initrd && ROOT_DEV != real_root_dev
diff -u --recursive --new-file v2.3.1/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.3.1/linux/kernel/ksyms.c	Fri May 14 18:55:30 1999
+++ linux/kernel/ksyms.c	Thu May 13 23:18:21 1999
@@ -116,6 +116,8 @@
 EXPORT_SYMBOL(get_fs_type);
 EXPORT_SYMBOL(getname);
 EXPORT_SYMBOL(__fput);
+EXPORT_SYMBOL(igrab);
+EXPORT_SYMBOL(iunique);
 EXPORT_SYMBOL(iget);
 EXPORT_SYMBOL(iput);
 EXPORT_SYMBOL(__namei);
@@ -184,6 +186,7 @@
 EXPORT_SYMBOL(vfs_unlink);
 EXPORT_SYMBOL(vfs_rename);
 EXPORT_SYMBOL(__pollwait);
+EXPORT_SYMBOL(ROOT_DEV);
 
 #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
 EXPORT_SYMBOL(do_nfsservctl);
diff -u --recursive --new-file v2.3.1/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.3.1/linux/net/ipv4/tcp_input.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv4/tcp_input.c	Fri May 14 17:48:04 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.164 1999/05/08 21:09:52 davem Exp $
+ * Version:	$Id: tcp_input.c,v 1.165 1999/05/14 23:10:08 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -748,7 +748,6 @@
 static __inline__ void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
 {
 	struct sk_buff *skb = skb_peek(&sk->write_queue);
-	__u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when);
 
 	/* Some data was ACK'd, if still retransmitting (due to a
 	 * timeout), resend more of the retransmit queue.  The
@@ -758,6 +757,9 @@
 		tcp_xmit_retransmit_queue(sk);
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	} else {
+		__u32 when = tp->rto - (tcp_time_stamp - TCP_SKB_CB(skb)->when);
+		if ((__s32)when < 0)
+			when = 1;
 		tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
 	}
 }
@@ -785,8 +787,6 @@
 	if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una))
 		goto uninteresting_ack;
 
-	dst_confirm(sk->dst_cache);
-
 	/* If there is data set flag 1 */
 	if (len != th->doff*4) {
 		flag |= FLAG_DATA;
@@ -882,6 +882,24 @@
 		/* Clear any aborted fast retransmit starts. */
 		tp->dup_acks = 0;
 	}
+	/* It is not a brain fart, I thought a bit now. 8)
+	 *
+	 * Forward progress is indicated, if:
+	 *   1. the ack acknowledges new data.
+	 *   2. or the ack is duplicate, but it is caused by new segment
+	 *      arrival. This case is filtered by:
+	 *      - it contains no data, syn or fin.
+	 *      - it does not update window.
+	 *   3. or new SACK. It is difficult to check, so that we ignore it.
+	 *
+	 * Forward progress is also indicated by arrival new data,
+	 * which was caused by window open from our side. This case is more
+	 * difficult and it is made (alas, incorrectly) in tcp_data_queue().
+	 *                                              --ANK (990513)
+	 */
+	if (ack != tp->snd_una || (flag == 0 && !th->fin))
+		dst_confirm(sk->dst_cache);
+
 	/* Remember the highest ack received. */
 	tp->snd_una = ack;
 	return 1;
@@ -2067,20 +2085,80 @@
 		 * not be in line code. [AC]
 		 */
 		if(th->ack) {
-			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
-
-			/* We got an ack, but it's not a good ack. */
-			if(!tcp_ack(sk,th, TCP_SKB_CB(skb)->seq,
-				    TCP_SKB_CB(skb)->ack_seq, len)) 
+			/* rfc793:
+			 * "If the state is SYN-SENT then
+			 *    first check the ACK bit
+			 *      If the ACK bit is set
+			 *	  If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
+			 *        a reset (unless the RST bit is set, if so drop
+			 *        the segment and return)"
+			 *
+			 *  I cite this place to emphasize one essential
+			 *  detail, this check is different of one
+			 *  in established state: SND.UNA <= SEG.ACK <= SND.NXT.
+			 *  SEG_ACK == SND.UNA == ISS is invalid in SYN-SENT,
+			 *  because we have no previous data sent before SYN.
+			 *                                        --ANK(990513)
+			 *
+			 *  We do not send data with SYN, so that RFC-correct
+			 *  test reduces to:
+			 */
+			if (sk->zapped ||
+			    TCP_SKB_CB(skb)->ack_seq != tp->snd_nxt)
 				return 1;
 
-			if(th->rst) {
+			/* Now ACK is acceptable.
+			 *
+			 * "If the RST bit is set
+			 *    If the ACK was acceptable then signal the user "error:
+			 *    connection reset", drop the segment, enter CLOSED state,
+			 *    delete TCB, and return."
+			 */
+
+			if (th->rst) {
 				tcp_reset(sk);
 				goto discard;
 			}
 
-			if(!th->syn) 
+			/* rfc793:
+			 *   "fifth, if neither of the SYN or RST bits is set then
+			 *    drop the segment and return."
+			 *
+			 *    See note below!
+			 *                                        --ANK(990513)
+		         */
+			
+			if (!th->syn)
 				goto discard;
+
+			/* rfc793:
+			 *   "If the SYN bit is on ...
+			 *    are acceptable then ...
+			 *    (our SYN has been ACKed), change the connection
+			 *    state to ESTABLISHED..."
+			 *
+			 * Do you see? SYN-less ACKs in SYN-SENT state are
+			 * completely ignored.
+			 *
+			 * The bug causing stalled SYN-SENT sockets
+			 * was here: tcp_ack advanced snd_una and canceled
+			 * retransmit timer, so that bare ACK received
+			 * in SYN-SENT state (even with invalid ack==ISS,
+			 * because tcp_ack check is too weak for SYN-SENT)
+			 * causes moving socket to invalid semi-SYN-SENT,
+			 * semi-ESTABLISHED state and connection hangs.
+			 *
+			 * There exist buggy stacks, which really send
+			 * such ACKs: f.e. 202.226.91.94 (okigate.oki.co.jp)
+			 * Actually, if this host did not try to get something
+			 * from ftp.inr.ac.ru I'd never find this bug 8)
+			 *
+			 *                                     --ANK (990514)
+			 */
+
+			tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
+			tcp_ack(sk,th, TCP_SKB_CB(skb)->seq,
+				TCP_SKB_CB(skb)->ack_seq, len);
 
 			/* Ok.. it's good. Set up sequence numbers and
 			 * move to established.
diff -u --recursive --new-file v2.3.1/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.3.1/linux/net/ipv4/tcp_output.c	Mon May 10 09:55:25 1999
+++ linux/net/ipv4/tcp_output.c	Fri May 14 17:48:04 1999
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.108 1999/05/08 21:48:59 davem Exp $
+ * Version:	$Id: tcp_output.c,v 1.109 1999/05/14 23:10:13 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -239,6 +239,11 @@
 
 	/* Rechecksum original buffer. */
 	skb->csum = csum_partial(skb->data, skb->len, 0);
+
+	/* Looks stupid, but our code really uses when of
+	 * skbs, which it never sent before. --ANK
+	 */
+	TCP_SKB_CB(buff)->when = TCP_SKB_CB(skb)->when;
 
 	/* Link BUFF into the send queue. */
 	__skb_append(skb, buff);
