diff -u --recursive --new-file v2.1.77/linux/CREDITS linux/CREDITS
--- v2.1.77/linux/CREDITS	Fri Jan  2 14:37:00 1998
+++ linux/CREDITS	Sun Jan  4 10:40:15 1998
@@ -430,6 +430,18 @@
 E: dje@cygnus.com
 D: Wrote Xenix FS (part of standard kernel since 0.99.15)
 
+N: Riccardo Facchetti
+E: fizban@tin.it
+P: 1024/6E657BB5 AF 22 90 33 78 76 04 8B  AF F9 97 1E B5 E2 65 30
+D: Audio Excel DSP 16 init driver author
+D: libmodem author
+D: Yet Another Micro Monitor port and current mantainer
+D: First ELF-HOWTO author
+D: random kernel hacker
+S: Via Paolo VI n.29
+S: 23900 - LECCO (Lc)
+S: Italy
+
 N: Rik Faith
 E: faith@cs.unc.edu
 E: r.faith@ieee.org
diff -u --recursive --new-file v2.1.77/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.77/linux/Documentation/devices.txt	Wed Dec 10 11:12:42 1997
+++ linux/Documentation/devices.txt	Sun Jan  4 10:40:15 1998
@@ -93,10 +93,14 @@
 		  the position within the series.
   
     block	Floppy disks
-		  0 = /dev/fd0		First floppy disk autodetect
-		  1 = /dev/fd1		Second floppy disk autodetect
-		  2 = /dev/fd2		Third floppy disk autodetect
-		  3 = /dev/fd3		Fourth floppy disk autodetect
+		  0 = /dev/fd0		Controller 1, drive 1 autodetect
+		  1 = /dev/fd1		Controller 1, drive 2 autodetect
+		  2 = /dev/fd2		Controller 1, drive 3 autodetect
+		  3 = /dev/fd3		Controller 1, drive 4 autodetect
+		128 = /dev/fd4		Controller 2, drive 1 autodetect
+		129 = /dev/fd5		Controller 2, drive 2 autodetect
+		130 = /dev/fd6		Controller 2, drive 3 autodetect
+		131 = /dev/fd7		Controller 2, drive 4 autodetect
 
 		To specify format, add to the autodetect device number:
 		  0 = /dev/fd?		Autodetect format
diff -u --recursive --new-file v2.1.77/linux/Documentation/networking/depca.txt linux/Documentation/networking/depca.txt
--- v2.1.77/linux/Documentation/networking/depca.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/networking/depca.txt	Sat Jan  3 20:49:41 1998
@@ -0,0 +1,92 @@
+
+DE10x
+=====
+
+Memory Addresses:
+
+	SW1	SW2	SW3	SW4
+64K	on	on	on	on	d0000	dbfff
+	off	on	on	on	c0000	cbfff
+	off	off	on	on	e0000	ebfff
+
+32K	on	on	off	on	d8000	dbfff
+	off	on	off	on	c8000	cbfff
+	off	off	off	on	e8000	ebfff
+
+DBR ROM	on	on			dc000	dffff
+	off	on			cc000	cffff
+	off	off			ec000	effff
+
+Note  that the 2K  mode   is set  by   SW3/SW4  on/off or  off/off.  Address
+assignment is through the RBSA register.
+
+I/O Address:
+	SW5
+0x300	on
+0x200	off
+
+Remote Boot:
+	SW6
+Disable	on
+Enable	off
+
+Remote Boot Timeout:
+	SW7
+2.5min	on
+30s	off
+
+IRQ:
+	SW8	SW9	SW10	SW11	SW12
+2	on	off	off	off	off
+3	off	on	off	off	off
+4	off	off	on	off	off
+5	off	off	off	on	off
+7	off	off	off	off	on
+
+DE20x
+=====
+
+Memory Size:
+
+	SW3	SW4
+64K	on	on
+32K	off	on
+2K	on 	off
+2K	off	off
+
+Start Addresses:
+
+	SW1	SW2	SW3	SW4
+64K	on	on	on	on	c0000	cffff
+	on	off	on	on	d0000	dffff
+	off	on	on	on	e0000	effff
+
+32K	on	on	off	off	c8000	cffff
+	on	off	off	off	d8000	dffff
+	off	on	off	off	e8000	effff
+
+Illegal	off	off	 -	 -	  -	  -
+
+I/O Address:
+	SW5
+0x300	on
+0x200	off
+
+Remote Boot:
+	SW6
+Disable	on
+Enable	off
+
+Remote Boot Timeout:
+	SW7
+2.5min	on
+30s	off
+
+IRQ:
+	SW8	SW9	SW10	SW11	SW12
+5	on	off	off	off	off
+9	off	on	off	off	off
+10	off	off	on	off	off
+11	off	off	off	on	off
+15	off	off	off	off	on
+
diff -u --recursive --new-file v2.1.77/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.77/linux/MAINTAINERS	Fri Jan  2 14:37:01 1998
+++ linux/MAINTAINERS	Sun Jan  4 11:56:05 1998
@@ -64,71 +64,40 @@
 			it has been replaced by a better system and you
 			should be using that.
 
-EXT2 FILE SYSTEM
-P:	Remy Card
-M:	Remy.Card@linux.org
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
-DISKQUOTA:
-P:	Marco van Wieringen
-M:	mvw@planets.elm.net
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
 3C501 NETWORK DRIVER
 P:	Alan Cox
 M:	net-patches@lxorguk.ukuu.org.uk
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
-8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
-P:	Paul Gortmaker
-M	gpg109@rsphy1.anu.edu.au
-L:	linux-net@vger.rutgers.edu
-S:	Maintained
-W:	http://rsphy1.anu.edu.au/~gpg109/ne2000.html
-
-ETHEREXPRESS-16 NETWORK DRIVER
-P:	Philip Blundell
-M:	Philip.Blundell@pobox.com
-L:	linux-net@vger.rutgers.edu
-S:	Maintained
-
 3C505 NETWORK DRIVER
 P:	Philip Blundell
 M:	Philip.Blundell@pobox.com
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
-NI5010 NETWORK DRIVER
-P:     Jan-Pascal van Best and Andreas Mohr
-M:     jvbest@qv3pluto.leidenuniv.nl (Best)
-M:     100.30936@germany.net (Mohr)
-L:     linux-net@vger.rutgers.edu
-S:     Maintained
-
-TLAN NETWORK DRIVER
-P:	James Banks
-M:	james.banks@caldera.com
+8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
+P:	Paul Gortmaker
+M	gpg109@rsphy1.anu.edu.au
 L:	linux-net@vger.rutgers.edu
-S:	Supported
+S:	Maintained
+W:	http://rsphy1.anu.edu.au/~gpg109/ne2000.html
 
-DIGI RIGHTSWITCH NETWORK DRIVER
-P:	Rick Richardson
-M:	rick@dgii.com
-L:	linux-net@vger.rutgers.edu
-W:	http://www.dgii.com/linux/
+AEDSP16 DRIVER
+P:	Riccardo Facchetti
+M:	fizban@tin.it
 S:	Maintained
 
-WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
-P:	Jean Tourrilhes
-M:	jt@hplb.hpl.hp.com
+ADVANSYS SCSI DRIVER
+P:	Bob Frey
+M:	Bob Frey <bobf@advansys.com>
+W:	http://www.advansys.com/linux
 S:	Maintained
 
-HP100: Driver for HP 10/100 Mbit/s Network Adapter Series
-P:	Jarsolav Kysela
-M:	perex@jcu.cz
+AHA152X SCSI DRIVER
+P:	Juergen E. Fischer
+M:	Juergen Fischer <fischer@et-inf.fho-emden.de>
+L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
 APM DRIVER
@@ -137,22 +106,16 @@
 L:	linux-laptop@vger.rutgers.edu
 S:	Maintained
 
-TOKEN-RING NETWORK DRIVER
-P:	Paul Norton
-M:	pnorton@cts.com
-L:	linux-net@vger.rutgers.edu
-S:	Maintained
-
 APPLETALK NETWORK LAYER
 P:	Jay Schulist
 M:	Jay.Schulist@spacs.k12.wi.us
 L:	linux-atalk@netspace.org
 S:	Maintained
 
-DECnet NETWORK LAYER
-P:	Steven Whitehouse
-M:	SteveW@ACM.org
-L:	netdev@roxanne.nuclecu.unam.mx
+ARPD SUPPORT
+P:	Jonathan Layes
+M:	layes@loran.com
+L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
 AX.25 NETWORK LAYER
@@ -161,24 +124,6 @@
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
-DAMA SLAVE for AX.25
-P:	Joerg Reuter
-M:	jreuter@lykos.oche.de
-L:	linux-hams@vger.rutgers.edu
-S:	Maintained
-
-Z8530 DRIVER FOR AX.25
-P:	Joerg Reuter
-M:	jreuter@lykos.oche.de
-L:	linux-hams@vger.rutgers.edu
-S:	Maintained
-
-HIGH-SPEED SCC DRIVER FOR AX.25
-P:	Klaus Kudielka
-M:	oe1kib@oe1xtu.ampr.org
-L:	linux-hams@vger.rutgers.edu
-S:	Maintained
-
 BAYCOM/HDLCDRV/SOUNDMODEM DRIVERS FOR AX.25
 P:	Thomas Sailer
 M:	sailer@ife.ee.ethz.ch
@@ -193,22 +138,58 @@
 W:	http://www.dandelion.com/Linux/
 S:	Maintained
 
+CONFIGURE.HELP
+P:	Axel Boldt
+M:	boldt@math.ucsb.edu
+S:	Maintained
+
+CREDITS FILE
+P:	John A. Martin
+M:	jam@acm.org
+S:	Maintained
+
 CYCLADES ASYNC MUX DRIVER
 P:	Marcio Saito
 M:	Marcio Saito <marcio@cyclades.com>
 W:	http://www.cyclades.com/
 S:	Supported
 
-EATA ISA/EISA/PCI SCSI DRIVER
-P:	Dario Ballabio
-M:	dario@milano.europe.dg.com
-L:	linux-scsi@vger.rutgers.edu
+DAMA SLAVE for AX.25
+P:	Joerg Reuter
+M:	jreuter@lykos.oche.de
+L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
-U14-34F SCSI DRIVER
-P:	Dario Ballabio
-M:	dario@milano.europe.dg.com
-L:	linux-scsi@vger.rutgers.edu
+DECnet NETWORK LAYER
+P:	Steven Whitehouse
+M:	SteveW@ACM.org
+L:	netdev@roxanne.nuclecu.unam.mx
+S:	Maintained
+
+DEVICE NUMBER REGISTRY
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+DIGI INTL. EPCA DRIVER:
+P:      Daniel Taylor
+M:      support@dgii.com
+M:      digilnux@dgii.com
+L:      digiboard@list.fuller.edu
+S:      Maintained
+
+DIGI RIGHTSWITCH NETWORK DRIVER
+P:	Rick Richardson
+M:	rick@dgii.com
+L:	linux-net@vger.rutgers.edu
+W:	http://www.dgii.com/linux/
+S:	Maintained
+
+DISKQUOTA:
+P:	Marco van Wieringen
+M:	mvw@planets.elm.net
+L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
 EATA-DMA SCSI DRIVER
@@ -217,18 +198,29 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+EATA ISA/EISA/PCI SCSI DRIVER
+P:	Dario Ballabio
+M:	dario@milano.europe.dg.com
+L:	linux-scsi@vger.rutgers.edu
+S:	Maintained
+
 EATA-PIO SCSI DRIVER
 P:	Michael Neuffer
 M:	mike@i-Connect.Net
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
-GDT SCSI DISK ARRAY CONTROLLER DRIVER
-P:	Achim Leubner
-M:	achim@vortex.de
-L:	linux-scsi@vger.rutgers.edu
-W:	http://www.icp-vortex.com/
-S:	Supported
+ETHEREXPRESS-16 NETWORK DRIVER
+P:	Philip Blundell
+M:	Philip.Blundell@pobox.com
+L:	linux-net@vger.rutgers.edu
+S:	Maintained
+
+EXT2 FILE SYSTEM
+P:	Remy Card
+M:	Remy.Card@linux.org
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
 
 FILE LOCKING (flock() and fcntl()/lockf())
 P:	Andy Walker
@@ -236,36 +228,55 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+FPU EMULATOR
+P:	Bill Metzenthen
+M:	billm@suburbia.net
+W:	http://suburbia.net/~billm/floating-point/emulator/
+S:	Maintained
+
 FRAME RELAY DLCI/FRAD (Sangoma drivers too)
 P:	Mike McLagan
 M:	mike.mclagan@linux.org
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
+FTAPE/QIC-117:
+P:	Claus-Justus Heine
+M:	claus@momo.math.rwth-aachen.de
+L:	linux-tape@vger.rutgers.edu
+W:	http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/
+S:	Maintained
+
 FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit)
 P:	Rik Faith
 M:	faith@cs.unc.edu
 L:	linux-scsi@vger.rutgers.edu
 S:	Odd fixes (e.g., new signatures)
 
-SCSI TAPE DRIVER
-P:	Kai Mdkisara
-M:	Kai.Makisara@metla.fi
+GDT SCSI DISK ARRAY CONTROLLER DRIVER
+P:	Achim Leubner
+M:	achim@vortex.de
 L:	linux-scsi@vger.rutgers.edu
-S:	Maintained
+W:	http://www.icp-vortex.com/
+S:	Supported
 
-FTAPE/QIC-117:
-P:	Claus-Justus Heine
-M:	claus@momo.math.rwth-aachen.de
-L:	linux-tape@vger.rutgers.edu
-W:	http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/
+HAYES ESP SERIAL DRIVER:
+P:	Andrew J. Robinson
+M:	arobinso@nyx.net
+L:	linux-kernel@vger.rutgers.edu
+W:	http://www.nyx.net/~arobinso
+S:	Maintainted
+
+HIGH-SPEED SCC DRIVER FOR AX.25
+P:	Klaus Kudielka
+M:	oe1kib@oe1xtu.ampr.org
+L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
-IPX NETWORK LAYER
-P:	
-M:
-L:	
-S:	Orphan
+HP100: Driver for HP 10/100 Mbit/s Network Adapter Series
+P:	Jarsolav Kysela
+M:	perex@jcu.cz
+S:	Maintained
 
 IDE DRIVER [GENERAL]
 P:	Mark Lord
@@ -285,12 +296,41 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+IPX NETWORK LAYER
+P:	
+M:
+L:	
+S:	Orphan
+
 ISDN SUBSYSTEM
 P:	Fritz Elfert
 M:	fritz@wuemaus.franken.de
 L:	isdn4linux@hub-wue.franken.de
 S:	Maintained
 
+JOYSTICK DRIVER
+P:	Vojtech Pavlik
+M:	vojtech@atrey.karlin.mff.cuni.cz
+S:	Maintained
+
+KERNEL AUTOMOUNTER (AUTOFS)
+P:	H. Peter Anvin
+M:	hpa@zytor.com
+L:	autofs@linux.kernel.org
+S:	Maintained
+
+LINUX FOR POWERPC (PREP)
+P:	Cort Dougan
+M:	cort@cs.nmt.edu
+W:	http://www.cs.nmt.edu/~linuxppc/
+S:	Maintained
+
+LINUX FOR POWER MACINTOSH
+P:	Paul Mackerras
+M:	paulus@cs.anu.edu.au
+L:	linux-pmac@samba.anu.edu.au
+S:	Maintained
+
 M68K:
 P:	Jes Sorensen
 M:	Jes.Sorensen@cern.ch
@@ -298,16 +338,17 @@
 L:	linux-m68k@lists.linux-m68k.org
 S:	Maintained
 
-MODULE SUPPORT [GENERAL], KERNELD
-P:	Richard Henderson
-M:	richard@gnu.ai.mit.edu
-L:	linux-kernel@vger.rutgers.edu
+M68K ON APPLE MACINTOSH:
+P:	Alan Cox
+M:	Alan.Cox@linux.org
+W:	http://www.mac.linux-m68k.org/home.html
+L:	linux-mac68k@wave.lm.com
 S:	Maintained
 
-ARPD SUPPORT
-P:	Jonathan Layes
-M:	layes@loran.com
-L:	linux-net@vger.rutgers.edu
+MENUCONFIG:
+P:	William Roadcap
+M:	roadcapw@cfw.com
+L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
 MIPS:
@@ -317,6 +358,18 @@
 L:	linux-mips@fnet.fr
 S:	Maintained
 
+MODULE SUPPORT [GENERAL], KERNELD
+P:	Richard Henderson
+M:	richard@gnu.ai.mit.edu
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+MOUSE AND MISC DEVICES [GENERAL]
+P:	Alessandro Rubini
+M:	rubini@ipvvis.unipv.it
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 NCP FILESYSTEM:
 P:	Volker Lendecke
 M:	lendecke@Math.Uni-Goettingen.de
@@ -329,6 +382,11 @@
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
+NETWORK BLOCK DEVICE
+P:	Pavel Machek
+M:	pavel@atrey.karlin.mff.cuni.cz
+S:	Maintained
+
 NETWORKING [GENERAL]:
 P:	Networking Teak
 M:	netdev@nuclecu.unam.mx
@@ -341,9 +399,63 @@
 M:	davem@caip.rutgers.edu
 P:	Eric Schenk
 M:	Eric.Schenk@dna.lth.se
+P:	Alexey Kuznetsov
+M:	kuznet@ms2.inr.ac.ru
 L:	netdev@roxanne.nuclecu.unam.mx
 S:	Maintained
 
+NI5010 NETWORK DRIVER
+P:     Jan-Pascal van Best and Andreas Mohr
+M:     jvbest@qv3pluto.leidenuniv.nl (Best)
+M:     100.30936@germany.net (Mohr)
+L:     linux-net@vger.rutgers.edu
+S:     Maintained
+
+NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
+P:	Eberhard Moenkeberg
+M:	emoenke@gwdg.de
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+PARALLEL PORT SHARING SUPPORT
+P:	Phil Blundell
+M:	Philip.Blundell@pobox.com
+P:	Tim Waugh
+M:	tim@cyberelk.demon.co.uk
+P:	David Campbell
+M:	campbell@tirian.che.curtin.edu.au
+L:	linux-parport@torque.net
+L:	pnp-list@redhat.com
+W:	http://www.cyberelk.demon.co.uk/parport.html
+W:	http://www.cage.curtin.edu.au/~campbell/parbus/
+S:	Maintained
+
+PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
+P:	Grant Guenther
+M:	grant@torque.net
+L:	linux-parport@torque.net
+W:	http://www.torque.net/linux-pp.html
+S:	Maintained
+
+PCI ID DATABASE
+P:	Jens Maurer
+M:	jmaurer@cck.uni-kl.de
+S:	Maintained
+
+PCNET32 NETWORK DRIVER
+P:	Thomas Bogendoerfer
+M:	tsbogend@alpha.franken.de
+L:	linux-net@vger.rutgers.edu
+S:	Maintained
+
+PNP SUPPORT
+P:	Tom Lees
+M:	tom@lpsg.demon.co.uk
+L:	pnp-list@lpsg.demon.co.uk
+L:	pnp-list@redhat.com (maybe)
+W:	http://www.lpsg.demon.co.uk/pnp-linux.html
+S:	Maintained
+
 PPP PROTOCOL DRIVERS AND COMPRESSORS
 P:	Al Longyear
 M:	longyear@pobox.com
@@ -356,18 +468,29 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
-P:	Stuart Cheshire
-M:	cheshire@cs.stanford.edu
-W:	http://mosquitonet.Stanford.EDU/strip.html
+RISCOM8 DRIVER:
+P:	Dmitry Gorodchanin
+M:	begemot@bgm.rosprint.net
+L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-WAN ROUTER AND SANGOMA WANPIPE DRIVERS (X.25, FRAME RELAY, PPP)
-P:     Gene Kozin
-M:     genek@compuserve.com
-M:     dm@sangoma.com
-W:     http://www.sangoma.com
-S:     Supported
+SBPCD CDROM DRIVER
+P:	Eberhard Moenkeberg
+M:	emoenke@gwdg.de
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+SCSI SUBSYSTEM
+P:	Leonard N. Zubkoff
+M:	Leonard N. Zubkoff <lnz@dandelion.com>
+L:	linux-scsi@vger.rutgers.edu
+S:	Maintained
+
+SCSI TAPE DRIVER
+P:	Kai Mdkisara
+M:	Kai.Makisara@metla.fi
+L:	linux-scsi@vger.rutgers.edu
+S:	Maintained
 
 SMB FILESYSTEM:
 P:	Volker Lendecke
@@ -381,51 +504,17 @@
 L:	linux-smp@vger.rutgers.edu
 S:	Maintained
 
+SOUND
+P:	Alan Cox 
+M:	Alan Cox@linux.org
+S:	Maintained
+
 SPARC:
 P:	David S. Miller
 M:	davem@caip.rutgers.edu
 L:	sparclinux@vger.rutgers.edu
 S:	Maintained
 
-SCSI SUBSYSTEM
-P:	Leonard N. Zubkoff
-M:	Leonard N. Zubkoff <lnz@dandelion.com>
-L:	linux-scsi@vger.rutgers.edu
-S:	Maintained
-
-SVGA HANDLING:
-P:	Martin Mares
-M:	mj@k332.feld.cvut.cz
-L:	linux-video@atrey.karlin.mff.cuni.cz
-S:	Maintained
-
-VFAT FILESYSTEM:
-P:	Gordon Chaffee
-M:	chaffee@cs.berkeley.edu
-L:	linux-kernel@vger.rutgers.edu
-W:	http://bmrc.berkeley.edu/people/chaffee
-S:	Maintained
-
-DIGI INTL. EPCA DRIVER:
-P:      Daniel Taylor
-M:      support@dgii.com
-M:      digilnux@dgii.com
-L:      digiboard@list.fuller.edu
-S:      Maintained
-
-RISCOM8 DRIVER:
-P:	Dmitry Gorodchanin
-M:	begemot@bgm.rosprint.net
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
-HAYES ESP SERIAL DRIVER:
-P:	Andrew J. Robinson
-M:	arobinso@nyx.net
-L:	linux-kernel@vger.rutgers.edu
-W:	http://www.nyx.net/~arobinso
-S:	Maintainted
-
 SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
 P:	Roger Wolff
 M:	R.E.Wolff@BitWizard.nl
@@ -440,140 +529,75 @@
 W:	http://www.stallion.com
 S:	Supported
 
-MOUSE AND MISC DEVICES [GENERAL]
-P:	Alessandro Rubini
-M:	rubini@ipvvis.unipv.it
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
-MENUCONFIG:
-P:	William Roadcap
-M:	roadcapw@cfw.com
-L:	linux-kernel@vger.rutgers.edu
+STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
+P:	Stuart Cheshire
+M:	cheshire@cs.stanford.edu
+W:	http://mosquitonet.Stanford.EDU/strip.html
 S:	Maintained
 
-CONFIGURE.HELP
-P:	Axel Boldt
-M:	boldt@math.ucsb.edu
+SVGA HANDLING:
+P:	Martin Mares
+M:	mj@k332.feld.cvut.cz
+L:	linux-video@atrey.karlin.mff.cuni.cz
 S:	Maintained
 
-PCI ID DATABASE
-P:	Jens Maurer
-M:	jmaurer@cck.uni-kl.de
+SYSV FILESYSTEM
+P:	Krzysztof G. Baranowski
+M:	kgb@manjak.knm.org.pl
 S:	Maintained
 
-PCNET32 NETWORK DRIVER
-P:	Thomas Bogendoerfer
-M:	tsbogend@alpha.franken.de
+TLAN NETWORK DRIVER
+P:	James Banks
+M:	james.banks@caldera.com
 L:	linux-net@vger.rutgers.edu
-S:	Maintained
+S:	Supported
 
-ADVANSYS SCSI DRIVER
-P:	Bob Frey
-M:	Bob Frey <bobf@advansys.com>
-W:	http://www.advansys.com/linux
+TOKEN-RING NETWORK DRIVER
+P:	Paul Norton
+M:	pnorton@cts.com
+L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
-AHA152X SCSI DRIVER
-P:	Juergen E. Fischer
-M:	Juergen Fischer <fischer@et-inf.fho-emden.de>
+U14-34F SCSI DRIVER
+P:	Dario Ballabio
+M:	dario@milano.europe.dg.com
 L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
-SBPCD CDROM DRIVER
-P:	Eberhard Moenkeberg
-M:	emoenke@gwdg.de
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
-NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
-P:	Eberhard Moenkeberg
-M:	emoenke@gwdg.de
-L:	linux-kernel@vger.rutgers.edu
-S:	Maintained
-
 UNIFORM CDROM DRIVER 
 P:	Erik Andersen
 M:	andersee@debian.org
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
-PARALLEL PORT SHARING SUPPORT
-P:	Phil Blundell
-M:	Philip.Blundell@pobox.com
-P:	Tim Waugh
-M:	tim@cyberelk.demon.co.uk
-P:	David Campbell
-M:	campbell@tirian.che.curtin.edu.au
-L:	linux-parport@torque.net
-L:	pnp-list@redhat.com
-W:	http://www.cyberelk.demon.co.uk/parport.html
-W:	http://www.cage.curtin.edu.au/~campbell/parbus/
-S:	Maintained
-
-PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
-P:	Grant Guenther
-M:	grant@torque.net
-L:	linux-parport@torque.net
-W:	http://www.torque.net/linux-pp.html
-S:	Maintained
-
-PNP SUPPORT
-P:	Tom Lees
-M:	tom@lpsg.demon.co.uk
-L:	pnp-list@lpsg.demon.co.uk
-L:	pnp-list@redhat.com (maybe)
-W:	http://www.lpsg.demon.co.uk/pnp-linux.html
-S:	Maintained
-
-LINUX FOR POWERPC (PREP)
-P:	Cort Dougan
-M:	cort@cs.nmt.edu
-W:	http://www.cs.nmt.edu/~linuxppc/
-S:	Maintained
-
-LINUX FOR POWER MACINTOSH
-P:	Paul Mackerras
-M:	paulus@cs.anu.edu.au
-L:	linux-pmac@samba.anu.edu.au
-S:	Maintained
-
-FPU EMULATOR
-P:	Bill Metzenthen
-M:	billm@suburbia.net
-W:	http://suburbia.net/~billm/floating-point/emulator/
-S:	Maintained
-
-CREDITS FILE
-P:	John A. Martin
-M:	jam@acm.org
-S:	Maintained
-
-KERNEL AUTOMOUNTER (AUTOFS)
-P:	H. Peter Anvin
-M:	hpa@zytor.com
-L:	autofs@linux.kernel.org
-S:	Maintained
-
-DEVICE NUMBER REGISTRY
-P:	H. Peter Anvin
-M:	hpa@zytor.com
+VFAT FILESYSTEM:
+P:	Gordon Chaffee
+M:	chaffee@cs.berkeley.edu
 L:	linux-kernel@vger.rutgers.edu
+W:	http://bmrc.berkeley.edu/people/chaffee
 S:	Maintained
 
-JOYSTICK DRIVER
-P:	Vojtech Pavlik
-M:	vojtech@atrey.karlin.mff.cuni.cz
+VIDEO FOR LINUX
+P:	Alan Cox
+M:	Alan.Cox@linux.org
 S:	Maintained
 
-NETWORK BLOCK DEVICE
-P:	Pavel Machek
-M:	pavel@atrey.karlin.mff.cuni.cz
+WAN ROUTER AND SANGOMA WANPIPE DRIVERS (X.25, FRAME RELAY, PPP)
+P:     Gene Kozin
+M:     genek@compuserve.com
+M:     dm@sangoma.com
+W:     http://www.sangoma.com
+S:     Supported
+
+WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
+P:	Jean Tourrilhes
+M:	jt@hplb.hpl.hp.com
 S:	Maintained
 
-SYSV FILESYSTEM
-P:	Krzysztof G. Baranowski
-M:	kgb@manjak.knm.org.pl
+Z8530 DRIVER FOR AX.25
+P:	Joerg Reuter
+M:	jreuter@lykos.oche.de
+L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
 REST:
diff -u --recursive --new-file v2.1.77/linux/Makefile linux/Makefile
--- v2.1.77/linux/Makefile	Fri Jan  2 14:37:01 1998
+++ linux/Makefile	Mon Jan  5 01:41:01 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 77
+SUBLEVEL = 78
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -209,19 +209,23 @@
 		mkdir include/linux/modules; \
 	fi
 
-oldconfig: symlinks
+oldconfig: symlinks scripts/split-include
 	$(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in
+	scripts/split-include include/linux/autoconf.h include/config
 
-xconfig: symlinks
+xconfig: symlinks scripts/split-include
 	$(MAKE) -C scripts kconfig.tk
 	wish -f scripts/kconfig.tk
+	scripts/split-include include/linux/autoconf.h include/config
 
-menuconfig: include/linux/version.h symlinks 
+menuconfig: include/linux/version.h symlinks scripts/split-include
 	$(MAKE) -C scripts/lxdialog all
 	$(CONFIG_SHELL) scripts/Menuconfig arch/$(ARCH)/config.in
+	scripts/split-include include/linux/autoconf.h include/config
 
-config: symlinks
+config: symlinks scripts/split-include
 	$(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
+	scripts/split-include include/linux/autoconf.h include/config
 
 linuxsubdirs: dummy
 	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i; done
@@ -339,8 +343,9 @@
 
 clean:	archclean
 	rm -f kernel/ksyms.lst include/linux/compile.h
-	rm -f core `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print`
-	rm -f core `find . -type f -name 'core' -print`
+	rm -f `find . -name '*.[oas]' ! -regex '.*lxdialog/.*' -print`
+	rm -f `find . -type f -name 'core' -print`
+	rm -f `find . -name '.*.flags' -print`
 	rm -f vmlinux System.map
 	rm -f .tmp*
 	rm -f drivers/char/consolemap_deftbl.c drivers/char/conmakehash
@@ -361,8 +366,9 @@
 	rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog
 	rm -f .menuconfig.log
 	rm -f include/asm
+	rm -rf include/config
 	rm -f .depend `find . -name .depend -print`
-	rm -f .hdepend scripts/mkdep
+	rm -f .hdepend scripts/mkdep scripts/split-include
 	rm -f $(TOPDIR)/include/linux/modversions.h
 	rm -rf $(TOPDIR)/include/linux/modules
 	rm -rf modules
@@ -380,10 +386,9 @@
 	find . -type f -print | sort | xargs sum > .SUMS
 
 dep-files: scripts/mkdep archdep include/linux/version.h
-	scripts/mkdep init/*.c > .tmpdepend
+	scripts/mkdep init/*.c > .depend
 	scripts/mkdep `find $(FINDHPATH) -follow -name \*.h ! -name modversions.h -print` > .hdepend
 	set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i fastdep; done
-	mv .tmpdepend .depend
 
 MODVERFILE :=
 
@@ -393,6 +398,9 @@
 
 depend dep: dep-files $(MODVERFILE)
 
+checkconfig:
+	perl -w scripts/checkconfig.pl `find $(FINDHPATH) $(SUBDIRS) -name '*.[hcS]' -print | sort`
+
 ifdef CONFIGURATION
 ..$(CONFIGURATION):
 	@echo
@@ -421,3 +429,6 @@
 
 scripts/mkdep: scripts/mkdep.c
 	$(HOSTCC) $(HOSTCFLAGS) -o scripts/mkdep scripts/mkdep.c
+
+scripts/split-include: scripts/split-include.c
+	$(HOSTCC) $(HOSTCFLAGS) -o scripts/split-include scripts/split-include.c
diff -u --recursive --new-file v2.1.77/linux/Rules.make linux/Rules.make
--- v2.1.77/linux/Rules.make	Mon Apr 14 16:28:05 1997
+++ linux/Rules.make	Mon Jan  5 01:41:01 1998
@@ -45,13 +45,18 @@
 #
 
 %.s: %.c
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -S $< -o $@
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -S $< -o $@
 
 %.i: %.c
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E $< > $@
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -E $< > $@
 
 %.o: %.c
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $<
+	@ ( \
+	    echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@)),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@)))' ; \
+	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+	    echo 'endif' \
+	) > .$@.flags
 
 %.o: %.s
 	$(AS) $(ASFLAGS) $(EXTRA_CFLAGS) -o $@ $<
@@ -66,29 +71,39 @@
 #
 ifdef O_TARGET
 ALL_O = $(OX_OBJS) $(O_OBJS)
-$(O_TARGET): $(ALL_O) $(TOPDIR)/include/linux/config.h
+$(O_TARGET): $(ALL_O)
 	rm -f $@
 ifneq "$(strip $(ALL_O))" ""
 	$(LD) $(EXTRA_LDFLAGS) -r -o $@ $(ALL_O)
 else
 	$(AR) rcs $@
 endif
+	@ ( \
+	    echo 'ifeq ($(strip $(EXTRA_LDFLAGS) $(ALL_O)),$$(strip $$(EXTRA_LDFLAGS) $$(ALL_O)))' ; \
+	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+	    echo 'endif' \
+	) > .$@.flags
 endif # O_TARGET
 
 #
 # Rule to compile a set of .o files into one .a file
 #
 ifdef L_TARGET
-$(L_TARGET): $(LX_OBJS) $(L_OBJS) $(TOPDIR)/include/linux/config.h
+$(L_TARGET): $(LX_OBJS) $(L_OBJS)
 	rm -f $@
 	$(AR) $(EXTRA_ARFLAGS) rcs $@ $(LX_OBJS) $(L_OBJS)
+	@ ( \
+	    echo 'ifeq ($(strip $(EXTRA_ARFLAGS) $(LX_OBJS) $(L_OBJS)),$$(strip $$(EXTRA_ARFLAGS) $$(LX_OBJS) $$(L_OBJS)))' ; \
+	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+	    echo 'endif' \
+	) > .$@.flags
 endif
 
 #
 # This make dependencies quickly
 #
 fastdep: dummy
-	$(TOPDIR)/scripts/mkdep *.[chS] > .depend
+	$(TOPDIR)/scripts/mkdep $(wildcard *.[chS] local.h.master) > .depend
 ifdef ALL_SUB_DIRS
 	set -e; for i in $(ALL_SUB_DIRS); do $(MAKE) -C $$i fastdep; done
 endif
@@ -131,7 +146,7 @@
 ifneq "$(strip $(ALL_MOBJS))" ""
 	echo $(PDWN)
 	cd $$TOPDIR/modules; for i in $(ALL_MOBJS); do \
-	    ln -sf ../$(PDWN)/$$i .; done
+	    ln -sf ../$(PDWN)/$$i $$i; done
 endif
 
 #
@@ -198,19 +213,54 @@
 
 ifneq "$(strip $(SYMTAB_OBJS))" ""
 $(SYMTAB_OBJS): $(TOPDIR)/include/linux/modversions.h $(SYMTAB_OBJS:.o=.c)
-	$(CC) $(CFLAGS) -DEXPORT_SYMTAB -c $(@:.o=.c)
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+	@ ( \
+	    echo 'ifeq ($(strip $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB),$$(strip $$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB))' ; \
+	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
+	    echo 'endif' \
+	) > .$@.flags
 endif
 
 endif # CONFIG_MODULES
 
 
 #
-# include dependency files they exist
+# include dependency files if they exist
 #
-ifeq (.depend,$(wildcard .depend))
+ifneq ($(wildcard .depend),)
 include .depend
 endif
 
-ifeq ($(TOPDIR)/.hdepend,$(wildcard $(TOPDIR)/.hdepend))
+ifneq ($(wildcard $(TOPDIR)/.hdepend),)
 include $(TOPDIR)/.hdepend
+endif
+
+#
+# Find files whose flags have changed and force recompilation.
+# For safety, this works in the converse direction:
+#   every file is forced, except those whose flags are positively up-to-date.
+#
+FILES_FLAGS_UP_TO_DATE :=
+
+FILES_FLAGS_EXIST := $(wildcard .*.flags)
+ifneq ($(FILES_FLAGS_EXIST),)
+include $(FILES_FLAGS_EXIST)
+endif
+
+FILES_FLAGS_CHANGED := $(strip \
+    $(filter-out $(FILES_FLAGS_UP_TO_DATE), \
+	$(O_TARGET) $(O_OBJS) $(OX_OBJS) \
+	$(L_TARGET) $(L_OBJS) $(LX_OBJS) \
+	$(M_OBJS) $(MX_OBJS) \
+	$(MI_OBJS) $(MIX_OBJS) \
+	))
+
+# A kludge: .S files don't get flag dependencies (yet),
+#   because that will involve changing a lot of Makefiles.
+FILES_FLAGS_CHANGED := $(strip \
+    $(filter-out $(patsubst %.S, %.o, $(wildcard *.S)), \
+    $(FILES_FLAGS_CHANGED)))
+
+ifneq ($(FILES_FLAGS_CHANGED),)
+$(FILES_FLAGS_CHANGED): dummy
 endif
diff -u --recursive --new-file v2.1.77/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.77/linux/arch/i386/config.in	Sun Dec 21 22:36:12 1997
+++ linux/arch/i386/config.in	Mon Jan  5 00:06:25 1998
@@ -109,10 +109,10 @@
 endmenu
 
 # Conditionally compile in the Uniform CD-ROM driver
-if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
+if [ "$CONFIG_BLK_DEV_IDECD" = "y" -o "$CONFIG_BLK_DEV_SR" = "y" -o "$CONFIG_SBPCD" = "y" -o "$CONFIG_MCD" = "y" -o "$CONFIG_MCDX" = "y" -o "$CONFIG_CM206" = "y" -o "$CONFIG_CDU31A" = "y" ]; then
   define_bool CONFIG_CDROM y
 else
-  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
+  if [ "$CONFIG_BLK_DEV_IDECD" = "m" -o "$CONFIG_BLK_DEV_SR" = "m" -o "$CONFIG_SBPCD" = "m" -o "$CONFIG_MCD" = "m" -o "$CONFIG_MCDX" = "m" -o "$CONFIG_CM206" = "m" -o "$CONFIG_CDU31A" = "m" ]; then
     define_bool CONFIG_CDROM m
   else
     define_bool CONFIG_CDROM n
diff -u --recursive --new-file v2.1.77/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.77/linux/arch/i386/defconfig	Fri Jan  2 14:37:01 1998
+++ linux/arch/i386/defconfig	Sun Jan  4 23:50:43 1998
@@ -237,6 +237,7 @@
 # CONFIG_NTFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
 # CONFIG_ROMFS_FS is not set
 CONFIG_AUTOFS_FS=y
 # CONFIG_UFS_FS is not set
diff -u --recursive --new-file v2.1.77/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- v2.1.77/linux/arch/i386/kernel/vm86.c	Mon Dec  1 12:04:11 1997
+++ linux/arch/i386/kernel/vm86.c	Sun Jan  4 10:55:08 1998
@@ -122,7 +122,7 @@
 
 
 
-static do_vm86_irq_handling(int subfunction, int irqnumber);
+static int do_vm86_irq_handling(int subfunction, int irqnumber);
 static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk);
 
 asmlinkage int sys_vm86old(struct vm86_struct * v86)
diff -u --recursive --new-file v2.1.77/linux/drivers/ap1000/ddv.c linux/drivers/ap1000/ddv.c
--- v2.1.77/linux/drivers/ap1000/ddv.c	Thu Dec  4 14:53:54 1997
+++ linux/drivers/ap1000/ddv.c	Sun Jan  4 10:40:15 1998
@@ -985,9 +985,10 @@
 int init_module(void)
 {
 	int error = ddv_init();
-	ddv_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 });
-	if (!error)
+	if (!error) {
+		ddv_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 });
 		printk(KERN_INFO "DDV: Loaded as module.\n");
+	}
 	return error;
 }
 
@@ -995,6 +996,7 @@
 void cleanup_module(void)
 {
 	int i;
+	struct gendisk ** gdp;
 
 	for (i = 0 ; i < NUM_DDVDEVS; i++)
 		invalidate_buffers(MKDEV(MAJOR_NR, i));
@@ -1004,6 +1006,11 @@
 	OPT_IO(PRST) = PRST_IRST; 
 
 	unregister_blkdev( MAJOR_NR, DEVICE_NAME );
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == &ddv_gendisk)
+			break;
+	if (*gdp)
+		*gdp = (*gdp)->next;
 	free_irq(APOPT0_IRQ, NULL);
 	blk_dev[MAJOR_NR].request_fn = 0;
 }
diff -u --recursive --new-file v2.1.77/linux/drivers/block/acsi.c linux/drivers/block/acsi.c
--- v2.1.77/linux/drivers/block/acsi.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/block/acsi.c	Sun Jan  4 10:40:15 1998
@@ -1820,12 +1820,19 @@
 
 void cleanup_module(void)
 {
+	struct gendisk ** gdp;
+
 	del_timer( &acsi_timer );
 	blk_dev[MAJOR_NR].request_fn = 0;
 	free_pages( (unsigned long)acsi_buffer, ACSI_BUFFER_ORDER );
 
 	if (unregister_blkdev( MAJOR_NR, "ad" ) != 0)
 		printk( KERN_ERR "acsi: cleanup_module failed\n");
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == &acsi_gendisk)
+			break;
+	if (*gdp)
+		*gdp = (*gdp)->next;
 }
 #endif
 
diff -u --recursive --new-file v2.1.77/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.77/linux/drivers/block/floppy.c	Sun Dec 21 22:36:12 1997
+++ linux/drivers/block/floppy.c	Sun Jan  4 10:55:08 1998
@@ -4246,7 +4246,7 @@
 
 void cleanup_module(void)
 {
-	int fdc, dummy;
+	int dummy;
 		
 	unregister_blkdev(MAJOR_NR, "fd");
 
diff -u --recursive --new-file v2.1.77/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.77/linux/drivers/block/genhd.c	Fri Jan  2 14:37:01 1998
+++ linux/drivers/block/genhd.c	Sun Jan  4 10:40:15 1998
@@ -255,6 +255,45 @@
 done:
 	brelse(bh);
 }
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+static void
+solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) {
+
+	struct buffer_head *bh;
+	struct solaris_x86_vtoc *v;
+	struct solaris_x86_slice *s;
+	int i;
+
+	if(!(bh = bread(dev, 0, get_ptable_blocksize(dev))))
+		return;
+	v = (struct solaris_x86_vtoc *)(bh->b_data + 512);
+	if(v->v_sanity != SOLARIS_X86_VTOC_SANE) {
+		brelse(bh);
+		return;
+	}
+	printk(" <solaris:");
+	if(v->v_version != 1) {
+		printk("  cannot handle version %ld vtoc>", v->v_version);
+		brelse(bh);
+		return;
+	}
+	for(i=0; i<SOLARIS_X86_NUMSLICE; i++) {
+		s = &v->v_slice[i];
+
+		if (s->s_tag == 0)
+			continue;
+		printk(" [s%d]", i);
+		/* solaris partitions are relative to current MS-DOS
+		 * one but add_partition starts relative to sector
+		 * zero of the disk.  Therefore, must add the offset
+		 * of the current partition */
+		add_partition(hd, current_minor, s->s_start+offset, s->s_size);
+		current_minor++;
+	}
+	brelse(bh);
+	printk(" >");
+}
+#endif
 
 #ifdef CONFIG_BSD_DISKLABEL
 /* 
@@ -419,6 +458,18 @@
 			printk(" <");
 			bsd_disklabel_partition(hd, MKDEV(hd->major, minor));
 			printk(" >");
+		}
+#endif
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+
+		/* james@bpgc.com: Solaris has a nasty indicator: 0x82
+		 * which also means linux swap.  For that reason, all
+		 * of the prints are done inside the
+		 * solaris_x86_partition routine */
+
+		if(SYS_IND(p) == SOLARIS_X86_PARTITION) {
+			solaris_x86_partition(hd, MKDEV(hd->major, minor),
+					      first_sector+START_SECT(p));
 		}
 #endif
 	}
diff -u --recursive --new-file v2.1.77/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.77/linux/drivers/block/ide-cd.c	Sun Dec 21 16:17:44 1997
+++ linux/drivers/block/ide-cd.c	Mon Jan  5 00:06:25 1998
@@ -27,10 +27,8 @@
  *     unless you have a patch to fix it.  I am working on it...)
  * -Implement ide_cdrom_select_speed using the generic cdrom interface
  * -Fix ide_cdrom_reset so that it works (it does nothing right now)
- *
- * MOSTLY DONE LIST:
- *  Query the drive to find what features are available
- *   before trying to use them.
+ * -Query the drive to find what features are available before trying to
+ *   use them (like trying to close the tray in drives that can't).
  *
  *
  * ----------------------------------
@@ -182,10 +180,12 @@
  * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
  * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
  *                     -- fix speed display for ACER 24X, 18X
+ * 4.09  Jan 04, 1998  -- fix handling of the last block so we return
+ *                         an end of file instead of an I/O error (Gadi)
  *
  *************************************************************************/
 
-#define IDECD_VERSION "4.07"
+#define IDECD_VERSION "4.09"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -1663,7 +1663,7 @@
 	if (stat) toc->capacity = 0x1fffff;
 
 	HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
-		= toc->capacity * SECTORS_PER_FRAME;
+		= (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
 	drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
 
 	/* Remember that we've read this stuff. */
diff -u --recursive --new-file v2.1.77/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- v2.1.77/linux/drivers/block/xd.c	Sat Nov 29 11:25:09 1997
+++ linux/drivers/block/xd.c	Sun Jan  4 10:40:15 1998
@@ -20,13 +20,14 @@
  *
  * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
  *
- * Revised: 13/09/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
+ * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
  *   Fixed some problems with disk initialization and module initiation.
  *   Added support for manual geometry setting (except Seagate controllers)
  *   in form:
  *      xd_geo=<cyl_xda>,<head_xda>,<sec_xda>[,<cyl_xdb>,<head_xdb>,<sec_xdb>]
- *   Recovered DMA access. Abridged messages. Added support for DTC5051CX &
- *   WD1002-27X controllers. Added alternate jumper geometry setting.
+ *   Recovered DMA access. Abridged messages. Added support for DTC5051CX,
+ *   WD1002-27X & XEBEC controllers. Driver uses now some jumper settings.
+ *   Extended ioctl() support.
  */
 
 #include <linux/module.h>
@@ -38,6 +39,7 @@
 #include <linux/timer.h>
 #include <linux/genhd.h>
 #include <linux/hdreg.h>
+#include <linux/ioport.h>
 #include <linux/init.h>
 
 #include <asm/system.h>
@@ -114,12 +116,15 @@
 	{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
 	{ 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
 	{ 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */
+	{ 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
 };
 
 static unsigned int xd_bases[] __initdata =
 {
 	0xC8000, 0xCA000, 0xCC000,
-	0xCE000, 0xD0000, 0xD8000,
+	0xCE000, 0xD0000, 0xD2000,
+	0xD4000, 0xD6000, 0xD8000,
+	0xDA000, 0xDC000, 0xDE000,
 	0xE0000
 };
 
@@ -219,6 +224,11 @@
 	if (xd_detect(&controller,&address)) {
 
 		printk("Detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address);
+		if (check_region(xd_iobase,4)) {
+			printk("xd: Ports at 0x%x are not available\n",xd_iobase);
+			return;
+		}
+		request_region(xd_iobase,4,"xd");
 		if (controller)
 			xd_sigs[controller].init_controller(address);
 		xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
@@ -331,6 +341,8 @@
 			if(arg > 0xff) return -EINVAL;
 			read_ahead[MAJOR(inode->i_rdev)] = arg;
 			return 0;
+		case BLKRAGET:
+			return put_user(read_ahead[MAJOR(inode->i_rdev)], (long*) arg);
 		case BLKGETSIZE:
 			if (!arg) return -EINVAL;
 			return put_user(xd_struct[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
@@ -339,6 +351,19 @@
 			fsync_dev(inode->i_rdev);
 			invalidate_buffers(inode->i_rdev);
 			return 0;
+		case HDIO_SET_DMA:
+			if (!suser()) return -EACCES;
+			if (xdc_busy) return -EBUSY;
+			nodma = !arg;
+			if (nodma && xd_dma_buffer) {
+				xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200);
+				xd_dma_buffer = 0;
+			}
+			return 0;
+		case HDIO_GET_DMA:
+			return put_user(!nodma, (long *) arg);
+		case HDIO_GET_MULTCOUNT:
+			return put_user(xd_maxsectors, (long *) arg);
 		case BLKRRPART:
 			return xd_reread_partitions(inode->i_rdev);
 		RO_IOCTLS(inode->i_rdev,arg);
@@ -929,6 +954,81 @@
 	xd_info[drive].control = 2;
 }
 
+/* Xebec support (AK) */
+__initfunc(static void xd_xebec_init_controller (unsigned int address))
+{
+/* iobase may be set manually in range 0x300 - 0x33C
+      irq may be set manually to 2(9),3,4,5,6,7
+      dma may be set manually to 1,2,3
+	(How to detect them ???)
+BIOS address may be set manually in range 0x0 - 0xF8000
+If you need non-standard settings use the xd=... command */
+
+	switch (address) {
+		case 0x00000:
+		case 0xC8000:	/* initially: xd_iobase==0x320 */
+		case 0xD0000:
+		case 0xD2000:
+		case 0xD4000:
+		case 0xD6000:
+		case 0xD8000:
+		case 0xDA000:
+		case 0xDC000:
+		case 0xDE000:
+		case 0xE0000:	break;
+		default:	printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address);
+				break;
+		}
+
+	xd_maxsectors = 0x01;
+	outb(0,XD_RESET);		/* reset the controller */
+
+	xd_timer.expires = jiffies + XD_INIT_DISK_DELAY;
+	add_timer(&xd_timer);
+	sleep_on(&xdc_wait);
+}
+
+__initfunc(static void xd_xebec_init_drive (u_char drive))
+{
+	/* values from controller's BIOS - BIOS chip may be removed */
+	static u_short geometry_table[][5] = {
+		{0x132,4,0x080,0x080,0x7},
+		{0x132,4,0x080,0x080,0x17},
+		{0x264,2,0x100,0x100,0x7},
+		{0x264,2,0x100,0x100,0x17},
+		{0x132,8,0x080,0x080,0x7},
+		{0x132,8,0x080,0x080,0x17},
+		{0x264,4,0x100,0x100,0x6},
+		{0x264,4,0x100,0x100,0x17},
+		{0x2BC,5,0x2BC,0x12C,0x6},
+		{0x3A5,4,0x3A5,0x3A5,0x7},
+		{0x26C,6,0x26C,0x26C,0x7},
+		{0x200,8,0x200,0x100,0x17},
+		{0x400,5,0x400,0x400,0x7},
+		{0x400,6,0x400,0x400,0x7},
+		{0x264,8,0x264,0x200,0x17},
+		{0x33E,7,0x33E,0x200,0x7}};
+	u_char n;
+
+	n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry 
+					is assumed for BOTH drives */
+	if (xd_geo[3*drive])
+		xd_manual_geo_set(drive);
+	else {
+		xd_info[drive].heads = (u_char)(geometry_table[n][1]);			/* heads */
+		xd_info[drive].cylinders = geometry_table[n][0];	/* cylinders */
+		xd_info[drive].sectors = 17;				/* sectors */
+#if 0
+		xd_info[drive].rwrite = geometry_table[n][2];	/* reduced write */
+		xd_info[drive].precomp = geometry_table[n][3]		/* write precomp */
+		xd_info[drive].ecc = 0x0B;				/* ecc length */
+#endif /* 0 */
+	}
+	xd_info[drive].control = geometry_table[n][4];			/* control byte */
+	xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B);
+	xd_recalibrate(drive);
+}
+
 /* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads
    etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */
 __initfunc(static void xd_override_init_drive (u_char drive))
@@ -1008,7 +1108,9 @@
 	cmdblk[12] = (u_char) (wprecomp & 0xFF);
 	cmdblk[13] = ecc;
 
-	if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
+	/* Some controllers require geometry info as data, not command */
+
+	if (xd_command(cmdblk,PIO_MODE,0,&cmdblk[6],0,XD_TIMEOUT * 2))
 		printk("xd: error setting characteristics for xd%c\n", 'a'+drive);
 }
 
@@ -1020,6 +1122,23 @@
 MODULE_PARM(xd_geo, "3-6i");
 MODULE_PARM(nodma, "i");
 
+static void xd_done (void)
+{
+	struct gendisk ** gdp;
+	
+	blksize_size[MAJOR_NR] = NULL;
+	blk_dev[MAJOR_NR].request_fn = NULL;
+	blk_size[MAJOR_NR] = NULL;
+	hardsect_size[MAJOR_NR] = NULL;
+	read_ahead[MAJOR_NR] = 0;
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == &xd_gendisk)
+			break;
+	if (*gdp)
+		*gdp = (*gdp)->next;
+	release_region(xd_iobase,4);
+}
+
 int init_module(void)
 {
 	int i,count = 0;
@@ -1036,6 +1155,7 @@
 		if (!xd_drives) {
 					/* no drives detected - unload module */
 			unregister_blkdev(MAJOR_NR, "xd");
+			xd_done();
 			return (-1);
 		}
 		for (i = 0; i < xd_drives; i++)
@@ -1047,7 +1167,20 @@
 
 void cleanup_module(void)
 {
+	int partition,dev,start;
+
 	unregister_blkdev(MAJOR_NR, "xd");
+	for (dev = 0; dev < xd_drives; dev++) {
+		start = dev << xd_gendisk.minor_shift; 
+		for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
+			int minor = (start | partition);
+			kdev_t devp = MKDEV(MAJOR_NR, minor);
+			start = dev << xd_gendisk.minor_shift; 
+			sync_dev(devp);
+			invalidate_buffers(devp);
+		}
+	}
+	xd_done();
 	if (xd_drives) {
 		free_irq(xd_irq, NULL);
 		free_dma(xd_dma);
diff -u --recursive --new-file v2.1.77/linux/drivers/block/xd.h linux/drivers/block/xd.h
--- v2.1.77/linux/drivers/block/xd.h	Sat Nov 29 11:25:09 1997
+++ linux/drivers/block/xd.h	Sun Jan  4 10:40:15 1998
@@ -46,6 +46,7 @@
 #define CMD_DTCGETGEOM	0xFF	/* get geometry data (DTC 5150X only?) */
 #define CMD_ST11GETGEOM 0xF8	/* get geometry data (Seagate ST11R/M only?) */
 #define CMD_WDSETPARAM	0x0C	/* set drive parameters (WD 1004A27X only?) */
+#define CMD_XBSETPARAM	0x0C	/* set drive parameters (XEBEC only?) */
 
 /* Bits for command status byte */
 #define CSB_ERROR	0x02	/* error */
@@ -136,6 +137,8 @@
 static void xd_seagate_init_drive (u_char drive);
 static void xd_omti_init_controller (unsigned int address);
 static void xd_omti_init_drive (u_char drive);
+static void xd_xebec_init_controller (unsigned int address);
+static void xd_xebec_init_drive (u_char drive);
 static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc);
 static void xd_override_init_drive (u_char drive);
 
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.77/linux/drivers/cdrom/cdrom.c	Fri Jan  2 14:37:01 1998
+++ linux/drivers/cdrom/cdrom.c	Mon Jan  5 00:06:26 1998
@@ -1,6 +1,6 @@
 /* linux/drivers/cdrom/cdrom.c. 
    Copyright (c) 1996, 1997 David A. van Leeuwen.
-   Copyright (c) 1997 Erik Andersen (andersee@debian.org)
+   Copyright (c) 1997, 1998 Erik Andersen (andersee@debian.org)
 
    May be copied or modified under the terms of the GNU General Public
    License.  See linux/COPYING for more information.
@@ -68,14 +68,10 @@
 #include <asm/uaccess.h>
 
 
-#define VERSION "$Id: cdrom.c,v 2.1 1997/12/28 15:11:47 david Exp $"
-#define REVISION "$Revision: 2.1 $"
+#define VERSION "$Id: cdrom.c,v 2.11 1998/01/04 01:11:18 erik Exp $"
+#define REVISION "Revision: 2.11"
 #define FM_WRITE	0x2                 /* file mode write bit */
 
-/* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't 
-   get compiled in */
-#define VERBOSE_STATUS_INFO
-
 /* I use an error-log mask to give fine grain control over the type of
    error messages dumped to the system logs.  The available masks include: */
 #define CD_WARNING	0x1
@@ -83,9 +79,14 @@
 #define CD_DO_IOCTL	0x4
 #define CD_OPEN		0x8
 #define CD_CLOSE	0x10
+#define CD_COUNT_TRACKS 0x20
+
+/* When VERBOSE_STATUS_INFO is not defined, the debugging printks don't 
+   get compiled in at all */
+#define VERBOSE_STATUS_INFO
 
 #define ERRLOGMASK (CD_WARNING) 
-/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_CLOSE) */
+/* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */
 /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE) */
 
 #ifdef VERBOSE_STATUS_INFO
@@ -480,7 +481,7 @@
 	tracks->cdi=0;
 	tracks->xa=0;
 	tracks->error=0;
-	cdinfo(CD_OPEN, "entering cdrom_count_tracks\n"); 
+	cdinfo(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); 
         if (!(cdi->ops->capability & CDC_PLAY_AUDIO)) { 
                 tracks->error=CDS_NO_INFO;
                 return;
@@ -508,10 +509,10 @@
 			tracks->data++;
 		} else
 		    tracks->audio++;
-		cdinfo(CD_OPEN, "track %d: format=%d, ctrl=%d\n",
+		cdinfo(CD_COUNT_TRACKS, "track %d: format=%d, ctrl=%d\n",
 		       i, entry.cdte_format, entry.cdte_ctrl);
 	}	
-	cdinfo(CD_OPEN, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", 
+	cdinfo(CD_COUNT_TRACKS, "disc has %d tracks: %d=audio %d=data %d=Cd-I %d=XA\n", 
 		header.cdth_trk1, tracks->audio, tracks->data, 
 		tracks->cdi, tracks->xa);
 }	
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v2.1.77/linux/drivers/cdrom/cdu31a.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/cdu31a.c	Mon Jan  5 00:06:26 1998
@@ -416,7 +416,8 @@
      return -EINVAL;
   }
 
-  return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
+  /*return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;*/
+  return sony_spun_up ? CDS_DISC_OK : CDS_TRAY_OPEN;
 }
 
 static inline void
@@ -1085,7 +1086,7 @@
    volatile int val;
 
 
-#if DEBUG
+#if 0*DEBUG
    printk("Entering handle_sony_cd_attention\n");
 #endif
    if (is_attention())
@@ -1166,7 +1167,7 @@
    }
 
    num_consecutive_attentions = 0;
-#if DEBUG
+#if 0*DEBUG
    printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
 #endif
    return(0);
@@ -2835,8 +2836,11 @@
 
     sony_audio_status = CDROM_AUDIO_INVALID;
     return do_sony_cd_cmd_chk("EJECT",SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
-  } else
-  return 0;
+  } else {
+    if (0 == scd_spinup())
+      sony_spun_up = 1;
+    return 0;
+  }
 }
 
 /*
@@ -3230,7 +3234,6 @@
             printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 1;
-printk("sony_xa_mode is set\n");
       }
       /* A non-XA disk.  Set the parms back if necessary. */
       else if (sony_xa_mode)
@@ -3247,7 +3250,6 @@
             printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
          }
          sony_xa_mode = 0;
-printk("sony_xa_mode is reset\n");
       }
 
       sony_spun_up = 1;
@@ -3293,7 +3295,7 @@
   scd_reset,                  /* hard reset */
   scd_audio_ioctl,            /* audio ioctl */
   scd_dev_ioctl,              /* device-specific ioctl */
-  CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_MULTI_SESSION | 
+  CDC_OPEN_TRAY | CDC_CLOSE_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_MULTI_SESSION | 
   CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO |
   CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS, /* capability */
   1,                            /* number of minor devices */
@@ -3571,6 +3573,7 @@
       cdu31a_abort_timer.function = handle_abort_timeout;
 
       scd_info.mask = deficiency;
+      strncpy(scd_info.name, "cdu31a", sizeof(scd_info.name));
 
       if (register_cdrom(&scd_info))
       {
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v2.1.77/linux/drivers/cdrom/cm206.c	Fri Jan  2 14:37:01 1998
+++ linux/drivers/cdrom/cm206.c	Sun Jan  4 10:55:08 1998
@@ -887,7 +887,7 @@
   uch * q = cd->q;
   uch ct;			/* current track */
   int binary=0;
-  const skip = 3*60*75;		/* 3 minutes */
+  const int skip = 3*60*75;		/* 3 minutes */
 
   for (i=track; i>0; i--) if (cd->toc[i].track) {
     min = fsm2lba(cd->toc[i].fsm);
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/mcdx.c linux/drivers/cdrom/mcdx.c
--- v2.1.77/linux/drivers/cdrom/mcdx.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/mcdx.c	Mon Jan  5 00:06:26 1998
@@ -75,9 +75,6 @@
 #define	mcdx_drive_map mcdx
 #include "mcdx.h"
 
-#define MCDX_QUIET 0
-
-
 #ifndef HZ
 #error HZ not defined
 #endif
@@ -288,7 +285,7 @@
   NULL,                       /* hard reset */
   mcdx_audio_ioctl,            /* audio ioctl */
   NULL,                  /* device-specific ioctl */
-  CDC_OPEN_TRAY | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO
+  CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO
   | CDC_DRIVE_STATUS, /* capability */
   0,                            /* number of minor devices */
 };
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/sbpcd.c linux/drivers/cdrom/sbpcd.c
--- v2.1.77/linux/drivers/cdrom/sbpcd.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/sbpcd.c	Mon Jan  5 00:06:26 1998
@@ -550,7 +550,6 @@
 static struct cdrom_subchnl SC;
 static struct cdrom_volctrl volctrl;
 static struct cdrom_read_audio read_audio;
-static struct cdrom_multisession ms_info;
 
 static unsigned char msgnum=0;
 static char msgbuf[80];
@@ -2297,12 +2296,52 @@
 	}
 	i=cmd_out();
 	msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
+
+	i=cc_ReadError();
+	flags_cmd_out |= f_respo2;
+	cc_ReadStatus(); /* command: give 1-byte status */
+	i=ResponseStatus();
+	if (famT_drive&&(i<0))
+	{
+		cc_DriveReset();
+		i=ResponseStatus();
+#if 0
+                sbp_sleep(HZ);
+#endif 0
+		i=ResponseStatus();
+	}
+	if (i<0)
+	{
+		msg(DBG_INF,"sbpcd cc_CloseTray: ResponseStatus timed out (%d).\n",i);
+	}
+	if (!(famT_drive))
+	{
+		if (!st_spinning)
+		{
+			cc_SpinUp();
+			if (st_check) i=cc_ReadError();
+			flags_cmd_out |= f_respo2;
+			cc_ReadStatus();
+			i=ResponseStatus();
+		} else {
+		}
+	}
+	i=DiskInfo();
 	return (i);
 }
 
 static int sbpcd_tray_move(struct cdrom_device_info *cdi, int position)
 {
-  return position ? cc_CloseTray() : 0; 
+  int i;
+  i = MINOR(cdi->dev);
+
+  switch_drive(i);
+  if (position == 1) {
+    cc_SpinDown();
+  } else {
+    return cc_CloseTray();
+  }
+  return 0;
 }
 
 /*==========================================================================*/
@@ -2768,11 +2807,13 @@
 		if (famLV_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1;
 	}
 	D_S[d].diskstate_flags |= toc_bit;
-	msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n",
+	msg(DBG_TOC,"TocDesc: xa %02X firstt %02X lastt %02X size %08X firstses %02X lastsess %02X\n",
 	    D_S[d].xa_byte,
 	    D_S[d].n_first_track,
 	    D_S[d].n_last_track,
-	    D_S[d].size_msf);
+	    D_S[d].size_msf,
+	    D_S[d].first_session,
+	    D_S[d].last_session);
 	return (0);
 }
 /*==========================================================================*/
@@ -3886,6 +3927,7 @@
 	msg(DBG_000,"DiskInfo entered.\n");
 	for (j=1;j<LOOP_COUNT;j++)
 	{
+#if 0
 		i=SetSpeed();
 		if (i<0)
 		{
@@ -3898,10 +3940,14 @@
 			msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i);
 			continue;
 		}
+#endif
 		i=cc_ReadCapacity();
 		if (i>=0) break;
 		msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i);
+#if 0
 		i=cc_DriveReset();
+#endif
+		if (!fam0_drive && j == 2) break;
 	}
 	if (j==LOOP_COUNT) return (-33); /* give up */
 	
@@ -3946,12 +3992,36 @@
 
 static int sbpcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
 {
-  if (CDSL_CURRENT != slot_nr) {
-     /* we have no changer support */
-     return -EINVAL;
-  }
+	int st;
+
+	if (CDSL_CURRENT != slot_nr) {
+		 /* we have no changer support */
+		 return -EINVAL;
+	}
+
+        cc_ReadStatus();
+	st=ResponseStatus();
+	if (st<0)
+	{
+		msg(DBG_INF,"sbpcd_drive_status: timeout.\n");
+		return (0);
+	}
+	msg(DBG_000,"Drive Status: door_locked =%d.\n", st_door_locked);
+	msg(DBG_000,"Drive Status: door_closed =%d.\n", st_door_closed);
+	msg(DBG_000,"Drive Status: caddy_in =%d.\n", st_caddy_in);
+	msg(DBG_000,"Drive Status: disk_ok =%d.\n", st_diskok);
+	msg(DBG_000,"Drive Status: spinning =%d.\n", st_spinning);
+	msg(DBG_000,"Drive Status: busy =%d.\n", st_busy);
+#if 0
+  if (!(D_S[MINOR(cdi->dev)].status_bits & p_door_closed)) return CDS_TRAY_OPEN;
+  if (D_S[MINOR(cdi->dev)].status_bits & p_disk_ok) return CDS_DISC_OK;
+  if (D_S[MINOR(cdi->dev)].status_bits & p_disk_in) return CDS_DRIVE_NOT_READY;
 
-  return D_S[d].status_bits & p1_disk_ok ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
+  return CDS_NO_DISC;
+#else
+  if (D_S[MINOR(cdi->dev)].status_bits & p_spinning) return CDS_DISC_OK;
+  return CDS_TRAY_OPEN;
+#endif
 }
 
 
@@ -4082,13 +4152,13 @@
 static int sbpcd_get_last_session(struct cdrom_device_info *cdi, struct cdrom_multisession *ms_infp)
 {
 	ms_infp->addr_format = CDROM_LBA;
-	ms_infp->addr.lba    = D_S[d].lba_multi;
-	if (D_S[d].f_multisession)
+	ms_infp->addr.lba    = D_S[MINOR(cdi->dev)].lba_multi;
+	if (D_S[MINOR(cdi->dev)].f_multisession)
 		ms_infp->xa_flag=1; /* valid redirection address */
 	else
 		ms_infp->xa_flag=0; /* invalid redirection address */
 
-	return  1;
+	return  0;
 }
 
 /*==========================================================================*/
@@ -4304,8 +4374,30 @@
 				error_flag=0;
 				p = D_S[d].aud_buf;
 				if (sbpro_type==1) OUT(CDo_sel_i_d,1);
-				if (do_16bit) insw(CDi_data, p, read_audio.nframes*(CD_FRAMESIZE_RAW>>1));
-				else insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
+				if (do_16bit)
+				{
+					u_short *p2 = (u_short *) p;
+
+					for (; (u_char *) p2 < D_S[d].aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
+				  	{
+						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+
+						/* get one sample */
+						*p2++ = inw_p(CDi_data);
+						*p2++ = inw_p(CDi_data);
+					}
+				} else {
+					for (; p < D_S[d].aud_buf + read_audio.nframes*CD_FRAMESIZE_RAW;)
+				  	{
+						if ((inb_p(CDi_status)&s_not_data_ready)) continue;
+
+						/* get one sample */
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
+						*p++ = inb_p(CDi_data);
+					}
+				}
 				if (sbpro_type==1) OUT(CDo_sel_i_d,0);
 				data_retrying = 0;
 			}
@@ -4388,28 +4480,6 @@
 		if(arg > 0xff) RETURN_UP(-EINVAL);
 		read_ahead[MAJOR(cdi->dev)] = arg;
 		RETURN_UP(0);
-#if 0
-	case CDROMEJECT:
-		msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
-		if (fam0_drive) return (0);
-		if (D_S[d].open_count>1) RETURN_UP(-EBUSY);
-		i=UnLockDoor();
-		D_S[d].open_count=-9; /* to get it locked next time again */
-		i=cc_SpinDown();
-		msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
-		msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
-		if (i<0) RETURN_UP(-EIO);
-		D_S[d].CD_changed=0xFF;
-		D_S[d].diskstate_flags=0;
-		D_S[d].audio_state=0;
-		RETURN_UP(0);
-
-	case CDROMEJECT_SW:
-		msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
-		if (fam0_drive) RETURN_UP(0);
-		D_S[d].f_eject=arg;
-		RETURN_UP(0);
-#endif		
 	default:
 		msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
 		RETURN_UP(-EINVAL);
@@ -5242,54 +5312,13 @@
 static int sbpcd_open(struct cdrom_device_info *cdi, int purpose)
 {
 	int i;
-	
+
 	i = MINOR(cdi->dev);
-	if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
-	{
-		msg(DBG_INF, "open: bad device: %04X\n", cdi->dev);
-		return (-ENXIO);             /* no such drive */
-	}
-	
+
 	MOD_INC_USE_COUNT;
 	down(&ioctl_read_sem);
 	switch_drive(i);
-	
-	i=cc_ReadError();
-	flags_cmd_out |= f_respo2;
-	cc_ReadStatus(); /* command: give 1-byte status */
-	i=ResponseStatus();
-	if (famT_drive&&(i<0))
-	{
-		cc_DriveReset();
-		i=ResponseStatus();
-#if 0
-                sbp_sleep(HZ);
-#endif 0
-		i=ResponseStatus();
-	}
-	if (i<0)
-	{
-		msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
-		MOD_DEC_USE_COUNT;
-		RETURN_UP(-EIO);                  /* drive doesn't respond */
-	}
-	if (famT_drive)	msg(DBG_TEA,"sbpcd_open: ResponseStatus=%02X\n", i);
-	if (!(famT_drive))
-		if (!st_spinning)
-		{
-			cc_SpinUp();
-			flags_cmd_out |= f_respo2;
-			cc_ReadStatus();
-			i=ResponseStatus();
-		}
-	if (famT_drive)	msg(DBG_TEA,"sbpcd_open: status %02X\n", D_S[d].status_bits);
-	if (!st_door_closed||!st_caddy_in)
-	{
-		msg(DBG_INF, "sbpcd_open: no disk in drive.\n");
-		D_S[d].open_count=0;
-		MOD_DEC_USE_COUNT;
-		RETURN_UP(-ENXIO);
-	}
+
 	/*
 	 * try to keep an "open" counter here and lock the door if 0->1.
 	 */
@@ -5360,24 +5389,6 @@
 /*
  *
  */
-#if 0
-static struct file_operations sbpcd_fops =
-{
-	NULL,                   /* lseek - default */
-	block_read,             /* read - general block-dev read */
-	block_write,            /* write - general block-dev write */
-	NULL,                   /* readdir - bad */
-	NULL,                   /* poll */
-	sbpcd_ioctl,            /* ioctl */
-	NULL,                   /* mmap */
-	sbpcd_open,             /* open */
-	sbpcd_release,          /* release */
-	NULL,                   /* fsync */
-	NULL,                   /* fasync */
-	sbpcd_chk_disk_change,  /* media_change */
-	NULL                    /* revalidate */
-};
-#endif
 static int sbpcd_media_changed( struct cdrom_device_info *cdi, int disc_nr);
 static struct cdrom_device_ops sbpcd_dops = {
   sbpcd_open,                   /* open */
@@ -5394,7 +5405,7 @@
   sbpcd_audio_ioctl,            /* audio ioctl */
   sbpcd_dev_ioctl,              /* device-specific ioctl */
   CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_MULTI_SESSION |
-    CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO, /* capability */
+    CDC_MEDIA_CHANGED | CDC_MCN | CDC_PLAY_AUDIO | CDC_IOCTLS, /* capability */
   1,                            /* number of minor devices */
 };
 
@@ -5556,7 +5567,7 @@
 	int i=0, j=0;
 	int addr[2]={1, CDROM_PORT};
 	int port_index;
-	
+
 	sti();
 	
 	msg(DBG_INF,"sbpcd.c %s\n", VERSION);
@@ -5630,14 +5641,6 @@
 	check_datarate();
 	msg(DBG_INI,"check_datarate done.\n");
 	
-#if 0
-	if (!famL_drive)
-	{
-		OUT(CDo_reset,0);
-		sbp_sleep(HZ);
-	}
-#endif 0
-
 	for (j=0;j<NR_SBPCD;j++)
 	{
 		if (D_S[j].drv_id==-1) continue;
@@ -5830,11 +5833,6 @@
 	
 	msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev));
 	i=MINOR(full_dev);
-	if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) )
-	{
-		msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev);
-		return (-1);
-	}
 	
 	if (D_S[i].CD_changed==0xFF)
         {
diff -u --recursive --new-file v2.1.77/linux/drivers/cdrom/sbpcd.h linux/drivers/cdrom/sbpcd.h
--- v2.1.77/linux/drivers/cdrom/sbpcd.h	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/sbpcd.h	Mon Jan  5 00:06:26 1998
@@ -119,14 +119,14 @@
 #define JUKEBOX 0
 #else
 #define JUKEBOX 1
-#endif DISTRIBUTION
+#endif /* DISTRIBUTION */
 
 /* tray control: eject tray after last use */
 #if DISTRIBUTION
 #define EJECT 0
 #else
 #define EJECT 1
-#endif DISTRIBUTION
+#endif /* DISTRIBUTION */
 
 /* max. number of audio frames to read with one     */
 /* request (allocates n* 2352 bytes kernel memory!) */
@@ -570,7 +570,7 @@
                                      resume (pr=80) audio playing
 
 Mode Select:
-         84 00 nn-nn ??-?? 00   (0)  nn-nn: 2048 or 2340
+         84 00 nn-nn ??.?? 00   (0)  nn-nn: 2048 or 2340
                                      possibly defines transfer size
 
 set_vol: 84 83 00 00 sw le 00.  (0)  sw(itch): lrxxxxxx (off=1)
@@ -604,12 +604,12 @@
 seek:    01 02 mm-ss-ff 00 00.  (0)  
 
 Read Data:
-read:    02 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2048 bytes,
+read:    02 xx-xx-xx nn-nn fl.  (?)  read nn-nn blocks of 2048 bytes,
                                      starting at block xx-xx-xx  
                                      fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
 
 Read XA-Data:
-read:    03 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2340 bytes, 
+read:    03 xx-xx-xx nn-nn fl.  (?)  read nn-nn blocks of 2340 bytes, 
                                      starting at block xx-xx-xx
                                      fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
 
@@ -838,7 +838,7 @@
 
 /*==========================================================================*/
 /*==========================================================================*/
-#endif _LINUX_SBPCD_H
+#endif /* _LINUX_SBPCD_H */
 /*==========================================================================*/
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -u --recursive --new-file v2.1.77/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
--- v2.1.77/linux/drivers/char/bttv.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/bttv.c	Sun Jan  4 10:40:15 1998
@@ -1712,6 +1712,12 @@
 
 	memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template));
 	idcard(btv);
+	
+	btv->picture.brightness=0x90<<8;
+	btv->picture.contrast = 0x70 << 8;
+	btv->picture.colour = 0x70<<8;
+	btv->picture.hue = 0x8000;
+	
 	if(video_register_device(&btv->video_dev)<0)
 		return -1;
 	return 0;
diff -u --recursive --new-file v2.1.77/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c
--- v2.1.77/linux/drivers/char/bw-qcam.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/bw-qcam.c	Sun Jan  4 10:41:52 1998
@@ -1,8 +1,6 @@
 /*
  *    QuickCam Driver For Video4Linux.
  *
- *	This version only works as a module.
- *
  *	Video4Linux conversion work by Alan Cox.
  */
 
@@ -297,7 +295,7 @@
 
 	/* Be liberal in what you accept...  */
 
-	if (count > 30 && count < 200)
+	if (count > 20 && count < 250)
 		return 1;	/* found */
 	else
 		return 0;	/* not found */
@@ -744,6 +742,7 @@
 			qcam->bpp = p.depth;
 			
 			qc_setscanmode(qcam);
+			qc_set(qcam);
 			return 0;
 		}
 		case VIDIOCSWIN:
@@ -842,6 +841,8 @@
 };
 
 
+#ifdef MODULE
+
 int io=0x378;
 
 MODULE_PARM(io,"i");
@@ -859,6 +860,7 @@
 	if(qc_detect(qcam)==0)
 	{
 		kfree(qcam);
+		printk(KERN_ERR "bw_qcam: No quickcam detected at 0x%03X\n", io);
 		return -ENODEV;
 	}
 	qc_calibrate(qcam);
@@ -875,3 +877,35 @@
 	video_unregister_device(&qcam->vdev);
 	kfree(qcam);
 }
+
+#else
+
+void init_bw_qcams(void)
+{
+	int io_ports[3]={0x278,0x378, 0x3BC};
+	struct qcam_device *qcam;
+	int i;
+	
+	for(i=0;i<3;i++)
+	{
+		qcam=qcam_init(io_ports[i]);
+		if(qcam==NULL)
+			continue;
+		
+		qc_reset(qcam);
+	
+		if(qc_detect(qcam)==0)
+		{
+			kfree(qcam);
+			continue;
+		}
+		qc_calibrate(qcam);
+	
+		printk(KERN_INFO "Connectix Quickcam at 0x%03X\n", qcam->port);
+	
+		if(video_register_device(&qcam->vdev)==-1)
+			return -ENODEV;
+	}
+}
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/drivers/char/ftape/compressor/Makefile linux/drivers/char/ftape/compressor/Makefile
--- v2.1.77/linux/drivers/char/ftape/compressor/Makefile	Wed Nov 26 16:24:01 1997
+++ linux/drivers/char/ftape/compressor/Makefile	Mon Jan  5 01:41:01 1998
@@ -38,11 +38,6 @@
 
 M_OBJS    = $(O_TARGET)
 
-include $(TOPDIR)/Rules.make
-
-#
-# sorry, a special rule. 
-#
-lzrw3.o: lzrw3.c
-	$(CC) $(CFLAGS) -O6 -funroll-all-loops -c $<
+CFLAGS_lzrw3.o	:= -O6 -funroll-all-loops
 
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.77/linux/drivers/char/pms.c linux/drivers/char/pms.c
--- v2.1.77/linux/drivers/char/pms.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/char/pms.c	Sun Jan  4 10:40:15 1998
@@ -612,7 +612,7 @@
 	int dw = 2*dev->width;
 	char *src = (char *)bus_to_virt((void *)mem_base);
 
-	char tmp[dw+16]; /* using a temp buffer is faster than direct  */
+	char tmp[dw+32]; /* using a temp buffer is faster than direct  */
 	int cnt = 0;
 	int len=0;
 	unsigned char r8 = 0x5;  /* value for reg8  */
@@ -626,7 +626,7 @@
 	for (y = 0; y < dev->height; y++ ) 
 	{
 		*src = 0;  /* synchronisiert neue Zeile */
-		memcpy(tmp, src, dw+16); /* discard 8 word   */
+		memcpy(tmp, src, dw+32); /* discard 16 word   */
 		cnt -= dev->height;
 		while (cnt <= 0) 
 		{ 
@@ -637,7 +637,7 @@
 			if(dt+len>count)
 				dt=count-len;
 			cnt += dev->height;
-			copy_to_user(buf, tmp+16, dt);
+			copy_to_user(buf, tmp+32, dt);
 			buf += dt;    
 			len += dt;
 		}
@@ -814,10 +814,10 @@
 			 *	Now load the card.
 			 */
 
-			pms_brightness(p.brightness);
-			pms_hue(p.hue);
-			pms_colour(p.colour);
-			pms_contrast(p.contrast);	
+			pms_brightness(p.brightness>>8);
+			pms_hue(p.hue>>8);
+			pms_colour(p.colour>>8);
+			pms_contrast(p.contrast>>8);	
 			return 0;
 		}
 		case VIDIOCSWIN:
diff -u --recursive --new-file v2.1.77/linux/drivers/char/rocket.c linux/drivers/char/rocket.c
--- v2.1.77/linux/drivers/char/rocket.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/char/rocket.c	Sun Jan  4 10:55:08 1998
@@ -637,7 +637,10 @@
 {
 	unsigned cflag;
 	unsigned long 	flags;
-	int	i, bits, baud;
+	int	bits, baud;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+	int i;
+#endif
 	CHANNEL_t	*cp;
 	
 	if (!info->tty || !info->tty->termios)
diff -u --recursive --new-file v2.1.77/linux/drivers/isdn/hisax/isdnl1.h linux/drivers/isdn/hisax/isdnl1.h
--- v2.1.77/linux/drivers/isdn/hisax/isdnl1.h	Thu May 29 21:53:06 1997
+++ linux/drivers/isdn/hisax/isdnl1.h	Sun Jan  4 10:55:08 1998
@@ -44,7 +44,7 @@
 extern void hscx_sched_event(struct HscxState *hsp, int event);
 extern void isac_sched_event(struct IsdnCardState *sp, int event);
 extern void isac_new_ph(struct IsdnCardState *sp);
-extern get_irq(int cardnr, void *routine);
+extern int get_irq(int cardnr, void *routine);
 
 #ifdef L2FRAME_DEBUG
 extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
diff -u --recursive --new-file v2.1.77/linux/drivers/isdn/sc/interrupt.c linux/drivers/isdn/sc/interrupt.c
--- v2.1.77/linux/drivers/isdn/sc/interrupt.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/sc/interrupt.c	Sun Jan  4 10:55:08 1998
@@ -32,7 +32,7 @@
 #include "message.h"
 #include "card.h"
 
-extern indicate_status(int, int, ulong, char *);
+extern int indicate_status(int, int, ulong, char *);
 extern void check_phystat(unsigned long);
 extern void dump_messages(int);
 extern int receivemessage(int, RspMessage *);
diff -u --recursive --new-file v2.1.77/linux/drivers/isdn/sc/message.c linux/drivers/isdn/sc/message.c
--- v2.1.77/linux/drivers/isdn/sc/message.c	Thu Feb 27 10:57:30 1997
+++ linux/drivers/isdn/sc/message.c	Sun Jan  4 10:55:08 1998
@@ -33,6 +33,7 @@
 #include "hardware.h"
 #include "message.h"
 #include "card.h"
+#include <asm/io.h>
 
 extern board *adapter[];
 extern unsigned int cinst;
@@ -202,7 +203,7 @@
 	 * wait for an empty slot in the queue
 	 */
 	while (!(inb(adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
-		SLOW_DOWN_IO;
+		__SLOW_DOWN_IO;
 
 	/*
 	 * Disable interrupts and map in shared memory
diff -u --recursive --new-file v2.1.77/linux/drivers/net/3c503.c linux/drivers/net/3c503.c
--- v2.1.77/linux/drivers/net/3c503.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/3c503.c	Sun Jan  4 10:55:08 1998
@@ -76,7 +76,7 @@
 static void el2_reset_8390(struct device *dev);
 static void el2_init_card(struct device *dev);
 static void el2_block_output(struct device *dev, int count,
-			     const unsigned char *buf, const start_page);
+			     const unsigned char *buf, int start_page);
 static void el2_block_input(struct device *dev, int count, struct sk_buff *skb,
 			   int ring_offset);
 static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
@@ -432,7 +432,7 @@
  */
 static void
 el2_block_output(struct device *dev, int count,
-		 const unsigned char *buf, const start_page)
+		 const unsigned char *buf, int start_page)
 {
     unsigned short int *wrd;
     int boguscount;		/* timeout counter */
diff -u --recursive --new-file v2.1.77/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v2.1.77/linux/drivers/net/3c59x.c	Fri Dec 19 15:52:58 1997
+++ linux/drivers/net/3c59x.c	Sun Jan  4 10:55:08 1998
@@ -20,9 +20,9 @@
 /* "Knobs" that adjust features and parameters. */
 /* Set the copy breakpoint for the copy-only-tiny-frames scheme.
    Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
+static const int rx_copybreak = 200;
 /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const mtu = 1500;
+static const int mtu = 1500;
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
 
diff -u --recursive --new-file v2.1.77/linux/drivers/net/CONFIG linux/drivers/net/CONFIG
--- v2.1.77/linux/drivers/net/CONFIG	Tue Dec 23 16:30:59 1997
+++ linux/drivers/net/CONFIG	Mon Jan  5 01:41:01 1998
@@ -74,17 +74,6 @@
 # The 8390 drivers share the EI_DEBUG setting.
 
 # General options for Space.c
-OPTS		= # -DETH0_ADDR=0x300 -DETH0_IRQ=11
-
-WD_OPTS		= #-DWD_SHMEM=0xDD000
-EL2_OPTS	= #-DEL2_AUI
-NE_OPTS		=
-HP_OPTS		=
-PLIP_OPTS	=
-DEPCA_OPTS	= 
-EWRK3_OPTS	= 
-DE4X5_OPTS	= 
-DEFXX_OPTS	=
-ELP_OPTS	=
-TULIP_OPTS	=
-CS89x0_OPTS	=
+CONFIG_Space.o	= # -DETH0_ADDR=0x300 -DETH0_IRQ=11
+CONFIG_3c503.o	= # -DEL2_AUI
+CONFIG_wd.o	= # -DWD_SHMEM=0xDD000
diff -u --recursive --new-file v2.1.77/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.77/linux/drivers/net/Config.in	Fri Jan  2 14:37:02 1998
+++ linux/drivers/net/Config.in	Sun Jan  4 10:40:16 1998
@@ -15,7 +15,7 @@
 tristate 'Dummy net driver support' CONFIG_DUMMY
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_NETLINK_DEV" != "n" ]; then
+  if [ "$CONFIG_NETLINK_DEV" = "y" -o "$CONFIG_NETLINK_DEV" = "m" ]; then
      dep_tristate 'Ethertap network tap' CONFIG_ETHERTAP $CONFIG_NETLINK_DEV
   fi
 fi
diff -u --recursive --new-file v2.1.77/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.77/linux/drivers/net/Makefile	Tue Dec 23 16:30:59 1997
+++ linux/drivers/net/Makefile	Mon Jan  5 01:41:01 1998
@@ -735,80 +735,5 @@
 clean:
 	rm -f core *.o *.a *.s
 
-wd.o:	wd.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(WD_OPTS) -c $<
-
-3c503.o:	3c503.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $<
-
-3c505.o: 3c505.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(ELP_OPTS) -c $<
-
-de4x5.o: de4x5.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DE4X5_OPTS) -c $<
-
-ewrk3.o: ewrk3.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(EWRK3_OPTS) -c $<
-
-depca.o: depca.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DEPCA_OPTS) -c $<
-
-Space.o: Space.c ../../include/linux/autoconf.h CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(OPTS) -c $<
-
-net_init.o: ../../include/linux/autoconf.h
-
-ne.o:	ne.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(NE_OPTS) -c $<
-
-hp.o:	hp.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
-
-plip.o:	plip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
-
-slip.o:	slip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-strip.o: strip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-dummy.o: dummy.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-de600.o: de600.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DE600_OPTS) -c $<
-
-de620.o: de620.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(DE620_OPTS) -c $<
-
-lance.o: lance.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(LANCE_OPTS) -c $<
-
-8390.o: 8390.c 8390.h CONFIG
-
-sdla.o: sdla.c CONFIG 
-
-dlci.o: dlci.c CONFIG
-
-sdladrv.o: sdladrv.c CONFIG
-
 wanpipe.o: $(WANPIPE_OBJS)
 	ld -r -o $@ $(WANPIPE_OBJS)
-
-sdlamain.o: sdlamain.c CONFIG
-
-sdla_x25.o: sdla_x25.c CONFIG
-
-sdla_fr.o: sdla_fr.c CONFIG
-
-sdla_ppp.o: sdla_ppp.c CONFIG
-
-dgrs.o: dgrs.c dgrs.h CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-ltpc.o: ltpc.c ltpc.h CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
-
-tulip.o: tulip.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(TULIP_OPTS) -c $<
diff -u --recursive --new-file v2.1.77/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v2.1.77/linux/drivers/net/dgrs.c	Wed Nov 12 13:34:26 1997
+++ linux/drivers/net/dgrs.c	Sun Jan  4 10:55:08 1998
@@ -406,7 +406,7 @@
 		 */
 		udelay(1);
 
-		csr = (volatile) priv->vplxdma[PLX_DMA_CSR/4];
+		csr = (volatile unsigned long) priv->vplxdma[PLX_DMA_CSR/4];
 
                 if (csr & PLX_DMA_CSR_0_DONE)
                         break;
@@ -871,7 +871,7 @@
 			/* Wait for old command to finish */
 			for (i = 0; i < 1000; ++i)
 			{
-				if ( (volatile) privN->bcomm->bc_filter_cmd <= 0 )
+				if ( (volatile long) privN->bcomm->bc_filter_cmd <= 0 )
 					break;
 				udelay(1);
 			}
diff -u --recursive --new-file v2.1.77/linux/drivers/net/e2100.c linux/drivers/net/e2100.c
--- v2.1.77/linux/drivers/net/e2100.c	Wed Dec 10 11:12:43 1997
+++ linux/drivers/net/e2100.c	Sun Jan  4 10:55:08 1998
@@ -103,7 +103,7 @@
 static void e21_block_input(struct device *dev, int count,
 						   struct sk_buff *skb, int ring_offset);
 static void e21_block_output(struct device *dev, int count,
-							 const unsigned char *buf, const start_page);
+							 const unsigned char *buf, int start_page);
 static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 							int ring_page);
 
diff -u --recursive --new-file v2.1.77/linux/drivers/net/es3210.c linux/drivers/net/es3210.c
--- v2.1.77/linux/drivers/net/es3210.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/es3210.c	Sun Jan  4 10:55:08 1998
@@ -71,7 +71,7 @@
 
 static void es_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
 static void es_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset);
-static void es_block_output(struct device *dev, int count, const unsigned char *buf, const start_page);
+static void es_block_output(struct device *dev, int count, const unsigned char *buf, int start_page);
 
 #define ES_START_PG	0x00    /* First page of TX buffer		*/
 #define ES_STOP_PG	0x40    /* Last page +1 of RX ring		*/
diff -u --recursive --new-file v2.1.77/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- v2.1.77/linux/drivers/net/eth16i.c	Fri Dec 19 15:52:58 1997
+++ linux/drivers/net/eth16i.c	Sun Jan  4 10:55:08 1998
@@ -97,6 +97,7 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/delay.h>
 
 /* Few macros */
 #define BIT(a)		        ( (1 << (a)) )
@@ -724,7 +725,7 @@
 	creg[0] &= 0x0F;      /* Mask collision cnr */
 	creg[2] &= 0x7F;      /* Mask DCLEN bit */
 
-#ifdef 0
+#if 0
 /*
 	This was removed because the card was sometimes left to state
   	from which it couldn't be find anymore. If there is need
diff -u --recursive --new-file v2.1.77/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in
--- v2.1.77/linux/drivers/net/hamradio/Config.in	Fri Dec 19 15:52:59 1997
+++ linux/drivers/net/hamradio/Config.in	Sun Jan  4 10:40:16 1998
@@ -26,7 +26,7 @@
 #     tristate 'Serial port 6PACK driver' CONFIG_6PACK
       tristate 'BPQ Ethernet driver' CONFIG_BPQETHER
     
-      tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25
+      dep_tristate 'High-speed (DMA) SCC driver for AX.25' CONFIG_DMASCC $CONFIG_AX25
       tristate 'Z8530 SCC driver' CONFIG_SCC
       if [ "$CONFIG_SCC" != "n" ]; then
         bool '   additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
diff -u --recursive --new-file v2.1.77/linux/drivers/net/hamradio/Makefile linux/drivers/net/hamradio/Makefile
--- v2.1.77/linux/drivers/net/hamradio/Makefile	Thu Dec  4 14:53:55 1997
+++ linux/drivers/net/hamradio/Makefile	Sun Jan  4 10:40:16 1998
@@ -21,6 +21,14 @@
 CONFIG_HDLCDRV_BUILTIN :=
 CONFIG_HDLCDRV_MODULE  :=
 
+ifeq ($(CONFIG_DMASCC),y)
+L_OBJS += dmascc.o
+else
+  ifeq ($(CONFIG_DMASCC),m)
+  M_OBJS += dmascc.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCC),y)
 L_OBJS += scc.o
 else
diff -u --recursive --new-file v2.1.77/linux/drivers/net/hamradio/dmascc.c linux/drivers/net/hamradio/dmascc.c
--- v2.1.77/linux/drivers/net/hamradio/dmascc.c	Fri Dec 19 15:52:59 1997
+++ linux/drivers/net/hamradio/dmascc.c	Sun Jan  4 10:40:16 1998
@@ -1,5 +1,5 @@
 /*
- * $Id: dmascc.c,v 1.2 1997/12/02 16:49:49 oe1kib Exp $
+ * $Id: dmascc.c,v 1.2.1.3 1997/12/19 13:40:15 oe1kib Exp $
  *
  * Driver for high-speed SCC boards (those with DMA support)
  * Copyright (C) 1997 Klaus Kudielka
@@ -21,11 +21,11 @@
 
 
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <linux/dmascc.h>
 #include <linux/errno.h>
 #include <linux/if_arp.h>
 #include <linux/in.h>
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
@@ -40,12 +40,56 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/segment.h>
-#include <asm/uaccess.h>
 #include <net/ax25.h>
 #include <stdio.h>
 #include "z8530.h"
 
 
+/* Linux 2.0 compatibility */
+
+#if LINUX_VERSION_CODE < 0x20100
+
+
+#define __init
+#define __initdata
+#define __initfunc(x) x
+
+#define MODULE_AUTHOR(x)
+#define MODULE_DESCRIPTION(x)
+#define MODULE_PARM(x,y)
+
+#define copy_to_user(x,y,z) memcpy_tofs(x,y,z)
+#define copy_from_user(x,y,z) memcpy_fromfs(x,y,z)
+#define test_and_set_bit(x,y) set_bit(x,y)
+#define register_netdevice(x) register_netdev(x)
+#define unregister_netdevice(x) unregister_netdev(x)
+
+static int dmascc_dev_init(struct device *dev)
+{
+  return 0;
+}
+
+static void dev_init_buffers(struct device *dev)
+{
+  int i;
+
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+    skb_queue_head_init(&dev->buffs[i]);
+}
+
+
+#else
+
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#define dmascc_dev_init NULL
+
+
+#endif
+
+
 /* Number of buffers per channel */
 
 #define NUM_TX_BUF      2          /* NUM_TX_BUF >= 1 (2 recommended) */
@@ -89,7 +133,7 @@
 #define SCCA_CMD        0x02
 #define SCCA_DATA       0x03
 
-/* 8254 registers relative to card base */
+/* 8253/8254 registers relative to card base */
 #define TMR_CNT0        0x00
 #define TMR_CNT1        0x01
 #define TMR_CNT2        0x02
@@ -265,7 +309,7 @@
     /* Unregister devices */
     for (i = 0; i < 2; i++) {
       if (info->dev[i].name)
-	unregister_netdev(&info->dev[i]);
+	unregister_netdevice(&info->dev[i]);
     }
 
     /* Reset board */
@@ -301,14 +345,19 @@
 
 __initfunc(int dmascc_init(void))
 {
-  int h, i, j, n, base[MAX_NUM_DEVS], tcmd, t0, t1, status;
-  unsigned long time, start[MAX_NUM_DEVS], stop[MAX_NUM_DEVS];
+  int h, i, j, n;
+  int base[MAX_NUM_DEVS], tcmd[MAX_NUM_DEVS], t0[MAX_NUM_DEVS],
+    t1[MAX_NUM_DEVS];
+  unsigned t_val;
+  unsigned long time, start[MAX_NUM_DEVS], delay[MAX_NUM_DEVS],
+    counting[MAX_NUM_DEVS];
 
   /* Initialize random number generator */
   rand = jiffies;
-
   /* Cards found = 0 */
   n = 0;
+  /* Warning message */
+  if (!io[0]) printk("dmascc: autoprobing (dangerous)\n");
 
   /* Run autodetection for each card type */
   for (h = 0; h < NUM_TYPES; h++) {
@@ -320,63 +369,66 @@
 	j = (io[i] - hw[h].io_region) / hw[h].io_delta;
 	if (j >= 0 &&
 	    j < hw[h].num_devs && 
-	    hw[h].io_region + j * hw[h].io_delta == io[i])
+	    hw[h].io_region + j * hw[h].io_delta == io[i]) {
 	  base[j] = io[i];
+	}
       }
     } else {
       /* Default I/O address regions */
-      for (i = 0; i < hw[h].num_devs; i++)
+      for (i = 0; i < hw[h].num_devs; i++) {
 	base[i] = hw[h].io_region + i * hw[h].io_delta;
+      }
     }
 
     /* Check valid I/O address regions */
     for (i = 0; i < hw[h].num_devs; i++)
-      if (base[i] && check_region(base[i], hw[h].io_size))
-	base[i] = 0;
+      if (base[i])
+	if (check_region(base[i], hw[h].io_size))
+	  base[i] = 0;
+	else {
+	  tcmd[i] = base[i] + hw[h].tmr_offset + TMR_CTRL;
+	  t0[i]   = base[i] + hw[h].tmr_offset + TMR_CNT0;
+	  t1[i]   = base[i] + hw[h].tmr_offset + TMR_CNT1;
+	}
 
     /* Start timers */
     for (i = 0; i < hw[h].num_devs; i++)
       if (base[i]) {
-	tcmd = base[i] + hw[h].tmr_offset + TMR_CTRL;
-	t0   = base[i] + hw[h].tmr_offset + TMR_CNT0;
-	t1   = base[i] + hw[h].tmr_offset + TMR_CNT1;
 	/* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */
-	outb_p(0x36, tcmd);
-	outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0);
-	outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0);
+	outb_p(0x36, tcmd[i]);
+	outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0[i]);
+	outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0[i]);
 	/* Timer 1: LSB+MSB, Mode 0, HZ/10 */
-	outb_p(0x70, tcmd);
-	outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1);
-	outb_p((TMR_0_HZ/HZ*10) >> 8, t1);
+	outb_p(0x70, tcmd[i]);
+	outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1[i]);
+	outb_p((TMR_0_HZ/HZ*10) >> 8, t1[i]);
+	start[i] = jiffies;
+	delay[i] = 0;
+	counting[i] = 1;
 	/* Timer 2: LSB+MSB, Mode 0 */
-	outb_p(0xb0, tcmd);
+	outb_p(0xb0, tcmd[i]);
       }
-
-    /* Initialize start values in case we miss the null count bit */
     time = jiffies;
-    for (i = 0; i < hw[h].num_devs; i++) start[i] = time;
+    /* Wait until counter registers are loaded */
+    udelay(2000000/TMR_0_HZ);
 
     /* Timing loop */
-    while (jiffies - time < 12) {
+    while (jiffies - time < 13) {
       for (i = 0; i < hw[h].num_devs; i++)
-	if (base[i]) {
-	  /* Read back Timer 1: Status */
-	  outb_p(0xE4, base[i] + hw[h].tmr_offset + TMR_CTRL);
-	  status = inb_p(base[i] + hw[h].tmr_offset + TMR_CNT1);
-	  if ((status & 0x3F) != 0x30) base[i] = 0;
-	  if (status & 0x40) start[i] = jiffies;
-	  if (~status & 0x80) stop[i] = jiffies;
+	if (base[i] && counting[i]) {
+	  /* Read back Timer 1: latch; read LSB; read MSB */
+	  outb_p(0x40, tcmd[i]);
+	  t_val = inb_p(t1[i]) + (inb_p(t1[i]) << 8);
+	  /* Also check whether counter did wrap */
+	  if (t_val == 0 || t_val > TMR_0_HZ/HZ*10) counting[i] = 0;
+	  delay[i] = jiffies - start[i];
 	}
     }
 
     /* Evaluate measurements */
     for (i = 0; i < hw[h].num_devs; i++)
       if (base[i]) {
-	time = stop[i] - start[i];
-	if (time < 9 || time > 11)
-	  /* The time expired doesn't match */
-	  base[i] = 0;
-	else {
+	if (delay[i] >= 9 && delay[i] <= 11) {
 	  /* Ok, we have found an adapter */
 	  if (setup_adapter(base[i], h, n) == 0)
 	    n++;
@@ -523,6 +575,7 @@
     dev->hard_header = ax25_encapsulate;
     dev->rebuild_header = ax25_rebuild_header;
     dev->set_mac_address = scc_set_mac_address;
+    dev->init = dmascc_dev_init;
     dev->type = ARPHRD_AX25;
     dev->hard_header_len = 73;
     dev->mtu = 1500;
@@ -530,9 +583,8 @@
     dev->tx_queue_len = 64;
     memcpy(dev->broadcast, ax25_broadcast, 7);
     memcpy(dev->dev_addr, ax25_test, 7);
-    dev->flags = 0;
     dev_init_buffers(dev);
-    if (register_netdev(dev)) {
+    if (register_netdevice(dev)) {
       printk("dmascc: could not register %s\n", dev->name);
       dev->name = NULL;
     }
diff -u --recursive --new-file v2.1.77/linux/drivers/net/hp-plus.c linux/drivers/net/hp-plus.c
--- v2.1.77/linux/drivers/net/hp-plus.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/hp-plus.c	Sun Jan  4 10:55:08 1998
@@ -103,13 +103,13 @@
 static void hpp_mem_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void hpp_mem_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						  int ring_page);
 static void hpp_io_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void hpp_io_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						  int ring_page);
 
@@ -385,7 +385,7 @@
    It's always safe to round up, so we do. */
 static void
 hpp_io_block_output(struct device *dev, int count,
-					const unsigned char *buf, const start_page)
+					const unsigned char *buf, int start_page)
 {
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
@@ -395,7 +395,7 @@
 
 static void
 hpp_mem_block_output(struct device *dev, int count,
-				const unsigned char *buf, const start_page)
+				const unsigned char *buf, int start_page)
 {
 	int ioaddr = dev->base_addr - NIC_OFFSET;
 	int option_reg = inw(ioaddr + HPP_OPTION);
diff -u --recursive --new-file v2.1.77/linux/drivers/net/hp.c linux/drivers/net/hp.c
--- v2.1.77/linux/drivers/net/hp.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/hp.c	Sun Jan  4 10:55:08 1998
@@ -66,7 +66,7 @@
 static void hp_block_input(struct device *dev, int count,
 					struct sk_buff *skb , int ring_offset);
 static void hp_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 
 static void hp_init_card(struct device *dev);
 
@@ -309,7 +309,7 @@
 
 static void
 hp_block_output(struct device *dev, int count,
-				const unsigned char *buf, const start_page)
+				const unsigned char *buf, int start_page)
 {
 	int nic_base = dev->base_addr;
 	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
diff -u --recursive --new-file v2.1.77/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.77/linux/drivers/net/plip.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/plip.c	Sun Jan  4 10:24:27 1998
@@ -12,11 +12,11 @@
  *		Modularization and ifreq/ifmap support by Alan Cox.
  *		Rewritten by Niibe Yutaka.
  *              parport-sharing awareness code by Philip Blundell.
+ *		SMP locking by Niibe Yutaka.
  *
  * Fixes:
  *		Niibe Yutaka
- *		  - Module initialization.  You can specify I/O addr and IRQ:
- *			# insmod plip.o io=0x3bc irq=7
+ *		  - Module initialization.
  *		  - MTU fix.
  *		  - Make sure other end is OK, before sending a packet.
  *		  - Fix immediate timer problem.
@@ -45,7 +45,7 @@
  *     To use with DOS box, please do (Turn on ARP switch):
  *	# ifconfig plip[0-2] arp
  */
-static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n";
+static const char *version = "NET3 PLIP version 2.3-parport gniibe@mri.co.jp\n";
 
 /*
   Sources:
@@ -108,6 +108,7 @@
 #include <asm/bitops.h>
 #include <asm/irq.h>
 #include <asm/byteorder.h>
+#include <asm/spinlock.h>
 
 #include <linux/parport.h>
 
@@ -214,6 +215,7 @@
 	int port_owner;
 	int should_relinquish;
 	int (*orig_rebuild_header)(struct sk_buff *skb);
+	spinlock_t lock;
 };
 
 /* Entry point of PLIP driver.
@@ -238,7 +240,7 @@
 		printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name);
 		return -ENODEV;
 	}
-	
+
 	pardev = parport_register_device(pb, dev->name, plip_preempt,
 					 plip_wakeup, plip_interrupt, 
 					 PARPORT_DEV_LURK, dev);
@@ -291,6 +293,7 @@
 	nl->deferred.sync = 0;
 	nl->deferred.routine = (void *)(void *)plip_kick_bh;
 	nl->deferred.data = dev;
+	spin_lock_init(&nl->lock);
 
 	return 0;
 }
@@ -367,7 +370,7 @@
 {
 	unsigned char c0;
 
-	cli();
+	spin_lock_irq(&nl->lock);
 	if (nl->connection == PLIP_CN_SEND) {
 
 		if (error != ERROR) { /* Timeout */
@@ -375,7 +378,7 @@
 			if ((snd->state == PLIP_PK_TRIGGER
 			     && nl->timeout_count <= 10)
 			    || nl->timeout_count <= 3) {
-				sti();
+				spin_unlock_irq(&nl->lock);
 				/* Try again later */
 				return TIMEOUT;
 			}
@@ -388,12 +391,12 @@
 	} else if (nl->connection == PLIP_CN_RECEIVE) {
 		if (rcv->state == PLIP_PK_TRIGGER) {
 			/* Transmission was interrupted. */
-			sti();
+			spin_unlock_irq(&nl->lock);
 			return OK;
 		}
 		if (error != ERROR) { /* Timeout */
 			if (++nl->timeout_count <= 3) {
-				sti();
+				spin_unlock_irq(&nl->lock);
 				/* Try again later */
 				return TIMEOUT;
 			}
@@ -413,12 +416,13 @@
 		dev_kfree_skb(snd->skb, FREE_WRITE);
 		snd->skb = NULL;
 	}
+	spin_unlock_irq(&nl->lock);
 	disable_irq(dev->irq);
+	synchronize_irq();
 	outb(PAR_INTR_OFF, PAR_CONTROL(dev));
 	dev->tbusy = 1;
 	nl->connection = PLIP_CN_ERROR;
 	outb(0x00, PAR_DATA(dev));
-	sti();
 
 	return TIMEOUT;
 }
@@ -493,6 +497,7 @@
 	switch (rcv->state) {
 	case PLIP_PK_TRIGGER:
 		disable_irq(dev->irq);
+		/* Don't need to synchronize irq, as we can safely ignore it */
 		outb(PAR_INTR_OFF, PAR_CONTROL(dev));
 		dev->interrupt = 0;
 		outb(0x01, PAR_DATA(dev)); /* send ACK */
@@ -577,10 +582,10 @@
 
 		/* Close the connection. */
 		outb (0x00, PAR_DATA(dev));
-		cli();
+		spin_lock_irq(&nl->lock);
 		if (snd->state != PLIP_PK_DONE) {
 			nl->connection = PLIP_CN_SEND;
-			sti();
+			spin_unlock_irq(&nl->lock);
 			queue_task(&nl->immediate, &tq_immediate);
 			mark_bh(IMMEDIATE_BH);
 			outb(PAR_INTR_ON, PAR_CONTROL(dev));
@@ -588,7 +593,7 @@
 			return OK;
 		} else {
 			nl->connection = PLIP_CN_NONE;
-			sti();
+			spin_unlock_irq(&nl->lock);
 			outb(PAR_INTR_ON, PAR_CONTROL(dev));
 			enable_irq(dev->irq);
 			return OK;
@@ -673,28 +678,34 @@
 		cx = nl->trigger;
 		while (1) {
 			udelay(PLIP_DELAY_UNIT);
-			cli();
+			spin_lock_irq(&nl->lock);
 			if (nl->connection == PLIP_CN_RECEIVE) {
-				sti();
-				/* interrupted */
+				spin_unlock_irq(&nl->lock);
+				/* Interrupted. */
 				nl->enet_stats.collisions++;
-				if (net_debug > 1)
-					printk("%s: collision.\n", dev->name);
 				return OK;
 			}
 			c0 = inb(PAR_STATUS(dev));
 			if (c0 & 0x08) {
+				spin_unlock_irq(&nl->lock);
 				disable_irq(dev->irq);
+				synchronize_irq();
+				if (nl->connection == PLIP_CN_RECEIVE) {
+					/* Interrupted.
+					   We don't need to enable irq,
+					   as it is soon disabled.    */
+					nl->enet_stats.collisions++;
+					return OK;
+				}
 				outb(PAR_INTR_OFF, PAR_CONTROL(dev));
 				if (net_debug > 2)
 					printk("%s: send start\n", dev->name);
 				snd->state = PLIP_PK_LENGTH_LSB;
 				snd->nibble = PLIP_NB_BEGIN;
 				nl->timeout_count = 0;
-				sti();
 				break;
 			}
-			sti();
+			spin_unlock_irq(&nl->lock);
 			if (--cx == 0) {
 				outb(0x00, data_addr);
 				return TIMEOUT;
@@ -755,13 +766,13 @@
 plip_connection_close(struct device *dev, struct net_local *nl,
 		      struct plip_local *snd, struct plip_local *rcv)
 {
-	cli();
+	spin_lock_irq(&nl->lock);
 	if (nl->connection == PLIP_CN_CLOSING) {
 		nl->connection = PLIP_CN_NONE;
 		dev->tbusy = 0;
 		mark_bh(NET_BH);
 	}
-	sti();
+	spin_unlock_irq(&nl->lock);
 	if (nl->should_relinquish) {
 		nl->should_relinquish = nl->port_owner = 0;
 		parport_release(nl->pardev);
@@ -805,7 +816,7 @@
 	unsigned char c0;
 
 	if (dev == NULL) {
-		printk ("plip_interrupt: irq %d for unknown device.\n", irq);
+		printk("plip_interrupt: irq %d for unknown device.\n", irq);
 		return;
 	}
 
@@ -825,7 +836,7 @@
 	if (net_debug > 3)
 		printk("%s: interrupt.\n", dev->name);
 
-	cli();
+	spin_lock_irq(&nl->lock);
 	switch (nl->connection) {
 	case PLIP_CN_CLOSING:
 		dev->tbusy = 0;
@@ -837,16 +848,18 @@
 		nl->timeout_count = 0;
 		queue_task(&nl->immediate, &tq_immediate);
 		mark_bh(IMMEDIATE_BH);
-		sti();
+		spin_unlock_irq(&nl->lock);
 		break;
 
 	case PLIP_CN_RECEIVE:
-		sti();
-		printk("%s: receive interrupt when receiving packet\n", dev->name);
+		/* May occur because there is race condition
+		   around test and set of dev->interrupt.
+		   Ignore this interrupt. */
+		spin_unlock_irq(&nl->lock);
 		break;
 
 	case PLIP_CN_ERROR:
-		sti();
+		spin_unlock_irq(&nl->lock);
 		printk("%s: receive interrupt in error state\n", dev->name);
 		break;
 	}
@@ -898,7 +911,7 @@
 	if (net_debug > 2)
 		printk("%s: send request\n", dev->name);
 
-	cli();
+	spin_lock_irq(&nl->lock);
 	dev->trans_start = jiffies;
 	snd->skb = skb;
 	snd->length.h = skb->len;
@@ -909,7 +922,7 @@
 	}
 	queue_task(&nl->immediate, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
-	sti();
+	spin_unlock_irq(&nl->lock);
 
 	return 0;
 }
@@ -946,24 +959,25 @@
 	nl->connection = PLIP_CN_NONE;
 	nl->is_deferred = 0;
 
-	/* Fill in the MAC-level header. */
+	/* Fill in the MAC-level header.
+	   (ab)Use "dev->broadcast" to store point-to-point MAC address.
 
+	   PLIP doesn't have a real mac address, but we need to create one
+	   to be DOS compatible.  */
 	memset(dev->dev_addr,  0xfc, ETH_ALEN);
+	memset(dev->broadcast, 0xfc, ETH_ALEN);
 
-	/* Now PLIP doesnt have a real mac addr which is a pain..
-	   we need to create one, and to be DOS compatible its a good
-	   idea to use the same rules. Layering purists please look away */
-	
-	if((in_dev=dev->ip_ptr)!=NULL)
-	{
+	if ((in_dev=dev->ip_ptr) != NULL) {
 		/*
-		 *	Any address wil do - we take the first
+		 *	Any address will do - we take the first
 		 */
 		struct in_ifaddr *ifa=in_dev->ifa_list;
-		if(ifa!=NULL)
-			memcpy(dev->dev_addr+2,&ifa->ifa_local,4);
+		if (ifa != NULL) {
+			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
+			memcpy(dev->broadcast+2, &ifa->ifa_address, 4);
+		}
 	}
-	
+
 	dev->interrupt = 0;
 	dev->start = 1;
 	dev->tbusy = 0;
@@ -982,8 +996,9 @@
 
 	dev->tbusy = 1;
 	dev->start = 0;
-	cli();
-	sti();
+	disable_irq(dev->irq);
+	synchronize_irq();
+
 #ifdef NOTDEF
 	outb(0x00, PAR_DATA(dev));
 #endif
@@ -1070,15 +1085,21 @@
 static int
 plip_config(struct device *dev, struct ifmap *map)
 {
+	struct net_local *nl = (struct net_local *) dev->priv;
+	struct pardevice *pardev = nl->pardev;
+
 	if (dev->flags & IFF_UP)
 		return -EBUSY;
 
-	if (map->base_addr != (unsigned long)-1
-	    && map->base_addr != dev->base_addr)
-		printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+	printk(KERN_WARNING "plip: Warning, changing irq with ifconfig will be obsoleted.\n");
+	printk("plip: Next time, please set with /proc/parport/*/irq instead.\n");
 
-	if (map->irq != (unsigned char)-1)
-		dev->irq = map->irq;
+	if (map->irq != (unsigned char)-1) {
+		pardev->port->irq = dev->irq = map->irq;
+		/* Dummy request */
+		request_irq(dev->irq, plip_interrupt, SA_INTERRUPT,
+			    pardev->name, NULL);
+	}
 	return 0;
 }
 
@@ -1233,6 +1254,6 @@
 
 /*
  * Local variables:
- * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c plip.c"
+ * compile-command: "gcc -DMODULE -DMODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -c plip.c"
  * End:
  */
diff -u --recursive --new-file v2.1.77/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v2.1.77/linux/drivers/net/smc-ultra.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/smc-ultra.c	Sun Jan  4 10:55:08 1998
@@ -75,13 +75,13 @@
 static void ultra_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void ultra_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
 						int ring_page);
 static void ultra_pio_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void ultra_pio_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 static int ultra_close_card(struct device *dev);
 
 
@@ -385,7 +385,7 @@
 }
 
 static void ultra_pio_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page)
+							const unsigned char *buf, int start_page)
 {
 	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
 	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
diff -u --recursive --new-file v2.1.77/linux/drivers/net/tlan.c linux/drivers/net/tlan.c
--- v2.1.77/linux/drivers/net/tlan.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/tlan.c	Sun Jan  4 10:55:09 1998
@@ -2184,7 +2184,6 @@
 {
 	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv;
 	u16			gen_ctl;
-	int			i;
 	u32			io;
 	u16			phy;
 	u16			value;
diff -u --recursive --new-file v2.1.77/linux/drivers/net/wd.c linux/drivers/net/wd.c
--- v2.1.77/linux/drivers/net/wd.c	Mon Nov  3 13:04:26 1997
+++ linux/drivers/net/wd.c	Sun Jan  4 10:55:09 1998
@@ -55,7 +55,7 @@
 static void wd_block_input(struct device *dev, int count,
 						  struct sk_buff *skb, int ring_offset);
 static void wd_block_output(struct device *dev, int count,
-							const unsigned char *buf, const start_page);
+							const unsigned char *buf, int start_page);
 static int wd_close_card(struct device *dev);
 
 
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.77/linux/drivers/scsi/Makefile	Fri Jan  2 14:37:02 1998
+++ linux/drivers/scsi/Makefile	Mon Jan  5 01:41:01 1998
@@ -13,8 +13,9 @@
 MOD_LIST_NAME := SCSI_MODULES
 SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c))
 
-AHA152X	= -DDEBUG_AHA152X -DAUTOCONF
-GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
+CFLAGS_aha152x.o =   -DDEBUG_AHA152X -DAUTOCONF
+CFLAGS_gdth.o    = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS
+CFLAGS_seagate.o =   -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY
 
 .SUFFIXES:
 .SUFFIXES: .c .o .h .a
@@ -455,23 +456,13 @@
 
 include $(TOPDIR)/Rules.make
 
-BusLogic.o: BusLogic.c FlashPoint.c
+# This gives correct output but uses old-style "excessive compilation".
+# This will be fixed soon (about December 1997 or January 1998).
+BusLogic.o: BusLogic.c FlashPoint.c ../../include/linux/autoconf.h
 	$(CC) $(CFLAGS) -c BusLogic.c -o BusLogic.O
 	$(CC) $(CFLAGS) -c FlashPoint.c -o FlashPoint.O
 	$(LD) -r -o BusLogic.o BusLogic.O FlashPoint.O
 	rm -f BusLogic.O FlashPoint.O
-
-aha152x.o: aha152x.c
-	$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
-
-gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h
-	$(CC) $(CFLAGS) $(GDTH) -c gdth.c 
-
-aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
-	$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
-
-seagate.o: seagate.c
-	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -DPARITY -c seagate.c 
 
 53c8xx_d.h 53c8xx_u.h : 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake.c
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.1.77/linux/drivers/scsi/aha1542.c	Sun Dec 21 22:36:14 1997
+++ linux/drivers/scsi/aha1542.c	Sun Jan  4 10:55:09 1998
@@ -133,8 +133,8 @@
 #define aha1542_intr_reset(base)  outb(IRST, CONTROL(base))
 
 #define WAIT(port, mask, allof, noneof)					\
- { register WAITbits;							\
-   register WAITtimeout = WAITnexttimeout;				\
+ { register int WAITbits;						\
+   register int WAITtimeout = WAITnexttimeout;				\
    while (1) {								\
      WAITbits = inb(port) & (mask);					\
      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
@@ -146,8 +146,8 @@
 /* Similar to WAIT, except we use the udelay call to regulate the
    amount of time we wait.  */
 #define WAITd(port, mask, allof, noneof, timeout)			\
- { register WAITbits;							\
-   register WAITtimeout = timeout;					\
+ { register int WAITbits;						\
+   register int WAITtimeout = timeout;					\
    while (1) {								\
      WAITbits = inb(port) & (mask);					\
      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.77/linux/drivers/scsi/aic7xxx.c	Thu Dec  4 14:53:56 1997
+++ linux/drivers/scsi/aic7xxx.c	Sun Jan  4 10:55:09 1998
@@ -6953,7 +6953,7 @@
   channel = cmd->channel ? 'B': 'A';
   tindex = TARGET_INDEX(cmd);
 
-#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
+#if 0   /* AIC7XXX_DEBUG_ABORT */
   if (scb != NULL)
   {
     printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h
--- v2.1.77/linux/drivers/scsi/dc390.h	Sun Dec 21 22:36:14 1997
+++ linux/drivers/scsi/dc390.h	Mon Jan  5 16:10:47 1998
@@ -65,7 +65,7 @@
 
 #define DC390_T    {			          \
    proc_dir:       &proc_scsi_tmscsim,            \
-   proc_info:      tmscsim_proc_info
+   proc_info:      tmscsim_proc_info,		  \
    name:           "Tekram DC390(T) V1.10 Dec-05-1996",\
    detect:         DC390_detect,   		  \
    release:        DC390_release,		  \
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c
--- v2.1.77/linux/drivers/scsi/pas16.c	Wed Apr 23 19:01:22 1997
+++ linux/drivers/scsi/pas16.c	Sun Jan  4 10:55:09 1998
@@ -516,7 +516,7 @@
     register unsigned char  *d = dst;
     register unsigned short reg = (unsigned short) (instance->io_port + 
 	P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
@@ -552,7 +552,7 @@
     int len) {
     register unsigned char *s = src;
     register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
-    register i = len;
+    register int i = len;
     int ii = 0;
 
     while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.77/linux/drivers/scsi/scsi.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/scsi.c	Sun Jan  4 10:55:09 1998
@@ -607,7 +607,7 @@
             {
                 SCSI_LOG_SCAN_BUS(4,printk("%p ", scmd));
             }
-                SCSI_LOG_SCAN_BUS(4,printk("\n", scmd));
+            SCSI_LOG_SCAN_BUS(4,printk("\n"));
         }
     }
 }
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- v2.1.77/linux/drivers/scsi/scsi.h	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/scsi.h	Mon Jan  5 16:10:47 1998
@@ -617,8 +617,8 @@
 #define SCSI_MLQUEUE_HOST_BUSY   0x1055
 #define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
 
-extern scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason);
-extern scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device);
+extern int scsi_mlqueue_insert(Scsi_Cmnd * cmd, int reason);
+extern int scsi_mlqueue_finish(struct Scsi_Host * host, Scsi_Device * device);
 
 
 #if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/sd_ioctl.c linux/drivers/scsi/sd_ioctl.c
--- v2.1.77/linux/drivers/scsi/sd_ioctl.c	Sun Dec 21 22:36:15 1997
+++ linux/drivers/scsi/sd_ioctl.c	Sun Jan  4 10:40:16 1998
@@ -104,6 +104,9 @@
 	
     case BLKRRPART: /* Re-read partition tables */
 	return revalidate_scsidisk(dev, 1);
+
+    RO_IOCTLS(dev, arg);
+
     default:
 	return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
     }
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/t128.c linux/drivers/scsi/t128.c
--- v2.1.77/linux/drivers/scsi/t128.c	Wed Apr 23 19:01:22 1997
+++ linux/drivers/scsi/t128.c	Sun Jan  4 10:55:09 1998
@@ -328,7 +328,7 @@
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
 	T_DATA_REG_OFFSET), *d = dst;
-    register i = len;
+    register int i = len;
 
 
 #if 0
@@ -372,7 +372,7 @@
     int len) {
     register unsigned char *reg = (unsigned char *) (instance->base + 
 	T_DATA_REG_OFFSET), *s = src;
-    register i = len;
+    register int i = len;
 
 #if 0
     for (; i; --i) {
diff -u --recursive --new-file v2.1.77/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.1.77/linux/drivers/scsi/wd7000.c	Mon Mar 17 14:54:30 1997
+++ linux/drivers/scsi/wd7000.c	Sun Jan  4 10:55:09 1998
@@ -473,7 +473,7 @@
 typedef struct icbUnsMask {     /* I'm totally guessing here */
   unchar op;
   volatile unchar mask[14];     /* mask bits */
-#ifdef 0
+#if 0
   unchar rsvd[12];              /* reserved */
 #endif
   volatile unchar vue;          /* vendor-unique error code */
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.77/linux/drivers/sound/Config.in	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/Config.in	Sun Jan  4 10:40:16 1998
@@ -149,7 +149,8 @@
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    dep_tristate 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER $CONFIG_SOUND
    dep_tristate 'AWE32 synth' CONFIG_AWE32_SYNTH $CONFIG_SOUND
-   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+   dep_tristate 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
    if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
       hex '  I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
    fi
@@ -193,6 +194,11 @@
 	    						AEDSP16_MPU_IRQ $MPU_IRQ
          fi
       fi
+   fi
+
+   if [ "$CONFIG_AEDSP16" = "y" -o "$CONFIG_AEDSP16" = "m" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
 
       if [ "$CONFIG_SC6600" = "y" ]; then
          comment 'SC-6600 specific configuration'
@@ -201,7 +207,6 @@
 	 					CONFIG_SC6600_CDROM 4
          hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
       fi
-
    fi
 fi
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.77/linux/drivers/sound/Makefile	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/Makefile	Sun Jan  4 10:40:16 1998
@@ -45,7 +45,7 @@
 else
   ifeq ($(CONFIG_SOUND),m)
      M_OBJS += sound.o
-     MX_OBJS += sound_syms.o
+     MIX_OBJS += sound_syms.o
   endif
 endif
 
@@ -242,7 +242,6 @@
 	ld -r -o sound.o soundcard.o dev_table.o audio.o dmabuf.o \
 	       sequencer.o sys_timer.o sound_timer.o lowlevel/lowlevel.o \
 	       midi_synth.o midibuf.o sound_firmware.o
-	rm sound_syms.o
 
 gus.o: gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
 	ld -r -o gus.o gus_card.o gus_midi.o gus_vol.o gus_wave.o ics2101.o
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.77/linux/drivers/sound/ad1848.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/ad1848.c	Mon Jan  5 09:39:52 1998
@@ -532,7 +532,7 @@
 	int val;
 
 	if (cmd == SOUND_MIXER_PRIVATE1) {
-		if (__get_user(val, (int *)arg))
+		if (get_user(val, (int *)arg))
 			return -EFAULT;
 
 		if (val != 0xffff) {
@@ -546,22 +546,22 @@
 				ad_write(devc, 26, ad_read(devc, 26) | 0x40);		/* Mute mono out */
 		}
 		val = devc->mixer_output_port;
-		return __put_user(val, (int *)arg);
+		return put_user(val, (int *)arg);
 	}
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
 			switch (cmd & 0xff) {
 			case SOUND_MIXER_RECSRC:
-				if (__get_user(val, (int *)arg))
+				if (get_user(val, (int *)arg))
 					return -EFAULT;
 				val = ad1848_set_recmask(devc, val);
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 				
 			    default:
-				if (__get_user(val, (int *)arg))
+				if (get_user(val, (int *)arg))
 					return -EFAULT;
 				val = ad1848_mixer_set(devc, cmd & 0xff, val);
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 			} 
 		else
 			switch (cmd & 0xff) {
@@ -571,28 +571,28 @@
 			    
 			case SOUND_MIXER_RECSRC:
 				val = devc->recmask;
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 				
 			case SOUND_MIXER_DEVMASK:
 				val = devc->supported_devices;
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 				
 			case SOUND_MIXER_STEREODEVS:
 				val = devc->supported_devices;
 				if (devc->model != MD_C930)
 					val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 				
 			case SOUND_MIXER_RECMASK:
 				val = devc->supported_rec_devices;
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 
 			case SOUND_MIXER_CAPS:
-				return __put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
+				return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg);
 
 			default:
 				val = ad1848_mixer_get(devc, cmd & 0xff);
-				return __put_user(val, (int *)arg);
+				return put_user(val, (int *)arg);
 			}
 	} else
 		return -EINVAL;
@@ -2566,7 +2566,6 @@
 	SOUND_LOCK_END;
 	if(loaded)
 		unload_ms_sound(&hw_config);
-/*      unregister_symtab(&ad1848_syms); */
 }
 
 #else
@@ -2574,7 +2573,6 @@
 void
 export_ad1848_syms(void)
 {
-	register_symtab(&ad1848_syms);
 }
 
 #endif
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.77/linux/drivers/sound/dev_table.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/sound/dev_table.c	Sun Jan  4 10:40:16 1998
@@ -73,9 +73,7 @@
 	{
 		extern void     sound_preinit_lowlevel_drivers(void);
 
-#ifdef FIXME
 		sound_preinit_lowlevel_drivers();
-#endif
 	}
 #endif
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.77/linux/drivers/sound/gus_wave.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/gus_wave.c	Mon Jan  5 13:37:03 1998
@@ -2834,117 +2834,124 @@
 
 int gus_default_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	int vol, voice, val;
+	int vol, val;
 
-	if (((cmd >> 8) & 0xff) == 'M') {
-		if (_SIOC_DIR(cmd) & _SIOC_WRITE)
-			switch (cmd & 0xff) {
-			case SOUND_MIXER_RECSRC:
-				if (__get_user(gus_recmask, (int *)arg))
-					return -EFAULT;
-				gus_recmask &= MIX_DEVS;
-				if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
-					gus_recmask = SOUND_MASK_MIC;
-				/* Note! Input volumes are updated during next open for recording */
-				return __put_user(gus_recmask, (int *)arg);
-
-			case SOUND_MIXER_MIC:
-				if (__get_user(vol, (int *)arg))
-					return -EFAULT;
-				vol &= 0xff;
-				if (vol < 0)
-					vol = 0;
-				if (vol > 100)
-					vol = 100;
-				gus_mic_vol = vol;
-				set_input_volumes();
-				vol |= (vol << 8);
-				return __put_user(vol, (int *)arg);
+	if (((cmd >> 8) & 0xff) != 'M')
+		return -EINVAL;
+
+	if (!access_ok(VERIFY_WRITE, (int *)arg, sizeof(int)))
+		return -EFAULT;
+
+	if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
+		if (__get_user(val, (int *) arg))
+			return -EFAULT;
+
+		switch (cmd & 0xff) {
+		case SOUND_MIXER_RECSRC:
+			gus_recmask = val & MIX_DEVS;
+			if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
+				gus_recmask = SOUND_MASK_MIC;
+			/* Note! Input volumes are updated during next open for recording */
+			val = gus_recmask;
+			break;
+
+		case SOUND_MIXER_MIC:
+			vol = val & 0xff;
+			if (vol < 0)
+				vol = 0;
+			if (vol > 100)
+				vol = 100;
+			gus_mic_vol = vol;
+			set_input_volumes();
+			val = vol | (vol << 8);
+			break;
 				
-			case SOUND_MIXER_LINE:
-				if (__get_user(vol, (int *)arg))
-					return -EFAULT;
-				vol &= 0xff;
-				if (vol < 0)
-					vol = 0;
-				if (vol > 100)
-					vol = 100;
-				gus_line_vol = vol;
-				set_input_volumes();
-				vol |= (vol << 8);
-				return __put_user(vol, (int *)arg);
-
-			case SOUND_MIXER_PCM:
-				if (__get_user(gus_pcm_volume, (int *)arg))
-					return -EFAULT;
-				gus_pcm_volume &= 0xff;
-				if (gus_pcm_volume < 0)
-					gus_pcm_volume = 0;
-				if (gus_pcm_volume > 100)
-					gus_pcm_volume = 100;
-				gus_audio_update_volume();
-				gus_pcm_volume |= (gus_pcm_volume << 8);
-				return __put_user(gus_pcm_volume, (int *)arg);
-
-			case SOUND_MIXER_SYNTH:
-				if (__get_user(gus_wave_volume, (int *)arg))
-					return -EFAULT;
-				gus_wave_volume &= 0xff;
-				if (gus_wave_volume < 0)
-					gus_wave_volume = 0;
-				if (gus_wave_volume > 100)
-					gus_wave_volume = 100;
-				if (active_device == GUS_DEV_WAVE)
-					for (voice = 0; voice < nr_voices; voice++)
-						dynamic_volume_change(voice);	/* Apply the new vol */
-				gus_wave_volume |= (gus_wave_volume << 8);
-				return __put_user(gus_wave_volume, (int *)arg);
-
-			default:
-				return -EINVAL;
-			} else
-				switch (cmd & 0xff) {
-					/*
-					 * Return parameters
-					 */
-				case SOUND_MIXER_RECSRC:
-					return __put_user(gus_recmask, (int *)arg);
+		case SOUND_MIXER_LINE:
+			vol = val & 0xff;
+			if (vol < 0)
+				vol = 0;
+			if (vol > 100)
+				vol = 100;
+			gus_line_vol = vol;
+			set_input_volumes();
+			val = vol | (vol << 8);
+			break;
+
+		case SOUND_MIXER_PCM:
+			gus_pcm_volume = val & 0xff;
+			if (gus_pcm_volume < 0)
+				gus_pcm_volume = 0;
+			if (gus_pcm_volume > 100)
+				gus_pcm_volume = 100;
+			gus_audio_update_volume();
+			val = gus_pcm_volume | (gus_pcm_volume << 8);
+			break;
+
+		case SOUND_MIXER_SYNTH:
+			gus_wave_volume = val & 0xff;
+			if (gus_wave_volume < 0)
+				gus_wave_volume = 0;
+			if (gus_wave_volume > 100)
+				gus_wave_volume = 100;
+			if (active_device == GUS_DEV_WAVE) {
+				int voice;
+				for (voice = 0; voice < nr_voices; voice++)
+					dynamic_volume_change(voice);	/* Apply the new vol */
+			}
+			val = gus_wave_volume | (gus_wave_volume << 8);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+
+	} else {
+		switch (cmd & 0xff) {
+		/*
+		 * Return parameters
+		 */
+		case SOUND_MIXER_RECSRC:
+			val = gus_recmask;
+			break;
 					
-				case SOUND_MIXER_DEVMASK:
-					return __put_user(MIX_DEVS, (int *)arg);
+		case SOUND_MIXER_DEVMASK:
+			val = MIX_DEVS;
+			break;
+
+		case SOUND_MIXER_STEREODEVS:
+			val = 0;
+			break;
+
+		case SOUND_MIXER_RECMASK:
+			val = SOUND_MASK_MIC | SOUND_MASK_LINE;
+			break;
+
+		case SOUND_MIXER_CAPS:
+			val = 0;
+			break;
+
+		case SOUND_MIXER_MIC:
+			val = gus_mic_vol | (gus_mic_vol << 8);
+			break;
+
+		case SOUND_MIXER_LINE:
+			val = gus_line_vol | (gus_line_vol << 8);
+			break;
+
+		case SOUND_MIXER_PCM:
+			val = gus_pcm_volume | (gus_pcm_volume << 8);
+			break;
+
+		case SOUND_MIXER_SYNTH:
+			val = gus_wave_volume | (gus_wave_volume << 8);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
 
-				case SOUND_MIXER_STEREODEVS:
-					return __put_user(0, (int *)arg);
-
-				case SOUND_MIXER_RECMASK:
-					val = SOUND_MASK_MIC | SOUND_MASK_LINE;
-					return __put_user(val, (int *)arg);
-
-				case SOUND_MIXER_CAPS:
-					return __put_user(0, (int *)arg);
-
-				case SOUND_MIXER_MIC:
-					val = gus_mic_vol | (gus_mic_vol << 8);
-					return __put_user(val, (int *)arg);
-
-				case SOUND_MIXER_LINE:
-					val = gus_line_vol | (gus_line_vol << 8);
-					return __put_user(val, (int *)arg);
-
-				case SOUND_MIXER_PCM:
-					val = ;
-					return __put_user(, (int *)arg);
-				    return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
-				    break;
-
-				case SOUND_MIXER_SYNTH:
-					return __put_user(gus_wave_volume | (gus_wave_volume << 8), (int *)arg);
-
-				default:
-					return -EINVAL;
-				}
-	} else
-		return -EINVAL;
+	return __put_user(val, (int *)arg);
 }
 
 static struct mixer_operations gus_mixer_operations =
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/ics2101.c linux/drivers/sound/ics2101.c
--- v2.1.77/linux/drivers/sound/ics2101.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/ics2101.c	Mon Jan  5 09:39:52 1998
@@ -118,11 +118,12 @@
 
 static int ics2101_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
+	int val;
+	
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
-			int val;
 			
-			if (__get_user(val, (int *)arg))
+			if (get_user(val, (int *)arg))
 				return -EFAULT;
 			switch (cmd & 0xff) {
 			case SOUND_MIXER_RECSRC:
@@ -151,7 +152,7 @@
 			default:
 				return -EINVAL;
 			}
-			return __put_user(val, (int *)arg);
+			return put_user(val, (int *)arg);
 		} else {
 			switch (cmd & 0xff) {
 				/*
@@ -199,7 +200,7 @@
 			default:
 				return -EINVAL;
 			}
-			return __put_user(val, (int *)arg);
+			return put_user(val, (int *)arg);
 		}
 	}
 	return -EINVAL;
@@ -220,7 +221,6 @@
 
 	if ((n = sound_alloc_mixerdev()) != -1)
 	{
-		n = num_mixers;
 		mixer_devs[n] = &ics2101_mixer_operations;
 
 		/*
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/lowlevel/Makefile linux/drivers/sound/lowlevel/Makefile
--- v2.1.77/linux/drivers/sound/lowlevel/Makefile	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/lowlevel/Makefile	Mon Jan  5 01:41:01 1998
@@ -16,15 +16,16 @@
 endif
 ifeq ($(CONFIG_AEDSP16),y)
    OBJS := $(OBJS) aedsp16.o
+else 
+  ifeq ($(CONFIG_AEDSP16),m)
+     MX_OBJS := $(MX_OBJS) aedsp16.o
+  endif
 endif
 endif
 
 ifndef TOPDIR
 TOPDIR=/usr/src/linux
 endif
-
-.c.o:
-	$(CC) $(CFLAGS) -c $<
 
 lowlevel.o:	$(OBJS)
 	$(LD) -r -o lowlevel.o $(OBJS)
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/lowlevel/README.aedsp16 linux/drivers/sound/lowlevel/README.aedsp16
--- v2.1.77/linux/drivers/sound/lowlevel/README.aedsp16	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/README.aedsp16	Sun Jan  4 10:40:16 1998
@@ -20,6 +20,37 @@
 If you want to change the configuration of the sound board, be sure to
 check off all the configuration items before re-configure it.
 
+Module parameters
+-----------------
+To use this driver as a module, you must configure some module parameters, to
+set up I/O addresses, IRQ lines and DMA channels. Some parameters are
+mandatory while some others are optional. Here a list of parameters you can
+use with this module:
+
+Name		Description
+====		===========
+MANDATORY
+io		I/O base address (0x220 or 0x240)
+irq		irq line (5, 7, 9, 10 or 11)
+dma		dma channel (0, 1 or 3)
+
+OPTIONAL
+mss_base	I/O base address for activate MSS mode (default SBPRO)
+		(0x530 or 0xE80)
+mpu_base	I/O base address for activate MPU-401 mode
+		(0x300, 0x310, 0x320 or 0x330)
+mpu_irq		MPU-401 irq line (5, 7, 9, 10 or 0)
+
+The /etc/conf.modules will have a line like this:
+
+options aedsp16 io=0x220 irq=10 dma=3 mss_base=0x530
+
+Of course, you must write the 'options' for all other subsequent modules,
+opl3, ad1848, adlib_card, sb
+
+Then you must load the sound modules stack in this order:
+sound -> aedsp16 -> [ ad1848, opl3 ]
+
 Sound cards supported
 ---------------------
 This driver supports the SC-6000 and SC-6600 based Gallant's sound card.
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/lowlevel/aedsp16.c linux/drivers/sound/lowlevel/aedsp16.c
--- v2.1.77/linux/drivers/sound/lowlevel/aedsp16.c	Wed Nov 12 13:34:27 1997
+++ linux/drivers/sound/lowlevel/aedsp16.c	Sun Jan  4 10:40:16 1998
@@ -1,8 +1,8 @@
 /*
-   sound/aedsp16.c
+   drivers/sound/lowlevel/aedsp16.c
 
    Audio Excel DSP 16 software configuration routines
-   Copyright (C) 1995  Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
+   Copyright (C) 1995,1996,1997,1998  Riccardo Facchetti (fizban@tin.it)
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -24,13 +24,24 @@
  * headers needed by this source.
  */
 #include <linux/config.h>
-#include "../sound_config.h"
 #include <linux/delay.h>
+#include <linux/module.h>
+#include "../sound_config.h"
+#include "../soundmodule.h"
+
+/*
+ * Sanity checks
+ */
 
-#ifndef AEDSP16_BASE
-#undef CONFIG_AEDSP16
+#if !defined(AEDSP16_BASE)
+# undef CONFIG_AEDSP16
+#else
+# if defined(MODULE) && defined(CONFIG_AEDSP16_MODULE)
+#  define CONFIG_AEDSP16 1
+# endif
 #endif
 
+
 #if defined(CONFIG_AEDSP16)
 
 #if defined(CONFIG_AEDSP16_SBPRO) && defined(CONFIG_AEDSP16_MSS)
@@ -232,6 +243,11 @@
    - Prep for merging with OSS Lite and Linux kernel 2.1.13
    - Corrected a bug in request/check/release region calls (thanks to the
      new kernel exception handling).
+   v1.1
+   - Revamped for integration with new modularized sound drivers: to enhance
+     the flexibility of modular version, I have removed all the conditional
+     compilation for SBPRO, MPU and MSS code. Now it is all managed with
+     the ae_config structure.
 
    Known Problems:
    - Audio Excel DSP 16 III don't work with this driver.
@@ -243,7 +259,7 @@
  */
 
 
-#define VERSION "1.0.0"		/* Version of Audio Excel DSP 16 driver */
+#define VERSION "1.1"		/* Version of Audio Excel DSP 16 driver */
 
 #undef	AEDSP16_DEBUG 1		/* Define this to enable debug code     */
 #undef	AEDSP16_DEBUG_MORE 1	/* Define this to enable more debug     */
@@ -439,6 +455,8 @@
 	int irq;                /* irq value for DSP I/O                */
 	int mpu_irq;            /* irq for mpu401 interface I/O         */
 	int dma;                /* dma value for DSP I/O                */
+	int mss_base;           /* base I/O for Microsoft Sound System  */
+	int mpu_base;           /* base I/O for MPU-401 emulation       */
 	int init;               /* Initialization status of the card    */
 };
 
@@ -477,6 +495,8 @@
 	DEF_AEDSP16_IRQ,
 	DEF_AEDSP16_MRQ,
 	DEF_AEDSP16_DMA,
+	-1,
+	-1,
 	INIT_NONE
 };
 
@@ -619,13 +639,9 @@
 /*
  * Now set up the real kernel configuration.
  */
-	decoded_hcfg.iobase	= AEDSP16_BASE;
-#if defined(CONFIG_AEDSP16_MSS)
-	decoded_hcfg.wssbase	= MSS_BASE;
-#endif
-#if defined(CONFIG_AEDSP16_MPU401)
-	decoded_hcfg.mpubase	= MPU_BASE;
-#endif
+	decoded_hcfg.iobase	= ae_config.base_io;
+	decoded_hcfg.wssbase	= ae_config.mss_base;
+	decoded_hcfg.mpubase	= ae_config.mpu_base;
 
 #if defined(CONFIG_SC6600_JOY)
  	decoded_hcfg.joystick	= CONFIG_SC6600_JOY; /* Enable */
@@ -805,8 +821,6 @@
 	return TRUE;
 }
 
-#if defined(CONFIG_AEDSP16_MSS)
-
 static int aedsp16_init_mss(int port)
 {
 	DBG(("aedsp16_init_mss:\n"));
@@ -824,15 +838,13 @@
 	if (aedsp16_cfg_write(port) == FALSE)
 		return FALSE;
 
-	outb(soft_cfg_1, MSS_BASE);
+	outb(soft_cfg_1, ae_config.mss_base);
 
 	DBG(("success.\n"));
 
 	return TRUE;
 }
 
-#endif /* CONFIG_AEDSP16_MSS */
-
 static int aedsp16_setup_board(int port) {
 	int	loop = RETRY;
 
@@ -1082,15 +1094,15 @@
 		return FALSE;
 	}
 
-#if defined(CONFIG_AEDSP16_MSS)
-	if (ae_config.init & INIT_MSS) {
-		if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
-			printk(
-		"[AEDSP16] Can not initialize Microsoft Sound System mode.\n");
-			return FALSE;
+	if (ae_config.mss_base != -1) {
+		if (ae_config.init & INIT_MSS) {
+			if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+				printk("[AEDSP16] Can not initialize"
+				       "Microsoft Sound System mode.\n");
+				return FALSE;
+			}
 		}
 	}
-#endif
 
 #if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
 
@@ -1098,27 +1110,26 @@
 		VERSION, DSPCopyright,
 		DSPVersion);
 
-#if defined(CONFIG_AEDSP16_MPU401)
-	if (ae_config.init & INIT_MPU401) {
-		printk("MPU401");
-		if ((ae_config.init & INIT_MSS) ||
-		    (ae_config.init & INIT_SBPRO))
-			printk(" ");
+	if (ae_config.mpu_base != -1) {
+		if (ae_config.init & INIT_MPU401) {
+			printk("MPU401");
+			if ((ae_config.init & INIT_MSS) ||
+			    (ae_config.init & INIT_SBPRO))
+				printk(" ");
+		}
 	}
-#endif
 
-#if defined(CONFIG_AEDSP16_SBPRO)
-	if (ae_config.init & INIT_SBPRO) {
-		printk("SBPro");
-		if (ae_config.init & INIT_MSS)
-			printk(" ");
+	if (ae_config.mss_base == -1) {
+		if (ae_config.init & INIT_SBPRO) {
+			printk("SBPro");
+			if (ae_config.init & INIT_MSS)
+				printk(" ");
+		}
 	}
-#endif
 
-#if defined(CONFIG_AEDSP16_MSS)
-	if (ae_config.init & INIT_MSS)
-		printk("MSS");
-#endif
+	if (ae_config.mss_base != -1)
+		if (ae_config.init & INIT_MSS)
+			printk("MSS");
 
 	printk("]\n");
 #endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
@@ -1128,8 +1139,6 @@
 	return TRUE;
 }
 
-#if defined(CONFIG_AEDSP16_SBPRO)
-
 static int init_aedsp16_sb(void)
 {
 	DBG(("init_aedsp16_sb: "));
@@ -1158,9 +1167,6 @@
 
 	DBG(("done.\n"));
 }
-#endif
-
-#if defined(CONFIG_AEDSP16_MSS)
 
 static int init_aedsp16_mss(void)
 {
@@ -1214,9 +1220,6 @@
 	ae_config.init &= ~INIT_MSS;
 	DBG(("done.\n"));
 }
-#endif
-
-#if defined(CONFIG_AEDSP16_MPU401)
 
 static int init_aedsp16_mpu(void)
 {
@@ -1262,43 +1265,45 @@
 
 	DBG(("done.\n"));
 }
-#endif
 
 int init_aedsp16(void)
 {
 	int initialized = FALSE;
 
+#if !defined(MODULE)
 	ae_config.base_io = AEDSP16_BASE;
 #if defined(CONFIG_AEDSP16_SBPRO)
 	ae_config.irq = AEDSP16_SBC_IRQ;
 	ae_config.dma = AEDSP16_SBC_DMA;
 #endif
 #if defined(CONFIG_AEDSP16_MSS)
+	ae_config.mss_base = MSS_BASE;
 	ae_config.irq = AEDSP16_MSS_IRQ;
 	ae_config.dma = AEDSP16_MSS_DMA;
 #endif
 #if defined(CONFIG_AEDSP16_MPU401)
+	ae_config.mpu_base = MPU_BASE;
 	ae_config.mpu_irq = AEDSP16_MPU_IRQ;
 #endif
-
+#endif /* !MODULE */
 	DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
 	     ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
 
-#if defined(CONFIG_AEDSP16_SBPRO)
-	if (init_aedsp16_sb() == FALSE)
-		uninit_aedsp16_sb();
-	else
-		initialized = TRUE;
-#endif
-
-#if defined(CONFIG_AEDSP16_MPU401)
-	if (init_aedsp16_mpu() == FALSE)
-		uninit_aedsp16_mpu();
-	else
-		initialized = TRUE;
-#endif
+	if (ae_config.mss_base == -1) {
+		if (init_aedsp16_sb() == FALSE) {
+			uninit_aedsp16_sb();
+		} else {
+			initialized = TRUE;
+		}
+	}
 
-#if defined(CONFIG_AEDSP16_MSS)
+	if (ae_config.mpu_base != -1) {
+		if (init_aedsp16_mpu() == FALSE) {
+			uninit_aedsp16_mpu();
+		} else {
+			initialized = TRUE;
+		}
+	}
 
 /*
  * In the sequence of init routines, the MSS init MUST be the last!
@@ -1306,11 +1311,13 @@
  * A board reset would disable the MSS mode restoring the default SBPRO
  * mode.
  */
-	if (init_aedsp16_mss() == FALSE)
-		uninit_aedsp16_mss();
-	else
-		initialized = TRUE;
-#endif
+	if (ae_config.mss_base != -1) {
+		if (init_aedsp16_mss() == FALSE) {
+			uninit_aedsp16_mss();
+		} else {
+			initialized = TRUE;
+		}
+	}
 
 	if (initialized)
 		initialized = aedsp16_init_board();
@@ -1319,16 +1326,62 @@
 
 void uninit_aedsp16(void)
 {
-#if defined(CONFIG_AEDSP16_MSS)
-	uninit_aedsp16_mss();
-#endif
+	if (ae_config.mss_base != -1)
+		uninit_aedsp16_mss();
+	else
+		uninit_aedsp16_sb();
+	if (ae_config.mpu_base != -1)
+		uninit_aedsp16_mpu();
+}
 
-#if defined(CONFIG_AEDSP16_SBPRO)
-	uninit_aedsp16_sb();
-#endif
+#if defined(MODULE)
 
-#if defined(CONFIG_AEDSP16_MPU401)
-	uninit_aedsp16_mpu();
-#endif
+int io = -1;
+int irq = -1;
+int dma = -1;
+int mpu_irq = -1;
+int mss_base = -1;
+int mpu_base = -1;
+
+
+MODULE_PARM(io,"i");
+MODULE_PARM(irq,"i");
+MODULE_PARM(dma,"i");
+MODULE_PARM(mpu_irq,"i");
+MODULE_PARM(mss_base,"i");
+MODULE_PARM(mpu_base,"i");
+
+int init_module(void) {
+	printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
+	if (io == -1 || dma == -1 || irq == -1) {
+		printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
+		return -EINVAL;
+	}
+
+	ae_config.base_io = io;
+	ae_config.irq = irq;
+	ae_config.dma = dma;
+
+	ae_config.mss_base = mss_base;
+	ae_config.mpu_base = mpu_base;
+	ae_config.mpu_irq = mpu_irq;
+
+	if (init_aedsp16() == FALSE) {
+		printk(KERN_ERR "aedsp16: initialization failed\n");
+		/*
+		 * XXX
+		 * What error should we return here ?
+		 */
+		return -EINVAL;
+	}
+	SOUND_LOCK;
+	return 0;
+}
+
+void cleanup_module(void) {
+	uninit_aedsp16();
+	SOUND_LOCK_END;
 }
-#endif				/* CONFIG_AEDSP16 */
+#endif /* MODULE */
+
+#endif /* CONFIG_AEDSP16 */
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/lowlevel/awe_version.h linux/drivers/sound/lowlevel/awe_version.h
--- v2.1.77/linux/drivers/sound/lowlevel/awe_version.h	Sat Nov 29 11:25:11 1997
+++ linux/drivers/sound/lowlevel/awe_version.h	Sun Jan  4 10:40:16 1998
@@ -10,15 +10,4 @@
 #define AWE_TINY_VERSION(id)	((id) & 0xff)
 
 #endif
-/* AWE32 driver version number */
 
-#ifndef AWE_VERSION_H_DEF
-#define AWE_VERSION_H_DEF
-
-#define AWE_VERSION_NUMBER	0x00040202
-#define AWEDRV_VERSION		"0.4.2b"
-#define AWE_MAJOR_VERSION(id)	(((id) >> 16) & 0xff)
-#define AWE_MINOR_VERSION(id)	(((id) >> 8) & 0xff)
-#define AWE_TINY_VERSION(id)	((id) & 0xff)
-
-#endif
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/lowlevel/init.c linux/drivers/sound/lowlevel/init.c
--- v2.1.77/linux/drivers/sound/lowlevel/init.c	Tue Mar  4 10:25:25 1997
+++ linux/drivers/sound/lowlevel/init.c	Sun Jan  4 10:40:16 1998
@@ -28,7 +28,7 @@
 void
 sound_preinit_lowlevel_drivers(void)
 {
-#ifdef CONFIG_AEDSP16
+#if defined(CONFIG_AEDSP16) && !defined(MODULE)
    init_aedsp16();
 #endif
 }
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.77/linux/drivers/sound/os.h	Wed Nov 12 13:34:27 1997
+++ linux/drivers/sound/os.h	Mon Jan  5 10:13:36 1998
@@ -50,6 +50,8 @@
 extern void sound_free_dma(int chn);
 extern void sound_close_dma(int chn);
 
+extern void reprogram_timer(void);
+
 #define RUNTIME_DMA_ALLOC
 #define USE_AUTOINIT_DMA
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c
--- v2.1.77/linux/drivers/sound/pas2_mixer.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/pas2_mixer.c	Mon Jan  5 09:50:22 1998
@@ -216,11 +216,11 @@
 
 static int pas_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
-	int level, v;
+	int level,v ;
 
 	DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 	if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
-		if (__get_user(level, (int *)arg))
+		if (get_user(level, (int *)arg))
 			return -EFAULT;
 		if (level == -1)  /* Return current settings */
 			level = (mode_control & 0x04);
@@ -231,10 +231,10 @@
 			set_mode(mode_control);
 		}
 		level = !!level;
-		return __put_user(level, (int *)arg);
+		return put_user(level, (int *)arg);
 	}
 	if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
-		if (__get_user(level, (int *)arg))
+		if (get_user(level, (int *)arg))
 			return -EFAULT;
 		if (level == -1) { /* Return current settings */
 			if (!(mode_control & 0x03))
@@ -254,13 +254,13 @@
 				i = (i + 1) * 20;
 			level = i;
 		}
-		return __put_user(level, (int *)arg);
+		return put_user(level, (int *)arg);
 	}
 	if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
-		if (__get_user(level, (int *)arg))
+		if (get_user(level, (int *)arg))
 			return -EFAULT;
 		if (level == -1)	/* Return current settings */
-			level = = !(pas_read(0x0B8A) & 0x20);
+			level = !(pas_read(0x0B8A) & 0x20);
 		else {
 			if (level)
 				pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
@@ -269,10 +269,10 @@
 
 			level = !(pas_read(0x0B8A) & 0x20);
 		}
-		return __put_user(level, (int *)arg);
+		return put_user(level, (int *)arg);
 	}
 	if (((cmd >> 8) & 0xff) == 'M') {
-		if (__get_user(v, (int *)arg))
+		if (get_user(v, (int *)arg))
 			return -EFAULT;
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 			v = pas_mixer_set(cmd & 0xff, v);
@@ -303,7 +303,7 @@
 				break;
 			}
 		}
-		return __put_user(v, (int *)arg);
+		return put_user(v, (int *)arg);
 	}
 	return -EINVAL;
 }
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.77/linux/drivers/sound/sb_audio.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/sb_audio.c	Sun Jan  4 10:40:16 1998
@@ -982,7 +982,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	sb1_audio_prepare_for_input,
 	sb1_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
@@ -1002,7 +1002,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	sb1_audio_prepare_for_input,
 	sb1_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
@@ -1022,7 +1022,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	sbpro_audio_prepare_for_input,
 	sbpro_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
@@ -1042,7 +1042,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	sbpro_audio_prepare_for_input,
 	sbpro_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
@@ -1062,7 +1062,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	sb16_audio_prepare_for_input,
 	sb16_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
@@ -1082,7 +1082,7 @@
 	sb_audio_close,
 	sb_set_output_parms,
 	sb_set_input_parms,
-	sb_audio_ioctl,
+	NULL,
 	ess_audio_prepare_for_input,
 	ess_audio_prepare_for_output,
 	sb1_audio_halt_xfer,
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.1.77/linux/drivers/sound/sb_mixer.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/sb_mixer.c	Mon Jan  5 09:39:52 1998
@@ -293,14 +293,14 @@
 	 * Use ioctl(fd, SOUND_MIXER_PRIVATE1, &mode) to turn AGC off (0) or on (1).
 	 */
 	if (cmd == SOUND_MIXER_PRIVATE1 && devc->model == MDL_SB16) {
-		if (__get_user(val, (int *)arg))
+		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		sb_setmixer(devc, 0x43, (~val) & 0x01);
 		return 0;
 	}
 	if (((cmd >> 8) & 0xff) == 'M') {
 		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
-			if (__get_user(val, (int *)arg))
+			if (get_user(val, (int *)arg))
 				return -EFAULT;
 			switch (cmd & 0xff) {
 			case SOUND_MIXER_RECSRC:
@@ -338,7 +338,7 @@
 				  ret = sb_mixer_get(devc, cmd & 0xff);
 				  break;
 			  }
-		return __put_user(ret, (int *)arg); 
+		return put_user(ret, (int *)arg); 
 	} else
 		return -EINVAL;
 }
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.77/linux/drivers/sound/sequencer.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/sequencer.c	Sun Jan  4 10:40:16 1998
@@ -12,6 +12,7 @@
  */
 /*
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox	   : reformatted and fixed a pair of null pointer bugs
  */
 #include <linux/config.h>
 
@@ -89,12 +90,11 @@
 #error Too many synthesizer devices enabled.
 #endif
 
-int
-sequencer_read(int dev, struct fileinfo *file, char *buf, int count)
+int sequencer_read(int dev, struct fileinfo *file, char *buf, int count)
 {
-	int             c = count, p = 0;
-	int             ev_len;
-	unsigned long   flags;
+	int c = count, p = 0;
+	int ev_len;
+	unsigned long flags;
 
 	dev = dev >> 4;
 
@@ -103,70 +103,65 @@
 	save_flags(flags);
 	cli();
 	if (!iqlen)
-	  {
-		  if ((file->flags & (O_NONBLOCK) ?
-		       1 : 0))
-		    {
-			    restore_flags(flags);
-			    return -EAGAIN;
-		    }
-		  {
-			  unsigned long   tlimit;
-
-			  if (pre_event_timeout)
-				  current->timeout = tlimit = jiffies + (pre_event_timeout);
-			  else
-				  tlimit = (unsigned long) -1;
-			  midi_sleep_flag.opts = WK_SLEEP;
-			  interruptible_sleep_on(&midi_sleeper);
-			  if (!(midi_sleep_flag.opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    midi_sleep_flag.opts |= WK_TIMEOUT;
-			    }
-			  midi_sleep_flag.opts &= ~WK_SLEEP;
-		  };
-
-		  if (!iqlen)
-		    {
-			    restore_flags(flags);
-			    return 0;
-		    }
-	  }
-	while (iqlen && c >= ev_len)
-	  {
+	{
+		if ((file->flags & (O_NONBLOCK) ? 1 : 0))
+		{
+			restore_flags(flags);
+			return -EAGAIN;
+		}
+		{
+			unsigned long   tlimit;
 
-		  {
-			  char           *fixit = (char *) &iqueue[iqhead * IEV_SZ];
+			if (pre_event_timeout)
+				current->timeout = tlimit = jiffies + (pre_event_timeout);
+			else
+				tlimit = (unsigned long) -1;
+			midi_sleep_flag.opts = WK_SLEEP;
+			interruptible_sleep_on(&midi_sleeper);
+			if (!(midi_sleep_flag.opts & WK_WAKEUP))
+			{
+				if (jiffies >= tlimit)
+					midi_sleep_flag.opts |= WK_TIMEOUT;
+			}
+			midi_sleep_flag.opts &= ~WK_SLEEP;
+		};
+
+		if (!iqlen)
+		{
+			restore_flags(flags);
+			return 0;
+		}
+	}
+	while (iqlen && c >= ev_len)
+	{
+		{
+			char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
+			copy_to_user(&(buf)[p], fixit, ev_len);
+		};
+		p += ev_len;
+		c -= ev_len;
 
-			  copy_to_user(&(buf)[p], fixit, ev_len);
-		  };
-		  p += ev_len;
-		  c -= ev_len;
-
-		  iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
-		  iqlen--;
-	  }
+		iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
+		iqlen--;
+	}
 	restore_flags(flags);
 
 	return count - c;
 }
 
-static void
-sequencer_midi_output(int dev)
+static void sequencer_midi_output(int dev)
 {
 	/*
 	 * Currently NOP
 	 */
 }
 
-void
-seq_copy_to_input(unsigned char *event_rec, int len)
+void seq_copy_to_input(unsigned char *event_rec, int len)
 {
 	unsigned long   flags;
 
 	/*
-	   * Verify that the len is valid for the current mode.
+	 * Verify that the len is valid for the current mode.
 	 */
 
 	if (len != 4 && len != 8)
@@ -184,20 +179,17 @@
 	iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
 
 	if ((midi_sleep_flag.opts & WK_SLEEP))
-	  {
-		  {
-			  midi_sleep_flag.opts = WK_WAKEUP;
-			  wake_up(&midi_sleeper);
-		  };
-	  }
+	{
+		midi_sleep_flag.opts = WK_WAKEUP;
+		wake_up(&midi_sleeper);
+	}
 	restore_flags(flags);
 }
 
-static void
-sequencer_midi_input(int dev, unsigned char data)
+static void sequencer_midi_input(int dev, unsigned char data)
 {
-	unsigned int    tstamp;
-	unsigned char   event_rec[4];
+	unsigned int tstamp;
+	unsigned char event_rec[4];
 
 	if (data == 0xfe)	/* Ignore active sensing */
 		return;
@@ -208,12 +200,12 @@
 		tstamp = jiffies - seq_time;
 
 	if (tstamp != prev_input_time)
-	  {
-		  tstamp = (tstamp << 8) | SEQ_WAIT;
+	{
+		tstamp = (tstamp << 8) | SEQ_WAIT;
 
-		  seq_copy_to_input((unsigned char *) &tstamp, 4);
-		  prev_input_time = tstamp;
-	  }
+		seq_copy_to_input((unsigned char *) &tstamp, 4);
+		prev_input_time = tstamp;
+	}
 	event_rec[0] = SEQ_MIDIPUTC;
 	event_rec[1] = data;
 	event_rec[2] = dev;
@@ -222,10 +214,9 @@
 	seq_copy_to_input(event_rec, 4);
 }
 
-void
-seq_input_event(unsigned char *event_rec, int len)
+void seq_input_event(unsigned char *event_rec, int len)
 {
-	unsigned long   this_time;
+	unsigned long this_time;
 
 	if (seq_mode == SEQ_2)
 		this_time = tmr->get_time(tmr_no);
@@ -235,28 +226,27 @@
 		this_time = jiffies - seq_time;
 
 	if (this_time != prev_input_time)
-	  {
-		  unsigned char   tmp_event[8];
+	{
+		unsigned char   tmp_event[8];
+
+		tmp_event[0] = EV_TIMING;
+		tmp_event[1] = TMR_WAIT_ABS;
+		tmp_event[2] = 0;
+		tmp_event[3] = 0;
+		*(unsigned int *) &tmp_event[4] = this_time;
 
-		  tmp_event[0] = EV_TIMING;
-		  tmp_event[1] = TMR_WAIT_ABS;
-		  tmp_event[2] = 0;
-		  tmp_event[3] = 0;
-		  *(unsigned int *) &tmp_event[4] = this_time;
-
-		  seq_copy_to_input(tmp_event, 8);
-		  prev_input_time = this_time;
-	  }
+		seq_copy_to_input(tmp_event, 8);
+		prev_input_time = this_time;
+	}
 	seq_copy_to_input(event_rec, len);
 }
 
-int
-sequencer_write(int dev, struct fileinfo *file, const char *buf, int count)
+int sequencer_write(int dev, struct fileinfo *file, const char *buf, int count)
 {
-	unsigned char   event_rec[EV_SZ], ev_code;
-	int             p = 0, c, ev_size;
-	int             err;
-	int             mode = file->mode & O_ACCMODE;
+	unsigned char event_rec[EV_SZ], ev_code;
+	int p = 0, c, ev_size;
+	int err;
+	int mode = file->mode & O_ACCMODE;
 
 	dev = dev >> 4;
 
@@ -268,100 +258,98 @@
 	c = count;
 
 	while (c >= 4)
-	  {
-		  copy_from_user((char *) event_rec, &(buf)[p], 4);
-		  ev_code = event_rec[0];
-
-		  if (ev_code == SEQ_FULLSIZE)
-		    {
-			    int             err, fmt;
-
-			    dev = *(unsigned short *) &event_rec[2];
-			    if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
-				    return -ENXIO;
-
-			    if (!(synth_open_mask & (1 << dev)))
-				    return -ENXIO;
-
-			    fmt = (*(short *) &event_rec[0]) & 0xffff;
-			    err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
-			    if (err < 0)
-				    return err;
-
-			    return err;
-		    }
-		  if (ev_code >= 128)
-		    {
-			    if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
-			      {
-				      printk("Sequencer: Invalid level 2 event %x\n", ev_code);
-				      return -EINVAL;
-			      }
-			    ev_size = 8;
-
-			    if (c < ev_size)
-			      {
-				      if (!seq_playing)
-					      seq_startplay();
-				      return count - c;
-			      }
-			    copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4);
-
-		  } else
-		    {
-			    if (seq_mode == SEQ_2)
-			      {
-				      printk("Sequencer: 4 byte event in level 2 mode\n");
-				      return -EINVAL;
-			      }
-			    ev_size = 4;
-
-			    if (event_rec[0] != SEQ_MIDIPUTC)
-				    obsolete_api_used = 1;
-		    }
-
-		  if (event_rec[0] == SEQ_MIDIPUTC)
-		    {
-
-			    if (!midi_opened[event_rec[2]])
-			      {
-				      int             mode;
-				      int             dev = event_rec[2];
+	{
+		copy_from_user((char *) event_rec, &(buf)[p], 4);
+		ev_code = event_rec[0];
 
-				      if (dev >= max_mididev)
-					{
-						printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);
-						return -ENXIO;
-					}
-				      mode = file->mode & O_ACCMODE;
+		if (ev_code == SEQ_FULLSIZE)
+		{
+			int err, fmt;
+
+			dev = *(unsigned short *) &event_rec[2];
+			if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
+				return -ENXIO;
+
+			if (!(synth_open_mask & (1 << dev)))
+				return -ENXIO;
+
+			fmt = (*(short *) &event_rec[0]) & 0xffff;
+			err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
+			if (err < 0)
+				return err;
 
-				      if ((err = midi_devs[dev]->open(dev, mode,
-								      sequencer_midi_input, sequencer_midi_output)) < 0)
-					{
-						seq_reset();
-						printk("Sequencer Error: Unable to open Midi #%d\n", dev);
-						return err;
-					}
-				      midi_opened[dev] = 1;
-			      }
-		    }
-		  if (!seq_queue(event_rec, (file->flags & (O_NONBLOCK) ?
-					     1 : 0)))
-		    {
-			    int             processed = count - c;
-
-			    if (!seq_playing)
-				    seq_startplay();
-
-			    if (!processed && (file->flags & (O_NONBLOCK) ?
-					       1 : 0))
-				    return -EAGAIN;
-			    else
-				    return processed;
-		    }
-		  p += ev_size;
-		  c -= ev_size;
-	  }
+			return err;
+		}
+		if (ev_code >= 128)
+		{
+			if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
+			{
+				printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code);
+				return -EINVAL;
+			}
+			ev_size = 8;
+
+			if (c < ev_size)
+			{
+				if (!seq_playing)
+					seq_startplay();
+				return count - c;
+			}
+			copy_from_user((char *) &event_rec[4], &(buf)[p + 4], 4);
+
+		}
+		else
+		{
+			if (seq_mode == SEQ_2)
+			{
+				printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n");
+				return -EINVAL;
+			}
+			ev_size = 4;
+
+			if (event_rec[0] != SEQ_MIDIPUTC)
+				obsolete_api_used = 1;
+		}
+
+		if (event_rec[0] == SEQ_MIDIPUTC)
+		{
+			if (!midi_opened[event_rec[2]])
+			{
+				int mode;
+				int dev = event_rec[2];
+
+				if (dev >= max_mididev || midi_devs[dev]==NULL)
+				{
+					/*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
+					return -ENXIO;
+				}
+				mode = file->mode & O_ACCMODE;
+
+				if ((err = midi_devs[dev]->open(dev, mode,
+								sequencer_midi_input, sequencer_midi_output)) < 0)
+				{
+					seq_reset();
+					printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev);
+					return err;
+				}
+				midi_opened[dev] = 1;
+			}
+		}
+		if (!seq_queue(event_rec, (file->flags & (O_NONBLOCK) ? 1 : 0)))
+		{
+			int processed = count - c;
+
+			if (!seq_playing)
+				seq_startplay();
+
+			if (!processed && (file->flags & (O_NONBLOCK) ? 1 : 0))
+				return -EAGAIN;
+			else
+				return processed;
+		}
+		p += ev_size;
+		c -= ev_size;
+	}
 
 	if (!seq_playing)
 		seq_startplay();
@@ -369,8 +357,7 @@
 	return count;
 }
 
-static int
-seq_queue(unsigned char *note, char nonblock)
+static int seq_queue(unsigned char *note, char nonblock)
 {
 
 	/*
@@ -384,21 +371,21 @@
 						 */
 
 	if (!nonblock && qlen >= SEQ_MAX_QUEUE && !(seq_sleep_flag.opts & WK_SLEEP))
-	  {
-		  /*
-		   * Sleep until there is enough space on the queue
-		   */
-
-		  seq_sleep_flag.opts = WK_SLEEP;
-		  interruptible_sleep_on(&seq_sleeper);
-		  seq_sleep_flag.opts &= ~WK_SLEEP;;
-	  }
+	{
+		/*
+		 * Sleep until there is enough space on the queue
+		 */
+
+		seq_sleep_flag.opts = WK_SLEEP;
+		interruptible_sleep_on(&seq_sleeper);
+		seq_sleep_flag.opts &= ~WK_SLEEP;;
+	}
 	if (qlen >= SEQ_MAX_QUEUE)
-	  {
-		  return 0;	/*
+	{
+		return 0;	/*
 				 * To be sure
 				 */
-	  }
+	}
 	memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
 
 	qtail = (qtail + 1) % SEQ_MAX_QUEUE;
@@ -407,8 +394,7 @@
 	return 1;
 }
 
-static int
-extended_event(unsigned char *q)
+static int extended_event(unsigned char *q)
 {
 	int             dev = q[2];
 
@@ -419,53 +405,51 @@
 		return -ENXIO;
 
 	switch (q[1])
-	  {
-	  case SEQ_NOTEOFF:
-		  synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
-		  break;
-
-	  case SEQ_NOTEON:
-		  if (q[4] > 127 && q[4] != 255)
-			  return 0;
-
-		  if (q[5] == 0)
-		    {
-			    synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
-			    break;
-		    }
-		  synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
-		  break;
-
-	  case SEQ_PGMCHANGE:
-		  synth_devs[dev]->set_instr(dev, q[3], q[4]);
-		  break;
-
-	  case SEQ_AFTERTOUCH:
-		  synth_devs[dev]->aftertouch(dev, q[3], q[4]);
-		  break;
-
-	  case SEQ_BALANCE:
-		  synth_devs[dev]->panning(dev, q[3], (char) q[4]);
-		  break;
-
-	  case SEQ_CONTROLLER:
-		  synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
-		  break;
-
-	  case SEQ_VOLMODE:
-		  if (synth_devs[dev]->volume_method != NULL)
-			  synth_devs[dev]->volume_method(dev, q[3]);
-		  break;
-
-	  default:
-		  return -EINVAL;
-	  }
+	{
+		case SEQ_NOTEOFF:
+			synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] > 127 && q[4] != 255)
+				return 0;
+
+			if (q[5] == 0)
+			{
+				synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
+				break;
+			}
+			synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
+			break;
+
+		case SEQ_PGMCHANGE:
+			synth_devs[dev]->set_instr(dev, q[3], q[4]);
+			break;
+
+		case SEQ_AFTERTOUCH:
+			synth_devs[dev]->aftertouch(dev, q[3], q[4]);
+			break;
+
+		case SEQ_BALANCE:
+			synth_devs[dev]->panning(dev, q[3], (char) q[4]);
+			break;
+
+		case SEQ_CONTROLLER:
+			synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
+			break;
+
+		case SEQ_VOLMODE:
+			if (synth_devs[dev]->volume_method != NULL)
+				synth_devs[dev]->volume_method(dev, q[3]);
+			break;
 
+		default:
+			return -EINVAL;
+	}
 	return 0;
 }
 
-static int
-find_voice(int dev, int chn, int note)
+static int find_voice(int dev, int chn, int note)
 {
 	unsigned short  key;
 	int             i;
@@ -479,8 +463,7 @@
 	return -1;
 }
 
-static int
-alloc_voice(int dev, int chn, int note)
+static int alloc_voice(int dev, int chn, int note)
 {
 	unsigned short  key;
 	int             voice;
@@ -495,8 +478,7 @@
 	return voice;
 }
 
-static void
-seq_chn_voice_event(unsigned char *event_rec)
+static void seq_chn_voice_event(unsigned char *event_rec)
 {
 #define dev event_rec[1]
 #define cmd event_rec[2]
@@ -504,7 +486,7 @@
 #define note event_rec[4]
 #define parm event_rec[5]
 
-	int             voice = -1;
+	int voice = -1;
 
 	if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
 		return;
@@ -514,60 +496,62 @@
 		return;
 
 	if (seq_mode == SEQ_2)
-	  {
-		  if (synth_devs[dev]->alloc_voice)
-			  voice = find_voice(dev, chn, note);
-
-		  if (cmd == MIDI_NOTEON && parm == 0)
-		    {
-			    cmd = MIDI_NOTEOFF;
-			    parm = 64;
-		    }
-	  }
+	{
+		if (synth_devs[dev]->alloc_voice)
+			voice = find_voice(dev, chn, note);
+
+		if (cmd == MIDI_NOTEON && parm == 0)
+		{
+			cmd = MIDI_NOTEOFF;
+			parm = 64;
+		}
+	}
+	
 	switch (cmd)
-	  {
-	  case MIDI_NOTEON:
-		  if (note > 127 && note != 255)	/* Not a seq2 feature */
-			  return;
-
-		  if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
-		    {		/* Internal synthesizer (FM, GUS, etc) */
-			    voice = alloc_voice(dev, chn, note);
-		    }
-		  if (voice == -1)
-			  voice = chn;
-
-		  if (seq_mode == SEQ_2 && (int) dev < num_synths)
-		    {
-			    /*
-			       * The MIDI channel 10 is a percussive channel. Use the note
-			       * number to select the proper patch (128 to 255) to play.
-			     */
-
-			    if (chn == 9)
-			      {
-				      synth_devs[dev]->set_instr(dev, voice, 128 + note);
-				      synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
-			      }
-			    synth_devs[dev]->setup_voice(dev, voice, chn);
-		    }
-		  synth_devs[dev]->start_note(dev, voice, note, parm);
-		  break;
-
-	  case MIDI_NOTEOFF:
-		  if (voice == -1)
-			  voice = chn;
-		  synth_devs[dev]->kill_note(dev, voice, note, parm);
-		  break;
-
-	  case MIDI_KEY_PRESSURE:
-		  if (voice == -1)
-			  voice = chn;
-		  synth_devs[dev]->aftertouch(dev, voice, parm);
-		  break;
+	{
+		case MIDI_NOTEON:
+			if (note > 127 && note != 255)	/* Not a seq2 feature */
+				return;
+
+			if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
+			{
+				/* Internal synthesizer (FM, GUS, etc) */
+				voice = alloc_voice(dev, chn, note);
+			}
+			if (voice == -1)
+				voice = chn;
+
+			if (seq_mode == SEQ_2 && (int) dev < num_synths)
+			{
+				/*
+				 * The MIDI channel 10 is a percussive channel. Use the note
+				 * number to select the proper patch (128 to 255) to play.
+				 */
+
+				if (chn == 9)
+				{
+					synth_devs[dev]->set_instr(dev, voice, 128 + note);
+					synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
+				}
+				synth_devs[dev]->setup_voice(dev, voice, chn);
+			}
+			synth_devs[dev]->start_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_NOTEOFF:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->kill_note(dev, voice, note, parm);
+			break;
+
+		case MIDI_KEY_PRESSURE:
+			if (voice == -1)
+				voice = chn;
+			synth_devs[dev]->aftertouch(dev, voice, parm);
+			break;
 
-	  default:;
-	  }
+		default:
+	}
 #undef dev
 #undef cmd
 #undef chn
@@ -576,8 +560,7 @@
 }
 
 
-static void
-seq_chn_common_event(unsigned char *event_rec)
+static void seq_chn_common_event(unsigned char *event_rec)
 {
 	unsigned char   dev = event_rec[1];
 	unsigned char   cmd = event_rec[2];
@@ -595,208 +578,208 @@
 		return;
 
 	switch (cmd)
-	  {
-	  case MIDI_PGM_CHANGE:
-		  if (seq_mode == SEQ_2)
-		    {
-			    synth_devs[dev]->chn_info[chn].pgm_num = p1;
-			    if ((int) dev >= num_synths)
-				    synth_devs[dev]->set_instr(dev, chn, p1);
-		  } else
-			  synth_devs[dev]->set_instr(dev, chn, p1);
-
-		  break;
-
-	  case MIDI_CTL_CHANGE:
-		  if (seq_mode == SEQ_2)
-		    {
-			    if (chn > 15 || p1 > 127)
-				    break;
-
-			    synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
-
-			    if (p1 < 32)	/* Setting MSB should clear LSB to 0 */
-				    synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
-
-			    if ((int) dev < num_synths)
-			      {
-				      int             val = w14 & 0x7f;
-				      int             i, key;
+	{
+		case MIDI_PGM_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].pgm_num = p1;
+				if ((int) dev >= num_synths)
+					synth_devs[dev]->set_instr(dev, chn, p1);
+			}
+			else
+				synth_devs[dev]->set_instr(dev, chn, p1);
+
+			break;
+
+		case MIDI_CTL_CHANGE:
+			if (seq_mode == SEQ_2)
+			{
+				if (chn > 15 || p1 > 127)
+					break;
+
+				synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
+
+				if (p1 < 32)	/* Setting MSB should clear LSB to 0 */
+					synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
+
+				if ((int) dev < num_synths)
+				{
+					int val = w14 & 0x7f;
+					int i, key;
 
-				      if (p1 < 64)	/* Combine MSB and LSB */
+					if (p1 < 64)	/* Combine MSB and LSB */
 					{
 						val = ((synth_devs[dev]->
 							chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
-						    | (synth_devs[dev]->
-						       chn_info[chn].controllers[p1 | 32] & 0x7f);
+							| (synth_devs[dev]->
+							chn_info[chn].controllers[p1 | 32] & 0x7f);
 						p1 &= ~32;
 					}
-				      /* Handle all playing notes on this channel */
+					/* Handle all playing notes on this channel */
 
-				      key = ((int) chn << 8);
+					key = ((int) chn << 8);
 
-				      for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
-					      if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
-						      synth_devs[dev]->controller(dev, i, p1, val);
-			    } else
-				    synth_devs[dev]->controller(dev, chn, p1, w14);
-		  } else	/* Mode 1 */
-			  synth_devs[dev]->controller(dev, chn, p1, w14);
-		  break;
-
-	  case MIDI_PITCH_BEND:
-		  if (seq_mode == SEQ_2)
-		    {
-			    synth_devs[dev]->chn_info[chn].bender_value = w14;
-
-			    if ((int) dev < num_synths)
-			      {	/* Handle all playing notes on this channel */
-				      int             i, key;
-
-				      key = (chn << 8);
-
-				      for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
-					      if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
-						      synth_devs[dev]->bender(dev, i, w14);
-			    } else
-				    synth_devs[dev]->bender(dev, chn, w14);
-		  } else	/* MODE 1 */
-			  synth_devs[dev]->bender(dev, chn, w14);
-		  break;
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->controller(dev, i, p1, val);
+				}
+				else
+					synth_devs[dev]->controller(dev, chn, p1, w14);
+			}
+			else	/* Mode 1 */
+				synth_devs[dev]->controller(dev, chn, p1, w14);
+			break;
+
+		case MIDI_PITCH_BEND:
+			if (seq_mode == SEQ_2)
+			{
+				synth_devs[dev]->chn_info[chn].bender_value = w14;
+
+				if ((int) dev < num_synths)
+				{
+					/* Handle all playing notes on this channel */
+					int i, key;
+
+					key = (chn << 8);
+
+					for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
+						if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
+							synth_devs[dev]->bender(dev, i, w14);
+				}
+				else
+					synth_devs[dev]->bender(dev, chn, w14);
+			}
+			else	/* MODE 1 */
+				synth_devs[dev]->bender(dev, chn, w14);
+			break;
 
-	  default:;
-	  }
+		default:
+	}
 }
 
-static int
-seq_timing_event(unsigned char *event_rec)
+static int seq_timing_event(unsigned char *event_rec)
 {
 	unsigned char   cmd = event_rec[1];
 	unsigned int    parm = *(int *) &event_rec[4];
 
 	if (seq_mode == SEQ_2)
-	  {
-		  int             ret;
+	{
+		int ret;
 
-		  if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
-		    {
-			    if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-			      {
-				      unsigned long   flags;
-
-				      save_flags(flags);
-				      cli();
-				      if ((seq_sleep_flag.opts & WK_SLEEP))
-					{
-						{
-							seq_sleep_flag.opts = WK_WAKEUP;
-							wake_up(&seq_sleeper);
-						};
-					}
-				      restore_flags(flags);
-			      }
-		    }
-		  return ret;
-	  }
+		if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
+		{
+			if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+			{
+				unsigned long   flags;
+
+				save_flags(flags);
+				cli();
+				if ((seq_sleep_flag.opts & WK_SLEEP))
+				{
+					seq_sleep_flag.opts = WK_WAKEUP;
+					wake_up(&seq_sleeper);
+				}
+				restore_flags(flags);
+			}
+		}
+		return ret;
+	}
 	switch (cmd)
-	  {
-	  case TMR_WAIT_REL:
-		  parm += prev_event_time;
-
-		  /*
-		     * NOTE!  No break here. Execution of TMR_WAIT_REL continues in the
-		     * next case (TMR_WAIT_ABS)
-		   */
-
-	  case TMR_WAIT_ABS:
-		  if (parm > 0)
-		    {
-			    long            time;
-
-			    time = parm;
-			    prev_event_time = time;
-
-			    seq_playing = 1;
-			    if (softsynthp != NULL)
-				    softsynthp(SSYN_REQUEST, time, 0, 0);
-			    else
-				    request_sound_timer(time);
-
-			    if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-			      {
-				      unsigned long   flags;
-
-				      save_flags(flags);
-				      cli();
-				      if ((seq_sleep_flag.opts & WK_SLEEP))
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+
+			/*
+			 * NOTE!  No break here. Execution of TMR_WAIT_REL continues in the
+			 * next case (TMR_WAIT_ABS)
+			 */
+
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				time = parm;
+				prev_event_time = time;
+
+				seq_playing = 1;
+				if (softsynthp != NULL)
+					softsynthp(SSYN_REQUEST, time, 0, 0);
+				else
+					request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+				{
+					unsigned long flags;
+
+					save_flags(flags);
+					cli();
+					if ((seq_sleep_flag.opts & WK_SLEEP))
 					{
-						{
-							seq_sleep_flag.opts = WK_WAKEUP;
-							wake_up(&seq_sleeper);
-						};
+						seq_sleep_flag.opts = WK_WAKEUP;
+						wake_up(&seq_sleeper);
 					}
-				      restore_flags(flags);
-			      }
-			    return TIMER_ARMED;
-		    }
-		  break;
-
-	  case TMR_START:
-		  if (softsynthp != NULL)
-		    {
-			    softsynthp(SSYN_START, 0, 0, 0);
-			    seq_time = 0;
-		  } else
-			  seq_time = jiffies;
-		  prev_input_time = 0;
-		  prev_event_time = 0;
-		  break;
-
-	  case TMR_STOP:
-		  break;
-
-	  case TMR_CONTINUE:
-		  break;
-
-	  case TMR_TEMPO:
-		  break;
-
-	  case TMR_ECHO:
-		  if (seq_mode == SEQ_2)
-			  seq_copy_to_input(event_rec, 8);
-		  else
-		    {
-			    parm = (parm << 8 | SEQ_ECHO);
-			    seq_copy_to_input((unsigned char *) &parm, 4);
-		    }
-		  break;
+					restore_flags(flags);
+				}
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			if (softsynthp != NULL)
+			{
+				softsynthp(SSYN_START, 0, 0, 0);
+				seq_time = 0;
+			}
+			else
+				seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			break;
+
+		case TMR_STOP:
+			break;
+
+		case TMR_CONTINUE:
+			break;
+
+		case TMR_TEMPO:
+			break;
+
+		case TMR_ECHO:
+			if (seq_mode == SEQ_2)
+				seq_copy_to_input(event_rec, 8);
+			else
+			{
+				parm = (parm << 8 | SEQ_ECHO);
+				seq_copy_to_input((unsigned char *) &parm, 4);
+			}
+			break;
 
-	  default:;
-	  }
+		default:
+	}
 
 	return TIMER_NOT_ARMED;
 }
 
-static void
-seq_local_event(unsigned char *event_rec)
+static void seq_local_event(unsigned char *event_rec)
 {
 	unsigned char   cmd = event_rec[1];
 	unsigned int    parm = *((unsigned int *) &event_rec[4]);
 
 	switch (cmd)
-	  {
-	  case LOCL_STARTAUDIO:
+	{
+		case LOCL_STARTAUDIO:
 #ifdef CONFIG_AUDIO
-		  DMAbuf_start_devices(parm);
+			DMAbuf_start_devices(parm);
 #endif
-		  break;
+			break;
 
-	  default:;
-	  }
+		default:
+	}
 }
 
-static void
-seq_sysex_message(unsigned char *event_rec)
+static void seq_sysex_message(unsigned char *event_rec)
 {
 	int             dev = event_rec[1];
 	int             i, l = 0;
@@ -819,261 +802,259 @@
 		synth_devs[dev]->send_sysex(dev, buf, l);
 }
 
-static int
-play_event(unsigned char *q)
+static int play_event(unsigned char *q)
 {
 	/*
-	   * NOTE! This routine returns
-	   *   0 = normal event played.
-	   *   1 = Timer armed. Suspend playback until timer callback.
-	   *   2 = MIDI output buffer full. Restore queue and suspend until timer
+	 * NOTE! This routine returns
+	 *   0 = normal event played.
+	 *   1 = Timer armed. Suspend playback until timer callback.
+	 *   2 = MIDI output buffer full. Restore queue and suspend until timer
 	 */
-	unsigned int   *delay;
+	unsigned int *delay;
 
 	switch (q[0])
-	  {
-	  case SEQ_NOTEOFF:
-		  if (synth_open_mask & (1 << 0))
-			  if (synth_devs[0])
-				  synth_devs[0]->kill_note(0, q[1], 255, q[3]);
-		  break;
-
-	  case SEQ_NOTEON:
-		  if (q[4] < 128 || q[4] == 255)
-			  if (synth_open_mask & (1 << 0))
-				  if (synth_devs[0])
-					  synth_devs[0]->start_note(0, q[1], q[2], q[3]);
-		  break;
-
-	  case SEQ_WAIT:
-		  delay = (unsigned int *) q;	/*
-						 * Bytes 1 to 3 are containing the *
-						 * delay in 'ticks'
-						 */
-		  *delay = (*delay >> 8) & 0xffffff;
+	{
+		case SEQ_NOTEOFF:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->kill_note(0, q[1], 255, q[3]);
+			break;
+
+		case SEQ_NOTEON:
+			if (q[4] < 128 || q[4] == 255)
+				if (synth_open_mask & (1 << 0))
+					if (synth_devs[0])
+						synth_devs[0]->start_note(0, q[1], q[2], q[3]);
+			break;
+
+		case SEQ_WAIT:
+			delay = (unsigned int *) q;	/*
+							 * Bytes 1 to 3 are containing the *
+							 * delay in 'ticks'
+							 */
+			*delay = (*delay >> 8) & 0xffffff;
 
-		  if (*delay > 0)
-		    {
-			    long            time;
-
-			    seq_playing = 1;
-			    time = *delay;
-			    prev_event_time = time;
-
-			    if (softsynthp != NULL)
-				    softsynthp(SSYN_REQUEST, time, 0, 0);
-			    else
-				    request_sound_timer(time);
-
-			    if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-			      {
-				      unsigned long   flags;
-
-				      save_flags(flags);
-				      cli();
-				      if ((seq_sleep_flag.opts & WK_SLEEP))
+			if (*delay > 0)
+			{
+				long time;
+
+				seq_playing = 1;
+				time = *delay;
+				prev_event_time = time;
+
+				if (softsynthp != NULL)
+					softsynthp(SSYN_REQUEST, time, 0, 0);
+				else
+					request_sound_timer(time);
+
+				if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
+				{
+					unsigned long   flags;
+
+					save_flags(flags);
+					cli();
+					if ((seq_sleep_flag.opts & WK_SLEEP))
 					{
 						{
 							seq_sleep_flag.opts = WK_WAKEUP;
 							wake_up(&seq_sleeper);
 						};
 					}
-				      restore_flags(flags);
-			      }
-			    /*
-			       * The timer is now active and will reinvoke this function
-			       * after the timer expires. Return to the caller now.
-			     */
-			    return 1;
-		    }
-		  break;
-
-	  case SEQ_PGMCHANGE:
-		  if (synth_open_mask & (1 << 0))
-			  if (synth_devs[0])
-				  synth_devs[0]->set_instr(0, q[1], q[2]);
-		  break;
-
-	  case SEQ_SYNCTIMER:	/*
-				   * Reset timer
-				 */
-		  if (softsynthp != NULL)
-			  seq_time = 0;
-		  else
-			  seq_time = jiffies;
-		  prev_input_time = 0;
-		  prev_event_time = 0;
-		  if (softsynthp != NULL)
-			  softsynthp(SSYN_START, 0, 0, 0);
-		  break;
-
-	  case SEQ_MIDIPUTC:	/*
-				 * Put a midi character
+					restore_flags(flags);
+				}
+				/*
+				 * The timer is now active and will reinvoke this function
+				 * after the timer expires. Return to the caller now.
 				 */
-		  if (midi_opened[q[2]])
-		    {
-			    int             dev;
-
-			    dev = q[2];
-
-			    if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
-				    break;
-
-			    if (!midi_devs[dev]->outputc(dev, q[1]))
-			      {
-				      /*
-				         * Output FIFO is full. Wait one timer cycle and try again.
-				       */
-
-				      seq_playing = 1;
-				      if (softsynthp != NULL)
-					      softsynthp(SSYN_REQUEST, -1, 0, 0);
-				      else
-					      request_sound_timer(-1);
-				      return 2;
-			    } else
-				    midi_written[dev] = 1;
-		    }
-		  break;
-
-	  case SEQ_ECHO:
-		  seq_copy_to_input(q, 4);	/*
-						 * Echo back to the process
-						 */
-		  break;
+				return 1;
+			}
+			break;
+
+		case SEQ_PGMCHANGE:
+			if (synth_open_mask & (1 << 0))
+				if (synth_devs[0])
+					synth_devs[0]->set_instr(0, q[1], q[2]);
+			break;
+
+		case SEQ_SYNCTIMER: 	/*
+					 * Reset timer
+					 */
+			if (softsynthp != NULL)
+				seq_time = 0;
+			else
+				seq_time = jiffies;
+			prev_input_time = 0;
+			prev_event_time = 0;
+			if (softsynthp != NULL)
+				softsynthp(SSYN_START, 0, 0, 0);
+			break;
+
+		case SEQ_MIDIPUTC:	/*
+					 * Put a midi character
+					 */
+			if (midi_opened[q[2]])
+			{
+				int dev;
+
+				dev = q[2];
+
+				if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
+					break;
+
+				if (!midi_devs[dev]->outputc(dev, q[1]))
+				{
+					/*
+					 * Output FIFO is full. Wait one timer cycle and try again.
+					 */
+
+					seq_playing = 1;
+					if (softsynthp != NULL)
+						softsynthp(SSYN_REQUEST, -1, 0, 0);
+					else
+						request_sound_timer(-1);
+					return 2;
+				}
+				else
+					midi_written[dev] = 1;
+			}
+			break;
+
+		case SEQ_ECHO:
+			seq_copy_to_input(q, 4);	/*
+							 * Echo back to the process
+							 */
+			break;
 
-	  case SEQ_PRIVATE:
-		  if ((int) q[1] < max_synthdev)
-			  synth_devs[q[1]]->hw_control(q[1], q);
-		  break;
-
-	  case SEQ_EXTENDED:
-		  extended_event(q);
-		  break;
-
-	  case EV_CHN_VOICE:
-		  seq_chn_voice_event(q);
-		  break;
-
-	  case EV_CHN_COMMON:
-		  seq_chn_common_event(q);
-		  break;
-
-	  case EV_TIMING:
-		  if (seq_timing_event(q) == TIMER_ARMED)
-		    {
-			    return 1;
-		    }
-		  break;
-
-	  case EV_SEQ_LOCAL:
-		  seq_local_event(q);
-		  break;
-
-	  case EV_SYSEX:
-		  seq_sysex_message(q);
-		  break;
+		case SEQ_PRIVATE:
+			if ((int) q[1] < max_synthdev)
+				synth_devs[q[1]]->hw_control(q[1], q);
+			break;
+
+		case SEQ_EXTENDED:
+			extended_event(q);
+			break;
+
+		case EV_CHN_VOICE:
+			seq_chn_voice_event(q);
+			break;
+
+		case EV_CHN_COMMON:
+			seq_chn_common_event(q);
+			break;
+
+		case EV_TIMING:
+			if (seq_timing_event(q) == TIMER_ARMED)
+			{
+				return 1;
+			}
+			break;
+
+		case EV_SEQ_LOCAL:
+			seq_local_event(q);
+			break;
+
+		case EV_SYSEX:
+			seq_sysex_message(q);
+			break;
 
-	  default:;
-	  }
+		default:;
+	}
 
 	return 0;
 }
 
-static void
-seq_startplay(void)
+static void seq_startplay(void)
 {
-	unsigned long   flags;
-	int             this_one, action;
+	unsigned long flags;
+	int this_one, action;
 
 	while (qlen > 0)
-	  {
+	{
+
+		save_flags(flags);
+		cli();
+		qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
+		qlen--;
+		restore_flags(flags);
 
-		  save_flags(flags);
-		  cli();
-		  qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
-		  qlen--;
-		  restore_flags(flags);
-
-		  seq_playing = 1;
-
-		  if ((action = play_event(&queue[this_one * EV_SZ])))
-		    {		/* Suspend playback. Next timer routine invokes this routine again */
-			    if (action == 2)
-			      {
-				      qlen++;
-				      qhead = this_one;
-			      }
-			    return;
-		    }
-	  }
+		seq_playing = 1;
+
+		if ((action = play_event(&queue[this_one * EV_SZ])))
+		{		/* Suspend playback. Next timer routine invokes this routine again */
+			if (action == 2)
+			{
+				qlen++;
+				qhead = this_one;
+			}
+			return;
+		}
+	}
 
 	seq_playing = 0;
 
 	if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-	  {
-		  unsigned long   flags;
+	{
+		unsigned long   flags;
 
-		  save_flags(flags);
-		  cli();
-		  if ((seq_sleep_flag.opts & WK_SLEEP))
-		    {
-			    {
-				    seq_sleep_flag.opts = WK_WAKEUP;
-				    wake_up(&seq_sleeper);
-			    };
-		    }
-		  restore_flags(flags);
-	  }
+		save_flags(flags);
+		cli();
+		if ((seq_sleep_flag.opts & WK_SLEEP))
+		{
+			{
+				seq_sleep_flag.opts = WK_WAKEUP;
+				wake_up(&seq_sleeper);
+			};
+		}
+		restore_flags(flags);
+	}
 }
 
 static void
 reset_controllers(int dev, unsigned char *controller, int update_dev)
 {
 
-	int             i;
+	int i;
 
 	for (i = 0; i < 128; i++)
 		controller[i] = ctrl_def_values[i];
 }
 
-static void
-setup_mode2(void)
+static void setup_mode2(void)
 {
-	int             dev;
+	int dev;
 
 	max_synthdev = num_synths;
 
 	for (dev = 0; dev < num_midis; dev++)
+	{
 		if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
-		  {
-			  synth_devs[max_synthdev++] =
-			      midi_devs[dev]->converter;
-		  }
+		{
+			synth_devs[max_synthdev++] = midi_devs[dev]->converter;
+		}
+	}
+	
 	for (dev = 0; dev < max_synthdev; dev++)
-	  {
-		  int             chn;
+	{
+		int chn;
 
-		  synth_devs[dev]->sysex_ptr = 0;
-		  synth_devs[dev]->emulation = 0;
+		synth_devs[dev]->sysex_ptr = 0;
+		synth_devs[dev]->emulation = 0;
 
-		  for (chn = 0; chn < 16; chn++)
-		    {
-			    synth_devs[dev]->chn_info[chn].pgm_num = 0;
-			    reset_controllers(dev,
-			      synth_devs[dev]->chn_info[chn].controllers,
-					      0);
-			    synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);	/* Neutral */
-			    synth_devs[dev]->chn_info[chn].bender_range = 200;
-		    }
-	  }
+		for (chn = 0; chn < 16; chn++)
+		{
+			synth_devs[dev]->chn_info[chn].pgm_num = 0;
+			reset_controllers(dev,
+				synth_devs[dev]->chn_info[chn].controllers,0);
+			synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);	/* Neutral */
+			synth_devs[dev]->chn_info[chn].bender_range = 200;
+		}
+	}
 
 	max_mididev = 0;
 	seq_mode = SEQ_2;
 }
 
-int
-sequencer_open(int dev, struct fileinfo *file)
+int sequencer_open(int dev, struct fileinfo *file)
 {
 	int             retval, mode, i;
 	int             level, tmp;
@@ -1090,28 +1071,30 @@
 	DEB(printk("sequencer_open(dev=%d)\n", dev));
 
 	if (!sequencer_ok)
-	  {
-		  printk("Soundcard: Sequencer not initialized\n");
-		  return -ENXIO;
-	  }
+	{
+/*		printk("Soundcard: Sequencer not initialized\n");*/
+		return -ENXIO;
+	}
 	if (dev)		/* Patch manager device (obsolete) */
-	  {
+	{
 		  return -ENXIO;
-	  }
+	}
 	if (mode == OPEN_READ)
+	{
 		if (!num_midis)
-		  {
-			  printk("Sequencer: No MIDI devices. Input not possible\n");
+		{
+			  /*printk("Sequencer: No MIDI devices. Input not possible\n");*/
 			  sequencer_busy = 0;
 			  return -ENXIO;
-		  }
+		}
+	}
 	save_flags(flags);
 	cli();
 	if (sequencer_busy)
-	  {
-		  restore_flags(flags);
-		  return -EBUSY;
-	  }
+	{
+		restore_flags(flags);
+		return -EBUSY;
+	}
 	sequencer_busy = 1;
 	obsolete_api_used = 0;
 	restore_flags(flags);
@@ -1122,61 +1105,65 @@
 	seq_mode = SEQ_1;
 
 	if (pending_timer != -1)
-	  {
-		  tmr_no = pending_timer;
-		  pending_timer = -1;
-	  }
+	{
+		tmr_no = pending_timer;
+		pending_timer = -1;
+	}
 	if (tmr_no == -1)	/* Not selected yet */
-	  {
-		  int             i, best;
+	{
+		int i, best;
 
-		  best = -1;
-		  for (i = 0; i < num_sound_timers; i++)
-			  if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
-			    {
+		best = -1;
+		for (i = 0; i < num_sound_timers; i++)
+			if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
+			{
 				    tmr_no = i;
 				    best = sound_timer_devs[i]->priority;
-			    }
-		  if (tmr_no == -1)	/* Should not be */
-			  tmr_no = 0;
-	  }
+			}
+		if (tmr_no == -1)	/* Should not be */
+			tmr_no = 0;
+	}
 	tmr = sound_timer_devs[tmr_no];
 
 	if (level == 2)
-	  {
-		  if (tmr == NULL)
-		    {
-			    printk("sequencer: No timer for level 2\n");
-			    sequencer_busy = 0;
-			    return -ENXIO;
-		    }
-		  setup_mode2();
-	  }
+	{
+		if (tmr == NULL)
+		{
+			/*printk("sequencer: No timer for level 2\n");*/
+			sequencer_busy = 0;
+			return -ENXIO;
+		}
+		setup_mode2();
+	}
 	if (!max_synthdev && !max_mididev)
 		return -ENXIO;
 
 	synth_open_mask = 0;
 
 	for (i = 0; i < max_mididev; i++)
-	  {
-		  midi_opened[i] = 0;
-		  midi_written[i] = 0;
-	  }
+	{
+		midi_opened[i] = 0;
+		midi_written[i] = 0;
+	}
 
 	for (i = 0; i < max_synthdev; i++)
-	  {
-		  if ((tmp = synth_devs[i]->open(i, mode)) < 0)
-		    {
-			    printk("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
-			    if (synth_devs[i]->midi_dev)
-				    printk("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
-		  } else
-		    {
-			    synth_open_mask |= (1 << i);
-			    if (synth_devs[i]->midi_dev)
-				    midi_opened[synth_devs[i]->midi_dev] = 1;
-		    }
-	  }
+	{
+		if (synth_devs[i]==NULL)
+			continue;
+			
+		if ((tmp = synth_devs[i]->open(i, mode)) < 0)
+		{
+			printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+			if (synth_devs[i]->midi_dev)
+				printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+		}
+		else
+		{
+			synth_open_mask |= (1 << i);
+			if (synth_devs[i]->midi_dev)
+				midi_opened[synth_devs[i]->midi_dev] = 1;
+		}
+	}
 
 	if (softsynthp != NULL)
 		seq_time = 0;
@@ -1189,21 +1176,25 @@
 		softsynthp(SSYN_START, 0, 0, 0);
 
 	if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
-	  {			/*
-				 * Initialize midi input devices
-				 */
-		  for (i = 0; i < max_mididev; i++)
-			  if (!midi_opened[i])
-			    {
-				    if ((retval = midi_devs[i]->open(i, mode,
-								     sequencer_midi_input, sequencer_midi_output)) >= 0)
-					    midi_opened[i] = 1;
-			    }
-	  }
+	{
+		/*
+		 * Initialize midi input devices
+		 */
+		
+		for (i = 0; i < max_mididev; i++)
+			if (!midi_opened[i])
+			{
+				if ((retval = midi_devs[i]->open(i, mode,
+					sequencer_midi_input, sequencer_midi_output)) >= 0)
+				{
+					midi_opened[i] = 1;
+				}
+			}
+	}
 	if (seq_mode == SEQ_2)
-	  {
-		  tmr->open(tmr_no, seq_mode);
-	  }
+	{
+		tmr->open(tmr_no, seq_mode);
+	}
 	seq_sleep_flag.opts = WK_NONE;
 	midi_sleep_flag.opts = WK_NONE;
 	output_threshold = SEQ_MAX_QUEUE / 2;
@@ -1211,8 +1202,7 @@
 	return 0;
 }
 
-void
-seq_drain_midi_queues(void)
+void seq_drain_midi_queues(void)
 {
 	int             i, n;
 
@@ -1223,43 +1213,42 @@
 	n = 1;
 
 	while (!signal_pending(current) && n)
-	  {
-		  n = 0;
+	{
+		n = 0;
+
+		for (i = 0; i < max_mididev; i++)
+			if (midi_opened[i] && midi_written[i])
+				if (midi_devs[i]->buffer_status != NULL)
+					if (midi_devs[i]->buffer_status(i))
+						n++;
 
-		  for (i = 0; i < max_mididev; i++)
-			  if (midi_opened[i] && midi_written[i])
-				  if (midi_devs[i]->buffer_status != NULL)
-					  if (midi_devs[i]->buffer_status(i))
-						  n++;
-
-		  /*
-		   * Let's have a delay
-		   */
-		  if (n)
-		    {
-
-			    {
-				    unsigned long   tlimit;
-
-				    if (HZ / 10)
-					    current->timeout = tlimit = jiffies + (HZ / 10);
-				    else
-					    tlimit = (unsigned long) -1;
-				    seq_sleep_flag.opts = WK_SLEEP;
-				    interruptible_sleep_on(&seq_sleeper);
-				    if (!(seq_sleep_flag.opts & WK_WAKEUP))
-				      {
-					      if (jiffies >= tlimit)
-						      seq_sleep_flag.opts |= WK_TIMEOUT;
-				      }
-				    seq_sleep_flag.opts &= ~WK_SLEEP;
-			    };
-		    }
-	  }
+		/*
+		 * Let's have a delay
+		 */
+		
+		if (n)
+		{
+			{
+				unsigned long   tlimit;
+
+				if (HZ / 10)
+					current->timeout = tlimit = jiffies + (HZ / 10);
+				else
+					tlimit = (unsigned long) -1;
+				seq_sleep_flag.opts = WK_SLEEP;
+				interruptible_sleep_on(&seq_sleeper);
+				if (!(seq_sleep_flag.opts & WK_WAKEUP))
+				{
+					if (jiffies >= tlimit)
+						seq_sleep_flag.opts |= WK_TIMEOUT;
+				}
+				seq_sleep_flag.opts &= ~WK_SLEEP;
+			};
+		}
+	}
 }
 
-void
-sequencer_release(int dev, struct fileinfo *file)
+void sequencer_release(int dev, struct fileinfo *file)
 {
 	int             i;
 	int             mode = file->mode & O_ACCMODE;
@@ -1274,27 +1263,30 @@
 
 	if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
 				   1 : 0))
+	{
 		while (!signal_pending(current) && qlen > 0)
-		  {
-			  seq_sync();
+		{
+			seq_sync();
 
-			  {
-				  unsigned long   tlimit;
+			{
+				unsigned long   tlimit;
 
-				  if (3 * HZ)
-					  current->timeout = tlimit = jiffies + (3 * HZ);
-				  else
-					  tlimit = (unsigned long) -1;
-				  seq_sleep_flag.opts = WK_SLEEP;
-				  interruptible_sleep_on(&seq_sleeper);
-				  if (!(seq_sleep_flag.opts & WK_WAKEUP))
-				    {
-					    if (jiffies >= tlimit)
-						    seq_sleep_flag.opts |= WK_TIMEOUT;
-				    }
-				  seq_sleep_flag.opts &= ~WK_SLEEP;
-			  };	/* Extra delay */
-		  }
+				if (3 * HZ)
+					current->timeout = tlimit = jiffies + (3 * HZ);
+				else
+					tlimit = (unsigned long) -1;
+				seq_sleep_flag.opts = WK_SLEEP;
+				interruptible_sleep_on(&seq_sleeper);
+				if (!(seq_sleep_flag.opts & WK_WAKEUP))
+				{
+					if (jiffies >= tlimit)
+						seq_sleep_flag.opts |= WK_TIMEOUT;
+				}
+				seq_sleep_flag.opts &= ~WK_SLEEP;
+			};	/* Extra delay */
+		}
+	}
+		  
 	if (mode != OPEN_READ)
 		seq_drain_midi_queues();	/*
 						 * Ensure the output queues are empty
@@ -1306,35 +1298,34 @@
 						 */
 
 	for (i = 0; i < max_synthdev; i++)
-	  {
-		  if (synth_open_mask & (1 << i))	/*
-							 * Actually opened
-							 */
-			  if (synth_devs[i])
-			    {
-				    synth_devs[i]->close(i);
-
-				    if (synth_devs[i]->midi_dev)
-					    midi_opened[synth_devs[i]->midi_dev] = 0;
-			    }
-	  }
+	{
+		if (synth_open_mask & (1 << i))	/*
+						 * Actually opened
+						 */
+			if (synth_devs[i])
+			{
+				synth_devs[i]->close(i);
+
+				if (synth_devs[i]->midi_dev)
+					midi_opened[synth_devs[i]->midi_dev] = 0;
+			}
+	}
 
 	for (i = 0; i < max_mididev; i++)
-	  {
-		  if (midi_opened[i])
+	{
+		if (midi_opened[i])
 			  midi_devs[i]->close(i);
-	  }
+	}
 
 	if (seq_mode == SEQ_2)
 		tmr->close(tmr_no);
 
 	if (obsolete_api_used)
-		printk("/dev/music: Obsolete (4 byte) API was used by this program\n");
+		printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm);
 	sequencer_busy = 0;
 }
 
-static int
-seq_sync(void)
+static int seq_sync(void)
 {
 	unsigned long   flags;
 
@@ -1344,32 +1335,30 @@
 	save_flags(flags);
 	cli();
 	if (qlen > 0)
-	  {
-
-		  {
-			  unsigned long   tlimit;
+	{
+		{
+			unsigned long   tlimit;
 
-			  if (HZ)
-				  current->timeout = tlimit = jiffies + (HZ);
-			  else
-				  tlimit = (unsigned long) -1;
-			  seq_sleep_flag.opts = WK_SLEEP;
-			  interruptible_sleep_on(&seq_sleeper);
-			  if (!(seq_sleep_flag.opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    seq_sleep_flag.opts |= WK_TIMEOUT;
-			    }
-			  seq_sleep_flag.opts &= ~WK_SLEEP;
-		  };
-	  }
+			if (HZ)
+				current->timeout = tlimit = jiffies + (HZ);
+			else
+				tlimit = (unsigned long) -1;
+			seq_sleep_flag.opts = WK_SLEEP;
+			interruptible_sleep_on(&seq_sleeper);
+			if (!(seq_sleep_flag.opts & WK_WAKEUP))
+			{
+				if (jiffies >= tlimit)
+					seq_sleep_flag.opts |= WK_TIMEOUT;
+			}
+			seq_sleep_flag.opts &= ~WK_SLEEP;
+		};
+	}
 	restore_flags(flags);
 
 	return qlen;
 }
 
-static void
-midi_outc(int dev, unsigned char data)
+static void midi_outc(int dev, unsigned char data)
 {
 	/*
 	 * NOTE! Calls sleep(). Don't call this from interrupt.
@@ -1389,31 +1378,29 @@
 	save_flags(flags);
 	cli();
 	while (n && !midi_devs[dev]->outputc(dev, data))
-	  {
-
-		  {
-			  unsigned long   tlimit;
+	{
+		{
+			unsigned long   tlimit;
 
-			  if (4)
-				  current->timeout = tlimit = jiffies + (4);
-			  else
-				  tlimit = (unsigned long) -1;
-			  seq_sleep_flag.opts = WK_SLEEP;
-			  interruptible_sleep_on(&seq_sleeper);
-			  if (!(seq_sleep_flag.opts & WK_WAKEUP))
-			    {
-				    if (jiffies >= tlimit)
-					    seq_sleep_flag.opts |= WK_TIMEOUT;
-			    }
-			  seq_sleep_flag.opts &= ~WK_SLEEP;
-		  };
-		  n--;
-	  }
+			if (4)
+				current->timeout = tlimit = jiffies + (4);
+			else
+				tlimit = (unsigned long) -1;
+			seq_sleep_flag.opts = WK_SLEEP;
+			interruptible_sleep_on(&seq_sleeper);
+			if (!(seq_sleep_flag.opts & WK_WAKEUP))
+			{
+				if (jiffies >= tlimit)
+					seq_sleep_flag.opts |= WK_TIMEOUT;
+			}
+			seq_sleep_flag.opts &= ~WK_SLEEP;
+		};
+		n--;
+	}
 	restore_flags(flags);
 }
 
-static void
-seq_reset(void)
+static void seq_reset(void)
 {
 	/*
 	 * NOTE! Calls sleep(). Don't call this from interrupt.
@@ -1441,82 +1428,79 @@
 				synth_devs[i]->reset(i);
 
 	if (seq_mode == SEQ_2)
-	  {
-
-		  for (chn = 0; chn < 16; chn++)
-			  for (i = 0; i < max_synthdev; i++)
-				  if (synth_open_mask & (1 << i))
-					  if (synth_devs[i])
-					    {
-						    synth_devs[i]->controller(i, chn, 123, 0);	/* All notes off */
-						    synth_devs[i]->controller(i, chn, 121, 0);	/* Reset all ctl */
-						    synth_devs[i]->bender(i, chn, 1 << 13);	/* Bender off */
-					    }
-	} else
-		/* seq_mode == SEQ_1 */
-	  {
-		  for (i = 0; i < max_mididev; i++)
-			  if (midi_written[i])	/*
+	{
+		for (chn = 0; chn < 16; chn++)
+			for (i = 0; i < max_synthdev; i++)
+				if (synth_open_mask & (1 << i))
+					if (synth_devs[i])
+					{
+						synth_devs[i]->controller(i, chn, 123, 0);	/* All notes off */
+						synth_devs[i]->controller(i, chn, 121, 0);	/* Reset all ctl */
+						synth_devs[i]->bender(i, chn, 1 << 13);	/* Bender off */
+					}
+	}
+	else	/* seq_mode == SEQ_1 */
+	{
+		for (i = 0; i < max_mididev; i++)
+			if (midi_written[i])	/*
 						 * Midi used. Some notes may still be playing
 						 */
-			    {
-				    /*
-				       *      Sending just a ACTIVE SENSING message should be enough to stop all
-				       *      playing notes. Since there are devices not recognizing the
-				       *      active sensing, we have to send some all notes off messages also.
-				     */
-				    midi_outc(i, 0xfe);
-
-				    for (chn = 0; chn < 16; chn++)
-				      {
-					      midi_outc(i,
-							(unsigned char) (0xb0 + (chn & 0x0f)));		/* control change */
-					      midi_outc(i, 0x7b);	/* All notes off */
-					      midi_outc(i, 0);	/* Dummy parameter */
-				      }
-
-				    midi_devs[i]->close(i);
-
-				    midi_written[i] = 0;
-				    midi_opened[i] = 0;
-			    }
-	  }
+			{
+				/*
+				 *      Sending just a ACTIVE SENSING message should be enough to stop all
+				 *      playing notes. Since there are devices not recognizing the
+				 *      active sensing, we have to send some all notes off messages also.
+				 */
+				midi_outc(i, 0xfe);
+
+				for (chn = 0; chn < 16; chn++)
+				{
+					midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f)));		/* control change */
+					midi_outc(i, 0x7b);	/* All notes off */
+					midi_outc(i, 0);	/* Dummy parameter */
+				}
+
+				midi_devs[i]->close(i);
+
+				midi_written[i] = 0;
+				midi_opened[i] = 0;
+			}
+	}
 
 	seq_playing = 0;
 
 	save_flags(flags);
 	cli();
+	
 	if ((seq_sleep_flag.opts & WK_SLEEP))
-	  {
-		  /*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
-		  {
-			  seq_sleep_flag.opts = WK_WAKEUP;
-			  wake_up(&seq_sleeper);
-		  };
-	  }
+	{
+		/*      printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
+		{
+			seq_sleep_flag.opts = WK_WAKEUP;
+			wake_up(&seq_sleeper);
+		};
+	}
 	restore_flags(flags);
-
 }
 
-static void
-seq_panic(void)
+static void seq_panic(void)
 {
 	/*
-	   * This routine is called by the application in case the user
-	   * wants to reset the system to the default state.
+	 * This routine is called by the application in case the user
+	 * wants to reset the system to the default state.
 	 */
 
 	seq_reset();
 
 	/*
-	   * Since some of the devices don't recognize the active sensing and
-	   * all notes off messages, we have to shut all notes manually.
-	   *
-	   *      TO BE IMPLEMENTED LATER
+	 * Since some of the devices don't recognize the active sensing and
+	 * all notes off messages, we have to shut all notes manually.
+	 *
+	 *      TO BE IMPLEMENTED LATER
 	 */
 
 	/*
-	   * Also return the controllers to their default states
+	 * Also return the controllers to their default states
 	 */
 }
 
@@ -1724,69 +1708,61 @@
 	return -EINVAL;
 }
 
-int
-sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
 	unsigned long   flags;
 
 	dev = dev >> 4;
 
 	switch (sel_type)
-	  {
-	  case SEL_IN:
-		  save_flags(flags);
-		  cli();
-		  if (!iqlen)
-		    {
-
-			    midi_sleep_flag.opts = WK_SLEEP;
-			    poll_wait(&midi_sleeper, wait);
-			    restore_flags(flags);
-			    return 0;
-		    }
-		  restore_flags(flags);
-		  return 1;
-		  break;
-
-	  case SEL_OUT:
-		  save_flags(flags);
-		  cli();
-		  if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
-		    {
-
-			    seq_sleep_flag.opts = WK_SLEEP;
-			    poll_wait(&seq_sleeper, wait);
-			    restore_flags(flags);
-			    return 0;
-		    }
-		  restore_flags(flags);
-		  return 1;
-		  break;
-
-	  case SEL_EX:
-		  return 0;
-	  }
+	{
+		case SEL_IN:
+			save_flags(flags);
+			cli();
+			if (!iqlen)
+			{
+				midi_sleep_flag.opts = WK_SLEEP;
+				poll_wait(&midi_sleeper, wait);
+				restore_flags(flags);
+				return 0;
+			}
+			restore_flags(flags);
+			return 1;
+
+		case SEL_OUT:
+			save_flags(flags);
+			cli();
+			if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
+			{
+				seq_sleep_flag.opts = WK_SLEEP;
+				poll_wait(&seq_sleeper, wait);
+				restore_flags(flags);
+				return 0;
+			}
+			restore_flags(flags);
+			return 1;
 
+		case SEL_EX:
+			return 0;
+	}
 	return 0;
 }
 
 
-void
-sequencer_timer(unsigned long dummy)
+void sequencer_timer(unsigned long dummy)
 {
 	seq_startplay();
 }
 
-int
-note_to_freq(int note_num)
+int note_to_freq(int note_num)
 {
 
 	/*
 	 * This routine converts a midi note to a frequency (multiplied by 1000)
 	 */
 
-	int             note, octave, note_freq;
-	static int      notes[] =
+	int note, octave, note_freq;
+	static int notes[] =
 	{
 		261632, 277189, 293671, 311132, 329632, 349232,
 		369998, 391998, 415306, 440000, 466162, 493880
@@ -1811,12 +1787,11 @@
 	return note_freq;
 }
 
-unsigned long
-compute_finetune(unsigned long base_freq, int bend, int range,
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
 		 int vibrato_cents)
 {
-	unsigned long   amount;
-	int             negative, semitones, cents, multiplier = 1;
+	unsigned long amount;
+	int negative, semitones, cents, multiplier = 1;
 
 	if (!bend)
 		return base_freq;
@@ -1847,18 +1822,17 @@
 	   bend = 2399;
 	 */
 	while (bend > 2399)
-	  {
+	{
 		  multiplier *= 4;
 		  bend -= 2400;
-	  }
+	}
 
 	semitones = bend / 100;
 	if (semitones > 99)
 		semitones = 99;
 	cents = bend % 100;
 
-	amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents])
-	    / 10000;
+	amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
 
 	if (negative)
 		return (base_freq * 10000) / amount;	/* Bend down */
@@ -1867,8 +1841,7 @@
 }
 
 
-void
-sequencer_init(void)
+void sequencer_init(void)
 {
 	if (sequencer_ok)
 		return;
@@ -1883,19 +1856,19 @@
 	if (sound_nblocks < 1024)
 		sound_nblocks++;;
 	if (queue == NULL)
-	  {
-		  printk("Sound: Can't allocate memory for sequencer output queue\n");
+	{
+		  printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n");
 		  return;
-	  }
+	}
 	iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc(SEQ_MAX_QUEUE * IEV_SZ));
 	sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
 	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+		sound_nblocks++;
 	if (iqueue == NULL)
-	  {
-		  printk("Sound: Can't allocate memory for sequencer input queue\n");
-		  return;
-	  }
+	{
+		printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n");
+		return;
+	}
 	sequencer_ok = 1;
 }
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c
--- v2.1.77/linux/drivers/sound/softoss.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/softoss.c	Sun Jan  4 10:40:16 1998
@@ -106,7 +106,7 @@
 
 static struct softsyn_devc sdev_info =
 {0};
-softsyn_devc   *devc = &sdev_info;
+static softsyn_devc   *devc = &sdev_info;
 
 static struct voice_alloc_info *voice_alloc;
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v2.1.77/linux/drivers/sound/sound_timer.c	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/sound_timer.c	Sun Jan  4 10:46:19 1998
@@ -46,8 +46,7 @@
 	return (tmp + (scale / 2)) / scale;
 }
 
-static void
-reprogram_timer(void)
+void reprogram_timer(void)
 {
 	unsigned long   usecs_per_tick;
 
diff -u --recursive --new-file v2.1.77/linux/drivers/sound/soundmodule.h linux/drivers/sound/soundmodule.h
--- v2.1.77/linux/drivers/sound/soundmodule.h	Fri Jan  2 14:37:02 1998
+++ linux/drivers/sound/soundmodule.h	Sun Jan  4 10:45:55 1998
@@ -1,13 +1,13 @@
 #ifndef _SOUNDMODULE_H
 #define _SOUNDMODULE_H
 
-#ifdef MODULE
-
 #include <linux/notifier.h>
 
 extern struct notifier_block *sound_locker;
 extern void sound_notifier_chain_register(struct notifier_block *);
 extern int lock_depth;
+
+#ifdef MODULE
 
 #ifdef SOUND_CORE
 
diff -u --recursive --new-file v2.1.77/linux/fs/Config.in linux/fs/Config.in
--- v2.1.77/linux/fs/Config.in	Sun Dec 21 22:36:15 1997
+++ linux/fs/Config.in	Sun Jan  4 10:40:16 1998
@@ -57,6 +57,7 @@
 
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
 tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
+tristate 'Apple Macintosh filesystem support (experimental)' CONFIG_HFS_FS
 tristate 'ROM filesystem support' CONFIG_ROMFS_FS
 tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS
 if [ "$CONFIG_AFFS_FS" != "n" ]; then
@@ -66,6 +67,7 @@
 if [ "$CONFIG_UFS_FS" != "n" ]; then
   bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
   bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
+  bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
 fi
 bool 'Macintosh partition map support' CONFIG_MAC_PARTITION
 endmenu
diff -u --recursive --new-file v2.1.77/linux/fs/Makefile linux/fs/Makefile
--- v2.1.77/linux/fs/Makefile	Sun Dec 21 16:17:44 1997
+++ linux/fs/Makefile	Sun Jan  4 10:40:16 1998
@@ -17,7 +17,7 @@
 
 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 lockd nfsd nls
+		hpfs sysv smbfs ncpfs ufs affs romfs autofs hfs lockd nfsd nls
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -90,6 +90,14 @@
 else
   ifeq ($(CONFIG_ISO9660_FS),m)
   MOD_SUB_DIRS += isofs
+  endif
+endif
+
+ifeq ($(CONFIG_HFS_FS),y)
+SUB_DIRS += hfs
+else
+  ifeq ($(CONFIG_HFS_FS),m)
+  MOD_SUB_DIRS += hfs
   endif
 endif
 
diff -u --recursive --new-file v2.1.77/linux/fs/affs/inode.c linux/fs/affs/inode.c
--- v2.1.77/linux/fs/affs/inode.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/inode.c	Sun Jan  4 00:53:42 1998
@@ -220,8 +220,9 @@
 }
 
 int
-affs_notify_change(struct inode *inode, struct iattr *attr)
+affs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *inode = dentry->d_inode;
 	int error;
 
 	pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
diff -u --recursive --new-file v2.1.77/linux/fs/affs/symlink.c linux/fs/affs/symlink.c
--- v2.1.77/linux/fs/affs/symlink.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/affs/symlink.c	Sun Jan  4 00:53:42 1998
@@ -19,8 +19,8 @@
 
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 
-static int		 affs_readlink(struct inode *, char *, int);
-static struct dentry	*affs_follow_link(struct inode *inode, struct dentry *base);
+static int		 affs_readlink(struct dentry *, char *, int);
+static struct dentry	*affs_follow_link(struct dentry *dentry, struct dentry *base);
 
 struct inode_operations affs_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -44,8 +44,9 @@
 };
 
 static int
-affs_readlink(struct inode *inode, char *buffer, int buflen)
+affs_readlink(struct inode *dentry, char *buffer, int buflen)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head	*bh;
 	struct slink_front	*lf;
 	int			 i, j;
@@ -97,8 +98,9 @@
 }
 
 static struct dentry *
-affs_follow_link(struct inode *inode, struct dentry *base)
+affs_follow_link(struct dentry *dentry, struct dentry *base)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head	*bh;
 	struct slink_front	*lf;
 	char			*buffer;
diff -u --recursive --new-file v2.1.77/linux/fs/attr.c linux/fs/attr.c
--- v2.1.77/linux/fs/attr.c	Sun Dec 21 22:36:15 1997
+++ linux/fs/attr.c	Sun Jan  4 00:53:41 1998
@@ -81,8 +81,9 @@
 	mark_inode_dirty(inode);
 }
 
-int notify_change(struct inode * inode, struct iattr * attr)
+int notify_change(struct dentry * dentry, struct iattr * attr)
 {
+	struct inode *inode = dentry->d_inode;
 	int error;
 	time_t now = CURRENT_TIME;
 	unsigned int ia_valid = attr->ia_valid;
@@ -93,11 +94,13 @@
 	if (!(ia_valid & ATTR_MTIME_SET))
 		attr->ia_mtime = now;
 
-	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->notify_change) 
-		return inode->i_sb->s_op->notify_change(inode, attr);
-
-	error = inode_change_ok(inode, attr);
-	if (!error)
-		inode_setattr(inode, attr);
+	if (inode->i_sb && inode->i_sb->s_op &&
+	    inode->i_sb->s_op->notify_change) 
+		error = inode->i_sb->s_op->notify_change(dentry, attr);
+	else {
+		error = inode_change_ok(inode, attr);
+		if (!error)
+			inode_setattr(inode, attr);
+	}
 	return error;
 }
diff -u --recursive --new-file v2.1.77/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- v2.1.77/linux/fs/autofs/symlink.c	Mon Aug  4 16:25:38 1997
+++ linux/fs/autofs/symlink.c	Sun Jan  4 00:53:43 1998
@@ -14,23 +14,24 @@
 #include <linux/sched.h>
 #include "autofs_i.h"
 
-static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
+static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	struct autofs_symlink *sl;
 	int len;
 
-	sl = (struct autofs_symlink *)inode->u.generic_ip;
+	sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
 	len = sl->len;
 	if (len > buflen) len = buflen;
-	copy_to_user(buffer,sl->data,len);
+	copy_to_user(buffer, sl->data, len);
 	return len;
 }
 
-static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * autofs_follow_link(struct dentry *dentry,
+					struct dentry *base)
 {
 	struct autofs_symlink *sl;
 
-	sl = (struct autofs_symlink *)inode->u.generic_ip;
+	sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
 	return lookup_dentry(sl->data, base, 1);
 }
 
diff -u --recursive --new-file v2.1.77/linux/fs/bad_inode.c linux/fs/bad_inode.c
--- v2.1.77/linux/fs/bad_inode.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/bad_inode.c	Sun Jan  4 00:53:41 1998
@@ -13,7 +13,7 @@
 /*
  * The follow_symlink operation must dput() the base.
  */
-static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base)
+static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base)
 {
 	dput(base);
 	return ERR_PTR(-EIO);
diff -u --recursive --new-file v2.1.77/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.77/linux/fs/buffer.c	Tue Dec 23 16:31:00 1997
+++ linux/fs/buffer.c	Sun Jan  4 00:53:57 1998
@@ -1522,8 +1522,9 @@
  * mark_buffer_uptodate() functions propagate buffer state into the
  * page struct once IO has completed.
  */
-int generic_readpage(struct inode * inode, struct page * page)
+int generic_readpage(struct dentry * dentry, struct page * page)
 {
+	struct inode *inode = dentry->d_inode;
 	unsigned long block;
 	int *p, nr[PAGE_SIZE/512];
 	int i;
diff -u --recursive --new-file v2.1.77/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.77/linux/fs/coda/file.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/file.c	Sun Jan  4 00:53:43 1998
@@ -25,10 +25,10 @@
 #include <linux/coda_cache.h>
 
 /* file operations */
-static int coda_readpage(struct inode * inode, struct page * page);
-static ssize_t coda_file_read(struct file *f, char *buf, size_t count, loff_t *off);
-static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
-static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
+static int coda_readpage(struct dentry *, struct page *);
+static ssize_t coda_file_read(struct file *, char *, size_t, loff_t *);
+static ssize_t coda_file_write(struct file *, const char *, size_t, loff_t *);
+static int coda_file_mmap(struct file *, struct vm_area_struct *);
 
 /* exported from this file */
 int coda_fsync(struct file *, struct dentry *dentry);
@@ -74,9 +74,9 @@
 };
 
 /*  File file operations */
-static int coda_readpage(struct inode * inode, struct page * page)
+static int coda_readpage(struct dentry *dentry, struct page * page)
 {
-        struct inode *open_inode;
+        struct inode *open_inode, *inode = dentry->d_inode;
         struct cnode *cnp;
 
         ENTRY;
@@ -93,6 +93,7 @@
 
         CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, open_inode->i_ino, page->offset);
 
+	/* N.B. This needs the dentry for open_inode */
         generic_readpage(open_inode, page);
         EXIT;
         return 0;
diff -u --recursive --new-file v2.1.77/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.77/linux/fs/coda/super.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/super.c	Sun Jan  4 00:53:43 1998
@@ -41,7 +41,7 @@
 /* VFS super_block ops */
 static struct super_block *coda_read_super(struct super_block *, void *, int);
 static void coda_read_inode(struct inode *);
-static int  coda_notify_change(struct inode *inode, struct iattr *attr);
+static int  coda_notify_change(struct dentry *, struct iattr *);
 static void coda_put_inode(struct inode *);
 static void coda_delete_inode(struct inode *);
 static void coda_put_super(struct super_block *);
@@ -133,6 +133,7 @@
 	printk("coda_read_super: rootinode is %ld dev %d\n", 
 	       root->i_ino, root->i_dev);
 	sbi->sbi_root = root;
+	/* N.B. check this for failure */
 	sb->s_root = d_alloc_root(root, NULL);
 	unlock_super(sb);
 	EXIT;  
@@ -140,7 +141,6 @@
 
 error:
 EXIT;  
-	MOD_DEC_USE_COUNT;
 	if (sbi) {
 		sbi->sbi_vcomm = NULL;
 		sbi->sbi_root = NULL;
@@ -154,6 +154,7 @@
                 coda_cnode_free(ITOC(root));
         }
         sb->s_dev = 0;
+	MOD_DEC_USE_COUNT;
         return NULL;
 }
 
@@ -224,8 +225,9 @@
 	EXIT;
 }
 
-static int  coda_notify_change(struct inode *inode, struct iattr *iattr)
+static int  coda_notify_change(struct dentry *dentry, struct iattr *iattr)
 {
+	struct inode *inode = dentry->d_inode;
         struct cnode *cnp;
         struct coda_vattr vattr;
         int error;
diff -u --recursive --new-file v2.1.77/linux/fs/coda/symlink.c linux/fs/coda/symlink.c
--- v2.1.77/linux/fs/coda/symlink.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/coda/symlink.c	Sun Jan  4 00:53:43 1998
@@ -24,8 +24,8 @@
 #include <linux/coda_cnode.h>
 #include <linux/coda_cache.h>
 
-static int coda_readlink(struct inode *inode, char *buffer, int length);
-static struct dentry *coda_follow_link(struct inode *, struct dentry *);
+static int coda_readlink(struct dentry *dentry, char *buffer, int length);
+static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
 
 struct inode_operations coda_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -50,8 +50,9 @@
         NULL                    /* revalidate */
 };
 
-static int coda_readlink(struct inode *inode, char *buffer, int length)
+static int coda_readlink(struct inode *dentry, char *buffer, int length)
 {
+	struct inode *inode = dentry->d_inode;
         int len;
 	int error;
         char *buf;
@@ -83,14 +84,15 @@
 	return error;
 }
 
-static struct dentry *coda_follow_link(struct inode *inode, 
-				       struct dentry *base)
+static struct dentry *coda_follow_link(struct dentry *dentry, 
+					struct dentry *base)
 {
+	struct inode *inode = dentry->d_inode;
 	int error;
 	struct cnode *cnp;
 	unsigned int len;
-	char mem[CFS_MAXPATHLEN];
 	char *path;
+	char mem[CFS_MAXPATHLEN]; /* N.B. too big for the stack? */
 ENTRY;
 	CDEBUG(D_INODE, "(%x/%ld)\n", inode->i_dev, inode->i_ino);
 	
diff -u --recursive --new-file v2.1.77/linux/fs/dcache.c linux/fs/dcache.c
--- v2.1.77/linux/fs/dcache.c	Fri Dec 19 15:53:00 1997
+++ linux/fs/dcache.c	Sun Jan  4 00:54:04 1998
@@ -52,6 +52,8 @@
 
 static inline void d_free(struct dentry *dentry)
 {
+	if (dentry->d_op && dentry->d_op->d_release)
+		dentry->d_op->d_release(dentry);
 	kfree(dentry->d_name.name);
 	kfree(dentry);
 }
@@ -502,6 +504,7 @@
 	dentry->d_name.len = name->len;
 	dentry->d_name.hash = name->hash;
 	dentry->d_op = NULL;
+	dentry->d_fsdata = NULL;
 	return dentry;
 }
 
diff -u --recursive --new-file v2.1.77/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- v2.1.77/linux/fs/ext2/inode.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/ext2/inode.c	Sun Jan  4 00:53:41 1998
@@ -631,23 +631,25 @@
 	return ext2_update_inode (inode, 1);
 }
 
-int ext2_notify_change(struct inode *inode, struct iattr *iattr)
+int ext2_notify_change(struct dentry *dentry, struct iattr *iattr)
 {
+	struct inode *inode = dentry->d_inode;
 	int		retval;
 	unsigned int	flags;
 	
+	retval = -EPERM;
 	if ((iattr->ia_attr_flags &
 	     (ATTR_FLAG_APPEND | ATTR_FLAG_IMMUTABLE)) ^
 	    (inode->u.ext2_i.i_flags &
 	     (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
 		if (securelevel > 0 || !fsuser())
-			return -EPERM;
-	} else
-		if ((current->fsuid != inode->i_uid) && !fsuser())
-			return -EPERM;
+			goto out;
+	} else if ((current->fsuid != inode->i_uid) && !fsuser())
+		goto out;
 
-	if ((retval = inode_change_ok(inode, iattr)) != 0)
-		return retval;
+	retval = inode_change_ok(inode, iattr);
+	if (retval != 0)
+		goto out;
 
 	inode_setattr(inode, iattr);
 	
@@ -681,7 +683,7 @@
 		inode->u.ext2_i.i_flags &= ~EXT2_IMMUTABLE_FL;
 	}
 	mark_inode_dirty(inode);
-
-	return 0;
+out:
+	return retval;
 }
 
diff -u --recursive --new-file v2.1.77/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.1.77/linux/fs/ext2/namei.c	Sat Nov 29 11:25:12 1997
+++ linux/fs/ext2/namei.c	Sun Jan  4 00:53:41 1998
@@ -793,8 +793,10 @@
 	goto out;
 }
 
-int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry)
+int ext2_link (struct dentry * old_dentry,
+		struct inode * dir, struct dentry *dentry)
 {
+	struct inode *inode = old_dentry->d_inode;
 	struct ext2_dir_entry * de;
 	struct buffer_head * bh;
 	int err;
diff -u --recursive --new-file v2.1.77/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.1.77/linux/fs/ext2/symlink.c	Thu Jul 17 10:06:06 1997
+++ linux/fs/ext2/symlink.c	Sun Jan  4 00:53:41 1998
@@ -24,8 +24,8 @@
 #include <linux/mm.h>
 #include <linux/stat.h>
 
-static int ext2_readlink (struct inode *, char *, int);
-static struct dentry *ext2_follow_link(struct inode *, struct dentry *);
+static int ext2_readlink (struct dentry *, char *, int);
+static struct dentry *ext2_follow_link(struct dentry *, struct dentry *);
 
 /*
  * symlinks can't do much...
@@ -51,10 +51,12 @@
 	NULL			/* smap */
 };
 
-static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry * ext2_follow_link(struct dentry * dentry,
+					struct dentry *base)
 {
-	int error;
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh = NULL;
+	int error;
 	char * link;
 
 	link = (char *) inode->u.ext2_i.i_data;
@@ -72,8 +74,9 @@
 	return base;
 }
 
-static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
+static int ext2_readlink (struct dentry * dentry, char * buffer, int buflen)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh = NULL;
 	char * link;
 	int i;
diff -u --recursive --new-file v2.1.77/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v2.1.77/linux/fs/fat/inode.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/fat/inode.c	Sun Jan  4 00:53:42 1998
@@ -783,9 +783,10 @@
 }
 
 
-int fat_notify_change(struct inode * inode,struct iattr * attr)
+int fat_notify_change(struct dentry * dentry, struct iattr * attr)
 {
-	struct super_block *sb = inode->i_sb;
+	struct super_block *sb = dentry->d_sb;
+	struct inode *inode = dentry->d_inode;
 	int error;
 
 	error = inode_change_ok(inode, attr);
diff -u --recursive --new-file v2.1.77/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.77/linux/fs/filesystems.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/filesystems.c	Sun Jan  4 10:41:52 1998
@@ -25,6 +25,7 @@
 #include <linux/romfs_fs.h>
 #include <linux/auto_fs.h>
 #include <linux/ntfs_fs.h>
+#include <linux/hfs_fs.h>
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -112,6 +113,10 @@
 
 #ifdef CONFIG_NTFS_FS
 	init_ntfs_fs();
+#endif
+
+#ifdef CONFIG_HFS_FS
+	init_hfs_fs();
 #endif
 
 #ifdef CONFIG_AFFS_FS
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/COPYING linux/fs/hfs/COPYING
--- v2.1.77/linux/fs/hfs/COPYING	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/COPYING	Sun Jan  4 10:40:16 1998
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/ChangeLog linux/fs/hfs/ChangeLog
--- v2.1.77/linux/fs/hfs/ChangeLog	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/ChangeLog	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,2294 @@
+Sun Dec 28 22:48:53 1997  a sun  <asun@zoology.washington.edu>
+
+	* sysdep.c, catalog.c, hfs.h
+	as a temporary workaround until catalog.c gets re-written, 
+	i flush the dcache if we need more entries. 
+	
+Fri Dec 19 15:11:21 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir_dbl.c
+	statically allocate tmp_name instead of doing it dynamically.
+
+	NOTE: well, those pesky hfs_cat_put messages still aren't gone. in
+	addition, catalog.c needs to be modified to free up some entries
+	when the cache gets filled up. 
+
+Sun Dec 14 11:51:11 1997  a sun  <asun@zoology.washington.edu>
+
+	* linux/hfs_fs.h
+	moved the dentry stuff into within the #ifdef __KERNEL__ 
+	part of hfs_fs.h and cleaned up a little.
+
+Sun Dec 14 11:24:54 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir.c
+	changed hfs_rename to delete all old dentries. hfs_cat_put
+	messages on umount should be a thing of the past now.
+
+Sun Dec 14 01:12:58 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir.c
+	changed mark_inodes_deleted to dget/d_delete/dput the dentry
+	instead of just dropping it. the bytes available should now
+	be updated updated properly upon deletion.
+
+Wed Dec 10 00:01:25 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir.c
+	changed mark_inodes_deleted to drop the dentry instead of 
+	just deleting it. 
+
+	TODO: bytes available aren't being properly updated when a
+	resource fork gets deleted.
+
+Mon Dec  8 23:22:40 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir_cap.c, dir_nat.c, dir_dbl.c, dir.c
+	* hfs.h, linux/hfs_sysdep.h, linux/hfs_fs_i.h
+	Added code to drop ({dbl,cap,nat}_drop_dentry) invalid
+	dentries when creating or moving a file.
+
+	* inode.c
+	Added code to delete cached dentries when a file gets deleted.
+
+	* current yuckiness: there's an extra hfs_cat_put somewhere. it's
+	harmless but bothersome.
+	
+Thu Dec  4 00:14:03 1997  a sun  <asun@zoology.washington.edu>
+
+	* dir.c, dir_cap.c, dir_nat.c, file.c, file_hdr.c, inode.c,
+	* linux/{hfs_sysdep.h, hfs_fs.h}, version.c:
+	Completed first code dentrification sweep. It mounts! It copies!
+	It dcaches! 
+
+Mon Apr 28 06:58:44 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, INSTALL.sgml, HFS.sgml:
+	Bump version to 0.95 (Woohoo! We're beta!)
+
+	* linux/hfs_fs.h:
+	Modify HFS_SB() and HFS_I() when compiled into the kernel.
+
+	* FAQ.sgml:
+	Add a new question (and its answer):
+	Why does my Macintosh show generic application and document icons?
+
+	* HFS.sgml:
+	Add some URLs and remove the (now empty) FAQ section.
+
+Sun Apr 27 22:17:01 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* HFS.sgml:
+	Don't call the version 1 headers "slightly modified".
+
+	* file_hdr.c, dir_nat.c:
+	Comment some AFPD compatibility stuff.
+
+	* FAQ.sgml:
+	Update for version 0.95.
+
+	* BUG_INFO:
+	Remove the BIG_INFO script since we no longer mention it.
+
+	* README.sgml, INSTALL.sgml, HFS.sgml, Makefile:
+	Split README.sgml into HFS.sgml and INSTALL.sgml.
+	Stop including the document sources in snapshots.
+
+	* file_hdr.c:
+	Fix hdr_truncate() not to truncate the data fork.
+
+Wed Apr 16 23:56:25 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* FAQ.sgml:
+	Bump version to 0.8.4 and add two answers:
+		How to fsck an HFS filesystem.
+		How to generate linux/version.h.
+
+	* version.c, README.sgml:
+	Bump version to 0.8.4.
+
+	* README.sgml, FAQ.sgml, Makefile:
+	Separate the FAQ from the README.
+
+	* linux/hfs_fs.h:
+	Add (struct hfs_fork) to the forward declarations.
+
+Thu Apr 10 05:47:16 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* linux/hfs_sysdep.h:
+	Work around the non-const declaration of test_bit()'s second argument.
+
+	* Makefile:
+	Use .config from the kernel source to check for MODVERSIONS.
+
+Wed Apr  9 07:57:17 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bnode.c:
+	Check the record table in each bnode as we read it from disk.
+
+	* super.c, mdb.c, hfs.h:
+	Deal with the ATTRIB_CLEAN bit of the MDB properly (in mdb.c).
+
+	* super.c, hfs.h, mdb.c:
+	Search for the alt-MDB rather than using the device size to find it.
+
+Wed Apr  9 03:39:05 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version to 0.8.3.
+
+Mon Apr  7 20:09:56 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* part_tbl.c:
+	Fix to allow bootable CDROMs (which have blocksize != 512) to mount.
+
+	* super.c:
+	Check that blk_size[MAJOR(dev)] is non-NULL before dereferencing.
+
+Sat Apr  5 10:44:42 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_btree.h, binsert.c, brec.c, bfind.c, bins_del.c, bdelete.c:
+	Make btree operations less likely to do
+	nasty things if the tree is corrupted.
+
+	* part_tbl.c, README.sgml:
+	Count partitions from 0 rather than from 1.
+
+Wed Apr  2 23:26:51 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bdelete.c:
+	Don't bother checking for oversized keys in hfs_bdelete().
+
+	* bdelete.c, bfind.c, binsert.c:
+	Verify key lengths against the maximum given for the tree.
+
+	* Makefile:
+	Check that /usr/include/linux/modversions.h exists before including it.
+	This allows compilation without CONFIG_MODVERSIONS enabled.
+
+Sat Mar 29 13:17:53 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* linux/hfs_fs.h, super.c, file_hdr.c, hfs.h, extent.c, file_cap.c,
+	  dir_dbl.c, dir_nat.c, dir.c, dir_cap.c, binsert.c, catalog.c,
+	  bfind.c:
+	Make (struct hfs_bkey) and (struct hfs_brec) more "abstract".
+
+	* binsert.c:
+	Remove redundant test in hfs_binsert().
+
+Sat Mar 29 05:24:23 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Fix formatting problems in README.sgml and bump version to 0.8.2.
+
+	* extent.c:
+	Fix bug that caused serious headaches with fragmented files.
+
+Fri Mar 28 00:23:18 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version to 0.8.1.
+
+	* btree.c, balloc.c:
+	Commit map nodes to buffers when new map nodes are added.
+
+Thu Mar 27 22:41:07 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* Makefile:
+	Include linux/modversions.h from the gcc command line.
+
+	* mdb.c:
+	Was updating modified date twice in hfs_mdb_commit().
+
+	* linux/hfs_sysdep.h, linux/hfs_fs.h, linux/hfs_fs_i.h,
+	  linux/hfs_fs_sb.h, sysdep.c, trans.c, super.c, hfs_sysdep.h, inode.c,
+	  hfs_fs_i.h, hfs_fs_sb.h, hfs_fs.h, hfs.h, file_cap.c, file_hdr.c,
+	  file.c, dir_nat.c, dir_cap.c, dir_dbl.c, Makefile, dir.c:
+	Rearrange headers in preparation for inclusion in the kernel.
+
+	* hfs_fs_sb.h, hfs_fs.h:
+	Add forward declarations so other code can include these headers.
+
+	* hfs_sysdep.h:
+	Include __constant_hton[ls]() for little-endian machines.
+
+	* hfs_fs.h, hfs_sysdep.h, hfs.h:
+	Move typedefs of hfs_{byte,word,lword}_t from hfs.h to hfs_sysdep.h.
+	Include hfs_sysdep.h from hfs_fs.h.
+
+	* trans.c, super.c, part_tbl.c, string.c, inode.c, mdb.c, hfs_fs_sb.h,
+	  hfs_sysdep.h, hfs_fs.h, hfs.h, hfs_btree.h, file_cap.c, file_hdr.c,
+	  file.c, dir_nat.c, extent.c, dir_dbl.c, dir.c, dir_cap.c, catalog.c,
+	  btree.c, bnode.c, brec.c, bitmap.c, bitops.c, bins_del.c, binsert.c,
+	  bdelete.c, bfind.c, balloc.c:
+	Big type system changes in preparation for kernel inclusion:
+	'[US](8|16|32)' -> 'hfs_[us](8|16|32)' (avoids name space pollution)
+	'hfs_name_t' -> 'struct hfs_name' (allows forward declaration)
+
+	* super.c, hfs_fs.h:
+	Add init_hfs_fs() to super.c for non-module compilation.
+
+Wed Mar 26 07:53:59 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version to 0.8.
+
+	* README.sgml:
+	Special compilation note for DEC Alpha.
+
+	* README.sgml:
+	Note status on non-Intel processors.
+
+	* hfs_fs.h:
+	Use long's for read() and write() on the Alpha.
+
+	* README.sgml:
+	Document the afpd mount option.
+
+	* inode.c:
+	Make files always writable for owner in afpd mode.
+
+Tue Mar 25 23:21:39 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* part_tbl.c:
+	Clean up the error checking code a bit.
+
+Sat Mar 22 19:43:40 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* part_tbl.c:
+	Fixed uninitialized variable in old-style partition code.
+
+	* bins_del.c, bdelete.c:
+	Fix extraneous "bad argument to shift_{left,right}" messages.
+
+	* bitops.c:
+	Note that these routines are now tested on Intel, PPC and Alpha.
+
+	* Makefile:
+	Add -fno-builtin the the CFLAGS.
+
+Fri Feb 14 10:50:14 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_sysdep.h:
+	Don't include <asm/*.h> until after <linux/types.h>.
+
+	* catalog.c:
+	Use volume create date in hashfn() rather than casting pointer to int.
+
+	* hfs.h, mdb.c:
+	Maintaing volume create, modify and backup dates in struct hfs_mdb.
+
+	* hfs_fs.h:
+	Include the header for put_user BEFORE using it!
+
+	* string.c, hfs.h:
+	Make hfs_strhash() return an unsigned int.
+
+	* trans.c, version.c, super.c, mdb.c, part_tbl.c, string.c, inode.c,
+	  hfs_sysdep.h, hfs_fs.h, hfs_fs_sb.h, hfs_btree.h, hfs.h, file_cap.c,
+	  file_hdr.c, extent.c, dir_dbl.c, dir_nat.c, dir_cap.c, dir.c,
+	  catalog.c, btree.c, bnode.c, brec.c, bitmap.c, binsert.c,
+	  bins_del.c, bdelete.c, balloc.c, README.sgml, Makefile:
+	Updated copyright notices.
+
+	* trans.c, part_tbl.c, string.c, super.c, inode.c, mdb.c, hfs_fs.h,
+	  hfs_fs_sb.h, hfs_sysdep.h, hfs_btree.h, hfs.h, file_cap.c,
+	  file_hdr.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, catalog.c,
+	  dir.c, brec.c, btree.c, bitmap.c, bnode.c, bdelete.c, bins_del.c,
+	  binsert.c, Makefile, TODO, balloc.c:
+	First shot at portability to the DEC Alpha and non-gcc compilers.
+	This invloved a significant overhaul of the type system.
+
+Tue Feb  4 04:26:54 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version to "pre-0.8-4".
+
+	* dir_nat.c:
+	Allow creat() in Netatalk .AppleDouble directories.
+
+	* dir_dbl.c:
+	Make local functions static.
+
+	* dir_dbl.c:
+	Removed unnecessary 'extern' qualifiers from forward declarations.
+
+	* file_hdr.c, TODO:
+	Fixed the 30-year time warp with afpd.
+
+	* TODO, trans.c:
+	Don't mangle the name .AppleDesktop under fork=netatalk.
+
+Mon Feb  3 23:18:45 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode.c:
+	Make header files always writable when the afpd mount option is given.
+	Otherwise it is impossible to unlock a locked file.
+
+	* TODO, inode.c:
+	Let afpd think chmod() always succeeds, so "New Folder" works right.
+
+	* super.c:
+	The 'afpd' mount option now makes 'fork=n,names=n' the default.
+
+	* TODO:
+	List the current known afpd-compatibility problems as bugs.
+
+	* file_hdr.c:
+	Make certain date changes through header files get written to disk.
+
+Sat Feb  1 02:24:12 1997  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* mdb.c:
+	Work around for Linux rounding device sizes to 1k increments.
+
+	* README.sgml:
+	Fixed a typo: "the a".
+
+Sat Dec 28 20:41:01 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* TODO:
+	Add ioctl() interface as a "missing feature."
+
+	* dir_nat.c:
+	Finish implementing the afpd-compatibility
+	mode using the new 'afpd' mount option.
+
+	* hfs_fs_sb.h, super.c:
+	Add new 'afpd' mount option.
+
+	* file_cap.c:
+	Spelling fix.
+
+Wed Dec 11 23:16:08 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* TODO, README.sgml:
+	Optimistically document the hybrid CD problem as fixed.
+
+	* part_tbl.c:
+	Fix the partition code so at least some of the hybrid
+	CDROMs that were previously rejected are now accepted.
+
+	* hfs.h:
+	Make fs_start a 32-bit integer rather than 16-bits.
+	The 16-bit value would overflow if a partition started
+	beyond the 32M mark (e.g. the Executor 2 Beta 1 CDROM).
+
+	* extent.c:
+	Fixed a typo in an error message.
+
+Tue Dec 10 14:43:46 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir_nat.c:
+	Merge in the (still dormant) afpd-compatibility changes.
+
+	* inode.c:
+	Make the .AppleDouble directory writable (again).
+
+	* version.c, README.sgml:
+	Bump version up to "pre-0.8-3".
+
+	* hfs_fs.h, file_cap.c, file_hdr.c:
+	Move AFP constants to hfs_fs.h and prefix them with "HFS_".
+
+	* dir_nat.c, inode.c:
+	Back-out changes that allowed writing to the .AppleDouble directory.
+
+	* Makefile:
+	Update rules for linuxdoc-sgml v1.5.
+
+	* extent.c:
+	Fixed serious bug in decode_extent() with handling of empty extents.
+
+	* file.c:
+	Rewrote hfs_getblk().
+	It will no longer hang if hfs_extent_map() is buggy.
+	Also halves the worst-case number of calls to hfs_extent_map().
+
+	* extent.c:
+	Fixed serious bug in decode_extent() with handling of empty extents.
+
+	* hfs_fs.h:
+	Small change so the PPC (and maybe other architectures?)
+	pick up the prototypes for the user-space access functions.
+
+	* super.c, file_cap.c, file_hdr.c, hfs_fs.h, file.c:
+	Updated for new user-space memory interface.
+
+Sun Dec  8 11:49:36 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir_nat.c:
+	Add special code for unlink(), and rename() in the .AppleDouble
+	directory and rmdir() of the .AppleDouble directory.
+
+	* inode.c:
+	Make the .AppleDouble directory writable.
+
+	* file_hdr.c:
+	Use AFP flags in version 1 headers (for Netatalk compatibility).
+
+	* trans.c:
+	Fixed bug with long names causing kernel Oops.
+
+Mon Oct  7 06:05:01 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, file_cap.c, file_hdr.c, hfs.h, extent.c, file.c, dir.c:
+	Fix types for various read/write/truncate computations.
+	Also allows compilation with 2.1.x kernels.
+
+Thu Sep 19 10:28:43 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml, version.c:
+	Bump version up to "pre-0.8-2".
+
+	* TODO:
+	Reformat the To Do list introducing prioritized categories.
+
+	* file_hdr.c, file.c:
+	Move comments about mmap() for headers from file.c to file_hdr.c.
+	Also revise the reasoning for not yet having it implemented.
+
+	* dir_nat.c, dir_cap.c, dir_dbl.c:
+	Remove 'hfs_' prefix from names of some purely local functions.
+
+	* dir_dbl.c, TODO:
+	Under AppleDouble make create(), mkdir(), mknod(), unlink(), rename()
+	and rename() check against header files when arguments start with '%'.
+
+	* super.c, hfs_fs_sb.h, hfs_fs.h, dir_dbl.c, dir_nat.c, dir_cap.c,
+	  dir.c, README.sgml:
+	Fix problem that prevented creating %RootInfo or .rootinfo in all
+	directories in addition to preventing deletion from the root directory.
+
+	* TODO:
+	Remove writable header files from the To Do list.
+
+	* README.sgml:
+	Add extensive discussion of writing to HFS filesystems and
+	the format of the special files.
+
+	* file_hdr.c:
+	Generate the 'homefs' field for version 1 header files.
+
+Wed Sep 18 23:07:45 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, file_cap.c:
+	Comment the definition of (struct hfs_cap_info).
+
+	* version.c, README.sgml:
+	Bump version up to "pre-0.8-1" and update the "How can I write?" FAQ.
+
+	* file_hdr.c:
+	Implement hdr_write() and hdr_truncate()!!
+
+	* hfs_fs_i.h, inode.c:
+	Make hdr_layout per-inode (not per-file) so hdr_truncate() will work.
+
+	* file.c, hfs.h, catalog.c, extent.c, balloc.c:
+	hfs_extent_adj() now uses fork->lsize to determine the target file size.
+
+Sun Sep 15 07:55:24 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml, trans.c:
+	Prevent creation of files & directories with '\0' or ':' in their names.
+
+	* string.c, hfs_fs.h, hfs.h, dir_dbl.c, dir_nat.c, dir_cap.c:
+	With case=lower could have run off end of string.
+
+Tue Sep 10 12:05:47 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode.c:
+	Small clean up of HFS_FIL_LOCK handling.
+
+	* inode.c:
+	Fix notify_change() not to accidentally make metadata executable.
+
+	* hfs_fs.h:
+	AppleSingle files should have HFS_ITYPE_NORM.
+
+	* inode.c:
+	Return to old behavior where MdDat = i_mtime.
+
+	* dir_dbl.c:
+	Fix serious bug in hfs_dbl_readdir() that would lock-up access to a
+	directory if one tried to write to a directory they had previously read.
+
+	* file.c:
+	Fix hfs_do_write() to adjust the fork's 'lsize' if it changed.
+
+	* inode.c, file_cap.c:
+	Allow truncate() to be called even on metadata.
+	Any size changes will last only until the next iput() of the inode.
+	Truncating a header file doesn't yet truncate the resource fork.
+
+	* inode.c:
+	Allow chmod() on a directory if it doesn't actually change i_mode.
+
+	* hfs_fs.h, trans.c, super.c:
+	Rename hfs_cap2mac() to hfs_colon2mac().
+	Rename hfs_apl2mac() to hfs_prcnt2mac().
+
+	* file_hdr.c:
+	Move header construction out of hdr_read() to create hdr_build_meta().
+
+	* hfs.h:
+	Add byte-order independent conversions: U32->U16, U32->U8 and U16->U8.
+
+	* file.c, file_cap.c, hfs_fs.h:
+	Rename fix_perms() to hfs_file_fix_mode() and
+	move it from from file_cap.c to file.c.
+
+	* README.sgml, super.c:
+	Make the default for the names mount option vary with the fork option.
+
+	* file_cap.c:
+	The umask was applied incorrectly in fix_perms().
+
+Mon Sep  9 13:11:28 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml:
+	Note that it compiles on m68k machines, but needs more testing.
+
+	* hfs_sysdep.h, Makefile:
+	Changes to compile unmodified on m68k (and possibly other machines).
+
+	* dir_cap.c:
+	hfs_cap_readdir() was mistakenly producing .rootinfo entries for
+	the .finderinfo and .resource subdirectories of the root directory.
+
+	* inode.c:
+	A directory's i_size was too small by 1 under CAP, so hfs_cap_readdir()
+	would omit the last directory entry.  i_nlink was also too large by 1.
+
+Sun Sep  8 12:56:06 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* file_hdr.c:
+	Rewrite hdr_read() to be more efficient and to deal correctly with
+	descriptors having lengths that differ from the actual size of the data.
+
+	* file_cap.c:
+	Add write support for CAP finderinfo files!!
+
+	* super.c, inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c,
+	  file_nat.c, file_hdr.c, file.c, file_cap.c, Makefile, dir.c:
+	Generate metadata (header files and CAP finderinfo files) on-the-fly.
+	The files file_{dbl,nat}.c are merged into file_hdr.c as a result.
+
+Sat Sep  7 08:09:24 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml:
+	Fix silly grammatical error.
+
+Fri Sep  6 09:17:12 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs_sb.h, super.c:
+	No need to cast type of s_reserved.
+
+	* file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, file_cap.c, dir_cap.c:
+	Add the missing NULL readpage and writepage entries to the inode_ops.
+
+	* file_dbl.c, file_nat.c, file.c, file_cap.c:
+	Cleanup error checking for read() and write().
+
+Thu Sep  5 05:29:53 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version up to "0.7.2".
+	User-visible changes from 0.7.0:
+	+ Corrected CAP finderinfo file format.
+	+ Support for more features of CAP finderinfo files.
+	+ No longer requires gcc 2.7.0 or newer.
+	+ Now implements mknod() system call.
+
+	* hfs_fs.h, dir_nat.c, file_cap.c, file_nat.c, README.sgml, dir_cap.c:
+	Include the CAP and Netatalk copyright notices.
+
+	* hfs_fs.h, file_cap.c:
+	Repair and improve CAP support.
+
+	* catalog.c:
+	Oops! The BkDat for new files and directories was in 1972 when
+	it should have been in 1904 (not that it matters that much).
+
+	* inode.c:
+	The HFS MdDat should be the larger of the i_mtime and i_ctime.
+
+	* README.sgml:
+	Change 'm_time' to 'i_mtime'.
+
+Wed Sep  4 13:27:35 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version up to "0.7.1".
+	User-visible changes from 0.7.0:
+	+ Minor bug in CAP finderinfo file format fixed.
+	+ No longer requires gcc 2.7.0 or newer.
+	+ Now implements mknod() system call.
+
+	* README.sgml:
+	Removed note about needing gcc 2.7.0 or newer.
+
+	* file.c:
+	Optimize hfs_do_read() based on the fact that HFS has no holes in files.
+	Minor code formatting changes.
+
+	* hfs.h, hfs_sysdep.h, mdb.c, extent.c, file.c, btree.c, catalog.c,
+	  balloc.c, bnode.c:
+	Reorganize memory management routines.
+	hfs_malloc() and hfs_free() are the main routines.
+	The macros FREE() and MALLOC() are gone.
+	HFS_NEW() and HFS_DELETE() are new 'shorthand' macros.
+
+	* btree.c:
+	Fix broken debugging code.
+
+	* super.c, hfs.h, mdb.c, part_tbl.c, Makefile:
+	Separate partition table handling into its own file.
+
+	* dir.c:
+	Spelling fixes.
+
+	* sysdep.c:
+	Oops!  Error check got sense reversed while editing.
+
+	* mdb.c, sysdep.c, hfs.h, hfs_btree.h, hfs_sysdep.h, btree.c, extent.c,
+	  bfind.c, bnode.c, balloc.c:
+	Make hfs_buffer a pointer to a buffer_head, rather than a buffer_head.
+
+	* hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+	Add a mknod() entry to the inode_operations for normal directories.
+	All it is good for is letting root create regular files.
+
+	* file_dbl.c, file_nat.c, file.c, file_cap.c, dir_cap.c, dir_dbl.c,
+	  dir_nat.c:
+	Add the missing NULL entries to the end of the file_operations.
+
+	* super.c, hfs_btree.h, hfs_fs.h, mdb.c, extent.c, hfs.h, catalog.c:
+	Make the remainder of the (untested) changes
+	to allow compilation with gcc 2.6.3.
+
+	* hfs_fs.h:
+	Fix hfs_fs.h to work with gcc 2.6.3.
+
+	* hfs_fs.h:
+	(struct hfs_cap_info) should never have been 'packed'.
+
+	* BUG_INFO:
+	Use -V for getting version of module utilities.
+
+	* super.c, sysdep.c, trans.c, hfs_fs_sb.h, inode.c, hfs_fs.h,
+	  hfs_fs_i.h, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+	  dir_nat.c, file.c, dir.c, dir_cap.c:
+	Fix up hfs_fs{,_i,_sb}.h in preparation for inclusion in kernel.
+
+Tue Sep  3 23:58:03 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs.h:
+	Change eventual destination to linux/fs/hfs rather than include/linux.
+
+	* super.c, inode.c, mdb.c, hfs_btree.h, hfs_fs.h, hfs_sysdep.h,
+	  file_dbl.c, file_nat.c, hfs.h, dir_nat.c, extent.c, dir_dbl.c,
+	  catalog.c, dir_cap.c, brec.c, btree.c, binsert.c, bnode.c, bdelete.c,
+	  bfind.c, bins_del.c, balloc.c:
+	Replace all the swap{16,32}() stuff w/ ntohl() and friends.
+
+Fri Aug 30 09:51:23 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Rewrite installation instructions and bump version up to "0.7.0".
+
+	* Makefile:
+	Remove the INCDIR variable; we now rely on the
+	user to have the correct links in /usr/include.
+
+Mon Aug 26 12:25:41 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Reformat the documentation and bump version up to "pre-0.7-9".
+	Hopefully this will become version 0.7 in a few days.
+
+Thu Aug 22 08:00:44 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml, version.c:
+	Bump version up to "pre-0.7-8".
+
+	* file_nat.c, file_dbl.c:
+	AppleDouble headers had resource fork size in wrong byte order.
+
+Wed Aug 21 05:22:28 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version up to "pre-0.7-7".
+
+	* bnode.c:
+	Fixed a long-standing bug in hfs_bnode_lock().
+	This bug occasionally caused lock-up under heavy load.
+
+Tue Aug 20 09:15:10 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* README.sgml, version.c:
+	Bump version up to "pre-0.7-6".
+
+	* catalog.c:
+	Fix a deadlock problem in catalog readers/writers locking.
+
+	* bins_del.c:
+	hfs_bnode_update_key() was still corrupting the header node sometimes.
+
+	* catalog.c, dir.c:
+	Fix problem with extending the catalog B-tree hanging hfs_cat_commit().
+	Fix a race that could delete a non-empty directory.
+
+Sun Aug 18 23:16:43 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version to "pre-0.7-5" for test release.
+
+	* dir_cap.c, README.sgml:
+	Change ".:rootinfo:" to ".rootinfo".
+
+	* hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c:
+	Mangle the names as first step in hfs_{cap,dbl,nat}_lookup().
+	Use the new hfs_streq() to catch mixed case matches to the special
+	files and directories in hfs_{cap,dbl,nat}_lookup().
+	Store reserved names only once.
+
+	* dir.c, hfs.h, string.c:
+	Implement hfs_streq() which tests for string equality more
+	rapidly than hfs_strcmp() by checking for equal length first,
+	and use it when checking for reserved names.
+
+	* inode.c, TODO, dir_cap.c, dir_dbl.c, README.sgml:
+	Provide the metadata for the root directory for the CAP and AppleDouble
+	schemes in the files ".:rootinfo:" and "%RootInfo", respectively.
+
+	* TODO, super.c:
+	Add (untested) support for the old Mac Plus style of partition map.
+
+	* bdelete.c, TODO:
+	Note the possibility of bdelete() to hanging on a corrupted B-tree.
+
+	* TODO:
+	Add items corresponding to some of the 'XXX' comments in the sources.
+
+	* dir_dbl.c, dir_cap.c:
+	Update comments, removing ref. to a comment that once existed in inode.c
+
+	* catalog.c:
+	Remove some redundant locking and error checks
+	that had been previously marked as questionable.
+
+Sat Aug 17 08:06:56 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* binsert.c, bfind.c, bins_del.c, balloc.c, bdelete.c:
+	Edited some comments for correctness.
+
+	* README.sgml, version.c:
+	Bump version up to "pre-0.7-4" in preparation for snapshot release.
+
+	* Makefile:
+	Have 'make dep' delete the *.o and *.s files.
+
+	* catalog.c, hfs.h, TODO, bfind.c:
+	Move looping from hfs_cat_next() into hfs_bsucc(),
+	where it can be done an entire node at a time.
+
+Fri Aug 16 05:02:59 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* TODO:
+	Add AppleShare support to the list of goals.
+
+	* trans.c, super.c, hfs_fs.h, README.sgml:
+	Add a "names=netatalk" mount option, since
+	Netatalk quotes initial periods and CAP doesn't.
+
+	* Makefile:
+	Oops! Had removed the 'include .depend' from Makefile.
+
+	* inode.c, hfs_fs.h, file_nat.c, file_dbl.c, file.c, dir_nat.c,
+	  dir_dbl.c, dir_cap.c, dir.c, README.sgml:
+	Update for 2.0.1 and newer kernels.
+
+	* Makefile:
+	Get rid of ifeq stuff and use a .tmpdepend file to make sure
+	a failed 'make depend' doesn't allow a 'make hfs.o'.
+
+Wed Aug 14 01:03:01 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump version up to "pre-0.7-3" in preparation for snapshot release.
+
+	* btree.c, extent.c, bnode.c:
+	Fix up some debugging code.
+
+Tue Aug 13 12:42:12 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* version.c, README.sgml:
+	Bump revision to "pre-0.7-2".
+
+	* super.c, sysdep.c, mdb.c, file_nat.c, inode.c, file_cap.c,
+	  file_dbl.c, file.c, extent.c, dir.c, catalog.c, btree.c, bnode.c,
+	  balloc.c:
+	Added the remaining missing function comments.
+
+	* Makefile, README.sgml:
+	Simplify the default make rule to build the dependency file AND hfs.o.
+	Change the installation instructions to reflect the change.
+
+	* hfs.h:
+	Added missing structure comments.
+
+	* bdelete.c:
+	Merge bdelete_brec() back into hfs_bdelete().
+	Add missing function comments.
+
+
+	* extent.c:
+	Insignificant code change removing an unneeded indirection.
+
+	* btree.c, hfs_btree.h, balloc.c, bnode.c:
+	Add a 'sys_mdb' field to (struct hfs_btree).
+
+	* extent.c, hfs_sysdep.h, sysdep.c, bnode.c, balloc.c, bfind.c,
+	  Makefile:
+	Move hfs_buffer_read() from hfs_sysdep.h to sysdep.c so it can use
+	the symbol HFS_SECTOR_SIZE rather than the manifest constant 512.
+	Have hfs_buffer_read() print an error message,
+	and remove redundant errors from the callers.
+
+	* hfs_sysdep.h, mdb.c, super.c, file.c, hfs.h, hfs_btree.h, catalog.c,
+	  extent.c, btree.c, balloc.c, bfind.c, bnode.c:
+	Get rid of the type hfs_device and the fields of that type,
+	using the type hfs_sysmdb and the 'sys_mdb' field in its place.
+
+	* Makefile:
+	Fix definition of HDRS variable.
+
+	* README.sgml, version.c:
+	Bump version up to "pre-0.7-1".
+
+	* Makefile:
+	Separate sources and headers into three groups:
+	B-tree code, HFS code and Linux code.
+
+	* bitmap.c, bitops.c, hfs.h, hfs_sysdep.h, balloc.c:
+	Implemented portable set of bit operations in hfs_sysdep.h
+
+	* mdb.c, hfs_sysdep.h, hfs_btree.h, extent.c, btree.c, bitmap.c,
+	  bnode.c, balloc.c:
+	Implement a portable set of buffer operations in hfs_sysdep.h
+
+	* TODO:
+	Remove note about separating header files into two parts.
+
+	* catalog.c:
+	Remove call to hfs_mdb_dirty(), since the hfs_brec_relse() does it.
+
+	* hfs.h, extent.c, file.c:
+	Move hfs_getblk() from extent.c to file.c, since that is now the
+	only file that actually uses it.
+
+	* balloc.c:
+	Replace use of hfs_getblk() in balloc.c with a local function
+	(get_new_node()) that doesn't retry, since B-trees can't shrink.
+
+	* hfs.h, hfs_btree.h, hfs_sysdep.h, mdb.c, extent.c:
+	Make hfs_buffer a typedef.
+
+	* inode.c, hfs.h, hfs_sysdep.h, dir.c:
+	Change hfs_sysentry to a typedef.
+	Rename 'sysentry' field of (struct hfs_cat_entry) to 'sys_entry'.
+
+	* super.c, mdb.c, catalog.c:
+	Rename hfs_cat_sync() to hfs_cat_commit() and call it
+	from hfs_mdb_commit() rather than from hfs_write_super().
+
+	* catalog.c, file.c:
+	Minimize the calls to hfs_mdb_dirty().   Now called when:
+	1) A buffer holding a volume bitmap block is dirtied.
+	2) A dirty B-tree node is written back to the buffers.
+	3) A dirty catalog entry is written back to the buffers.
+
+	* hfs_sysdep.h, hfs.h:
+	Make hfs_sysmdb a typedef.
+
+Sun Aug 11 08:46:10 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_sysdep.h, extent.c, hfs.h:
+	Replace hfs_mdb_{lock,unlock} with more portable
+	scheme using a wait queue in the MDB.
+
+	* hfs.h, hfs_btree.h, hfs_sysdep.h, bnode.c, catalog.c, binsert.c:
+	Make hfs_wait_queue a typedef'd pointer to a (struct wait_queue).
+	Rename hfs_wait_on() to hfs_sleep_on().
+
+	* catalog.c, hfs_sysdep.h, super.c, bfind.c, bnode.c, balloc.c:
+	Implemented hfs_dev_name() in hfs_sysdep.h
+	as a portable call to produce a device name.
+
+	* super.c, hfs.h, mdb.c:
+	Rename hfs_mdb_read() to hfs_mdb_get(), and don't take a
+	'sys_mdb' argument.  That's the callers responsibility.
+
+	* sysdep.c, Makefile:
+	Remove the pointless file sysdep.c
+
+	* README.sgml:
+	Clean up the "System Requirements" section.
+
+Sat Aug 10 22:41:24 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* sysdep.h, sysdep.c, super.c, hfs_sysdep.h, mdb.c, string.c,
+	  hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree_private.h, hfs_btree.h,
+	  file_cap.c, file_dbl.c, file_nat.c, hfs.h, file.c, dir_nat.c,
+	  extent.c, dir.c, dir_cap.c, dir_dbl.c, catalog.c, bnode.c, brec.c,
+	  btree.c, binsert.c, bitmap.c, bitops.c, bfind.c, bins_del.c,
+	  Makefile, balloc.c, bdelete.c:
+	Includes the hfs.h that was missing from the previous check in.
+	MAJOR include-file cleanup:
+		hfs_btree.h merged into hfs.h
+		hfs_btree_private.h renamed hfs_btree.h
+		sysdep.h renamed hfs_sysdep.h
+	Fixed some minor portability fixes shown up by the header split.
+
+	* README.sgml:
+	Add instructions for a dealing with a missing linux/version.h
+
+	* hfs_fs.h, mdb.c, string.c, catalog.c, extent.c, btree.c, bitmap.c,
+	  bitops.c, bnode.c, brec.c, bins_del.c, binsert.c, bdelete.c, bfind.c,
+	  balloc.c:
+	Major split of hfs_fs.h into Linux-specific
+	part (hfs_fs.h) and HFS-specific part (hfs.h).
+
+	* file.c, extent.c:
+	Move hfs_getblk() from file.c to extent.c
+
+	* sysdep.h, super.c, mdb.c, hfs_fs_sb.h, hfs_fs.h, file.c, extent.c,
+	  catalog.c, bnode.c, bitmap.c:
+	Make the field 's_mdb' in (struct hfs_sb_info) a pointer to
+	the MDB, rather than the actual MDB.  This allowed the definition
+	of (struct hfs_mdb) to be moved from hfs_fs_sb.h to hfs_fs.h.
+
+	* ccache.c, hfs_fs.h, Makefile, catalog.c:
+	Merged ccache.c and catalog.c into the latter.
+	Moved definition of (struct hfs_cat_rec) into catalog.c
+
+	* extent.c:
+	Oops!  Last set of changes didn't compile but they're OK now.
+
+	* hfs_btree.h, hfs_fs.h, mdb.c, ccache.c, extent.c, btree.c:
+	Move the definition of (struct hfs_raw_extent) inside
+	extent.c and treat it as simple array of U16's elsewhere.
+
+	* hfs_fs.h, dir_dbl.c, dir_nat.c, ccache.c, catalog.c, dir_cap.c:
+	Make hfs_cat_next() return the CNID and cdrType of the entry.
+	Now catalog.c and ccache.c are the only files which
+	depend on the structure of a catalog record on disk.
+
+	* dir.c, hfs_fs.h, catalog.c:
+	Replace hfs_cat_new_{file,dir}() with hfs_cat_{create,mkdir}()
+	which are wrappers for what used to be hfs_cat_create().
+
+	* hfs_fs.h, mdb.c, super.c, Makefile:
+	Split super.c into super.c (Linux stuff) and mdb.c (MDB stuff).
+
+	* super.c, hfs_fs_sb.h:
+	Add the MDB field 'drAtrb' to (struct hfs_mdb) as the field 'attrib'.
+
+	* hfs_fs_sb.h, super.c:
+	Split hfs_read_super() into hfs_read_super() and hfs_mdb_read().
+
+	* super.c, hfs_fs_sb.h:
+	Remove the unneeded 'hs' field from (struct hfs_mdb).
+
+	* TODO:
+	Remove item about hfs_notify_change() needing to update metadata.
+
+	* inode.c, hfs_fs.h, hfs_fs_sb.h, file_cap.c, file_dbl.c, file_nat.c,
+	  file.c, dir.c:
+	Add a flags argument to hfs_{cap,dbl,nat}_buildmeta() so that
+	it only builds the parts that are currently out-of-date.
+	Call hfs_{cap,dbl,nat}_buildmeta() through hfs_update_meta()
+	in hfs_notify_change() and hfs_rename() to update the metadata.
+
+	* dir.c:
+	Make test for normal dir in update_dirs_{plus,minus}() more explicit.
+
+	* inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c,
+	  file.c, README.sgml, dir_cap.c:
+	Resolve the "meta-data" vs. "metadata" rivalry in favor of the latter.
+
+	* btree.c:
+	Simplify some debugging code.
+
+	* hfs_btree_private.h, bnode.c, btree.c, balloc.c:
+	Put the in-core copy of the header node IN the
+	B-tree structure rather than just a pointer to it.
+
+	* hfs_btree_private.h, btree.c, bnode.c:
+	Have hfs_btree_commit() call hfs_bnode_commit()
+	to commit the header and root nodes.
+
+	* hfs_fs.h, super.c, hfs_btree_private.h, btree.c, hfs_btree.h,
+	  balloc.c:
+	Change hfs_commit_mdb() to hfs_mdb_commit().
+	Make hfs_mdb_commit() call hfs_btree_commit().
+	Move code to update B-tree size and extent
+	from hfs_btree_extend() to hfs_btree_commit().
+	Make hfs_btree_extend() call hfs_mdb_commit().
+
+	* super.c:
+	Change hfs_commit_super() to hfs_commit_mdb().
+
+	* btree.c, bnode.c, bfind.c:
+	Fixed up broken debugging code and error messages.
+
+	* super.c, hfs_btree_private.h, btree.c, hfs_btree.h, bdelete.c,
+	  binsert.c, balloc.c:
+	Now use write-back caching of B-tree header fields.
+
+	* hfs_fs.h:
+	Get rid of the add{16,32}() inlines as they are no longer used.
+
+	* hfs_btree_private.h, binsert.c, btree.c, bdelete.c, bfind.c, balloc.c:
+	All the needed fields of the B-tree header are
+	now cached for reading, but not yet writing.
+
+	* TODO:
+	Remove "Implement write count" from TODO list.
+
+	* file.c, super.c, bnode.c:
+	Implement write count.
+
+	* catalog.c:
+	Fix directory entry counting in hfs_cat_move().
+
+	* balloc.c:
+	Simplify hfs_btree_extend(), since the allocation
+	request will get rounded up to the clumpsize.
+
+	* extent.c:
+	Honor clumpsize when allocating blocks to files.
+
+	* file_cap.c, file_dbl.c, file_nat.c, super.c, dir.c, file.c,
+	  ccache.c, catalog.c, balloc.c:
+	Mark 44 functions in need of commenting.
+
+	* hfs_fs_sb.h, super.c, extent.c, hfs_fs.h, ccache.c, btree.c, balloc.c:
+	Record clumpsize in allocation blocks rather than 512-byte blocks.
+
+	* sysdep.h, super.c, TODO, balloc.c, hfs_fs_sb.h:
+	Now updates the backup MDB when a B-tree grows.
+
+	* extent.c:
+	hfs_extent_free() had test against NULL backward.
+	The result is that access to a file with extents in the extents
+	B-tree would result in an infinite loop in hfs_cat_put().
+
+	* hfs_fs_sb.h, super.c, hfs_fs.h:
+	Reorganize partition map code to get size of partition
+	in preparation for dealing with the alternate MDB.
+
+Fri Aug  9 03:25:13 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* Makefile:
+	Add make rules for README.{ps,info}
+
+	* README, README.sgml, DOC, FAQ, Makefile, .cvsignore, 
+	Merge CHANGES into ChangeLog.
+	Merge DOC, FAQ and README into README.sgml.
+	Add make rules for building README.{txt,dvi}
+
+	* BUG_INFO, Makefile:
+	Added a BUG_INFO script which attempts to collect some useful
+	information which I'd like to see in every bug report I receive.
+
+	* Makefile, version.c:
+	Added version.c which contains a version string.
+
+Thu Aug  8 21:48:24 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* trans.c:
+	Fix Latin-1 -> Macintosh filename mapping to change colons to pipes.
+
+	* trans.c:
+	Fixed Mac->Latin-1 translation to behave as documented for the
+	extended 8-bit characters without corresponding Latin-1 characters.
+
+	* inode.c, super.c, file.c, hfs_fs_i.h, hfs_fs_sb.h, DOC:
+	Added a conv={binary,text,auto} mount option similar to that of the
+	msdos, hpfs and iso9660 filesystems, but applying only to data forks.
+	As compared to those filesystems, HFS has the advantage that only a
+	single CR need be converted to a NL, rather than a CR/NL sequence, so
+	it is quite safe to seek in the file.
+	Additionally the 'Type' field is far more reliable indicator of text
+	files than a file extension.
+
+	* super.c:
+	Simplified parsing of mount options.
+
+	* super.c:
+	Oops!  The part=<n> mount option was being parsed in octal!
+
+	* TODO:
+	Remove "case=lower" from the list of goals.
+
+	* super.c, hfs_fs.h, hfs_fs_sb.h, string.c, dir_dbl.c, dir_nat.c,
+	  dir_cap.c, DOC:
+	Resurrect the case={asis,lower} mount option.
+
+	* dir.c:
+	Simpler test for "normal" directory in update_dirs_{plus,minus}().
+
+	* hfs_fs_sb.h, super.c, dir.c, hfs_fs.h, catalog.c, DOC:
+	Add mount options to specify what Type and Creator will be used for
+	new files and change the default from NULLs to "????".
+
+Wed Aug  7 11:32:22 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* catalog.c:
+	In hfs_cat_next() use entry->cnid rather than the key of the initial
+	brec to identify the end of the directory.
+
+	* README:
+	Update for pre-0.7 version.
+
+	* hfs_fs.h:
+	Create versioned module if CONFIG_MODVERSIONS is set in linux/config.h
+
+	* TODO:
+	Note need for special steps for unaligned accesses on some machines.
+
+	* FAQ:
+	Added Q0: What is HFS?
+	Added Q7: Does hfs_fs work w/ 400k and 800k diskettes?
+	Brought Q6 (about writability) up to date.
+	Made a few other answers more verbose.
+
+Tue Aug  6 00:58:46 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* Makefile:
+	Changed 'snapshot' rule to include cvs tag command.
+
+	* hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+	Implemented readers half of dir locking scheme so readdir() should
+	produce consistent results and count_dir_entries() is not race prone.
+
+	* catalog.c:
+	hfs_cat_move() was calling hfs_cat_decache() after changing
+	the key rather than before, corrupting the hash lists.
+
+Mon Aug  5 14:03:46 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, catalog.c:
+	Implemented the writers half of a locking scheme for directories.
+
+	* inode.c:
+	Fixed a serious bug in hfs_notify_change() that would allow a chmod()
+	on directory meta-data and would cause the directory inode (if it was
+	in memory at the time) to change into a file inode.
+
+	* inode.c:
+	Fixed a problem with write permissions on directory meta-data.
+
+	* dir_dbl.c, dir_nat.c, dir_cap.c:
+	hfs_{cap,dbl,nat}_readdir() now return the correct value in the 'd_ino'
+	field of the dirent for all cases, something I think has always been
+	done incorrectly until now.
+
+	* dir_nat.c, inode.c, dir_cap.c:
+	In hfs_{cap,nat}_lookup() take advantage of the
+	'file_type' field of (struct hfs_inode_info).
+
+	* TODO:
+	Removed two accomplished goals (rename() and improved readdir()).
+
+	* inode.c, dir_dbl.c, dir_nat.c, hfs_fs_i.h, dir.c, dir_cap.c:
+	Rewrite hfs_{cap,dbl,nat}_readdir() to take advantage of hfs_cat_next().
+	They now use a uniform 'i_size' for all inodes for a given directory.
+	This simplifies update_dirs_{plus,minus}() and eliminates the need for
+	the 'file_size' and 'dir_link' fields of (struct hfs_inode_info).
+	For the CAP and Netatalk schemes the meta-data directories are now the
+	last entries rather than coming just after '.' and '..'.  This is in
+	preparation for the day when we can write to the files in those
+	directories, and ensures that when using 'tar' to copy HFS filesystems
+	the file or directory will be created before the meta-data is written.
+	Otherwise we could be stuck writing meta-data and not knowing if it is
+	for a file or a directory!
+
+	* ccache.c:
+	Updated count_dir_entries() for new hfs_cat_next().
+
+	* hfs_fs.h, catalog.c:
+	hfs_cat{nth,next}() no longer take a 'types' argument,
+	so they now return all entries.
+	hfs_cat_next() now uses the ParID of the key to detect
+	the end of the directory.
+	hfs_cat_nth() now accepts n=0 as a valid input, requesting the thread.
+
+	* trans.c, string.c, super.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c,
+	  dir_dbl.c, catalog.c:
+	Rename (struct hfs_cname) to the more appropriate (struct hfs_pstr).
+
+	* hfs_fs.h, hfs_btree.h:
+	Move some constants from hfs_fs.h to hfs_btree.h
+
+	* bdelete.c, hfs_btree.h:
+	Remove hfs_bdelete_brec() from public B-tree interface.
+
+	* hfs_btree_private.h, hfs_fs.h, btree.c, hfs_btree.h, bnode.c, brec.c,
+	  bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c, Makefile:
+	Split B-tree stuff into public and private parts:
+		brec.c split into bfind.c and brec.c
+		hfs_btree.h split into hfs_btree.h and hfs_btree_private.c
+
+	* inode.c:
+	The tests and sets of the HFS_FIL_LOCK bit where all reversed!
+
+	* hfs_fs.h, ccache.c:
+	Redo some ccache stuff, removing the 'error' field from
+	(struct hfs_cat_entry) and ensuring that hfs_cat_put()
+	will not sleep on an uninitialized entry.
+
+Sun Aug  4 23:43:28 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* sysdep.h:
+	Change swap{16,32}() back to macros since hton[ls]() are functions.
+
+	* hfs_fs.h, ccache.c:
+	Use only lowest order byte of parent CNID in hashing a catalog key.
+
+	* bdelete.c:
+	The "improved" bdelete() was TOO paranoid looking for missing parents.
+
+	* ccache.c:
+	Get rid of pointless swap16const(0).
+
+	* hfs_fs.h, inode.c, extent.c, ccache.c, dir_cap.c, dir_nat.c,
+	  binsert.c, catalog.c:
+	Store cnid and associated constants in big-endian byte order.
+	This reduces the number of byte-order swaps required.
+
+	* sysdep.h:
+	Make swap32() and swap16() inline functions.
+
+	* dir_nat.c, dir_cap.c, dir_dbl.c:
+	Added hfs_rename() to the inode_operations for normal directories.
+
+	* dir.c, hfs_fs.h:
+	Added hfs_rename() and cleaned up hfs_{create,mkdir,unlink,rmdir}().
+
+	* catalog.c:
+	Added the missing check for moving a directory into itself.
+
+	* catalog.c, ccache.c, hfs_fs.h:
+	Implement a nearly ideal hfs_cat_move().
+	It still needs to prevent moving a directory into itself.
+	The functions hfs_cat_{create,delete,move}() still need work with
+	respect to their atomicity (especially vs. readdir).
+
+	* bdelete.c:
+	Fixed a serious bug in hfs_bdelete_brec() that would yield a corrupted
+	b-tree when the first record in a bnode was deleted.
+	Made bdelete() more aggressive when checking for missing parents.
+
+Sat Aug  3 06:11:50 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* btree.c, super.c:
+	Fixed a problem that caused a kernel oops when no HFS filesystem
+	is found.
+
+Wed Jul 24 13:06:12 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* catalog.c:
+	Remove race in hfs_cat_create() that could overflow directory valence.
+
+	* catalog.c:
+	Fix hfs_cat_create() so the parent directory doesn't get deleted
+	out from under it.  Otherwise we could have created files and
+	directories in deleted directories.
+
+	* hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+	Redo hfs_cat_{next,nth}() in terms of which entry types to
+	allow, rather than which to skip.
+
+	* catalog.c:
+	The function hfs_cat_create() would fail to hfs_cat_put(entry) if
+	the 'record' argument was invalid or if the 'result' argument was NULL.
+
+	* dir.c:
+	The functions hfs_{create,mkdir,unlink,rmdir} all failed to
+	call iput() when their arguments conflicted with a reserved name.
+
+	* catalog.c, hfs_fs_sb.h:
+	Start over on rename().  Still unfinished.
+	Fix silly bug in hfs_cat_create() that made it always fail.
+
+	* ccache.c:
+	Fix byte-order bug in write_entry().
+
+Tue Jul 23 12:12:58 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+	Remove the macros KEY() and PARENT() since the key is now easy
+	to access through the catalog entry.
+	Replace the macros NAME{IN,OUT}() with inline functions
+	hfs_name{in,out}() to gain type checking of arguments.
+
+	* catalog.c:
+	Remove the macro TYPE().
+
+	* inode.c, file_dbl.c, file_nat.c, file.c, file_cap.c:
+	Remove the #define's of the unused macro KEY().
+
+	* hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, dir.c:
+	Replace hfs_lookup_parent() in dir.c with hfs_cat_parent() in catalog.c.
+	This new function performs locking to protect against rename() changing
+		the parent during I/O.
+	It is also intended for use with files as well as directories.
+	Change hfs_{cap,dbl,nat}_lookup() to use the new function.
+
+	* dir.c, hfs_fs.h, catalog.c:
+	Remerge hfs_cat_{create,mkdir}() into hfs_cat_create() and resurrect
+		hfs_cat_new_{file,dir}().
+	Fix hfs_cat_{create,delete} to use the improved catalog cache for
+		locking in place of directory-level create/delete locks.
+	Fix hfs_{create,mkdir}() to use the new hfs_cat_create().
+
+	* hfs_fs.h, ccache.c:
+	Rewrite parts to remove need for specialized create/delete locking.
+	Use new case-independent hash function.
+	Fix bug in hfs_cat_get() that would read an entry w/o locking it.
+	Call hfs_relinquish() before retrying a deleted entry in hfs_cat_get.
+	If there is a read error, then don't retry in hfs_cat_get().
+	Remove unused 'version' field from (struct hfs_cat_entry).
+
+	* sysdep.h:
+	Add hfs_relinquish(), a system-independent alias for schedule().
+
+	* hfs_fs.h, string.c:
+	Add hfs_strhash(), a simplistic case-independent hash function.
+
+	* hfs_fs.h, inode.c:
+	Make hfs_iget() an inline function.
+
+	* TODO:
+	Add a few goals and removed those that have been achieved.
+
+	* Makefile:
+	Add ccache.c to list of source files.
+	Add rule for *.s files and include them in the 'clean' rule.
+
+Wed Jul 17 17:22:45 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* sysdep.h, trans.c, string.c, super.c, hfs_fs_i.h, hfs_fs_sb.h,
+	  inode.c, hfs_btree.h, hfs_fs.h, file_dbl.c, file_nat.c, extent.c,
+	  file.c, file_cap.c, dir_dbl.c, dir_nat.c, ccache.c, dir.c,
+	  dir_cap.c, btree.c, catalog.c, bnode.c, brec.c, balloc.c:
+	Total rewrite of the inode-handling stuff to be centered around
+	a catalog entry cache (ccache.c).  This results not only in a far
+	more sensible way of doing things, but also removed many race
+	conditions. (The source and object code both got smaller too!)
+	Many small "undocumented features" were also fixed.
+	Replace HFS_CNAME with (struct hfs_cname).
+	rename() has been temporarily abandoned.
+
+Thu Jul 11 01:14:38 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir.c:
+	As written hfs_lookup_parent() had two overlapping read requests
+	in the catalog tree.  This could have led to deadlock.
+
+Wed Jul 10 09:27:00 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* catalog.c, hfs_fs.h, bdelete.c:
+	More work on getting rename() fleshed out.  Still not done.
+	Before I can finish it looks like I'll need to build a
+	mechanism for exclusive access to the catalog tree.  There
+	just doesn't seem to be any other way to get proper POSIX
+	semantics without a bunch of race conditions elsewhere.
+
+	* hfs_fs.h, inode.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c:
+	More work on the still incomplete rename() code.
+	Merge hfs_cat_add_{dir,file}() into hfs_cat_create().
+	Add file-thread support to hfs_cat_{create,delete,rename}.
+
+Tue Jul  9 09:43:15 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode.c, dir_dbl.c, dir_nat.c, extent.c, dir_cap.c:
+	The indirect (struct hfs_file) was causing blocks not to be freed
+	when files where deleted, and an omission in hfs_put_inode() was
+	preventing the inode from getting freed.  Both are now fixed.
+
+	* hfs_fs.h, dir_dbl.c, dir_nat.c, hfs_btree.h, catalog.c, dir_cap.c,
+	  bdelete.c:
+	Made unlink() and rmdir() more race resistant and did some more
+	work on the still incomplete code for rename().
+
+	* btree.c, bnode.c:
+	There was a serious race condition in the bnode cache, so
+	hfs_bnode_find() is now modeled after Linus's inode cache.
+
+Mon Jul  8 10:33:38 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs_i.h, inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+	  dir_nat.c, file.c, dir.c, dir_cap.c:
+	More changes to layout of (struct hfs_inode_info).
+
+	* super.c, inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs_i.h,
+	  hfs_fs_sb.h, file_nat.c, hfs_fs.h, file.c, file_cap.c, file_dbl.c,
+	  Makefile, catalog.c:
+	Implemented new layout for (struct hfs_inode_info) resulting in the
+	elimination of lots of duplicated code for hfs_*_write_inode(),
+	hfs_*_put_inode() and *_open() functions.
+	Merged inode_*.c files back into inode.c.
+	Not fully tested.
+
+	* TODO:
+	Add a few more of my goals to the list.
+
+	* README:
+	Documentation updates.
+
+	* inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+	  file.c, file_cap.c, file_dbl.c, file_nat.c, catalog.c:
+	(struct hfs_file) and metadata are read when file is opened or
+	truncated and are released by iput().
+
+Sun Jul  7 23:55:43 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, dir_nat.c, hfs_fs.h,
+	  hfs_fs_i.h, dir_cap.c, dir_dbl.c, catalog.c, dir.c:
+	(struct hfs_dir) is now inside (struct hfs_inode_info) once again.
+
+	* inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, file_nat.c,
+	  hfs_btree.h, hfs_fs.h, extent.c, file_cap.c, file_dbl.c, dir_nat.c,
+	  dir_cap.c, dir_dbl.c, btree.c, catalog.c, dir.c, bpath.c, brec.c,
+	  bins_del.c, binsert.c, bnode.c, bfind.c, balloc.c, bdelete.c,
+	  Makefile:
+	Remerged (struct hfs_bpath) and (struct hfs_brec), merging the
+	files bfind.c and bpath.c as a resurrected brec.c.
+
+Sat Jul  6 21:47:05 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+	  file_cap.c, file_dbl.c, file_nat.c, hfs_btree.h, dir_nat.c, extent.c,
+	  dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bfind.c, bpath.c,
+	  binsert.c, bdelete.c:
+	Renamed (struct hfs_brec_key) to (struct hfs_bkey).
+
+Tue May 28 07:53:24 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode_cap.c, catalog.c:
+	Spelling fixes.
+
+	* inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h,
+	  hfs_fs_i.h, hfs_fs_sb.h, file.c, file_dbl.c, file_nat.c, dir_dbl.c,
+	  dir_nat.c, extent.c, dir.c, dir_cap.c, catalog.c:
+	Structures got too big, so I had to add a layer of indirection
+	to (struct hfs_inode_info).
+	This means we must clear_inode() in inode_put().
+
+Mon May 27 01:32:42 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* catalog.c, file_cap.c:
+	Some sizeof() stuff now uses variable not type.
+
+	* hfs_fs.h:
+	Make HFS_I() and HFS_SB() inline to gain type checking.
+
+Sun May 26 13:34:17 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir_nat.c:
+	Oops.  Had left some debugging printk()s in place.
+
+	* file_dbl.c, file_nat.c, file_cap.c:
+	Cleaned up variable names for consistency.
+
+	* hfs_fs_sb.h:
+	Add a couple 'const's to function typedefs.
+
+	* hfs_fs.h:
+	Add and update function prototypes.
+	Cleaned up type names.
+	Fix debugging malloc code.
+	Add hfs_iget_by_name() as an inline function.
+
+	* sysdep.h:
+	Remove extra semicolon from macro definitions.
+
+	* super.c:
+	Use new hfs_iget_by_name() to get root inode.
+
+	* extent.c:
+	Cleaned up some variable naming for consistency.
+
+	* catalog.c:
+	Added (untested) code for hfs_cat_move_file().
+
+	* catalog.c:
+	Fix one missed call to hfs_cat_build_key().
+	Make hfs_cat_add_{file,dir}() take a cat_entry as an argument.
+	Add hfs_cat_new_{file,dir}() to generate new cat_entry's.
+
+	* dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+	Cleaned up type and variable names.
+	Updated calls to hfs_cat_build_key() and NAMEOUT()
+	Use new hfs_iget_by_*() calls.
+
+	* inode_cap.c, inode_dbl.c, inode_nat.c:
+	Cleaned up type and variable names.
+
+	* inode.c:
+	Update calls to hfs_cat_build_key().
+	Cleaned up type and variable names.
+	Implemented a hierarchy of hfs_iget_by*() calls.
+
+	* catalog.c:
+	Change hfs_cat_build_key() to take a HFS_CNAME as input.
+
+	* btree.c:
+	Initialize lsize and psize fields of file.
+
+	* trans.c:
+	Now passes type HFS_CNAME and has name/len in "normal" order.
+
+Tue May 21 07:02:34 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bnode.c:
+	Attempt to read invalid bnode would have led to an infinite loop under
+	certain circumstances.  One way to cause this was with an invalid
+	partition table which points beyond the end of the device.
+
+Sat May 11 12:38:42 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* sysdep.h, sysdep.c, inode_dbl.c, inode_nat.c, super.c, inode_cap.c,
+	  inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, file_nat.c,
+	  hfs_btree.h, extent.c, file.c, file_cap.c, dir_nat.c, dir.c,
+	  dir_cap.c, dir_dbl.c, btree.c, catalog.c, bitmap.c, bitops.c,
+	  bnode.c, bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c:
+	Another big wave of portability-oriented changes.
+
+Tue May  7 11:28:35 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* super.c, sysdep.c, sysdep.h, inode_cap.c, inode_dbl.c, inode_nat.c,
+	  hfs_fs_i.h, inode.c, file_nat.c, hfs_btree.h, hfs_fs.h, file.c,
+	  file_cap.c, file_dbl.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c,
+	  btree.c, catalog.c, dir.c, bnode.c, bpath.c, binsert.c, bitmap.c,
+	  bitops.c, bdelete.c, bfind.c, bins_del.c, Makefile, balloc.c:
+	Start a big move to abstract all the Linux-specific stuff
+	out of the lower levels.  Created sysdep.[ch] to hold it.
+
+	* FAQ, TODO:
+	Bring some documentation up-to-date.
+
+Fri May  3 20:15:29 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* super.c, inode_dbl.c, inode_nat.c, inode.c, inode_cap.c, extent.c,
+	  hfs_fs.h, hfs_fs_i.h, dir_dbl.c, dir_nat.c, catalog.c, dir.c,
+	  dir_cap.c, bpath.c, btree.c, binsert.c, bnode.c:
+	"FID reform": 'fid' became 'cnid' (Catalog Node ID), and is now
+	a field in (struct hfs_file).  The new name is more consistent
+	with Apple's documentation.  The presence of 'cnid' in (struct
+	hfs_file) help move more of the code toward OS-independence.
+
+	* inode_nat.c, super.c, trans.c, inode.c, inode_cap.c, inode_dbl.c,
+	  hfs_fs.h, file_cap.c, file_dbl.c, file_nat.c, dir_nat.c, extent.c,
+	  file.c, dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bnode.c,
+	  bpath.c, bins_del.c, binsert.c, bitmap.c, bitops.c, bdelete.c,
+	  bfind.c, balloc.c:
+	A lot of changes in what headers are included and in what order.
+
+Sat Apr 27 12:28:54 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* FAQ:
+	Updated for current writability status.
+
+	* .cvsignore:
+	Added ChangeLog.
+
+	* file_dbl.c, file_nat.c, file_cap.c, file.c, dir_dbl.c, dir_nat.c,
+	  dir_cap.c:
+	Added the default fsync() to all file_operations structures.
+
+	* dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+	Add rmdir() for normal directories.
+
+	* binsert.c:
+	I had messed up insertion so that is would sometime fail to
+	split the root, but its OK now.
+
+	* dir.c:
+	hfs_do_unlink() decremented directory counts rather than file counts.
+
+Wed Apr 24 13:20:08 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, bnode.c, hfs_btree.h:
+	Fixed a couple more type size assumptions.
+
+	* hfs_fs.h, balloc.c, bitmap.c, bitops.c:
+	"Portable" bitmap handling was wrong for just about everything but
+	the i386 and the "inverse big-endian" bit ordering that I thought
+	the m68k port was using.  It seems the m68k port is now using standard
+	big-endian bit-numbering conventions.
+	This code is now correct for the standard big- and little-endian bit
+	orderings. (which should cover all Linux systems?)
+	Also no longer assumes sizeof(long) == 4, though that might still be
+	a problem in other parts of the code.
+
+Tue Apr 23 19:19:27 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* FAQ:
+	Bring uptodate for this snapshot.
+
+	* Makefile:
+	Add FAQ to $(MISC)
+
+	* README, TODO:
+	Documentation updates.
+
+	* bdelete.c:
+	Spelling fixes.
+
+	* dir_cap.c:
+	In unlink() don't force metadata into memory if not present.
+
+	* bdelete.c:
+	Some function comments and some clean up.
+
+	* bins_del.c:
+	Added missing function comment for hfs_bnode_update_key().
+
+	* binsert.c, bitmap.c:
+	Spelling and grammar corrections to comments.
+
+	* hfs_btree.h, hfs_fs.h, bins_del.c, binsert.c, Makefile, bdelete.c:
+	Clean up of hfs_bdelete(), splitting bins_del.c into three files:
+		bins_del.c, binsert.c and bdelete.c
+
+	* bpath.c, bins_del.c:
+	hfs_bdelete() is now working "correctly", but needs some cleaning up.
+
+Mon Apr 22 05:35:41 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, bpath.c, hfs_btree.h, bins_del.c, bnode.c, balloc.c,
+	  bfind.c:
+	Rewrite bnode handling, heading toward a more write-behind approach.
+	Have done away with HFS_LOCK_BLIND.
+
+	* inode_dbl.c, inode_nat.c, extent.c, hfs_fs_i.h, inode_cap.c:
+	Was trying to truncate resource fork of directories!
+
+Sun Apr 21 08:15:43 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* balloc.c:
+	Updated to use truncate() to grow full trees.
+
+	* extent.c, hfs_fs.h, file.c, inode.c:
+	Added truncate() for normal files.
+
+	* bins_del.c:
+	hfs_bdelete() fixes for handling removal of root.
+
+	* inode_cap.c, inode_dbl.c, inode_nat.c:
+	Release storage for deleted files in hfs_*_put_inode().
+
+	* bitmap.c:
+	Make len=0 valid for hfs_{set,clear}_vbm_bits().
+
+	* super.c, inode.c, hfs_fs_i.h, hfs_fs_sb.h, btree.c, balloc.c:
+	Changed from clumpsize to clumpblks.
+
+	* inode_nat.c, hfs_fs.h, inode_cap.c, inode_dbl.c, btree.c, extent.c,
+	  balloc.c:
+	Some extent-related changes in preparation for truncate() support.
+
+Sat Apr 20 10:59:13 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, inode_dbl.c,
+	  dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+	Removed dir.valence from hfs inode.
+	Added unlink(), but still need truncate() and some more support
+	in hfs_*_put_inode() to free the disk space used by deleted files.
+
+	* bnode.c:
+	Check for NULL bnode in hfs_bnode_relse().
+
+	* bins_del.c:
+	Fixed a byte-order problem in bdelete_nonempty().
+
+	* hfs_fs.h, bnode.c, bpath.c, hfs_btree.h, balloc.c, bins_del.c:
+	First attempt at hfs_bdelete().
+
+	* dir.c:
+	The Finder would display strange things if it couldn't set frView.
+	Therefore initialize frView field for new directories.
+
+	* file_cap.c, file_dbl.c, file_nat.c, hfs_fs.h:
+	Define User/Finder info fields of catalog entry in more detail.
+
+	* hfs_fs.h:
+	HFS_BFIND_DELETE should require exact match.
+
+	* dir.c:
+	Set "record in use" bit of filFlags for new files.
+
+	* inode.c:
+	Was doing the wrong thing with i_ctime.
+
+	* dir_nat.c, dir_cap.c, dir_dbl.c:
+	Added some missing updates to the inode in hfs_*_{create,mkdir}().
+
+Sun Apr 14 00:10:52 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, file_dbl.c, file_nat.c, file.c:
+	Work around the ever-changing type of f_reada.
+
+Sat Apr 13 00:43:41 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bpath.c, bfind.c:
+	Spelling corrections in comments.
+
+	* bins_del.c:
+	ifdef out shift_left() until it is actually used.
+
+	* hfs_btree.h, hfs_fs.h, bins_del.c, bpath.c, bfind.c:
+	Cleaned up code related to 'flags' argument to hfs_bpath_find().
+
+Fri Apr 12 23:30:01 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bpath.c:
+	Updated comments.
+	Rewrote hfs_bpath_init() and hfs_bpath_next().
+
+	* hfs_btree.h:
+	Updated prototype for hfs_bpath_init().
+
+	* bins_del.c:
+	Updated call to hfs_bpath_init().
+
+	* inode.c, inode_cap.c, inode_dbl.c, inode_nat.c, extent.c, file_cap.c,
+	  file_dbl.c, file_nat.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c,
+	  dir.c:
+	Renamed hfs_brec_relse() to hfs_brelse().
+
+	* hfs_fs.h, hfs_btree.h:
+	Updated prototypes to reflect new names in bpath.c
+
+	* bins_del.c:
+	Updated calls to functions in bpath.c
+	Updated comments.
+
+	* Makefile:
+	Renamed brec.c to bpath.c
+
+	* bfind.c:
+	Updated calls to functions in bpath.c
+	Added hfs_brelse() which was previously hfs_brec_relse() in brec.c
+
+	* bpath.c:
+	brec.c renamed to bpath.c
+	Functions renamed to reflect their current actions.
+	Comments are still out of date.
+	hfs_brec_relse() renamed to hfs_brelse() and moved to bfind.c
+
+	* brec.c:
+	brec.c renamed to bpath.c
+
+Wed Apr 10 07:20:28 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs.h, extent.c, hfs_btree.h, brec.c, dir.c, bfind.c,
+	  bins_del.c:
+	Backed-out changes to hfs_binsert() that added the ability to
+	return the new record, since it will probably not ever be needed.
+
+	* extent.c:
+	Since 1.3.45 truncate() has locked the file, so there is no need
+	for all the things I've been doing to hfs_file_extend() & new_extent().
+	Those two functions have been cleaned up a bit (similar to older forms).
+
+	* extent.c:
+	hfs_file_extend() now more "robust", but new_extent() is still
+	not fully "concurrency safe."
+
+Tue Apr  9 09:01:18 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bins_del.c:
+	Made split() inline.
+
+	* inode.c, dir_nat.c, hfs_fs.h, dir_cap.c:
+	Added hfs_itry() to get in-core inodes.
+
+	* inode_dbl.c, inode_nat.c, hfs_fs.h, inode.c, inode_cap.c, file_dbl.c,
+	  file_nat.c, hfs_btree.h, extent.c, file_cap.c, dir_cap.c, dir_dbl.c,
+	  dir_nat.c, brec.c, catalog.c, dir.c, bins_del.c, bnode.c,
+	  bfind.c:
+	Rewrite of all the (struct hfs_brec) stuff.
+
+Mon Apr  8 21:50:01 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* btree.c, extent.c, bnode.c:
+	Fixed format strings in a few debugging printk()'s.
+
+	* brec.c, hfs_fs.h:
+	Removed hfs_brec_relse_one().
+
+	* hfs_fs.h, bnode.c, brec.c, hfs_btree.h, bfind.c, bins_del.c, balloc.c:
+	(struct hfs_bnode_ref)s are now returned by value rather than reference
+	and they are in (struct hfs_brec) rather than pointed to.  Cuts down on
+	a lot of kmalloc() and kfree() traffic.
+
+	* hfs_fs.h, dir.c, extent.c, bins_del.c:
+	Modified hfs_binsert() to be able to return the new record.
+
+	* bins_del.c, hfs_btree.h:
+	Added shift_left(), still untested.
+
+	* bins_del.c:
+	new_root() was missing its comment.
+
+	* super.c, trans.c, hfs_fs_i.h, inode.c, inode_dbl.c, inode_nat.c,
+	  file_nat.c, hfs_btree.h, hfs_fs.h, file.c, file_dbl.c, dir_dbl.c,
+	  dir_nat.c, extent.c, dir.c, dir_cap.c, bitops.c, bnode.c, brec.c,
+	  bfind.c, bins_del.c, bitmap.c, balloc.c:
+	Fixed lines over 80 characters and tabified files.
+
+	* bins_del.c:
+	Fixed line(s) over 80 columns.
+
+	* trans.c, inode_nat.c, string.c, super.c, inode.c, inode_cap.c,
+	  inode_dbl.c, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree.h, hfs_fs.h, file.c,
+	  file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, extent.c, btree.c,
+	  dir_cap.c, bitops.c, bnode.c, brec.c, bfind.c, bins_del.c, bitmap.c,
+	  DOC, README, TODO, balloc.c, CHANGES:
+	About 150 spelling corrections.
+
+Sun Apr  7 23:14:28 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+	Cleaned-up check for special names in mkdir().
+
+	* extent.c:
+	More verbose error message.
+
+	* inode_dbl.c, inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, dir.c,
+	  hfs_fs.h:
+	Limit directories to 32767 entries, since Mac uses 16-bit integer.
+
+Fri Apr  5 07:27:57 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* FAQ:
+	Initial version.
+
+	* dir_dbl.c, dir_nat.c, bins_del.c, dir.c, dir_cap.c:
+	Added missing function comments.
+
+Wed Apr  3 06:38:36 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* brec.c:
+	Cleaned-up code for brec->flags.
+
+	* extent.c:
+	Added function comments.
+
+	* bins_del.c:
+	Added function comments.
+	hfs_binsert() was incrementing record count even on failure.
+
+Mon Apr  1 08:35:51 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* extent.c:
+	Rewrote find_ext() and new_extent() for new hfs_btree_extend().
+	Moved hfs_btree_extend() to balloc.c
+	Fixed potential kernel OOPS in new_extent().
+
+	* brec.c:
+	Fixed potential kernel OOPS in hfs_brec_get_root().
+	Removed hfs_brec_find_first().
+	Fixed return value of hfs_brec_find().
+
+	* bins_del.c:
+	Updated call to hfs_btree_extend().
+
+	* balloc.c:
+	Merged hfs_bnode_add() and hfs_btree_extend() into the later.
+	Commented init_mapnode().
+
+	* bfind.c:
+	Removed hfs_bfind_first().
+
+	* hfs_fs.h, hfs_btree.h:
+	Updated prototypes.
+
+Sat Mar 30 22:56:47 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* CHANGES, README, TODO:
+	Updated documentation in preparation for 0.6 release.
+
+	* inode.c, hfs_fs.h:
+	Got rid of HFS_FAKE_EXEC in favor of noexec mount option.
+
+	* inode.c, super.c, DOC, hfs_fs_sb.h:
+	Added "quiet" mount option, like the fat filesystem.
+
+	* inode.c, dir_cap.c, dir_nat.c:
+	Pseudo-directories are read-only (at least for now).
+
+	* hfs_fs.h, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+	mkdir() updated to check against reserved names, but the
+	AppleDouble scheme still has problems with names starting with '%'.
+
+	* dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+	Added mkdir(). (It only took 2 tries to get it right!!)
+	Only works in "normal" directories and doesn't yet stop
+	one from creating dirs with the reserved names.
+
+	* brec.c, extent.c, bins_del.c:
+	Now have a way to get an EEXIST back from hfs_binsert().
+
+	* btree.c, inode.c, hfs_fs_i.h, file.c, bfind.c, bnode.c, balloc.c:
+	Added 'dev' field to struct hfs_file.
+
+	* hfs_fs_i.h, inode.c, btree.c, extent.c, file.c, bnode.c, brec.c,
+	  balloc.c:
+	Removed duplicated fields from struct hfs_file since
+	even B*-trees now have that information in the inode.
+
+	* extent.c:
+	zero_blocks() neglected allocation block size in computing start.
+
+Fri Mar 29 16:04:37 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* super.c:
+	hfs_statfs(): f_files and f_ffree fields are now -1, which is
+	documented as the value for "undefined" fields in struct statfs.
+
+	* trans.c, inode_nat.c, string.c, super.c, inode_dbl.c, inode_cap.c,
+	  inode.c, file_nat.c, file_dbl.c, file_cap.c, file.c, dir_dbl.c,
+	  extent.c, dir_cap.c, catalog.c, btree.c, brec.c, bnode.c, bitops.c,
+	  bitmap.c, bins_del.c, balloc.c:
+	Stylistic editing: {} for all 'for', 'while' and 'if' blocks.
+	I hope I didn't screw-up anything.
+
+	* hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c, dir_nat.c:
+	Added creation of normal files to all three fork schemes!
+	Strange things may happen when trying to create "non-normal" files.
+
+	* brec.c:
+	Cleaned up some debugging code.
+
+	* hfs_fs_i.h:
+	File and directory counts could have overflown 16-bit integer.
+
+	* hfs_btree.h:
+	Added HFS_BREC_RIGHT to help fix insertion problem.
+
+	* extent.c:
+	Various fixes to hfs_{file,btree}_extend().
+
+	* catalog.c:
+	Made hfs_build_cat_key() more "correct".
+
+	* btree.c:
+	Added and fixed debugging code.
+
+	* brec.c:
+	Fixed overflow detection.
+	Added some debugging code.
+
+	* bnode.c:
+	Dirtied some buffers in places that might have been missed.
+	Fixed some debugging code that had broken.
+
+	* bitops.c:
+	hfs_count_free_bits() was running off end of bitmap.
+
+	* bins_del.c:
+	Fixed various bugs, mostly related to variable-length keys.
+
+	* balloc.c:
+	Had forgotten to set a bit in new mapnodes.
+	Node counts were overflowing 16-bit integers.
+
+	* bitmap.c:
+	Oops! clear/set did opposite operation on full words.
+
+Wed Mar 27 10:59:07 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* hfs_fs_i.h:
+	Updated struct hfs_extent for concurrent access.
+	Also caused a slight modification to struct hfs_file.
+
+	* hfs_fs.h, hfs_btree.h:
+	Added/updated prototypes.
+
+	* balloc.c:
+	hfs_bnode_alloc() finished but still untested.
+
+	* bins_del.c:
+	Fixed up deadlock avoidance in hfs_binsert() again.
+	Perhaps I even got it right this time.
+
+	* extent.c:
+	hfs_file_extend() now safe under concurrent operations?
+
+	* file.c:
+	hfs_getblk() now safe under concurrent operations?
+
+Tue Mar 26 23:26:35 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* btree.c:
+	Added call to hfs_extent_trim() to fix memory leak.
+
+	* extent.c:
+	Oops, had left a "#define static" in from debugging.
+
+	* bins_del.c:
+	hfs_binsert() rewritten to avoid deadlock when extending
+	the extents B*-tree.
+
+	* btree.c:
+	Moved hfs_btree_extend() to extent.c
+
+	* inode_nat.c, inode_cap.c, inode_dbl.c:
+	hfs_*_put_inode() rewritten to call hfs_extent_trim().
+
+	* extent.c:
+	Big rewrite for new struct hfs_extent:
+		Now keep linked list of extents.
+		Cache is now a pointer to a list element.
+		Now have 'end' field to aid decode_extent().
+	New functions:
+		hfs_extent_trim(): frees linked list.
+		hfs_btree_extend(): for extending B*-trees.
+	Improved debugging output.
+
+	* balloc.c:
+	Added hfs_bnode_add() (incomplete and uncommented).
+
+	* btree.c:
+	Moved some work from hfs_btree_extend() to hfs_bnode_add().
+
+	* bfind.c:
+	Added hfs_bfind_first() as wrapper for hfs_brec_find_first().
+
+	* brec.c:
+	Added hfs_brec_find_first() to search first leaf node.
+
+	* bins_del.c:
+	Added error returns to hfs_binsert() and binsert().
+
+	* bins_del.c:
+	Check to see that we really need ancestors before starting.
+	Check that hfs_btree_alloc() gave us enough nodes.
+	binsert() uses info precomputed by hfs_binsert().
+
+Mon Mar 25 11:33:53 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* bnode.c:
+	Collected together the error returns in hfs_bnode_lock().
+
+	* Makefile:
+	Added ChangeLog to $(MISC).
+
+Wed Mar 20 19:41:45 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* super.c, hfs_fs.h, file.c, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+	Removed support for kernels older than about 1.3.70
+	Most of that support had been broken recently anyway.
+
+	* super.c:
+	Fixed so DEBUG_MEM works w/o DEBUG_ALL.
+	Updated call to hfs_btree_init().
+
+	* hfs_fs.h:
+	Updated/added prototypes.
+
+	* hfs_btree.h:
+	HFS_BFIND_CHAIN removed.
+	struct hfs_brec gets new 'flags' field with bits:
+		HFS_BREC_{FIRST,OVERFLOW,UNDERFLOW,UNINITIALIZED}
+	Removed bitmap size constants.
+	Changes to struct hfs_btree:
+	        'file' and 'cache' now structs rather than pointers.
+		Added 'reserved' field (used during insertion).
+		Added pointers to size and extent in MDB.
+
+	* file.c:
+	Made hfs_getblk() public.
+	Removed (fil->inode == NULL) special cases.
+
+	* extent.c:
+	{find,update}_ext() are no longer inline.
+	new_extent() fails when called for the extents tree;
+		previously it would hanging calling hfs_binsert().
+	extend_file():
+		renamed to hfs_file_extend() and made public.
+		fixed to work for B*-trees.
+		zeros-out blocks as they are allocated.
+		fixed bugs for (allocation block) != (physical block).
+
+	* btree.c:
+	hfs_btree_{init,free}() modified for changes to struct:
+		'file' and 'cache' moved back into structure
+		file.inode initialized to reduce special cases
+	hfs_btree_init() gets pointer to size in MDB instead of size.
+	Added hfs_btree_extend() (incomplete and uncommented).
+
+	* bnode.c:
+	hfs_bnode_{alloc,free}() moved to separate file.
+	Removed 'const' from some function arguments
+		due to change in struct hfs_btree.
+	hfs_bnode_lock(): added WRITE/RESRV->READ transition.
+
+	* brec.c:
+	hfs_brec_get_{root,child}() now take a 'keep_mask' argument
+		indicating when to keep ancestor nodes, and store
+		information about why ancestors were kept.
+	HFS_BFIND_CHAIN eliminated in favor of HFS_BFIND_{INSERT,DELETE}
+		which are now implemented using 'keep_mask'.
+	Added hfs_brec_relse_one() that doesn't release ancestors.
+
+	* bins_del.c:
+	Lots of rewrites to cleanup insertion.
+	Now tries to extend tree before insertion starts.
+	binsert() iterative rather than recursive.
+	No point in keeping track as it is still not "stable".
+
+	* balloc.c:
+	New file: started with hfs_bnode_{free,alloc}()
+	Added hfs_bnode_init() to initialize a newly allocated bnode.
+	hfs_bnode_free():
+		Renamed hfs_bnode_bitop().
+		Can set or clear a specified bit.
+		Gets bitmap sizes from nodes directly.
+	hfs_bnode_alloc():
+		Returns actual node, calling hfs_bnode_init().
+		Gets bitmap sizes from nodes directly.
+
+	* bfind.c:
+	Removed obsolete comment from hfs_bsucc()
+	Removed 'const' from tree arg of hfs_bfind()
+		due to changes in struct hfs_btree.
+
+	* Makefile:
+	Added new file: balloc.c
+
+Sat Mar  9 22:03:53 1996  Paul H. Hargrove  <hargrove@sccm.stanford.edu>
+
+	* Start of detailed CVS logging.
+
+Mar 09, 1996: snapshot-09Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	NOT AN OFFICIAL RELEASE
+	Fixed up debugging code that was broken by split of btree.c
+	Added debugging kmalloc/kfree
+	Fixed memory leak in hfs_bnode_relse()
+
+Mar 08, 1996: snapshot-08Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	NOT AN OFFICIAL RELEASE
+	now reset blocksize on device when done.
+	hfs_binsert done (except for the full tree case).
+	btree.c split up into manageable pieces (need to sort out hfs_btree.h)
+
+Feb 26, 1996: snapshot-26Feb96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	NOT AN OFFICIAL RELEASE
+	Some writability.
+	Bug with multiple opens of meta data fixed.
+	Netatalk support no longer considered experimental.
+
+Virtually everything has changed, so I've lost track here.
+
+Nov 16, 1995: snapshot-16Nov95 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	NOT AN OFFICIAL RELEASE
+	Still more comments.
+	btree.c back to 80 columns.  will do same to other files soon.
+	Starting with btree.c have begun to put file contents into some
+		sort of standard order.
+	Moved metadata reading to VFS open() routine and now free it in
+		the VFS release() routine.  Much cleaner than the old way.
+	Unified hfs_iget by shifting scheme-dependent code into a function
+		pointer in the superblock.  This could/should be shifted to
+		a VFS read_inode() routine if that can be done cleanly.
+	Probably lots of other changes; I've lost track.
+
+Nov 05, 1995: version 0.5.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	NOT AN OFFICIAL RELEASE
+	1.2.x compatibility removed
+	Added lots of comments to btree.c and cleanup some code.  The result
+		is that the source file doubled in size while the object
+		file dropped in size by 20%.
+	Added some comments to super.c and dir.c as well.
+	Cleaned up some stuff in dir.c adding some additional error checking
+		and moving closer to using a unified hfs_iget by migrating
+		common code into lookup_parent().
+	Changed btree.c to use a separate bnode cache per filesystem.
+	Renamed a bunch of the bnode functions in btree.c
+
+Jun 29, 1995: version 0.5.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	BUG FIX and 1.3.x-compatibility release.
+	Will compile under 1.2.x or 1.3.x by changing one line in Makefile.
+	Started adding magic numbers to structures for "safety".
+	Don't strip internal symbols when linking or loading, as this made
+		good bug reports rather difficult.
+	Fixed a bug that could cause the fs to lock-up after trying to open
+		a non-existent file.
+	Fixed a bug that allowed files to appear truncated, when in fact it
+		is still not possible to truncate a file.
+	Added more/better comments to header files.
+	Deal with volume and b-tree bitmaps in preparation for writing.
+	Fixed readdir() to deal properly with the case where the directory
+		changes while writing to user-space. (which can't yet
+		actually happen, until directories are writable).
+
+Jun 23, 1995: version 0.5.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	BUG FIX RELEASE
+	Removed two debugging messages that didn't belong.
+	Fixed a typo that prevented modified inodes from being written to disk.
+	Added a missing line which prevented rmmod'ing sometimes.
+	Added a missing line which caused errors when modifying .finderinfo or
+		.resource under the CAP system.
+	Added a notify_change() to keep mode bits sensible, and to cause
+		changes to an inode to affect the data fork and resource fork
+		of a file together.
+
+Jun 22, 1995: version 0.5 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	Fixed a bug that was giving wrong values for i_blocks
+	Partly writable (can only 'touch' existing files, so far)
+	Removed case= mount option.  It will be back eventually.
+	Can now deal with CDROMs (and hard disks?), many thanks to
+		Holger Schemel for this work.
+	Latin-1 filename conversion also due to Holger Schemel.
+	Rewritten btree operations.
+
+Feb 28, 1995: version 0.4 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	Requires Linux >= 1.1.94: depends on changes made to asm/byteorder.h
+	Now using string comparison code donated by ARDI (see string.c)
+	Code reorganized to use data structures more like ARDI's.
+	More code reorganization to abstract the btree operations.
+	Added the fork= mount option.
+	Added AppleDouble support.  Executor, from ARDI, can now run programs
+		from HFS filesystems mounted w/ the HFS module.
+
+Jan 28, 1995: version 0.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	Major code reorganization.
+	Known for certain to work ONLY on floppies.
+	Started caching extents, so got faster on long file reads.
+	Now compiles separate from kernel tree.
+	Supports 5 filename conversion methods.
+	Supports forks, using the method from CAP.
+	All external symbols now start with HFS_ or hfs_
+
+Jan 12, 1995: version 0.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	Should now work on all HFS volumes, but still only tested on floppies.
+	Got smaller and faster with some code reorganization.
+	Since Linus moved htons() and friends to an asm file, should now be
+	 truly endian-independent, but still only tested on Intel machines.
+	Requires Linux >= 1.1.77, since Linus moved htons().
+
+Jan 05, 1995: version 0.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+	First release.
+	1.44Mb floppies only
+	no resource forks
+	trivial name mangling only
+	read only
+	for Linux >= 1.1.75
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/FAQ.txt linux/fs/hfs/FAQ.txt
--- v2.1.77/linux/fs/hfs/FAQ.txt	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/FAQ.txt	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,342 @@
+  Frequently Asked Questions about the HFS filesystem for
+  Linux
+  Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+  version 1.0.3, 27 Apr 1997
+
+  This document provides answers to some of the most frequently asked
+  questions about the HFS filesystem for Linux.  It is currently pretty
+  rough and totally unorganized.  Corrections, additions and clarifica-
+  tions are appreciated.  The most current version of this document is
+  kept on The HFS for Linux Page <http://www-sccm.Stanford.EDU/~har-
+  grove/HFS/>.
+  ______________________________________________________________________
+
+  Table of Contents:
+
+  1.      What is this FAQ about?
+
+  2.      What is HFS?
+
+  3.      How I mount AppleShare volumes?
+
+  4.      What is the current version of the HFS filesystem.
+
+  5.      How stable is the current version?
+
+  6.      Is there a mailing list for discussion of the HFS filesystem?
+
+  7.      What version of Linux do I need to be running?
+
+  8.      Will it run on my (your processor type here)?
+
+  9.      Will it run under (your non-Linux operating system here)?
+
+  10.     Why can I mount some HFS CDROMs but not others?
+
+  11.     What does ``only 1024-char blocks implemented (512)'' mean?
+
+  12.     Why do I get a message about a bad or unknown partition table?
+
+  13.     Can I mount multiple HFS partitions from the same Macintosh
+  disk?
+
+  14.     In what ways can I write to HFS filesystems?
+
+  15.     Does the HFS filesystem work with 400k or 800k Macintosh
+  diskettes?
+
+  16.     How can I format an HFS filesystem?
+
+  17.     How can I fsck an HFS filesystem?
+
+  18.     Why do I get ``error -50'' messages from my Mac when using
+  netatalk?
+
+  19.     Why does my Macintosh show generic application and document
+  icons?
+
+  20.     How owns all the copyrights and trademarks? ;-)
+
+  20.1.   This Document
+
+  20.2.   The Software
+
+  20.3.   Trademarks
+  ______________________________________________________________________
+
+  11..  WWhhaatt iiss tthhiiss FFAAQQ aabboouutt??
+
+  This FAQ is about the HFS filesystem for Linux, which is available in
+  two forms.  The stand-alone version (called hfs_fs) is a Linux kernel
+  loadable module implementing the Macintosh HFS filesystem.  The HFS
+  filesystem is also included in some distributions of the Linux kernel
+  source (in the directory linux/fs/hfs).  This version can be compiled
+  as a loadable module or compiled into the kernel.
+
+  Either version allows a machine running Linux to read and write disks
+  from a Macintosh (almost) as though they were native Linux disks.
+
+  22..  WWhhaatt iiss HHFFSS??
+
+  HFS stands for ``Hierarchical File System'' and is the filesystem used
+  by the Mac Plus and all later Macintosh models.  Earlier Macintosh
+  models used MFS (``Macintosh File System''), which is not supported.
+
+  33..  HHooww II mmoouunntt AApppplleeSShhaarree vvoolluummeess??
+
+  The HFS filesystem is for mounting local filesystems only.  There is
+  an experimental afpfs by Ben Hekster heksterb@acm.org available from
+  http://www.odyssey.co.il/~heksterb/Software/afpfs/.
+
+  44..  WWhhaatt iiss tthhee ccuurrrreenntt vveerrssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm..
+
+  As of version 1.0.3 of this FAQ, version 0.95 is the most recent.  You
+  can always find the most recent version on The HFS for Linux Page
+  <http://www-sccm.Stanford.EDU/~hargrove/HFS/>.  Announcements of new
+  versions are made to the comp.os.linux.announce newsgroup.
+
+  55..  HHooww ssttaabbllee iiss tthhee ccuurrrreenntt vveerrssiioonn??
+
+  Version 0.95 is considered to be ``beta'' software, so I recommend
+  making backups of anything important before you start playing.  It is
+  relatively free of bugs due to lots of testing of the previous
+  releases.
+
+  After a suitable period without new bugs the I will consider the
+  software to be ``stable'' and the version number will jump to 1.0.
+
+  66..  IIss tthheerree aa mmaaiilliinngg lliisstt ffoorr ddiissccuussssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm??
+
+  There is no mailing list devoted exclusively to the HFS filesystem.
+  However, announcements of new versions are posted to the ``linux-
+  atalk'' and ``hfs-interest'' lists.  I will see bug reports sent to
+  those lists but e-mail is more reliable (hargrove@sccm.Stanford.EDU).
+
+  To subscribe to hfs-interest send e-mail with a body of ``subscribe
+  hfs-interest (your e-mail address)'' to majordomo@ccs.neu.edu.
+
+  To subscribe to linux-atalk send e-mail with a body of ``SUBSCRIBE
+  LINUX-ATALK (Your full name)'' to listserv@netspace.org.
+
+  77..  WWhhaatt vveerrssiioonn ooff LLiinnuuxx ddoo II nneeeedd ttoo bbee rruunnnniinngg??
+
+  To compile and use the stand-alone distribution of the HFS filesystem
+  you will need Linux kernel version 2.0.1 or newer compiled with
+  modules enabled (CONFIG_MODULES).  To compile you will need the kernel
+  headers which match the kernel you are running.  This is covered in
+  more detail in the installation instructions in INSTALL.txt.
+
+  If your kernel came with HFS in the kernel source tree then HFS should
+  work with your Linux version.  There may be small problems with a few
+  of the development kernel releases.  For these releases check the HFS
+  for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/> for
+  patches.
+
+  88..  WWiillll iitt rruunn oonn mmyy ((yyoouurr pprroocceessssoorr ttyyppee hheerree))??
+
+  The code is carefully written to be independent of your processor's
+  word size and byte-order, so if your machine runs Linux it can run the
+  HFS filesystem.  However some younger ports don't yet have support for
+  loadable modules.
+
+  Note that HFS is tested most extensively on Intel platforms.  So there
+  could be subtle compilation problems on other platforms.  If you
+  encounter any that are not addressed by the documentation then please
+  let me know.
+
+  99..  WWiillll iitt rruunn uunnddeerr ((yyoouurr nnoonn--LLiinnuuxx ooppeerraattiinngg ssyysstteemm hheerree))??
+
+  No.  There is a port in progress to NetBSD.  I know of no other active
+  porting attempts.  If you are interested in porting the HFS filesystem
+  to another Unix-like operating system, I am interested in providing
+  what guidance I can.
+
+  1100..  WWhhyy ccaann II mmoouunntt ssoommee HHFFSS CCDDRROOMMss bbuutt nnoott ootthheerrss??
+
+  In the past there was a known incompatibility with some ``hybrid''
+  CDROMs that appear as HFS disks on Macs and as ISO9660 disks on other
+  systems.  I think I have fixed the problem.  So, if you encounter this
+  particular problem or have problems with specific non-hybrid CDROMs
+  please e-mail me with the title and manufacturer of the CD.
+
+  1111..  WWhhaatt ddooeess ````oonnllyy 11002244--cchhaarr bblloocckkss iimmpplleemmeenntteedd ((551122))'''' mmeeaann??
+
+  This message comes from the kernel and indicates that an attempt was
+  made to read a 512-byte block from a device that doesn't support
+  512-byte blocks.  The HFS filesystem only works with 512-byte blocks,
+  and therefore doesn't function with these devices.  Eventually it may
+  be able to use 1024-byte (or even 2048-byte) blocks when necessary.
+  Ideally the device driver should be enhanced to support 512-byte
+  blocks so that the various filesystems which need 512-byte blocks
+  don't each need to work around it.
+
+  1122..  WWhhyy ddoo II ggeett aa mmeessssaaggee aabboouutt aa bbaadd oorr uunnkknnoowwnn ppaarrttiittiioonn ttaabbllee??
+
+  If your Linux kernel doesn't understand Macintosh partition tables it
+  gives this warning when it can't find a partition table it recognizes.
+  To support partitioned media with such kernels, decoding of Mac
+  partition tables is done by the HFS filesystem so you should still be
+  able to mount the disk.  However, to do so you will need to mount the
+  raw device (such as /dev/sdb instead of /dev/sdb4) and use the part
+  mount option to indicate which partition you want.
+
+  1133..  CCaann II mmoouunntt mmuullttiippllee HHFFSS ppaarrttiittiioonnss ffrroomm tthhee ssaammee MMaacciinnttoosshh ddiisskk??
+
+  Only if your kernel understands Macintosh partition tables.  It the
+  kernel doesn't understand the Macintosh partition table, the HFS
+  filesystem must access the raw device.  Therefore, the kernel thinks
+  the entire drive is in use and prevents additional mounts on it.
+
+  1144..  IInn wwhhaatt wwaayyss ccaann II wwrriittee ttoo HHFFSS ffiilleessyysstteemmss??
+
+  The HFS filesystem is as capable as the MS-DOS or VFAT filesystems,
+  except that certain things can only be done with a file's data fork.
+
+  You ccaann:
+
+  +o  Create, delete and rename directories and data forks of files with
+     the caveat that names are case insensitive (so foo and Foo are the
+     same file or directory).
+
+  +o  Run Linux executables or shared libraries on an HFS disk if they
+     are stored in the data fork of a file.
+
+  +o  Read, write and truncate both forks of files and the Finder's
+     metadata of files and directories.
+
+  +o  Mmap data forks of files (and the resource fork if the filesystem
+     is mounted with the fork=cap option).
+
+  +o  Toggle the 'w' permission bits (as a group) of data forks.
+
+  +o  Change the i_mtime of files and directories.
+
+  You ccaannnnoott:
+
+  +o  Create, delete or rename resource forks of files or the Finder's
+     metadata.  Note, however, that they are created (with defaults
+     values), deleted and renamed along with the corresponding data fork
+     or directory.
+
+  +o  Run Linux executables or shared libraries on an HFS disk if they
+     are stored in the resource fork of a file.
+
+  +o  Mmap the Finder's metadata (when fork=cap) or AppleDouble header
+     files (when fork=double or fork=netatalk).
+
+  +o  Change permissions on directories.
+
+  +o  Change the uid or gid of files or directories.
+
+  +o  Set the set-uid, set-gid or sticky permission bits.
+
+  +o  Create multiple links to files.
+
+  +o  Create symlinks, device files, sockets or FIFOs.
+
+  1155..  DDooeess tthhee HHFFSS ffiilleessyysstteemm wwoorrkk wwiitthh 440000kk oorr 880000kk MMaacciinnttoosshh
+  ddiisskkeetttteess??
+
+  Yes and no.  The software is fully capable of dealing with HFS disks
+  of any size.  However, the 400k and 800k diskettes are written in a
+  physical format that is incompatible with most non-Macintosh floppy
+  drives.  Note also that almost all 400k Macintosh diskettes are MFS,
+  not HFS.
+
+  1166..  HHooww ccaann II ffoorrmmaatt aann HHFFSS ffiilleessyysstteemm??
+
+  Robert Leslie (rob@mars.org) has written a package for working with
+  HFS filesystems (like mtools plus a graphical interface).  One program
+  in the package is hformat which can format HFS filesystems.  The
+  latest version can be found on the HFS Utilities home page
+  <http://www.mars.org/home/rob/proj/hfs/>.
+
+  1177..  HHooww ccaann II ffsscckk aann HHFFSS ffiilleessyysstteemm??
+
+  Right now you'll have to use a Macintosh to do this.  However, Rob
+  Leslie is working on an fsck for HFS filesystems.
+
+  1188..  WWhhyy ddoo II ggeett ````eerrrroorr --5500'''' mmeessssaaggeess ffrroomm mmyy MMaacc wwhheenn uussiinngg
+  nneettaattaallkk??
+
+  To be compatible with netatalk's afpd you will need to use netatalk
+  version 1.4b1 or newer and mount the HFS filesystem with the ``afpd''
+  mount option.  More information is provided in the ``afpd'' subsection
+  of the ``Mount Options'' section of the HFS documentation (HFS.txt if
+  you have the stand-alone HFS distribution or
+  linux/Documentation/filesystems/hfs.txt if HFS is in your kernel
+  source tree.)
+
+  1199..  WWhhyy ddooeess mmyy MMaacciinnttoosshh sshhooww ggeenneerriicc aapppplliiccaattiioonn aanndd ddooccuummeenntt
+  iiccoonnss??
+
+  When using the ``afpd'' mount option the Desktop database on the disk
+  is not made available to Netatalk's afpd.  Because of this mounting an
+  HFS filesystem across the network to a Macintosh may result in the
+  Finder showing generic application and document icons.  Additionally
+  double clicking on a document will fail to start the correct
+  application.
+
+  If the disk is writable you can make Netatalk build a new Desktop
+  database in its own format by holding down the Option key while
+  selecting the volume in the Chooser.  If the disk is not writable then
+  these problems can be worked around by copying the application to a
+  local disk on the Macintosh.
+
+  2200..  HHooww oowwnnss aallll tthhee ccooppyyrriigghhttss aanndd ttrraaddeemmaarrkkss?? ;;--))
+
+  2200..11..  TThhiiss DDooccuummeenntt
+
+  This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+  Permission is granted to make and distribute verbatim copies of this
+  document provided the copyright notice and this permission notice are
+  preserved on all copies.
+
+  Permission is granted to copy and distribute modified versions of this
+  document under the conditions for verbatim copies above, provided a
+  notice clearly stating that the document is a modified version is also
+  included in the modified document.
+
+  Permission is granted to copy and distribute translations of this
+  document into another language, under the conditions specified above
+  for modified versions.
+
+  Permission is granted to convert this document into another media
+  under the conditions specified above for modified versions provided
+  the requirement to acknowledge the source document is fulfilled by
+  inclusion of an obvious reference to the source document in the new
+  media. Where there is any doubt as to what defines ``obvious'' the
+  copyright owner reserves the right to decide.
+
+  2200..22..  TThhee SSooffttwwaarree
+
+  The HFS filesystem software is Copyright (c) 1994-1997 by Paul H.
+  Hargrove.
+
+  The software is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2, or (at your option)
+  any later version.
+
+  The software is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with the software in the file ``COPYING''; if not, write to the
+  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+  USA.
+
+  2200..33..  TTrraaddeemmaarrkkss
+
+  +o  ``Finder'' is a trademark of Apple Computer, Inc.
+
+  +o  ``Apple'', ``AppleShare'', and ``Macintosh'' are registered
+     trademarks of Apple Computer, Inc.
+
+  +o  ``MS-DOS'' is a registered trademarks of Microsoft Corporation.
+
+  +o  All other trademarks are the property of their respective owners.
+
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/HFS.txt linux/fs/hfs/HFS.txt
--- v2.1.77/linux/fs/hfs/HFS.txt	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/HFS.txt	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,1042 @@
+  Macintosh HFS Filesystem for Linux
+  Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+  version 0.95, 28 Apr 1997
+
+  This document describes version 0.95 of the Macintosh HFS filesystem
+  for Linux.  The most current versions of this document and the
+  software are kept at The HFS for Linux Page
+  <http://www-sccm.Stanford.EDU/~hargrove/HFS/>.
+  ______________________________________________________________________
+
+  Table of Contents:
+
+  1.      Introduction
+
+  2.      Mounting HFS Filesystems
+
+  2.1.    afpd
+
+  2.2.    case={asis, lower}
+
+  2.3.    conv={auto, binary, text}
+
+  2.4.    creator=cccc
+
+  2.5.    fork={cap, double, netatalk}
+
+  2.6.    gid=n
+
+  2.7.    names={7bit, 8bit, alpha, cap, latin, netatalk, trivial}
+
+  2.8.    part=n
+
+  2.9.    quiet
+
+  2.10.   type=cccc
+
+  2.11.   uid=n
+
+  2.12.   umask=n
+
+  3.      Writing to HFS Filesystems
+
+  3.1.    Writing with fork=cap
+
+  3.2.    Writing with fork=double
+
+  3.3.    Writing with fork=netatalk
+
+  4.      A Guide to Special File Formats
+
+  4.1.    CAP .finderinfo Files
+
+  4.2.    AppleDouble Header Files
+
+  5.      Reporting Bugs
+
+  5.1.    What Goes in a Bug Report
+
+  5.2.    How to Report a Kernel Oops or GPF
+
+  6.      Legal Notices
+
+  6.1.    This Document
+
+  6.2.    The Software
+
+  6.2.1.  The Columbia AppleTalk Package for UNIX
+
+  6.2.2.  Netatalk
+
+  6.3.    Trademarks
+  ______________________________________________________________________
+
+  11..  IInnttrroodduuccttiioonn
+
+  This software implements the Macintosh HFS filesystem under Linux.  It
+  allows you to read and write HFS filesystems on floppy disks, CDROMs,
+  hard drives, ZIP drives, etc.  It is _n_o_t an AppleShare client.
+
+  If you use this software, please send me a note telling of your
+  success or failure with it.  Your feedback lets me know that this
+  project is not a waste of my time.
+
+  This code is still experimental, so backup anything important before
+  you start playing.  I'd like you to know that I've never lost any
+  files while using this software, or I would not release it.  However,
+  a ``better safe than sorry'' attitude is probably best.
+
+  If, for instance, the buffer cache were to become corrupted you could
+  start losing things on other disks.  Because of this, if you get a
+  General Protection Fault, or a kernel Oops, I _s_t_r_o_n_g_l_y recommend that
+  you reboot before writing any files.
+
+  22..  MMoouunnttiinngg HHFFSS FFiilleessyysstteemmss
+
+  Once you have the HFS filesystem compiled into the kernel or installed
+  as a loadable module, you will be able to use hfs as a filesystem type
+  option to mount.  For instance, to mount a Macintosh floppy disk on
+  the directory /mnt using the default mount options you would execute
+  ``mount -t hfs /dev/fd0 /mnt''.
+
+  The remainder of this section describes the several mount options
+  available to control how the HFS filesystem is mapped onto a Linux
+  filesystem structure.  The values for the multiple-choice options
+  (case, conv, fork and names) can be abbreviated by their first
+  character.
+
+  22..11..  aaffppdd
+
+  If included in the options, then the behavior of the filesystem is
+  changed to make it fully read-write compatible with Netatalk's afpd.
+  In this mode you should not use normal user-level tools to modify the
+  filesystem, though reading from it is acceptable.  This is because the
+  return codes from some system calls are changed to fool afpd.  These
+  changes will confuse many user-level tools.  In particular ``rm -r''
+  will loop forever.
+
+  This option implies fork=netatalk, which in turn implies
+  names=netatalk.  If either of these options are explicitly set to
+  something else they will take precedence and will confuse afpd.  The
+  quiet option has no effect.  The case= option functions normally, but
+  afpd usually does the same thing for you.  The conv= and part= options
+  also function normally.
+
+  You will probably want to use the uid=, gid= and umask= mount options.
+  Note that because all the files on an HFS filesystem belong to a
+  single user and group and have a single umask, the full AppleShare
+  permission scheme will not work through Netatalk.
+
+  One additional limitation is that the Desktop database on the disk is
+  stored in afpd's format and is separate from any existing database
+  maintained by the Finder when the volume is used on a Macintosh.
+  Because of this mounting an HFS CDROM across the network to a
+  Macintosh may result in applications and documents showing up with
+  default application and document icons.  Additionally double clicking
+  on a document will fail to start the correct application.  Both of
+  these problems can be worked around by copying the application to a
+  local disk on the Macintosh.
+
+  This mode is known to be compatible with afpd from Netatalk versions
+  1.4b1 and 1.4b2, and known to be incompatible with the afpd from
+  version 1.3.3.  As of this writing Netatalk version 1.4 has not yet
+  been released.  However, it is expected that this mode will be
+  compatible with afpd from Netatalk version 1.4 when it is released.
+
+  22..22..  ccaassee=={{aassiiss,, lloowweerr}}
+
+  default value: asis
+
+  This option determines if Macintosh filenames are presented in their
+  original case or in all lowercase.  Filename lookup is always case
+  insensitive, so either way foo and Foo refer to the same file but ls
+  will list Foo with case=asis, and foo with case=lower.  (Same as for
+  the HPFS filesystem.)
+
+     aassiiss
+        Filenames are reported in the case they were created with.
+
+     lloowweerr
+        Filenames are reported in lowercase.
+
+  22..33..  ccoonnvv=={{aauuttoo,, bbiinnaarryy,, tteexxtt}}
+
+  default value: binary
+
+  This option controls CR<->NL conversion of Macintosh _d_a_t_a _f_o_r_k_s.  Any
+  translation takes place only for files accessed with the read() and
+  write() system calls (either directly or through the stdio functions).
+  Access through mmap() is unaffected.  (Similar to the conv= option for
+  the MS-DOS filesystem.)
+
+     aauuttoo
+        If the Finder's type for a file is TEXT or ttro, then CR
+        characters are converted to NL characters when read, and NL
+        characters are converted to CR characters when written.
+
+        Be warned that some Macintosh applications create files with
+        type TEXT even though the contents is clearly binary.
+
+     bbiinnaarryy
+        No CR<->NL conversion is done.
+
+     tteexxtt
+        In all data forks, regardless of the Finder's type for the file,
+        CR characters are converted to NL characters when read, and NL
+        characters are converted to CR characters when written.
+
+  22..44..  ccrreeaattoorr==cccccccc
+
+  default value: ``????''
+
+  Specifies the 4-character string specifying the Finder's Creator for
+  new files.
+
+  22..55..  ffoorrkk=={{ccaapp,, ddoouubbllee,, nneettaattaallkk}}
+
+  default value: cap
+
+  This option determines how resource forks and the Finder's metadata
+  are represented within the structure of the Linux filesystem.
+
+     ccaapp
+        The scheme used by the Columbia AppleTalk Package's AUFS.
+
+        Associated with each directory are two special directories and a
+        metadata file.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks of files, and the following two special directories.
+
+        ..//bbaarr//..rreessoouurrccee
+           A special directory holding resource forks of the files in
+           ./bar.
+
+        ..//bbaarr//..ffiinnddeerriinnffoo
+           A special directory holding metadata files for the files and
+           subdirectories in ./bar.
+
+        ..//..ffiinnddeerriinnffoo//bbaarr
+           The metadata file for the directory ./bar.
+
+        The files in a directory are represented as three files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//..rreessoouurrccee//ffoooo
+           The resource fork of the file ./foo.
+
+        ..//..ffiinnddeerriinnffoo//ffoooo
+           The metadata file for the file ./foo.
+
+        Additionally, the file .rootinfo in the root directory of the
+        HFS filesystem is a metadata file for the root directory.
+
+        Brief documentation on the format of file containing the
+        Finder's metadata is included in the section ``A Guide to
+        Special File Formats'' in this document.  More detailed
+        information is available in the Columbia AppleTalk Package.
+
+     ddoouubbllee
+        The ``AppleDouble'' format recommended by Apple.  (Apple's other
+        recommended format, ``AppleSingle'', is not yet implemented.)
+
+        Associated with each directory is an AppleDouble ``header
+        file''.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks for files, and the header files for files and
+           subdirectories.
+
+        ..//%%bbaarr
+           The header file for the directory ./bar, containing the
+           Finder's metadata for the directory.
+
+        The files in a directory are represented as two files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//%%ffoooo
+           The header file for the file ./foo, containing the resource
+           fork and the Finder's metadata for the file.
+
+        Additionally, the file %RootInfo in the root directory of the
+        HFS filesystem is a header file for the root directory.  This is
+        not quite the %RootInfo file referred to in the AppleDouble
+        specification.
+
+        The header files used in this scheme are version 2 AppleDouble
+        header files.  Their format is described briefly in the section
+        ``A Guide to Special File Formats'' in this document.  They are
+        documented in detail in ``AppleSingle/AppleDouble Formats:
+        Developer's Note (9/94)'', available from from Apple's Developer
+        Services Page <http://devworld.apple.com>.
+
+        Note that the naming convention for the header file can cause
+        name conflicts.  For instance, using Apple's 7-bit ASCII name
+        conversion (see the names mount option) the name %Desktop could
+        be interpreted either as the header file for the file Desktop or
+        as the file with 0xDE as the hexadecimal representation of its
+        first character, and "sktop" as the remaining 5 characters.  The
+        problem arises when both files exist, since only one will be
+        accessible.  The behavior of the HFS filesystem in the case of
+        such a conflict is undefined, and may change in future releases.
+        (If this causes problems for you, please don't report it as a
+        bug; I didn't design this ``standard'', Apple did.)
+
+     nneettaattaallkk
+        The scheme used by the Netatalk afpd.
+
+        Associated with each directory is a special directory and a
+        metadata file.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks of files, and the following special directory.
+
+        ..//bbaarr//..AApppplleeDDoouubbllee
+           A special directory holding AppleDouble header files for
+           ./bar and the files it contains, but not for the
+           subdirectories it contains.
+
+        ..//bbaarr//..AApppplleeDDoouubbllee//..PPaarreenntt
+           The header file for the directory ./bar, containing the
+           Finder's metadata for the directory.
+
+        The files in a directory are represented as two files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//..AApppplleeDDoouubbllee//ffoooo
+           The header file for file ./foo, containing the resource fork
+           and the Finder's metadata.
+
+        The header files used in this scheme are version 1 AppleDouble
+        header files.  They are described briefly in the section ``A
+        Guide to Special File Formats'' in this document.  The format is
+        documented in detail in the ``Apple II File Type Notes'' under
+        the type ``$E0.0002/$E0.0003-AppleDouble'', and in Appendix B of
+        the ``A/UX Toolbox: Macintosh ROM Interface'' manual.
+
+  22..66..  ggiidd==nn
+
+  default value: gid of the mounting process
+
+  Specifies the group that owns all files and directories on the
+  filesystem.  (Same as for the MS-DOS and HPFS filesystems.)
+
+  22..77..  nnaammeess=={{77bbiitt,, 88bbiitt,, aallpphhaa,, ccaapp,, llaattiinn,, nneettaattaallkk,, ttrriivviiaall}}
+
+  default value: varies as follows
+
+  +o  If the fork option is set to double, then names defaults to alpha.
+
+  +o  If the fork option is set to netatalk, then names defaults to
+     netatalk.
+
+  +o  If the fork option is set to cap (or has taken that value by
+     default), then names defaults to cap.
+
+  This option determines how to convert between valid Macintosh
+  filenames and valid Linux filenames.  The 7bit, 8bit and alpha options
+  correspond to Apple's recommended conventions named ``7-bit ASCII'',
+  ``8-bit'' and ``7-bit alphanumeric''.
+
+     77bbiitt
+        When converting from Macintosh filenames to Linux filenames the
+        NULL (0x00), slash (/) and percent (%) characters and the
+        extended 8-bit characters (hexadecimal codes 0x80-0xff) are
+        replaced by a percent character (%) followed by the two-digit
+        hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     88bbiitt
+        When converting from Macintosh filenames to Linux filenames the
+        NULL (0x00), slash (/) and percent (%) characters are replaced
+        by a percent character (%) followed by the two-digit hexadecimal
+        code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     aallpphhaa
+        When converting from Macintosh filenames to Linux filenames only
+        the alphanumeric characters (a-z, A-Z and 0-9), the underscore
+        (_) and the last period (.) in the filename are unchanged.  The
+        remaining characters are replaced by a percent character (%)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     ccaapp
+        The convention used by the Columbia AppleTalk Package's AUFS.
+
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/).  The slash (/) and
+        all characters outside the range 32-126 are replaced by a colon
+        (:) followed by the two-digit hexadecimal code for the
+        character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string ":YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the colon is replaced by a pipe
+        character (|).
+
+     llaattiinn
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/) and percent (%).  The
+        extended 8-bit Macintosh characters with equivalents in the
+        Latin-1 character set are replaced by those equivalents.  The
+        remaining characters are replaced by a percent character (%)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged. The
+        Latin-1 characters with equivalents in the extended 8-bit
+        Macintosh character set are replaced by those equivalents.  A
+        colon (:) is replaced by a pipe character (|).
+
+        Thanks to Holger Schemel (aeglos@valinor.owl.de) for
+        contributing this conversion mode.
+
+     nneettaattaallkk
+        The convention used by the Netatalk afpd.
+
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/) and any initial
+        period (.).  The slash (/) and any initial period (.)  and all
+        characters outside the range 32-126 are replaced by a colon (:)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string ":YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the colon is replaced by a pipe
+        character (|).
+
+     ttrriivviiaall
+        When converting from Macintosh filenames to Linux filenames a
+        slash character (/) is replaced by a colon (:).
+
+        When converting from Linux filenames to Macintosh filenames a
+        colon (:) is replaced by a slash character (/).
+
+  22..88..  ppaarrtt==nn
+
+  default value: 0
+
+  Specifies which HFS partition to mount from a Macintosh CDROM or hard
+  drive.  Partitions are numbered from 0 and count only those identified
+  in the partition table as containing HFS filesystems.  This option is
+  only useful when the Linux platform doesn't fully support Macintosh
+  partition tables.  In particular on MkLinux and Linux-Pmac this option
+  is useless.
+
+  Note that in versions before 0.8.3 partitions were numbered from 1.
+
+  22..99..  qquuiieett
+
+  If included in the options, then chown and chmod operations will not
+  return errors, but will instead fail silently.  (Same as for the MS-
+  DOS and HPFS filesystems.)
+
+  22..1100..  ttyyppee==cccccccc
+
+  default value: ``????''
+
+  Specifies the 4-character string specifying the Finder's Type for new
+  files.
+
+  22..1111..  uuiidd==nn
+
+  default value: uid of the mounting process
+
+  Specifies the user that owns all files and directories on the
+  filesystem.  (Same as for the MS-DOS and HPFS filesystems.)
+
+  22..1122..  uummaasskk==nn
+
+  default value: umask of the mounting process
+
+  Specifies (in octal) the umask used for all files and directories.
+  (Same as for the MS-DOS and HPFS filesystems.)
+
+  33..  WWrriittiinngg ttoo HHFFSS FFiilleessyysstteemmss
+
+  Each of the values of the fork mount option yields a different
+  representation of the Macintosh-specific parts of a file within the
+  structure of the Linux filesystem.  There are, therefore, slightly
+  different steps involved in copying files if you want to preserve the
+  resource forks and the Finder's metadata.
+
+  It is important to remember not to use normal user-level tools to
+  modify a filesystem mounted with the afpd mount option.
+
+  Regardless of the value of the fork mount option you can do virtually
+  everything to the data fork of a file that you can to a file on any
+  other filesystem.  The limitations are essentially the same as those
+  imposed by the MS-DOS filesystem:
+
+  +o  You can't change the uid or gid of files.
+
+  +o  You can't set the set-uid, set-gid or sticky permission bits.
+
+  +o  You can't clear the execute permission bits.
+
+  Likewise you can do virtually everything to a directory that you can
+  to a directory on another file system with the following exceptions:
+
+  +o  You can't create, delete or rename resource forks of files or the
+     Finder's metadata.  Note, however, that they are created (with
+     defaults values), deleted and renamed along with the corresponding
+     data fork or directory.
+
+  +o  You can't change permissions on directories.
+
+  +o  You can't change the uid or gid of directories.
+
+  +o  You can't create multiple links to files.
+
+  +o  You can't create symlinks, device files, sockets or FIFOs.
+
+  33..11..  WWrriittiinngg wwiitthh ffoorrkk==ccaapp
+
+  Unlike the other schemes for representing forked files, the CAP scheme
+  presents the resource fork as an independent file; the resource fork
+  of ./foo is ./.resource/foo.  Therefore, you can treat it as a normal
+  file.  You can do anything to a resource fork that you can do to a
+  data fork, except that you cannot enable execute permissions on a
+  resource fork.  Therefore, resource forks are not suitable for holding
+  Linux executables or shared libraries.
+
+  If you plan to use the resource fork on a Macintosh then you must obey
+  the format of a valid resource fork.  This format is documented in
+  Chapter 1 of Apple's _I_n_s_i_d_e _M_a_c_i_n_t_o_s_h_: _M_o_r_e _M_a_c_i_n_t_o_s_h _T_o_o_l_b_o_x.  The
+  filesystem knows nothing about this format and so does nothing to
+  enforce it.
+
+  The current support for reading and writing is sufficient to allow
+  copying of entire directories with tar, as long as both the source and
+  destination are mounted with fork=cap.  tar may complain about being
+  unable to change the uid, gid or mode of files.  This is normal and is
+  an unavoidable side effect of the having a single uid, gid and umask
+  for the entire filesystem.
+
+  It is impossible to create a resource fork or a Finder metadata file.
+  However, they are created automatically when the data fork is created.
+  Therefore, if you wish to copy a single file including both forks and
+  the Finder's metadata then you must create the data fork first.  Then
+  you can copy the resource fork and the Finder's metadata.  For
+  instance to copy the file foo to dir/bar you should do the following:
+
+  1. cp foo dir/bar
+
+  2. cp .resource/foo dir/.resource/bar
+
+  3. cp .finderinfo/foo dir/.finderinfo/bar
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute ``mv foo dir/bar'' and the
+  resource fork and the Finder's metadata will move too.  However, if
+  foo and dir are on different filesystem then this will lose the
+  resource fork and metadata.  Therefore, it is safest to always move
+  files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp .resource/foo dir/.resource/bar
+
+  3. cp .finderinfo/foo dir/.finderinfo/bar
+
+  4. rm foo
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the Finder metadata
+  file.  To give the directory bar the same location, layout, creation
+  date and modify date as foo you simply execute ``cp .finderinfo/foo
+  .finderinfo/bar''.
+
+  When copying an entire directory with ``cp -R'' you may also wish to
+  copy the metadata for the directory:
+
+  1. cp -R foo bar
+
+  2. cp .finderinfo/foo .finderinfo/bar
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.
+
+  33..22..  WWrriittiinngg wwiitthh ffoorrkk==ddoouubbllee
+
+  The current support for reading and writing header files is sufficient
+  to allow copying of entire directories with tar, as long as both the
+  source and destination are mounted with fork=double.  tar may complain
+  about being unable to change the uid, gid or mode of files.  This is
+  normal and is an unavoidable side effect of the having a single uid,
+  gid and umask for the entire filesystem.
+
+  It is impossible to create a header file.  However, they are created
+  automatically when the data fork is created.  Therefore, if you wish
+  to copy a single file including both forks and the Finder's metadata
+  then you must create the data fork first.  Then you can copy the
+  header file.  instance to copy the file foo to dir/bar you should do
+  the following:
+
+  1. cp foo dir/bar
+
+  2. cp %foo dir/%bar
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute ``mv foo dir/bar'' and the
+  header file will move too.  However, if foo and dir are on different
+  filesystem then this will lose the header file.  Therefore, it is
+  safest to always move files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp %foo dir/%bar
+
+  3. rm foo
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the corresponding
+  header file.  To give the directory bar the same location, layout,
+  creation date and modify date as foo simply execute ``cp %foo %bar''.
+
+  When copying an entire directory with ``cp -R'' you may also wish to
+  copy the header file for the directory as well:
+
+  1. cp -R foo bar
+
+  2. cp %foo %bar
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.
+
+  33..33..  WWrriittiinngg wwiitthh ffoorrkk==nneettaattaallkk
+
+  The current support for reading and writing header files is sufficient
+  to allow copying of entire directories with tar, as long as both the
+  source and destination are mounted fork=netatalk.  tar may complain
+  about being unable to change the uid, gid or mode of files.  This is
+  normal and is an unavoidable side effect of the having a single uid,
+  gid and umask for the entire filesystem.
+
+  It is impossible to create a header file.  However, they are created
+  automatically when the data fork is created.  Therefore, if you wish
+  to copy a single file including both forks and the Finder's metadata
+  then you must create the data fork first.  Then you can copy the
+  header file.  instance to copy the file foo to dir/bar you should do
+  the following:
+
+  1. cp foo dir/bar
+
+  2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute ``mv foo dir/bar'' and the
+  header file will move too.  However, if foo and dir are on different
+  filesystem then this will lose the header file.  Therefore, it is
+  safest to always move files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+  3. rm foo
+
+  You may get ``Operation not permitted'' errors from cp when it tries
+  to change the permissions on files.  These errors can safely be
+  ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the corresponding
+  header file.  To give the directory bar the same location, layout,
+  creation date and modify date as foo you simply execute ``cp
+  foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent''.
+
+  Because the fork=netatalk scheme holds the header file for a directory
+  within that directory, directories can safely be copied with ``cp -R
+  foo bar'' with no loss of information.  However, you may get
+  ``Operation not permitted'' errors from cp when it tries to change the
+  permissions on files.  These errors can safely be ignored.
+
+  44..  AA GGuuiiddee ttoo SSppeecciiaall FFiillee FFoorrmmaattss
+
+  Each of the values of the fork mount option yields different special
+  files to represent the Macintosh-specific parts of a file within the
+  structure of the Linux filesystem.  You can write to these special
+  files to change things such as the Creator and Type of a file.
+  However, to do so safely you must follow certain rules to avoid
+  corrupting the data.  Additionally, there are certain fields in the
+  special files that you can't change (writes to them will fail
+  silently).
+
+  44..11..  CCAAPP ..ffiinnddeerriinnffoo FFiilleess
+
+  The Finder's metadata for the file ./foo in held in the file
+  ./.finderinfo/foo.  The file has a fixed format defined in hfs_fs.h as
+  follows:
+
+       ______________________________________________________________________
+       struct hfs_cap_info {
+               __u8    fi_fndr[32];            /* Finder's info */
+               __u16   fi_attr;                /* AFP attributes */
+               __u8    fi_magic1;              /* Magic number: */
+       #define HFS_CAP_MAGIC1          0xFF
+               __u8    fi_version;             /* Version of this structure: */
+       #define HFS_CAP_VERSION         0x10
+               __u8    fi_magic;               /* Another magic number: */
+       #define HFS_CAP_MAGIC           0xDA
+               __u8    fi_bitmap;              /* Bitmap of which names are valid: */
+       #define HFS_CAP_SHORTNAME       0x01
+       #define HFS_CAP_LONGNAME        0x02
+               __u8    fi_shortfilename[12+1]; /* "short name" (unused) */
+               __u8    fi_macfilename[32+1];   /* Original (Macintosh) name */
+               __u8    fi_comln;               /* Length of comment (always 0) */
+               __u8    fi_comnt[200];          /* Finder comment (unused) */
+               /* optional:    used by aufs only if compiled with USE_MAC_DATES */
+               __u8    fi_datemagic;           /* Magic number for dates extension: */
+       #define HFS_CAP_DMAGIC          0xDA
+               __u8    fi_datevalid;           /* Bitmap of which dates are valid: */
+       #define HFS_CAP_MDATE           0x01
+       #define HFS_CAP_CDATE           0x02
+               __u8    fi_ctime[4];            /* Creation date (in AFP format) */
+               __u8    fi_mtime[4];            /* Modify date (in AFP format) */
+               __u8    fi_utime[4];            /* Un*x time of last mtime change */
+       };
+       ______________________________________________________________________
+
+  The type __u8 is an unsigned character, and __u16 is an unsigned
+  16-bit integer.
+
+  Currently only the fields fi_fndr, fi_attr, fi_ctime and fi_mtime can
+  be changed.  Writes to the other fields are silently ignored.
+  However, you shouldn't write random bytes to the other fields, since
+  they may be writable in the future.
+
+  The fi_fndr field is the ``Finder info'' and ``Extended Finder info''
+  for a file or directory.  These structures are described in various
+  books on Macintosh programming.  The portion of the most interest is
+  probably the first 8 bytes which, for a file, give the 4-byte Type
+  followed by the 4-byte Creator.
+
+  The fi_attr field is the AFP attributes of the file or directory.
+  While you can write any value to this field, only the ``write-
+  inhibit'' bit is significant.  Setting or clearing this bit will clear
+  or set the write bits in the file's permissions.  When you read from
+  this field anything you may have written is lost.  If the file has
+  write permissions enabled then you will read zero from this field.
+  With write permission disabled you will read back 0x01 0xA0, which
+  corresponds to setting the ``write-inhibit'', ``rename-inhibit'' and
+  ``delete-inhibit'' bits.
+
+  The fi_ctime and fi_mtime are the Macintosh created and modified time
+  for the file or directory, and are 32-bit signed integers in network
+  byteorder giving seconds from 00:00 GMT Jan. 1, 2000.
+
+  44..22..  AApppplleeDDoouubbllee HHeeaaddeerr FFiilleess
+
+  Both the fork=double and fork=netatalk schemes for representing forked
+  files use AppleDouble header files to contain the resource fork and
+  the Finder's metadata together in a single file.
+
+  The AppleDouble format specifies a fixed-format header which describes
+  which fields are contained in the remainder of the file, where they
+  are located in the file and how long they are.  A full description of
+  the version 1 format used when fork=netatalk is available from ??????.
+  The version 2 format used when fork=double is documented in ??????.
+  The discussion that follows assumes you have read and understood these
+  documents, which may be difficult until I've replaced the ``??????''s
+  above with something more informative :-).
+
+  Due to the variable structure of an AppleDouble header file you must
+  not use buffered I/O when reading or writing them; you should only use
+  the read() and write() system calls.  It is also important that you
+  make some effort to coordinate processes that are reading and writing
+  the same header file, since a reader will receive the wrong data if
+  the location of a given entry has changed since it read the descriptor
+  for the entry.  If a process tries to read the descriptor table while
+  it is changing then it is possible to read totally meaningless data.
+
+  When a header file is opened it is initially presented with a default
+  header layout.  You may write to the header to change the layout, but
+  when all file descriptors for the file or directory have been closed
+  the change in format is lost and subsequent opens will yield the
+  default layout.  Changes to supported entries are made directly to the
+  filesystem and are thus preserved when the file is closed and
+  reopened.
+
+  The HFS filesystem currently uses a fixed-size table to hold the
+  descriptors.  Therefore you are limited to HFS_HDR_MAX (currently 10)
+  descriptors.  In the unlikely event that you try to write a header
+  with more descriptors, a warning will be issued by the kernel, and
+  extra descriptors will be ignored.  This should be considered a bug
+  and will hopefully change sooner rather than later.
+
+  The results of specifying overlapping entries is undefined and should
+  not be relied upon to remain unchanged from one version of the HFS
+  filesystem to the next.  There is no valid reason to define
+  overlapping entries, so just don't do it!
+
+  Changes to the magic number and version fields are preserved until all
+  file descriptors are closed, however the only significance given to
+  them internally is that the 16 bytes following the version changes
+  meaning according to the version.  For version 1 header files these 16
+  bytes contain the string ``Macintosh'' followed by 7 spaces.  For any
+  other value of the version field these 16 bytes are all zeros.  In
+  either case writes to these 16 bytes are silently ignored.
+
+  Since the magic number and version are given no other significance
+  internally, you are free to do many things that violate the official
+  formats.  For instance you can create an entry for the data fork in a
+  header file with an AppleDouble magic number or create ``File Info''
+  (id=7) entries in version 2 header files and ``File Dates Info''
+  (id=8) entries in version 1 header files.  However, future versions of
+  the filesystem may enforce the format more strictly.
+
+  Entry id 1 (``Data Fork'') is read-only.  You should use the data file
+  to modify the data fork.  The data fork is, of course, not supported
+  for directories.
+
+  Entry ids 2, 7, 8, 9 and 10 (``Resource Fork'', ``File Info'', ``File
+  Dates Info'', ``Finder Info'' and ``Macintosh File Info'') are fully
+  supported, meaning that their contents may be read and written and
+  that data written is preserved when the file is closed and reopened.
+  The resource fork is, of course, not supported for directories.
+
+  Entry id 7 specifies some of the same data given by ids 8 and 10.  If
+  you create a header file with an entry for id 7 and for ids 8 or 10,
+  then the behavior with respect to their interaction is undefined.  A
+  header that contains an entry for id 7 and for ids 8 or 10 is not
+  valid as either a version 1 or a version 2 header file, so there is no
+  reason to do this and future versions may prevent it.
+
+  Entry id 3 (``Real Name'') is read-only, since it will change
+  automatically when a file is renamed.  Writes to the corresponding
+  entry are silently ignored.
+
+  All other entry ids are ignored.  You may create descriptors for them;
+  in fact the default header layout when fork=netatalk includes a
+  descriptor for id 4 (``Comment'').  However writes to the entries
+  corresponding to the ignored ids fail silently and reads from the
+  entries always return zeros.  However, you shouldn't write random
+  bytes to unsupported entries, since they may be supported in the
+  future.
+
+  All of the supported entry types except the data and resource forks
+  have a fixed length.  If you give them a smaller length in the
+  descriptor then you are unable to access part of the corresponding
+  entry.  If you give them a larger length in the descriptor, then the
+  corresponding entry is padded with zeros and writes to the extra space
+  are silently ignored.
+
+  Writes to the length field of descriptors for the data and resource
+  forks will cause the corresponding fork to grow (with zero padding) or
+  shrink to the indicated length.
+
+  If you have an entry for the data fork then the descriptor's length
+  field does not change automatically to reflect any modification of the
+  data fork directly (the data does change however).  If the data fork
+  is longer than the descriptor indicates, then a portion of it is
+  inaccessible.  If the data fork is shorter than the descriptor
+  indicates then reads will be padded with zeros.
+
+  Writes beyond the end of the resource fork that extend into empty
+  space between entries or beyond the end of the file will extend the
+  fork, automatically changing the length field of the corresponding
+  descriptor.  Writes to any other space between entries are silently
+  ignored and read of such spaces always return zeros.
+
+  Calling truncate() on a header file can change the length of the
+  resource fork and such a change will automatically be reflected in the
+  length field of the corresponding descriptor.  If truncate() shortens
+  the file so that the entry for the resource fork would extend beyond
+  the new end of the file then the fork is shortened to fit in the space
+  that remains, or to zero bytes if the entry is now entirely beyond the
+  end of the file.  If the last entry in a header file is the resource
+  fork then a call to truncate() that extends the header file will
+  extend the fork with zeros.  Note that this happens even if there was
+  previously space between the end of the fork and the end of the file.
+
+  55..  RReeppoorrttiinngg BBuuggss
+
+  If you'd like any problems you encounter fixed, you'll need to provide
+  a detailed bug report.  However, you should check the FAQ (available
+  from the HFS for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/>)
+  first to be certain that your problem is not a known limitation of the
+  filesystem.  If your bug doesn't appear in the FAQ then you should e-mail
+  me at hargrove@sccm.Stanford.EDU.
+
+  55..11..  WWhhaatt GGooeess iinn aa BBuugg RReeppoorrtt
+
+  When writing your bug report, include any facts you think might be
+  relevant; I'd much rather have a bunch of extra facts than need to
+  e-mail you to get the information.  At a minimum the following
+  information should be included:
+
+  +o  The version of the HFS filesystem you are using (see
+     linux/fs/hfs/version.h).
+
+  +o  The kernel version you are using.
+
+  +o  Any unofficial kernel patches or loadable modules you are using.
+
+  +o  If you are loading the HFS filesystem as a module, then version of
+     the module utilities used to load hfs.o.
+
+  +o  The type of media you are working with (floppy, CDROM, ZIP Drive,
+     etc.).
+
+  +o  The steps required to reproduce the bug, including mount options
+     used.  (If you can't reproduce the bug tell me everything you did
+     the one time it did occur, but be warned that non-reproducible bugs
+     can only rarely be fixed.)
+
+  55..22..  HHooww ttoo RReeppoorrtt aa KKeerrnneell OOooppss oorr GGPPFF
+
+  If you encounter a bug that causes a kernel Oops or a General
+  Protection Fault then you'll need to collect some additional
+  information for the bug report.  If you are loading the HFS filesystem
+  as a module, then is important that you do this before rebooting,
+  since the module is unlikely to be loaded at the same address after
+  the reboot.
+
+  You should include all the information that the kernel prints to the
+  console or to the system logs.  However, the EIP and Stack Trace are
+  addresses in _y_o_u_r kernel and mean nothing to me without more
+  information.  Using your System.map file (or either ksymoops or klogd)
+  determine which functions the EIP and Stack Trace are in.  If you do
+  this by hand using your System.map file then the correct symbol is the
+  one of type t or T with the largest address less than or equal to the
+  one you are resolving.
+
+  If you are loading the HFS filesystem as a module and the Oops or GPF
+  was in the HFS code then the EIP and the top levels of the Stack Trace
+  will be in a loadable module, rather than in the kernel proper.  So,
+  their symbols will not be in the file System.map.  Therefore, you will
+  need to use /proc/ksyms, or a loadmap produced by passing the -m
+  option to insmod, to locate those symbols.
+
+  66..  LLeeggaall NNoottiicceess
+
+  66..11..  TThhiiss DDooccuummeenntt
+
+  This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+  Permission is granted to make and distribute verbatim copies of this
+  document provided the copyright notice and this permission notice are
+  preserved on all copies.
+
+  Permission is granted to copy and distribute modified versions of this
+  document under the conditions for verbatim copies above, provided a
+  notice clearly stating that the document is a modified version is also
+  included in the modified document.
+
+  Permission is granted to copy and distribute translations of this
+  document into another language, under the conditions specified above
+  for modified versions.
+
+  Permission is granted to convert this document into another media
+  under the conditions specified above for modified versions provided
+  the requirement to acknowledge the source document is fulfilled by
+  inclusion of an obvious reference to the source document in the new
+  media. Where there is any doubt as to what defines ``obvious'' the
+  copyright owner reserves the right to decide.
+
+  66..22..  TThhee SSooffttwwaarree
+
+  The HFS filesystem for Linux is Copyright (c) 1994-1997 by Paul H.
+  Hargrove.
+
+  This software is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2, or (at your option)
+  any later version.
+
+  This software is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this software in the file ``COPYING''; if not, write to the
+  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+  USA.
+
+  66..22..11..  TThhee CCoolluummbbiiaa AApppplleeTTaallkk PPaacckkaaggee ffoorr UUNNIIXX
+
+  The source code distribution of the Columbia AppleTalk Package for
+  UNIX, version 6.0, (CAP) was used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location
+  and format of files used by CAP's Aufs.  No code from CAP appears in
+  the HFS filesystem. The HFS filesystem is not a work ``derived'' from
+  CAP in the sense of intellectual property law.
+
+  66..22..22..  NNeettaattaallkk
+
+  The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2,
+  were used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location and format of files used
+  by Netatalk's afpd.  No code from Netatalk appears in the HFS
+  filesystem.  The HFS filesystem is not a work ``derived'' from
+  Netatalk in the sense of intellectual property law.
+
+  66..33..  TTrraaddeemmaarrkkss
+
+  +o  ``Finder'' is a trademarks of Apple Computer, Inc.
+
+  +o  ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are
+     registered trademarks of Apple Computer, Inc.
+
+  +o  ``Microsoft'' and ``MS-DOS'' are registered trademarks of Microsoft
+     Corporation.
+
+  +o  All other trademarks are the property of their respective owners.
+
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/INSTALL.txt linux/fs/hfs/INSTALL.txt
--- v2.1.77/linux/fs/hfs/INSTALL.txt	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/INSTALL.txt	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,126 @@
+  Installation instructions for the HFS Filesystem for Linux
+  Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+  version 0.95 28 Apr 1997
+
+  This document explains how to compile and install version 0.95 of
+  hfs_fs, the HFS filesystem for Linux.
+
+  11..  SSyysstteemm RReeqquuiirreemmeennttss
+
+  You will need the following to compile and use this release of hfs_fs:
+
+  +o  Kernel version 2.0.1 or newer compiled with modules enabled
+     (CONFIG_MODULES).
+
+  +o  The kernel sources (or at least the header files) available online.
+
+  +o  The module utilities package current for your kernel version and an
+     understanding of how to use it.  (The file
+     Documentation/modules.txt in the kernel source directory provides a
+     brief introduction.)
+
+  22..  IInnssttaallllaattiioonn
+
+  This release of the HFS filesystem is not part of the official kernel
+  distribution.  Therefore, it is compiled as a module and then loaded
+  into the kernel using the module utilities.  Therefore, your kernel
+  must be compiled with CONFIG_MODULES enabled.
+
+  22..11..  CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee
+
+  To compile hfs.o you should only need to execute ``make'' in the
+  hfs_fs source directory.
+
+  If gcc complains about not finding a large number of header files with
+  names beginning with ``linux/'' then you probably don't have the
+  kernel header files installed correctly.  Either /usr/include/linux,
+  /usr/include/asm and /usr/include/scsi should be symbolic links to
+  include/linux, include/asm and include/scsi in the kernel source tree
+  for the kernel you wish to use hfs_fs with, or else they should be
+  directories containing the header files for the kernel you wish to use
+  hfs_fs with.
+
+  If gcc complains about not finding linux/version.h, then you will need
+  to run ``make dep'' in the kernel source directory to build it.  Under
+  MkLinux, run ``make include/linux/version.h'' instead.
+
+  If gcc complains about not finding the files linux/config.h or
+  linux/autoconf.h, then you will need to run ``make config'' and ``make
+  dep'' in the kernel source directory to build these two files.
+
+  If you are compiling on a DEC Alpha and receive messages saying
+  assignment from incompatible pointer type when compiling files dir_*.c
+  and file_*.c, then you need to change a single line in the file
+  linux/hfs_fs.h.  Remove the text ``&& !defined(__alpha__)'' from the
+  end of line 217.
+
+  22..22..  IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall))
+
+  If you plan to use kerneld to automatically load the module or if you
+  wish to use modprobe or insmod without supplying a complete path to
+  hfs.o, then you will need to copy hfs.o into a directory where the
+  module utilities expect to find it.
+
+  The proper directory may depend slightly on your configuration.
+  However, /lib/modules/default/fs/ is a common one for filesystem
+  modules.  Once hfs.o is in the proper directory you should run depmod
+  -a to update the dependency list used by kerneld and modprobe.
+
+  22..33..  LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell
+
+  There are three ways to accomplish this:
+
+  1. If you are running kerneld and have installed hfs.o in the modules
+     directory then you don't need to issue any commands; the module
+     will be loaded when you attempt to mount an HFS filesystem.
+
+  2. If you are _n_o_t running kerneld then you can load hfs.o manually by
+     running modprobe hfs.o.  If you have not installed hfs.o in one of
+     the standard module directories, then you will need provide a full
+     path to the file hfs.o.
+
+  3. If you have been experiencing kernel crashes with hfs_fs, then you
+     should file a bug report including the names of the functions which
+     the EIP and Stack Trace point into.  To help with this you can ask
+     for relocation map for the module when you load it.  To do this
+     load the module with ``insmod -m hfs.o >loadmap''.  Again, you may
+     need a full path to the file hfs.o if you have not placed it in one
+     of the standard module directories.
+
+  22..44..  UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss
+
+  All the interface between the module and the kernel take place through
+  very stable (since the mid-1.3.x kernels) parts of the kernel.  If you
+  enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your
+  kernel you should often be able to compile this module once and then
+  use it with many kernels newer than the one you compiled it for.
+
+  In any case, it is unlikely that this module will need changes with
+  each new kernel patch; simple recompilation should usually suffice.
+
+  33..  LLeeggaall NNoottiicceess
+
+  33..11..  TThhiiss DDooccuummeenntt
+
+  This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+  Permission is granted to make and distribute verbatim copies of this
+  document provided the copyright notice and this permission notice are
+  preserved on all copies.
+
+  Permission is granted to copy and distribute modified versions of this
+  document under the conditions for verbatim copies above, provided a
+  notice clearly stating that the document is a modified version is also
+  included in the modified document.
+
+  Permission is granted to copy and distribute translations of this
+  document into another language, under the conditions specified above
+  for modified versions.
+
+  Permission is granted to convert this document into another media
+  under the conditions specified above for modified versions provided
+  the requirement to acknowledge the source document is fulfilled by
+  inclusion of an obvious reference to the source document in the new
+  media. Where there is any doubt as to what defines ``obvious'' the
+  copyright owner reserves the right to decide.
+
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/Makefile linux/fs/hfs/Makefile
--- v2.1.77/linux/fs/hfs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/Makefile	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,18 @@
+#
+# Makefile for the linux nfs-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 := hfs.o
+O_OBJS   := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o \
+	    bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o \
+	    dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o \
+            part_tbl.o string.o super.o sysdep.o trans.o version.o
+
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/TODO linux/fs/hfs/TODO
--- v2.1.77/linux/fs/hfs/TODO	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/TODO	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,54 @@
+The hfs_fs "to do" list.
+------------------------
+Items are broken down into groups and the groups are listed in order
+from most important to least important.  The items within each group
+are not placed in any particular order.  The order in which items are
+listed probably doesn't correlate well with the order they will be
+addressed.
+
+Genuine bugs:
+1.	Header files have compiled-in limit (currently 10) on descriptors.
+
+Missing features:
+1.	The partition code should be migrated into the kernel to allow
+	simultaneous access to multiple partitions on a single disk.
+2.	1k block support is needed for some devices.
+3.	An ioctl()-based interface is needed to provide a consistent way
+	to do things under all of the representations of forked files.
+
+Possible additional "fork" mount options:
+1.	AppleSingle.
+2.	The scheme MacOS uses on FAT disks (PC Exchange).
+3.	"Flat" (no resource forks or metadata).
+
+Performance issues:
+1.	Use drAllocPtr to speed block allocations.
+2.	Keep a real cache of bnodes, rather than just a hash table of
+	the ones that are currently in use.
+3.	Keep a real cache of extent records, rather than just a linked
+	list of the ones that are currently in use and the one most
+	recently used.  This is particularly needed to get acceptable
+	performance with multiple readers on a file.  Perhaps simply
+	keep them in memory once they've been read until the file is
+	closed.
+
+Implementation details:
+1.	Allocation scheme could/should be closer to that used by Apple.
+2.	B*-tree insertion could/should be closer to that used by Apple.
+3.	Magic-number checks on data structures are rarely done.
+4.	Error recovery is needed for failed binsert(), bdelete() and rename().
+5.	Deadlock detection is needed to make insert_empty_bnode() and
+	bdelete() less likely to hang on a corrupted B-tree.
+6.	Metadata for covered directories shouldn't appear in the filesystem.
+	Under CAP and AppleDouble it currently does.  However, the obvious
+	solution is a real performance killer and is not worth implementing.
+
+Fantasy features:
+1.	Access Desktop file/database for comment and icon.
+2.	Implement mmap() for AppleDouble header files and CAP info files.
+3.	Implement AppleShare client support.
+
+Suggestions/comments/questions are welcome.
+Code addressing any of the issues listed above is especially welcome.
+Paul H. Hargrove
+hargrove@sccm.Stanford.EDU
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/balloc.c linux/fs/hfs/balloc.c
--- v2.1.77/linux/fs/hfs/balloc.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/balloc.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,437 @@
+/*
+ * linux/fs/hfs/balloc.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * hfs_bnode_alloc() and hfs_bnode_bitop() are based on GPLed code
+ * Copyright (C) 1995  Michael Dreher
+ *
+ * This file contains the code to create and destroy nodes
+ * in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * get_new_node()
+ *
+ * Get a buffer for a new node with out reading it from disk.
+ */
+static hfs_buffer get_new_node(struct hfs_btree *tree, hfs_u32 node)
+{
+	int tmp;
+	hfs_buffer retval = HFS_BAD_BUFFER;
+
+  	tmp = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+	if (tmp) {
+		retval = hfs_buffer_get(tree->sys_mdb, tmp, 0);
+	}
+	return retval;
+}
+
+/*
+ * hfs_bnode_init()
+ *
+ * Description:
+ *   Initialize a newly allocated bnode.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ *   hfs_u32 node: the node number to allocate
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref for the new node
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   'node' exists and has been allocated in the bitmap of bnodes.
+ * Postconditions:
+ *   On success:
+ *    The node is not read from disk, nor added to the bnode cache.
+ *    The 'sticky' and locking-related fields are all zero/NULL.
+ *    The bnode's nd{[FB]Link, Type, NHeight} fields are uninitialized.
+ *    The bnode's ndNRecs field and offsets table indicate an empty bnode.
+ *   On failure:
+ *    The node is deallocated.
+ */
+static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree,
+					   hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	extern int bnode_count;
+#endif
+	struct hfs_bnode_ref retval;
+
+	retval.lock_type = HFS_LOCK_NONE;
+	if (!HFS_NEW(retval.bn)) {
+		hfs_warn("hfs_bnode_init: out of memory.\n");
+		goto bail2;
+	}
+
+	/* Partially initialize the in-core structure */
+	memset(retval.bn, 0, sizeof(*retval.bn));
+	retval.bn->magic = HFS_BNODE_MAGIC;
+	retval.bn->tree = tree;
+	retval.bn->node = node;
+	hfs_bnode_lock(&retval, HFS_LOCK_WRITE);
+
+	retval.bn->buf = get_new_node(tree, node);
+	if (!hfs_buffer_ok(retval.bn->buf)) {
+		goto bail1;
+	}
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	++bnode_count;
+#endif
+
+	/* Partially initialize the on-disk structure */
+	memset(hfs_buffer_data(retval.bn->buf), 0, HFS_SECTOR_SIZE);
+	hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval.bn, 1));
+
+	return retval;
+
+bail1:
+	HFS_DELETE(retval.bn);
+bail2:
+	/* clear the bit in the bitmap */
+	hfs_bnode_bitop(tree, node, 0);
+	return retval;
+}
+
+/*
+ * init_mapnode()
+ *
+ * Description:
+ *   Initializes a given node as a mapnode in the given tree.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: the node to add the mapnode after.
+ *   hfs_u32: the node to use as a mapnode.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode *: the new mapnode or NULL
+ * Preconditions:
+ *   'tree' is a valid (struct hfs_btree).
+ *   'node' is the number of the first node in 'tree' that is not
+ *    represented by a bit in the existing mapnodes.
+ * Postconditions:
+ *   On failure 'tree' is unchanged and NULL is returned.
+ *   On success the node given by 'node' has been added to the linked
+ *    list of mapnodes attached to 'tree', and has been initialized as
+ *    a valid mapnode with its first bit set to indicate itself as
+ *    allocated.
+ */
+static struct hfs_bnode *init_mapnode(struct hfs_bnode *bn, hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	extern int bnode_count;
+#endif
+	struct hfs_bnode *retval;
+
+	if (!HFS_NEW(retval)) {
+		hfs_warn("hfs_bnode_add: out of memory.\n");
+		return NULL;
+	}
+
+	memset(retval, 0, sizeof(*retval));
+	retval->magic = HFS_BNODE_MAGIC;
+	retval->tree = bn->tree;
+	retval->node = node;
+	retval->sticky = HFS_STICKY;
+	retval->buf = get_new_node(bn->tree, node);
+	if (!hfs_buffer_ok(retval->buf)) {
+		HFS_DELETE(retval);
+		return NULL;
+	}
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	++bnode_count;
+#endif
+
+	/* Initialize the bnode data structure */
+	memset(hfs_buffer_data(retval->buf), 0, HFS_SECTOR_SIZE);
+	retval->ndFLink = 0;
+	retval->ndBLink = bn->node;
+	retval->ndType = ndMapNode;
+	retval->ndNHeight = 0;
+	retval->ndNRecs = 1;
+	hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval, 1));
+	hfs_put_hs(0x1fa,                         RECTBL(retval, 2));
+	*((hfs_u8 *)bnode_key(retval, 1)) = 0x80; /* set first bit of bitmap */
+	retval->prev = bn;
+	hfs_bnode_commit(retval);
+
+	bn->ndFLink = node;
+	bn->next = retval;
+	hfs_bnode_commit(bn);
+
+	return retval;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_bitop()
+ *
+ * Description:
+ *   Allocate/free the requested node of a B-tree of the hfs filesystem
+ *   by setting/clearing the corresponding bit in the B-tree bitmap.
+ *   The size of the B-tree will not be changed.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ *   hfs_u32 bitnr: The node number to free
+ *   int set: 0 to clear the bit, non-zero to set it.
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: The node was already allocated/free, nothing has been done.
+ *   -2: The node is out of range of the B-tree.
+ *   -4: not enough map nodes to hold all the bits
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   'bitnr' is a node number within the range of the btree, which is
+ *   currently free/allocated.
+ * Postconditions:
+ *   The bit number 'bitnr' of the node bitmap is set/cleared and the
+ *   number of free nodes in the btree is decremented/incremented by one.
+ */
+int hfs_bnode_bitop(struct hfs_btree *tree, hfs_u32 bitnr, int set)
+{
+	struct hfs_bnode *bn;   /* the current bnode */
+	hfs_u16 start;		/* the start (in bits) of the bitmap in node */
+	hfs_u16 len;		/* the len (in bits) of the bitmap in node */
+	hfs_u32 *u32;		/* address of the u32 containing the bit */
+
+	if (bitnr >= tree->bthNNodes) {
+		hfs_warn("hfs_bnode_bitop: node number out of range.\n");
+		return -2;
+	}
+
+	bn = &tree->head;
+	for (;;) {
+		start = bnode_offset(bn, bn->ndNRecs) << 3;
+		len = (bnode_offset(bn, bn->ndNRecs + 1) << 3) - start;
+
+		if (bitnr < len) {
+			break;
+		}
+
+		/* continue on to next map node if available */
+		if (!(bn = bn->next)) {
+			hfs_warn("hfs_bnode_bitop: too few map nodes.\n");
+			return -4;
+		}
+		bitnr -= len;
+	}
+
+	/* Change the correct bit */
+	bitnr += start;
+	u32 = (hfs_u32 *)hfs_buffer_data(bn->buf) + (bitnr >> 5);
+	bitnr %= 32;
+	if ((set && hfs_set_bit(bitnr, u32)) ||
+	    (!set && !hfs_clear_bit(bitnr, u32))) {
+		hfs_warn("hfs_bnode_bitop: bitmap corruption.\n");
+		return -1;
+	}
+	hfs_buffer_dirty(bn->buf);
+
+	/* adjust the free count */
+	tree->bthFree += (set ? -1 : 1);
+	tree->dirt = 1;
+
+	return 0;
+}
+
+/*
+ * hfs_bnode_alloc()
+ *
+ * Description:
+ *   Find a cleared bit in the B-tree node bitmap of the hfs filesystem,
+ *   set it and return the corresponding bnode, with its contents zeroed.
+ *   When there is no free bnode in the tree, an error is returned, no
+ *   new nodes will be added by this function!
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref for the new bnode
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   There is at least one free bnode.
+ * Postconditions:
+ *   On success:
+ *     The corresponding bit in the btree bitmap is set.
+ *     The number of free nodes in the btree is decremented by one.
+ *   The node is not read from disk, nor added to the bnode cache.
+ *   The 'sticky' field is uninitialized.
+ */
+struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *tree)
+{
+	struct hfs_bnode *bn;   /* the current bnode */
+	hfs_u32 bitnr = 0;	/* which bit are we examining */
+	hfs_u16 first;		/* the first clear bit in this bnode */
+	hfs_u16 start;		/* the start (in bits) of the bitmap in node */
+	hfs_u16 end;		/* the end (in bits) of the bitmap in node */
+	hfs_u32 *data;		/* address of the data in this bnode */
+	
+	bn = &tree->head;
+	for (;;) {
+		start = bnode_offset(bn, bn->ndNRecs) << 3;
+		end = bnode_offset(bn, bn->ndNRecs + 1) << 3;
+		data =  (hfs_u32 *)hfs_buffer_data(bn->buf);
+		
+		/* search the current node */
+		first = hfs_find_zero_bit(data, end, start);
+		if (first < end) {
+			break;
+		}
+
+		/* continue search in next map node */
+		bn = bn->next;
+
+		if (!bn) {
+			hfs_warn("hfs_bnode_alloc: too few map nodes.\n");
+			goto bail;
+		}
+		bitnr += (end - start);
+	}
+
+	if ((bitnr += (first - start)) >= tree->bthNNodes) {
+		hfs_warn("hfs_bnode_alloc: no free nodes found, "
+			 "count wrong?\n");
+		goto bail;
+	}
+
+	if (hfs_set_bit(first % 32, data + (first>>5))) {
+		hfs_warn("hfs_bnode_alloc: bitmap corruption.\n");
+		goto bail;
+	}
+	hfs_buffer_dirty(bn->buf);
+
+	/* decrement the free count */
+	--tree->bthFree;
+	tree->dirt = 1;
+
+	return hfs_bnode_init(tree, bitnr);
+
+bail:
+	return (struct hfs_bnode_ref){NULL, HFS_LOCK_NONE};
+}
+
+/*
+ * hfs_btree_extend()
+ *
+ * Description:
+ *   Adds nodes to a B*-tree if possible.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the btree to add nodes to.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'tree' is a valid (struct hfs_btree *).
+ * Postconditions:
+ *   If possible the number of nodes indicated by the tree's clumpsize
+ *    have been added to the tree, updating all in-core and on-disk
+ *    allocation information.
+ *   If insufficient disk-space was available then fewer nodes may have
+ *    been added than would be expected based on the clumpsize.
+ *   In the case of the extents B*-tree this function will add fewer
+ *    nodes than expected if adding more would result in an extent
+ *    record for the extents tree being added to the extents tree.
+ *    The situation could be dealt with, but doing so confuses Macs.
+ */
+void hfs_btree_extend(struct hfs_btree *tree)
+{
+	struct hfs_bnode_ref head;
+	struct hfs_bnode *bn, *tmp;
+	struct hfs_cat_entry *entry = &tree->entry;
+	struct hfs_mdb *mdb = entry->mdb;
+	hfs_u32 old_nodes, new_nodes, total_nodes, new_mapnodes, seen;
+
+	old_nodes = entry->u.file.data_fork.psize;
+
+	entry->u.file.data_fork.lsize += 1; /* rounded up to clumpsize */
+	hfs_extent_adj(&entry->u.file.data_fork);
+
+	total_nodes = entry->u.file.data_fork.psize;
+	entry->u.file.data_fork.lsize = total_nodes << HFS_SECTOR_SIZE_BITS;
+	new_nodes = total_nodes - old_nodes;
+	if (!new_nodes) {
+		return;
+	}
+
+	head = hfs_bnode_find(tree, 0, HFS_LOCK_WRITE);
+	if (!(bn = head.bn)) {
+		hfs_warn("hfs_btree_extend: header node not found.\n");
+		return;
+	}
+
+	seen = 0;
+	new_mapnodes = 0;
+	for (;;) {
+		seen += bnode_rsize(bn, bn->ndNRecs) << 3;
+
+		if (seen >= total_nodes) {
+			break;
+		}
+
+		if (!bn->next) {
+			tmp = init_mapnode(bn, seen);
+			if (!tmp) {
+				hfs_warn("hfs_btree_extend: "
+					 "can't build mapnode.\n");
+				hfs_bnode_relse(&head);
+				return;
+			}
+			++new_mapnodes;
+		}
+		bn = bn->next;
+	}
+	hfs_bnode_relse(&head);
+
+	tree->bthNNodes = total_nodes;
+	tree->bthFree += (new_nodes - new_mapnodes);
+	tree->dirt = 1;
+
+	/* write the backup MDB, not returning until it is written */
+	hfs_mdb_commit(mdb, 1);
+
+	return;
+}
+
+/*
+ * hfs_bnode_free()
+ *
+ * Remove a node from the cache and mark it free in the bitmap.
+ */
+int hfs_bnode_free(struct hfs_bnode_ref *bnr)
+{
+	hfs_u32 node = bnr->bn->node;
+	struct hfs_btree *tree = bnr->bn->tree;
+
+	if (bnr->bn->count != 1) {
+		hfs_warn("hfs_bnode_free: count != 1.\n");
+		return -EIO;
+	}
+
+	hfs_bnode_relse(bnr);
+	hfs_bnode_bitop(tree, node, 0);
+	return 0;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bdelete.c linux/fs/hfs/bdelete.c
--- v2.1.77/linux/fs/hfs/bdelete.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bdelete.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,483 @@
+/*
+ * linux/fs/hfs/bdelete.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to delete records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Variable-like macros ================*/
+
+#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
+#define NO_SPACE (HFS_SECTOR_SIZE+1)
+
+/*================ File-local functions ================*/
+
+/*
+ * bdelete_nonempty()
+ *
+ * Description:
+ *   Deletes a record from a given bnode without regard to it becoming empty.
+ * Input Variable(s):
+ *   struct hfs_brec* brec: pointer to the brec for the deletion
+ *   struct hfs_belem* belem: which node in 'brec' to delete from
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec).
+ *   'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * Postconditions:
+ *   The record has been inserted in the position indicated by 'brec'.
+ */
+static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem)
+{
+	int i, rec, nrecs, tomove;
+	hfs_u16 size;
+	hfs_u8 *start;
+	struct hfs_bnode *bnode = belem->bnr.bn;
+
+	rec = belem->record;
+	nrecs = bnode->ndNRecs;
+	size = bnode_rsize(bnode, rec);
+	tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1);
+	
+	/* adjust the record table */
+	for (i = rec+1; i <= nrecs; ++i) {
+		hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i));
+	}
+
+	/* move it down */
+	start = bnode_key(bnode, rec);
+	memmove(start, start + size, tomove);
+
+	/* update record count */
+	--bnode->ndNRecs;
+}
+
+/*
+ * del_root()
+ *
+ * Description:
+ *   Delete the current root bnode.
+ * Input Variable(s):
+ *   struct hfs_bnode_ref *root: reference to the root bnode
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 0 on success, error code on failure
+ * Preconditions:
+ *   'root' refers to the root bnode with HFS_LOCK_WRITE access.
+ *   None of 'root's children are held with HFS_LOCK_WRITE access.
+ * Postconditions:
+ *   The current 'root' node is removed from the tree and the depth
+ *    of the tree is reduced by one.
+ *   If 'root' is an index node with exactly one child, then that
+ *    child becomes the new root of the tree.
+ *   If 'root' is an empty leaf node the tree becomes empty.
+ *   Upon return access to 'root' is relinquished.
+ */
+static int del_root(struct hfs_bnode_ref *root)
+{
+	struct hfs_btree *tree = root->bn->tree;
+	struct hfs_bnode_ref child;
+	hfs_u32 node;
+
+	if (root->bn->ndNRecs > 1) {
+		return 0;
+	} else if (root->bn->ndNRecs == 0) {
+		/* tree is empty */
+		tree->bthRoot = 0;
+		tree->root = NULL;
+		tree->bthRoot = 0;
+		tree->bthFNode = 0;
+		tree->bthLNode = 0;
+		--tree->bthDepth;
+		tree->dirt = 1;
+		if (tree->bthDepth) {
+			hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
+				 tree->bthDepth);
+			goto bail;
+		}
+		return hfs_bnode_free(root);
+	} else if (root->bn->ndType == ndIndxNode) {
+		/* tree is non-empty */
+		node = hfs_get_hl(bkey_record(bnode_datastart(root->bn)));
+
+		child = hfs_bnode_find(tree, node, HFS_LOCK_READ);
+		if (!child.bn) {
+			hfs_warn("hfs_bdelete: can't read child node.\n");
+			goto bail;
+		}
+			
+		child.bn->sticky = HFS_STICKY;
+        	if (child.bn->next) {
+                	child.bn->next->prev = child.bn->prev;
+        	}
+        	if (child.bn->prev) {
+                	child.bn->prev->next = child.bn->next;
+        	}
+        	if (bhash(tree, child.bn->node) == child.bn) {
+                	bhash(tree, child.bn->node) = child.bn->next;
+        	}
+		child.bn->next = NULL;
+		child.bn->prev = NULL;
+
+		tree->bthRoot = child.bn->node;
+		tree->root = child.bn;
+		hfs_bnode_relse(&child);
+
+		tree->bthRoot = node;
+		tree->bthFNode = node;
+		tree->bthLNode = node;
+		--tree->bthDepth;
+		tree->dirt = 1;
+		if (!tree->bthDepth) {
+			hfs_warn("hfs_bdelete: non-empty tree with "
+				 "bthDepth == 0\n");
+			goto bail;
+		}
+		return hfs_bnode_free(root);	/* marks tree dirty */
+	}
+	hfs_bnode_relse(root);
+	return 0;
+
+bail:
+	hfs_bnode_relse(root);
+	return -EIO;
+}
+
+
+/*
+ * delete_empty_bnode()
+ *
+ * Description:
+ *   Removes an empty non-root bnode from between 'left' and 'right'
+ * Input Variable(s):
+ *   hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid
+ *   struct hfs_bnode_ref *left: reference to the left neighbor of the
+ *    bnode to remove, or invalid if no such neighbor exists.
+ *   struct hfs_bnode_ref *center: reference to the bnode to remove
+ *   hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid
+ *   struct hfs_bnode_ref *right: reference to the right neighbor of the
+ *    bnode to remove, or invalid if no such neighbor exists.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left_node' is as described above.
+ *   'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the left neighbor of 'center' if such a
+ *    neighbor exists, or invalid if no such neighbor exists.
+ *   'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the bnode to delete.
+ *   'right_node' is as described above.
+ *   'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the right neighbor of 'center' if such a
+ *    neighbor exists, or invalid if no such neighbor exists.
+ * Postconditions:
+ *   If 'left' is valid its 'ndFLink' field becomes 'right_node'.
+ *   If 'right' is valid its 'ndBLink' field becomes 'left_node'.
+ *   If 'center' was the first leaf node then the tree's 'bthFNode'
+ *    field becomes 'right_node' 
+ *   If 'center' was the last leaf node then the tree's 'bthLNode'
+ *    field becomes 'left_node' 
+ *   'center' is NOT freed and access to the nodes is NOT relinquished.
+ */
+static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left,
+			       struct hfs_bnode_ref *center,
+			       hfs_u32 right_node, struct hfs_bnode_ref *right)
+{
+	struct hfs_bnode *bnode = center->bn;
+
+	if (left_node) {
+		left->bn->ndFLink = right_node;
+	} else if (bnode->ndType == ndLeafNode) {
+		bnode->tree->bthFNode = right_node;
+		bnode->tree->dirt = 1;
+	}
+
+	if (right_node) {
+		right->bn->ndBLink = left_node;
+	} else if (bnode->ndType == ndLeafNode) {
+		bnode->tree->bthLNode = left_node;
+		bnode->tree->dirt = 1;
+	}
+}
+
+/*
+ * balance()
+ *
+ * Description:
+ *   Attempt to equalize space usage in neighboring bnodes.
+ * Input Variable(s):
+ *   struct hfs_bnode *left: the left bnode.
+ *   struct hfs_bnode *right: the right bnode.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s obtained
+ *    with HFS_LOCK_WRITE access, and are neighbors.
+ * Postconditions:
+ *   Records are shifted either left or right to make the space usage
+ *   nearly equal.  When exact equality is not possible the break
+ *   point is chosen to reduce data movement.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+static void balance(struct hfs_bnode *left, struct hfs_bnode *right)
+{
+	int index, left_free, right_free, half;
+
+	left_free = bnode_freespace(left);
+	right_free = bnode_freespace(right);
+	half = (left_free + right_free)/2;
+
+	if (left_free < right_free) {
+		/* shift right to balance */
+		index = left->ndNRecs + 1;
+		while (right_free >= half) {
+			--index;
+			right_free -= bnode_rsize(left,index)+sizeof(hfs_u16);
+		}
+		if (index < left->ndNRecs) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+			hfs_warn("shifting %d of %d recs right to balance: ",
+			       left->ndNRecs - index, left->ndNRecs);
+#endif
+			hfs_bnode_shift_right(left, right, index+1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+			hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+		}
+	} else {
+		/* shift left to balance */
+		index = 0;
+		while (left_free >= half) {
+			++index;
+			left_free -= bnode_rsize(right,index)+sizeof(hfs_u16);
+		}
+		if (index > 1) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+			hfs_warn("shifting %d of %d recs left to balance: ",
+			       index-1, right->ndNRecs);
+#endif
+			hfs_bnode_shift_left(left, right, index-1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+			hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+		}
+	}
+}
+
+/*
+ * bdelete()
+ *
+ * Delete the given record from a B-tree.
+ */
+static int bdelete(struct hfs_brec *brec)
+{
+	struct hfs_btree *tree = brec->tree;
+	struct hfs_belem *belem = brec->bottom;
+	struct hfs_belem *parent = (belem-1);
+	struct hfs_bnode *bnode;
+	hfs_u32 left_node, right_node;
+	struct hfs_bnode_ref left, right;
+	int left_space, right_space, min_space;
+	int fix_right_key;
+	int fix_key;
+	
+	while ((belem > brec->top) &&
+	       (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) {
+		bnode = belem->bnr.bn;
+		fix_key = belem->flags & HFS_BPATH_FIRST;
+		fix_right_key = 0;
+
+		bdelete_nonempty(brec, belem);
+
+		if (bnode->node == tree->root->node) {
+			del_root(&belem->bnr);
+			--brec->bottom;
+			goto done;
+		}
+
+		/* check for btree corruption which could lead to deadlock */
+		left_node = bnode->ndBLink;
+		right_node = bnode->ndFLink;
+		if ((left_node && hfs_bnode_in_brec(left_node, brec)) ||
+		    (right_node && hfs_bnode_in_brec(right_node, brec)) ||
+		    (left_node == right_node)) {
+			hfs_warn("hfs_bdelete: corrupt btree\n");
+			hfs_brec_relse(brec, NULL);
+			return -EIO;
+		}
+
+		/* grab the left neighbor if it exists */
+		if (left_node) {
+			hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV);
+			left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE);
+			if (!left.bn) {
+				hfs_warn("hfs_bdelete: unable to read left "
+					 "neighbor.\n");
+				hfs_brec_relse(brec, NULL);
+				return -EIO;
+			}
+			hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE);
+			if (parent->record != 1) {
+				left_space = bnode_freespace(left.bn);
+			} else {
+				left_space = NO_SPACE;
+			}
+		} else {
+			left.bn = NULL;
+			left_space = NO_SPACE;
+		}
+
+		/* grab the right neighbor if it exists */
+		if (right_node) {
+			right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE);
+			if (!right.bn) {
+				hfs_warn("hfs_bdelete: unable to read right "
+					 "neighbor.\n");
+				hfs_bnode_relse(&left);
+				hfs_brec_relse(brec, NULL);
+				return -EIO;
+			}
+			if (parent->record < parent->bnr.bn->ndNRecs) {
+				right_space = bnode_freespace(right.bn);
+			} else {
+				right_space = NO_SPACE;
+			}
+		} else {
+			right.bn = NULL;
+			right_space = NO_SPACE;
+		}
+
+		if (left_space < right_space) {
+			min_space = left_space;
+		} else {
+			min_space = right_space;
+		}
+
+		if (min_space == NO_SPACE) {
+			hfs_warn("hfs_bdelete: no siblings?\n");
+			hfs_brec_relse(brec, NULL);
+			return -EIO;
+		}
+
+		if (bnode->ndNRecs == 0) {
+			delete_empty_bnode(left_node, &left, &belem->bnr,
+					   right_node, &right);
+		} else if (min_space + bnode_freespace(bnode) >= FULL) {
+			if ((right_space == NO_SPACE) ||
+			    ((right_space == min_space) &&
+			     (left_space != NO_SPACE))) {
+				hfs_bnode_shift_left(left.bn, bnode,
+						     bnode->ndNRecs);
+			} else {
+				hfs_bnode_shift_right(bnode, right.bn, 1);
+				fix_right_key = 1;
+			}
+			delete_empty_bnode(left_node, &left, &belem->bnr,
+					   right_node, &right);
+		} else if (min_space == right_space) {
+			balance(bnode, right.bn);
+			fix_right_key = 1;
+		} else {
+			balance(left.bn, bnode);
+			fix_key = 1;
+		}
+
+		if (fix_right_key) {
+			hfs_bnode_update_key(brec, belem, right.bn, 1);
+		}
+
+		hfs_bnode_relse(&left);
+		hfs_bnode_relse(&right);
+
+		if (bnode->ndNRecs) {
+			if (fix_key) {
+				hfs_bnode_update_key(brec, belem, bnode, 0);
+			}
+			goto done;
+		}
+
+		hfs_bnode_free(&belem->bnr);
+		--brec->bottom;
+		belem = parent;
+		--parent;
+	}
+
+	if (belem < brec->top) {
+		hfs_warn("hfs_bdelete: Missing parent.\n");
+		hfs_brec_relse(brec, NULL);
+		return -EIO;
+	}
+
+	bdelete_nonempty(brec, belem);
+
+done:
+	hfs_brec_relse(brec, NULL);
+	return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bdelete()
+ *
+ * Delete the requested record from a B-tree.
+ */
+int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key)
+{ 
+	struct hfs_belem *belem;
+	struct hfs_bnode *bnode;
+	struct hfs_brec brec;
+	int retval;
+
+	if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) {
+		hfs_warn("hfs_bdelete: invalid arguments.\n");
+		return -EINVAL;
+	}
+
+	retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE);
+	if (!retval) {
+		belem = brec.bottom;
+		bnode = belem->bnr.bn;
+
+		belem->flags = 0;
+        	if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) -
+		     bnode_rsize(bnode, belem->record)) < FULL/2) {
+			belem->flags |= HFS_BPATH_UNDERFLOW;
+		}
+		if (belem->record == 1) {
+			belem->flags |= HFS_BPATH_FIRST;
+		}
+
+		if (!belem->flags) {
+			hfs_brec_lock(&brec, brec.bottom);
+		} else {
+			hfs_brec_lock(&brec, NULL);
+		}
+
+		retval = bdelete(&brec);
+		if (!retval) {
+			--brec.tree->bthNRecs;
+			brec.tree->dirt = 1;
+		}
+		hfs_brec_relse(&brec, NULL);
+	}
+	return retval;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bfind.c linux/fs/hfs/bfind.c
--- v2.1.77/linux/fs/hfs/bfind.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bfind.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,322 @@
+/*
+ * linux/fs/hfs/bfind.c
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_relse()
+ *
+ * Description:
+ *   This function releases some of the nodes associated with a brec.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the brec to release some nodes from.
+ *   struct hfs_belem *elem: the last node to release or NULL for all
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions: 
+ *   All nodes between the indicated node and the beginning of the path
+ *    are released.
+ */
+void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+	if (!elem) {
+		elem = brec->bottom;
+	}
+
+	while (brec->top <= elem) {
+		hfs_bnode_relse(&brec->top->bnr);
+		++brec->top;
+	}
+}
+
+/*
+ * hfs_bfind()
+ *
+ * Description:
+ *   This function has sole responsibility for locating existing
+ *   records in a B-tree.  Given a B-tree and a key it locates the
+ *   "greatest" record "less than or equal to" the given key.  The
+ *   exact behavior is determined by the bits of the flags variable as
+ *   follows:
+ *     ('flags' & HFS_LOCK_MASK):
+ *      The lock_type argument to be used when calling hfs_bnode_find().
+ *     HFS_BFIND_EXACT: only accept an exact match, otherwise take the
+ *	"largest" record less than 'target' as a "match"
+ *     HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing
+ *	the "matching" record when it is located
+ *     HFS_BPATH_FIRST: keep access to internal nodes when accessing their
+ *      first child.
+ *     HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed
+ *      child is too full to insert another pointer record.
+ *     HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed
+ *      child is would be less than half full upon removing a pointer record.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold
+ *    the search results.
+ *   struct hfs_bkey *target: pointer to the (struct hfs_bkey)
+ *    to search for
+ *   int flags: bitwise OR of flags which determine the function's behavior
+ * Output Variable(s):
+ *   'brec' contains the results of the search on success or is invalid
+ *    on failure.
+ * Returns:
+ *   int: 0 or 1 on success or an error code on failure:
+ *     -EINVAL: one of the input variables was NULL.
+ *     -ENOENT: tree is valid but empty or no "matching" record was located.
+ *	 If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no
+ *	 matching record will give a 'brec' with a 'record' field of zero
+ *	 rather than returning this error.
+ *     -EIO: an I/O operation or an assertion about the structure of a
+ *       valid B-tree failed indicating corruption of either the B-tree
+ *       structure on the disk or one of the in-core structures representing
+ *       the B-tree.
+ *	 (This could also be returned if a kmalloc() call failed in a
+ *	 subordinate routine that is intended to get the data from the
+ *	 disk or the buffer cache.)
+ * Preconditions:
+ *   'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field
+ *    which points to a valid (struct hfs_btree).
+ *   'target' is NULL or points to a "valid" (struct hfs_bkey)
+ * Postconditions:
+ *   If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL but the tree
+ *   is empty then -ENOENT is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL but the call to
+ *   hfs_brec_init() fails then '*brec' is NULL and -EIO is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is
+ *   non-empty then the tree is searched as follows:
+ *    If any call to hfs_brec_next() fails or returns a node that is
+ *     neither an index node nor a leaf node then -EIO is returned to
+ *     indicate that the B-tree or buffer-cache are corrupted.
+ *    If every record in the tree is "greater than" the given key
+ *     and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ *    If every record in the tree is "greater than" the given key
+ *     and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers
+ *     to the first leaf node in the tree and has a 'record' field of
+ *     zero, and 1 is returned.
+ *    If a "matching" record is located with key "equal to" 'target'
+ *     then the return value is 0 and 'brec' indicates the record.
+ *    If a "matching" record is located with key "greater than" 'target'
+ *     then the behavior is determined as follows:
+ *	If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned
+ *       and 'brec' refers to the "matching" record.
+ *	If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ *    If the return value is non-negative and the HFS_BFIND_LOCK bit of
+ *     'flags' is set then hfs_brec_lock() is called on the bottom element
+ *     of 'brec' before returning.
+ */
+int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree,
+	      const struct hfs_bkey *target, int flags)
+{
+	struct hfs_belem *curr;
+	struct hfs_bkey *key;
+	struct hfs_bnode *bn;
+	int result, ntype;
+
+	/* check for invalid arguments */
+	if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) {
+		return -EINVAL;
+	}
+
+	/* check for empty tree */
+	if (!tree->root || !tree->bthNRecs) {
+		return -ENOENT;
+	}
+
+	/* start search at root of tree */
+	if (!(curr = hfs_brec_init(brec, tree, flags))) {
+		return -EIO;
+	}
+
+	/* traverse the tree */
+	do {
+		bn = curr->bnr.bn;
+
+		if (!curr->record) {
+			hfs_warn("hfs_bfind: empty bnode\n");
+			hfs_brec_relse(brec, NULL);
+			return -EIO;
+		}
+
+		/* reverse linear search yielding largest key "less
+		   than or equal to" 'target'.
+		   It is questionable whether a binary search would be
+		   significantly faster */
+		do {
+			key = belem_key(curr);
+			if (!key->KeyLen) {
+				hfs_warn("hfs_bfind: empty key\n");
+				hfs_brec_relse(brec, NULL);
+				return -EIO;
+			}
+			result = (tree->compare)(target, key);
+		} while ((result<0) && (--curr->record));
+
+		ntype = bn->ndType;
+
+		/* see if all keys > target */
+		if (!curr->record) {
+			if (bn->ndBLink) {
+				/* at a node other than the left-most at a
+				   given level it means the parent had an
+				   incorrect key for this child */
+				hfs_brec_relse(brec, NULL);
+				hfs_warn("hfs_bfind: corrupted b-tree %d.\n",
+					 (int)ntohl(tree->entry.cnid));
+				return -EIO;
+			}
+			if (flags & HFS_BFIND_EXACT) {
+				/* we're not going to find it */
+				hfs_brec_relse(brec, NULL);
+				return -ENOENT;
+			}
+			if (ntype == ndIndxNode) {
+				/* since we are at the left-most node at
+				   the current level and looking for the
+				   predecessor of 'target' keep going down */
+				curr->record = 1;
+			} else {
+				/* we're at first leaf so fall through */
+			}
+		}
+
+		/* get next node if necessary */
+		if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) {
+			return -EIO;
+		}
+	} while (ntype == ndIndxNode);
+
+	if (key->KeyLen > tree->bthKeyLen) {
+		hfs_warn("hfs_bfind: oversized key\n");
+		hfs_brec_relse(brec, NULL);
+		return -EIO;
+	}
+
+	if (ntype != ndLeafNode) {
+		hfs_warn("hfs_bfind: invalid node type %02x in node %d of "
+		         "btree %d\n", bn->ndType, bn->node,
+		         (int)ntohl(tree->entry.cnid));
+		hfs_brec_relse(brec, NULL);
+		return -EIO;
+	}
+
+	if ((flags & HFS_BFIND_EXACT) && result) {
+		hfs_brec_relse(brec, NULL);
+		return -ENOENT;
+	}
+
+	if (!(flags & HFS_BPATH_MASK)) {
+		hfs_brec_relse(brec, brec->bottom-1);
+	}
+
+	if (flags & HFS_BFIND_LOCK) {
+		hfs_brec_lock(brec, brec->bottom);
+	}
+
+	brec->key  = brec_key(brec);
+	brec->data = bkey_record(brec->key);
+
+	return result ? 1 : 0;
+}
+
+/*
+ * hfs_bsucc()
+ *
+ * Description:
+ *   This function overwrites '*brec' with its successor in the B-tree,
+ *   obtaining the same type of access.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite
+ *    with its successor
+ * Output Variable(s):
+ *   struct hfs_brec *brec: address of the successor of the original
+ *    '*brec' or to invalid data
+ * Returns:
+ *   int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure
+ * Preconditions:
+ *   'brec' pointers to a "valid" (struct hfs_brec)
+ * Postconditions:
+ *   If the given '*brec' is not "valid" -EINVAL is returned and
+ *    '*brec' is unchanged.
+ *   If the given 'brec' is "valid" but has no successor then -ENOENT
+ *    is returned and '*brec' is invalid.
+ *   If a call to hfs_bnode_find() is necessary to find the successor,
+ *    but fails then -EIO is returned and '*brec' is invalid.
+ *   If none of the three previous conditions prevents finding the
+ *    successor of '*brec', then 0 is returned, and '*brec' is overwritten
+ *    with the (struct hfs_brec) for its successor.
+ *   In the cases when '*brec' is invalid, the old records is freed.
+ */
+int hfs_bsucc(struct hfs_brec *brec, int count)
+{
+	struct hfs_belem *belem;
+	struct hfs_bnode *bn;
+
+	if (!brec || !(belem = brec->bottom) || (belem != brec->top) ||
+	    !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) ||
+	    !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) ||
+	    !hfs_buffer_ok(bn->buf)) {
+		hfs_warn("hfs_bsucc: invalid/corrupt arguments.\n");
+		return -EINVAL;
+	}
+
+	while (count) {
+		int left = bn->ndNRecs - belem->record;
+
+		if (left < count) {
+			struct hfs_bnode_ref old;
+			hfs_u32 node;
+
+			/* Advance to next node */
+			if (!(node = bn->ndFLink)) {
+				hfs_brec_relse(brec, belem);
+				return -ENOENT;
+			}
+			if (node == bn->node) {
+				hfs_warn("hfs_bsucc: corrupt btree\n");
+				hfs_brec_relse(brec, belem);
+				return -EIO;
+			}
+			old = belem->bnr;
+			belem->bnr = hfs_bnode_find(brec->tree, node,
+						    belem->bnr.lock_type);
+			hfs_bnode_relse(&old);
+			if (!(bn = belem->bnr.bn)) {
+				return -EIO;
+			}
+			belem->record = 1;
+			count -= (left + 1);
+		} else {
+			belem->record += count;
+			break;
+		}
+	}
+	brec->key  = belem_key(belem);
+	brec->data = bkey_record(brec->key);
+
+	if (brec->key->KeyLen > brec->tree->bthKeyLen) {
+		hfs_warn("hfs_bsucc: oversized key\n");
+		hfs_brec_relse(brec, NULL);
+		return -EIO;
+	}
+
+	return 0;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bins_del.c linux/fs/hfs/bins_del.c
--- v2.1.77/linux/fs/hfs/bins_del.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bins_del.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,231 @@
+/*
+ * linux/fs/hfs/bins_del.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code common to inserting and deleting records
+ * in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_update_key()
+ *
+ * Description:
+ *   Updates the key for a bnode in its parent.
+ *   The key change is propagated up the tree as necessary.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: the search path to update keys in
+ *   struct hfs_belem *belem: the search path element with the changed key
+ *   struct hfs_bnode *bnode: the bnode with the changed key
+ *   int offset: the "distance" from 'belem->bn' to 'bnode':
+ *    0 if the change is in 'belem->bn',
+ *    1 if the change is in its right sibling, etc.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec)
+ *   'belem' points to a valid (struct hfs_belem) in 'brec'.
+ *   'bnode' points to a valid (struct hfs_bnode) which is non-empty
+ *    and is 'belem->bn' or one of its siblings.
+ *   'offset' is as described above.
+ * Postconditions:
+ *   The key change is propagated up the tree as necessary.
+ */
+void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem,
+			  struct hfs_bnode *bnode, int offset)
+{
+	int record = (--belem)->record + offset;
+	void *key = bnode_datastart(bnode) + 1;
+	int keysize = brec->tree->bthKeyLen;
+	struct hfs_belem *limit;
+
+  	memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize);
+
+	/* don't trash the header */
+	if (brec->top > &brec->elem[1]) {
+		limit = brec->top;
+	} else {
+		limit = &brec->elem[1];
+	}
+
+	while ((belem > limit) && (record == 1)) {
+		record = (--belem)->record;
+		memcpy(1+belem_key(belem), key, keysize);
+	}
+}
+
+/*
+ * hfs_bnode_shift_right()
+ *
+ * Description:
+ *   Shifts some records from a node to its right neighbor.
+ * Input Variable(s):
+ *   struct hfs_bnode* left: the node to shift records from
+ *   struct hfs_bnode* right: the node to shift records to
+ *   hfs_u16 first: the number of the first record in 'left' to move to 'right'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s.
+ *   'left' contains at least 'first' records.
+ *   'right' has enough free space to hold the records to be moved from 'left'
+ * Postconditions:
+ *   The record numbered 'first' and all records after it in 'left' are
+ *   placed at the beginning of 'right'.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right,
+			   int first)
+{
+	int i, adjust, nrecs;
+	unsigned size;
+	hfs_u16 *to, *from;
+
+	if ((first <= 0) || (first > left->ndNRecs)) {
+		hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n",
+		       first, left->ndNRecs);
+		return;
+	}
+
+	/* initialize variables */
+	nrecs = left->ndNRecs + 1 - first;
+	size = bnode_end(left) - bnode_offset(left, first);
+
+	/* move (possibly empty) contents of right node forward */
+	memmove(bnode_datastart(right) + size,
+		bnode_datastart(right), 
+		bnode_end(right) - sizeof(struct NodeDescriptor));
+
+	/* copy in new records */
+	memcpy(bnode_datastart(right), bnode_key(left,first), size);
+
+	/* fix up offsets in right node */
+	i = right->ndNRecs + 1;
+	from = RECTBL(right, i);
+	to = from - nrecs;
+	while (i--) {
+		hfs_put_hs(hfs_get_hs(from++) + size, to++);
+	}
+	adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first);
+	i = nrecs-1;
+	from = RECTBL(left, first+i);
+	while (i--) {
+		hfs_put_hs(hfs_get_hs(from++) + adjust, to++);
+	}
+
+	/* fix record counts */
+	left->ndNRecs -= nrecs;
+	right->ndNRecs += nrecs;
+}
+
+/*
+ * hfs_bnode_shift_left()
+ *
+ * Description:
+ *   Shifts some records from a node to its left neighbor.
+ * Input Variable(s):
+ *   struct hfs_bnode* left: the node to shift records to
+ *   struct hfs_bnode* right: the node to shift records from
+ *   hfs_u16 last: the number of the last record in 'right' to move to 'left'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s.
+ *   'right' contains at least 'last' records.
+ *   'left' has enough free space to hold the records to be moved from 'right'
+ * Postconditions:
+ *   The record numbered 'last' and all records before it in 'right' are
+ *   placed at the end of 'left'.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right,
+			  int last)
+{
+	int i, adjust, nrecs;
+	unsigned size;
+	hfs_u16 *to, *from;
+
+	if ((last <= 0) || (last > right->ndNRecs)) {
+		hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n",
+		       last, right->ndNRecs);
+		return;
+	}
+
+	/* initialize variables */
+	size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor);
+
+	/* copy records to left node */
+	memcpy(bnode_dataend(left), bnode_datastart(right), size);
+
+	/* move (possibly empty) remainder of right node backward */
+	memmove(bnode_datastart(right), bnode_datastart(right) + size, 
+			bnode_end(right) - bnode_offset(right, last + 1));
+
+	/* fix up offsets */
+	nrecs = left->ndNRecs;
+	i = last;
+	from = RECTBL(right, 2);
+	to = RECTBL(left, nrecs + 2);
+	adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor);
+	while (i--) {
+		hfs_put_hs(hfs_get_hs(from--) + adjust, to--);
+	}
+	i = right->ndNRecs + 1 - last;
+	++from;
+	to = RECTBL(right, 1);
+	while (i--) {
+		hfs_put_hs(hfs_get_hs(from--) - size, to--);
+	}
+
+	/* fix record counts */
+	left->ndNRecs += last;
+	right->ndNRecs -= last;
+}
+
+/*
+ * hfs_bnode_in_brec()
+ *
+ * Description:
+ *   Determines whethet a given bnode is part of a given brec.
+ *   This is used to avoid deadlock in the case of a corrupted b-tree.
+ * Input Variable(s):
+ *   hfs_u32 node: the number of the node to check for.
+ *   struct hfs_brec* brec: the brec to check in.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 1 it found, 0 if not
+ * Preconditions:
+ *   'brec' points to a valid struct hfs_brec.
+ * Postconditions:
+ *   'brec' is unchanged.
+ */
+int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec)
+{
+	const struct hfs_belem *belem = brec->bottom;
+
+	while (belem && (belem >= brec->top)) {
+		if (belem->bnr.bn && (belem->bnr.bn->node == node)) {
+			return 1;
+		}
+		--belem;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/binsert.c linux/fs/hfs/binsert.c
--- v2.1.77/linux/fs/hfs/binsert.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/binsert.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,541 @@
+/*
+ * linux/fs/hfs/binsert.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to insert records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * binsert_nonfull()
+ *
+ * Description:
+ *   Inserts a record in a given bnode known to have sufficient space.
+ * Input Variable(s):
+ *   struct hfs_brec* brec: pointer to the brec for the insertion
+ *   struct hfs_belem* belem: the element in the search path to insert in
+ *   struct hfs_bkey* key: pointer to the key for the record to insert
+ *   void* data: pointer to the record to insert
+ *   hfs_u16 keysize: size of the key to insert
+ *   hfs_u16 datasize: size of the record to insert
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   NONE
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec).
+ *   'belem' points to a valid (struct hfs_belem) in 'brec', the node
+ *    of which has enough free space to insert 'key' and 'data'.
+ *   'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize'
+ *    which, in sorted order, belongs at the location indicated by 'brec'.
+ *   'data' is non-NULL an points to appropriate data of length 'datasize'
+ * Postconditions:
+ *   The record has been inserted in the position indicated by 'brec'.
+ */
+static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem,
+			    const struct hfs_bkey *key, const void *data,
+			    hfs_u8 keysize, hfs_u16 datasize)
+{
+	int i, rec, nrecs, size, tomove;
+	hfs_u8 *start;
+	struct hfs_bnode *bnode = belem->bnr.bn;
+
+	rec = ++(belem->record);
+	size = ROUND(keysize+1) + datasize;
+	nrecs = bnode->ndNRecs + 1;
+	tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec);
+	
+	/* adjust the record table */
+	for (i = nrecs; i >= rec; --i) {
+		hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1));
+	}
+
+	/* make room */
+	start = bnode_key(bnode, rec);
+	memmove(start + size, start, tomove);
+
+	/* copy in the key and the data*/
+	*start = keysize;
+	keysize = ROUND(keysize+1);
+	memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1);
+	memcpy(start + keysize, data, datasize);
+
+	/* update record count */
+	++bnode->ndNRecs;
+}
+
+/*
+ * add_root()
+ *
+ * Description:
+ *   Adds a new root to a B*-tree, increasing its height.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the tree to add a new root to
+ *   struct hfs_bnode *left: the new root's first child or NULL
+ *   struct hfs_bnode *right: the new root's second child or NULL
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree).
+ *   'left' and 'right' point to valid (struct hfs_bnode)s, which
+ *    resulted from splitting the old root node, or are both NULL
+ *    if there was no root node before.
+ * Postconditions:
+ *   Upon success a new root node is added to 'tree' with either
+ *    two children ('left' and 'right') or none.
+ */
+static void add_root(struct hfs_btree *tree,
+		     struct hfs_bnode *left,
+		     struct hfs_bnode *right)
+{
+	struct hfs_bnode_ref bnr;
+	struct hfs_bnode *root;
+	struct hfs_bkey *key;
+	int keylen = tree->bthKeyLen;
+
+	if (left && !right) {
+		hfs_warn("add_root: LEFT but no RIGHT\n");
+		return;
+	}
+
+	bnr = hfs_bnode_alloc(tree);
+	if (!(root = bnr.bn)) {
+		return;
+	}
+
+	root->sticky = HFS_STICKY;
+	tree->root = root;
+	tree->bthRoot = root->node;
+	++tree->bthDepth;
+
+	root->ndNHeight = tree->bthDepth;
+	root->ndFLink = 0;
+	root->ndBLink = 0;
+
+	if (!left) {
+		/* tree was empty */
+		root->ndType  = ndLeafNode;
+		root->ndNRecs = 0;
+
+		tree->bthFNode = root->node;
+		tree->bthLNode = root->node;
+	} else {
+		root->ndType  = ndIndxNode;
+		root->ndNRecs = 2;
+
+		hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) +
+			   sizeof(hfs_u32), RECTBL(root, 2));
+		key = bnode_key(root, 1);
+		key->KeyLen = keylen;
+		memcpy(key->value,
+		       ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen);
+		hfs_put_hl(left->node, bkey_record(key));
+		
+		hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) +
+			   2*sizeof(hfs_u32), RECTBL(root, 3));
+		key = bnode_key(root, 2);
+		key->KeyLen = keylen;
+		memcpy(key->value,
+		       ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen);
+		hfs_put_hl(right->node, bkey_record(key));
+
+		/* the former root (left) is now just a normal node */
+		left->sticky = HFS_NOT_STICKY;
+		if ((left->next = bhash(tree, left->node))) {
+			left->next->prev = left;
+		}
+		bhash(tree, left->node) = left;
+	}
+	hfs_bnode_relse(&bnr);
+	tree->dirt = 1;
+}
+
+/*
+ * insert_empty_bnode()
+ *
+ * Description:
+ *   Adds an empty node to the right of 'left'.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the tree to add a node to
+ *   struct hfs_bnode *left: the node to add a node after
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref *: reference to the new bnode.
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree) with at least 1 free node.
+ *   'left' points to a valid (struct hfs_bnode) belonging to 'tree'.
+ * Postconditions:
+ *   If NULL is returned then 'tree' and 'left' are unchanged.
+ *   Otherwise a node with 0 records is inserted in the tree to the right
+ *   of the node 'left'.  The 'ndFLink' of 'left' and the 'ndBLink' of
+ *   the former right-neighbor of 'left' (if one existed) point to the
+ *   new node.	If 'left' had no right neighbor and is a leaf node the
+ *   the 'bthLNode' of 'tree' points to the new node.  The free-count and
+ *   bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies
+ *   the required node.
+ */
+static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree,
+					       struct hfs_bnode *left)
+{
+	struct hfs_bnode_ref retval;
+	struct hfs_bnode_ref right;
+
+	retval = hfs_bnode_alloc(tree);
+	if (!retval.bn) {
+		hfs_warn("hfs_binsert: out of bnodes?.\n");
+		goto done;
+	}
+	retval.bn->sticky = HFS_NOT_STICKY;
+	if ((retval.bn->next = bhash(tree, retval.bn->node))) {
+		retval.bn->next->prev = retval.bn;
+	}
+	bhash(tree, retval.bn->node) = retval.bn;
+
+	if (left->ndFLink) {
+		right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE);
+		if (!right.bn) {
+			hfs_warn("hfs_binsert: corrupt btree.\n");
+			hfs_bnode_bitop(tree, retval.bn->node, 0);
+			hfs_bnode_relse(&retval);
+			goto done;
+		}
+		right.bn->ndBLink = retval.bn->node;
+		hfs_bnode_relse(&right);
+	} else if (left->ndType == ndLeafNode) {
+		tree->bthLNode = retval.bn->node;
+		tree->dirt = 1;
+	}
+
+	retval.bn->ndFLink   = left->ndFLink;
+	retval.bn->ndBLink   = left->node;
+	retval.bn->ndType    = left->ndType;
+	retval.bn->ndNHeight = left->ndNHeight;
+	retval.bn->ndNRecs   = 0;
+
+	left->ndFLink = retval.bn->node;
+
+ done:
+	return retval;
+}
+
+/*
+ * split()
+ *
+ * Description:
+ *   Splits an over full node during insertion.
+ *   Picks the split point that results in the most-nearly equal
+ *   space usage in the new and old nodes.
+ * Input Variable(s):
+ *   struct hfs_belem *elem: the over full node.
+ *   int size: the number of bytes to be used by the new record and its key.
+ * Output Variable(s):
+ *   struct hfs_belem *elem: changed to indicate where the new record
+ *    should be inserted.
+ * Returns:
+ *   struct hfs_bnode_ref: reference to the new bnode.
+ * Preconditions:
+ *   'elem' points to a valid path element corresponding to the over full node.
+ *   'size' is positive.
+ * Postconditions:
+ *   The records in the node corresponding to 'elem' are redistributed across
+ *   the old and new nodes so that after inserting the new record, the space
+ *   usage in these two nodes is as equal as possible.
+ *   'elem' is updated so that a call to binsert_nonfull() will insert the
+ *   new record in the correct location.
+ */
+static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size)
+{
+	struct hfs_bnode *bnode = elem->bnr.bn;
+	int nrecs, cutoff, index, tmp, used, in_right;
+	struct hfs_bnode_ref right;
+
+	right = insert_empty_bnode(bnode->tree, bnode);
+	if (right.bn) {
+		nrecs = bnode->ndNRecs;
+		cutoff = (size + bnode_end(bnode) -
+			      sizeof(struct NodeDescriptor) +
+			      (nrecs+1)*sizeof(hfs_u16))/2;
+		used = 0;
+		in_right = 1;
+		/* note that this only works because records sizes are even */
+		for (index=1; index <= elem->record; ++index) {
+			tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+			used += tmp;
+			if (used > cutoff) {
+				goto found;
+			}
+			used += tmp;
+		}
+		tmp = (size + sizeof(hfs_u16))/2;
+		used += tmp;
+		if (used > cutoff) {
+			goto found;
+		}
+		in_right = 0;
+		used += tmp;
+		for (; index <= nrecs; ++index) {
+			tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+			used += tmp;
+			if (used > cutoff) {
+				goto found;
+			}
+			used += tmp;
+		}
+		/* couldn't find the split point! */
+		hfs_bnode_relse(&right);
+	}
+	return right;
+
+found:
+	if (in_right) {
+		elem->bnr = right;
+		elem->record -= index-1;
+	}
+	hfs_bnode_shift_right(bnode, right.bn, index);
+
+	return right;
+}
+
+/*
+ * binsert()
+ *
+ * Description:
+ *   Inserts a record in a tree known to have enough room, even if the
+ *   insertion requires the splitting of nodes.
+ * Input Variable(s):
+ *    struct hfs_brec *brec: partial path to the node to insert in
+ *    const struct hfs_bkey *key: key for the new record
+ *    const void *data: data for the new record
+ *    hfs_u8 keysize: size of the key
+ *    hfs_u16 datasize: size of the data
+ *    int reserve: number of nodes reserved in case of splits
+ * Output Variable(s):
+ *    *brec = NULL
+ * Returns:
+ *    int: 0 on success, error code on failure
+ * Preconditions:
+ *    'brec' points to a valid (struct hfs_brec) corresponding to a
+ *     record in a leaf node, after which a record is to be inserted,
+ *     or to "record 0" of the leaf node if the record is to be inserted
+ *     before all existing records in the node.	 The (struct hfs_brec)
+ *     includes all ancestors of the leaf node that are needed to
+ *     complete the insertion including the parents of any nodes that
+ *     will be split.
+ *    'key' points to a valid (struct hfs_bkey) which is appropriate
+ *     to this tree, and which belongs at the insertion point.
+ *    'data' points data appropriate for the indicated node.
+ *    'keysize' gives the size in bytes of the key.
+ *    'datasize' gives the size in bytes of the data.
+ *    'reserve' gives the number of nodes that have been reserved in the
+ *     tree to allow for splitting of nodes.
+ * Postconditions:
+ *    All 'reserve'd nodes have been either used or released.
+ *    *brec = NULL
+ *    On success the key and data have been inserted at the indicated
+ *    location in the tree, all appropriate fields of the in-core data
+ *    structures have been changed and updated versions of the on-disk
+ *    data structures have been scheduled for write-back to disk.
+ *    On failure the B*-tree is probably invalid both on disk and in-core.
+ *
+ *    XXX: Some attempt at repair might be made in the event of failure,
+ *    or the fs should be remounted read-only so things don't get worse.
+ */
+static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key,
+		   const void *data, hfs_u8 keysize, hfs_u16 datasize,
+		   int reserve)
+{
+	struct hfs_bnode_ref left, right, other;
+	struct hfs_btree *tree = brec->tree;
+	struct hfs_belem *belem = brec->bottom;
+	int tmpsize = 1 + tree->bthKeyLen;
+	struct hfs_bkey *tmpkey = hfs_malloc(tmpsize);
+	hfs_u32 node;
+	
+	while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) {
+		left = belem->bnr;
+		if (left.bn->ndFLink &&
+                    hfs_bnode_in_brec(left.bn->ndFLink, brec)) {
+			hfs_warn("hfs_binsert: corrupt btree\n");
+			tree->reserved -= reserve;
+			hfs_free(tmpkey, tmpsize);
+			return -EIO;
+		}
+			
+		right = split(belem, ROUND(keysize+1) + ROUND(datasize));
+		--reserve;
+		--tree->reserved;
+		if (!right.bn) {
+			hfs_warn("hfs_binsert: unable to split node!\n");
+			tree->reserved -= reserve;
+			hfs_free(tmpkey, tmpsize);
+			return -ENOSPC;
+		}
+		binsert_nonfull(brec, belem, key, data, keysize, datasize);
+	
+		if (belem->bnr.bn == left.bn) {
+			other = right;
+			if (belem->record == 1) {
+				hfs_bnode_update_key(brec, belem, left.bn, 0);
+			}
+		} else {
+			other = left;
+		}
+
+		if (left.bn->node == tree->root->node) {
+			add_root(tree, left.bn, right.bn);
+			hfs_bnode_relse(&other);
+			goto done;
+		}
+
+		data = &node;
+		datasize = sizeof(node);
+		node = htonl(right.bn->node);
+		key = tmpkey;
+		keysize = tree->bthKeyLen;
+		memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1);
+		hfs_bnode_relse(&other);
+		
+		--belem;
+	}
+
+	if (belem < brec->top) {
+		hfs_warn("hfs_binsert: Missing parent.\n");
+		tree->reserved -= reserve;
+		hfs_free(tmpkey, tmpsize);
+		return -EIO;
+	}
+
+	binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+done:
+	tree->reserved -= reserve;
+	hfs_free(tmpkey, tmpsize);
+	return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_binsert()
+ *
+ * Description:
+ *   This function inserts a new record into a b-tree.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in
+ *   struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert
+ *   void *data: pointer to the data to associate with 'key' in the b-tree
+ *   unsigned int datasize: the size of the data
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 0 on success, error code on failure
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree)
+ *   'key' points to a valid (struct hfs_bkey)
+ *   'data' points to valid memory of length 'datasize'
+ * Postconditions:
+ *   If zero is returned then the record has been inserted in the
+ *    indicated location updating all in-core data structures and
+ *    scheduling all on-disk data structures for write-back.
+ */
+int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key,
+		const void *data, hfs_u16 datasize)
+{ 
+	struct hfs_brec brec;
+	struct hfs_belem *belem;
+	int err, reserve, retval;
+	hfs_u8 keysize;
+
+	if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) {
+		hfs_warn("hfs_binsert: invalid arguments.\n");
+		return -EINVAL;
+	}
+
+	if (key->KeyLen > tree->bthKeyLen) {
+		hfs_warn("hfs_binsert: oversized key\n");
+		return -EINVAL;
+	}
+
+restart:
+	if (!tree->bthNRecs) {
+		/* create the root bnode */
+		add_root(tree, NULL, NULL);
+		if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) {
+			hfs_warn("hfs_binsert: failed to create root.\n");
+			return -ENOSPC;
+		}
+	} else {
+		err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT);
+		if (err < 0) {
+			hfs_warn("hfs_binsert: hfs_brec_find failed.\n");
+			return err;
+		} else if (err == 0) {
+			hfs_brec_relse(&brec, NULL);
+			return -EEXIST;
+		}
+	}
+
+	keysize = key->KeyLen;
+	datasize = ROUND(datasize);
+	belem = brec.bottom;
+	belem->flags = 0;
+	if (bnode_freespace(belem->bnr.bn) <
+			    (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) {
+		belem->flags |= HFS_BPATH_OVERFLOW;
+	}
+	if (belem->record == 0) {
+		belem->flags |= HFS_BPATH_FIRST;
+	}
+
+	if (!belem->flags) {
+		hfs_brec_lock(&brec, brec.bottom);
+		reserve = 0;
+	} else {
+		reserve = brec.bottom - brec.top;
+		if (brec.top == 0) {
+			++reserve;
+		}
+		/* make certain we have enough nodes to proceed */
+		if ((tree->bthFree - tree->reserved) < reserve) {
+			hfs_brec_relse(&brec, NULL);
+			while (tree->lock) {
+				hfs_sleep_on(&tree->wait);
+			}
+			tree->lock = 1;
+			if ((tree->bthFree - tree->reserved) < reserve) {
+				hfs_btree_extend(tree);
+			}
+			tree->lock = 0;
+			hfs_wake_up(&tree->wait);
+			if ((tree->bthFree - tree->reserved) < reserve) {
+				return -ENOSPC;
+			} else {
+				goto restart;
+			}
+		}
+		tree->reserved += reserve;
+		hfs_brec_lock(&brec, NULL);
+	}
+
+	retval = binsert(&brec, key, data, keysize, datasize, reserve);
+	hfs_brec_relse(&brec, NULL);
+	if (!retval) {
+		++tree->bthNRecs;
+		tree->dirt = 1;
+	}
+	return retval;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bitmap.c linux/fs/hfs/bitmap.c
--- v2.1.77/linux/fs/hfs/bitmap.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bitmap.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,412 @@
+/*
+ * linux/fs/hfs/bitmap.c
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Based on GPLed code Copyright (C) 1995  Michael Dreher
+ *
+ * This file contains the code to modify the volume bitmap:
+ * search/set/clear bits.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_vbm_count_free()
+ *
+ * Description:
+ *   Count the number of consecutive cleared bits in the bitmap blocks of
+ *   the hfs MDB starting at bit number 'start'.  'mdb' had better
+ *   be locked or the indicated number of blocks may be no longer free,
+ *   when this functions returns!
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: bit number to start at
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   The number of consecutive cleared bits starting at bit 'start'
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   NONE
+ */
+hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start)
+{
+	hfs_u16 block_nr;	/* index of the current bitmap block */
+	hfs_u16 bit_nr;		/* index of the current bit in block */
+	hfs_u16 count;		/* number of bits found so far */
+	hfs_u16 len;		/* number of bits found in this block */
+	hfs_u16 max_block;	/* index of last bitmap block */
+	hfs_u16 max_bits;	/* index of last bit in block */
+
+	/* is this a valid HFS MDB? */
+	if (!mdb) {
+		return 0;
+	}
+
+	block_nr = start / HFS_BM_BPB;
+	bit_nr	 = start % HFS_BM_BPB;
+	max_block = (mdb->fs_ablocks + HFS_BM_BPB - 1) / HFS_BM_BPB - 1;
+
+	count = 0;
+	while (block_nr <= max_block) {
+		if (block_nr != max_block) {
+			max_bits = HFS_BM_BPB;
+		} else {
+			max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+		}
+
+		len=hfs_count_zero_bits(hfs_buffer_data(mdb->bitmap[block_nr]),
+					max_bits, bit_nr);
+		count += len;
+
+		/* see if we fell short of the end of this block */
+		if ((len + bit_nr) < max_bits) {
+			break;
+		}
+
+		++block_nr;
+		bit_nr = 0;
+	}
+	return count;
+}
+
+/*
+ * hfs_vbm_search_free()
+ *
+ * Description:
+ *   Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
+ *   the hfs MDB. 'mdb' had better be locked or the returned range
+ *   may be no longer free, when this functions returns!
+ *   XXX Currently the search starts from bit 0, but it should start with
+ *   the bit number stored in 's_alloc_ptr' of the MDB.
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 *num_bits: Pointer to the number of cleared bits
+ *     to search for
+ * Output Variable(s):
+ *   hfs_u16 *num_bits: The number of consecutive clear bits of the
+ *     returned range. If the bitmap is fragmented, this will be less than
+ *     requested and it will be zero, when the disk is full.
+ * Returns:
+ *   The number of the first bit of the range of cleared bits which has been
+ *   found. When 'num_bits' is zero, this is invalid!
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ *   'num_bits' points to a variable of type (hfs_u16), which contains
+ *	the number of cleared bits to find.
+ * Postconditions:
+ *   'num_bits' is set to the length of the found sequence.
+ */
+hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *mdb, hfs_u16 *num_bits)
+{
+	hfs_u16 block_nr; /* index of the current bitmap block */
+
+	/* position and length of current portion of a run */
+	hfs_u16 cur_pos, cur_len;
+
+	/* position and length of current complete run */
+	hfs_u16 pos=0, len=0;
+	
+	/* position and length of longest complete run */
+	hfs_u16 longest_pos=0, longest_len=0;
+
+	void *bitmap; /* contents of the current bitmap block */
+	hfs_u16 max_block; /* upper limit of outer loop */
+	hfs_u16 max_bits; /* upper limit of inner loop */
+
+	/* is this a valid HFS MDB? */
+	if (!mdb) {
+		*num_bits = 0;
+		hfs_warn("hfs_vbm_search_free: not a valid MDB\n");
+		return 0;
+	}
+	
+	/* make sure we have actual work to perform */
+	if (!(*num_bits)) {
+		return 0;
+	}
+
+	max_block = (mdb->fs_ablocks+HFS_BM_BPB-1) / HFS_BM_BPB - 1;
+	
+	/* search all bitmap blocks */
+	for (block_nr = 0; block_nr <= max_block; block_nr++) {
+		bitmap = hfs_buffer_data(mdb->bitmap[block_nr]);
+
+		if (block_nr != max_block) {
+			max_bits = HFS_BM_BPB;
+		} else {
+			max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+		}
+
+		cur_pos = 0;
+		do {
+			cur_len = hfs_count_zero_bits(bitmap, max_bits,
+						      cur_pos);
+			len += cur_len;
+			if (len > longest_len) {
+				longest_pos = pos;
+				longest_len = len;
+				if (len >= *num_bits) {
+					goto search_end;
+				}
+			}
+			if ((cur_pos + cur_len) == max_bits) {
+				break; /* zeros may continue into next block */
+			}
+
+			/* find start of next run of zeros */
+			cur_pos = hfs_find_zero_bit(bitmap, max_bits,
+						    cur_pos + cur_len);
+			pos = cur_pos + HFS_BM_BPB*block_nr;
+			len = 0;
+		} while (cur_pos < max_bits);
+	}
+
+search_end:
+	*num_bits = longest_len;
+	return longest_pos;
+}
+
+
+/*
+ * hfs_set_vbm_bits()
+ *
+ * Description:
+ *   Set the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: The offset of the first bit
+ *   hfs_u16 count: The number of bits
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: One of the bits was already set.  This is a strange
+ *	 error and when it happens, the filesystem must be repaired!
+ *   -2: One or more of the bits are out of range of the bitmap.
+ *   -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   Starting with bit number 'start', 'count' bits in the volume bitmap
+ *   are set. The affected bitmap blocks are marked "dirty", the free
+ *   block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+	hfs_u16 block_nr;	/* index of the current bitmap block */
+	hfs_u16 u32_nr;		/* index of the current hfs_u32 in block */
+	hfs_u16 bit_nr;		/* index of the current bit in hfs_u32 */
+	hfs_u16 left = count;	/* number of bits left to be set */
+	hfs_u32 *bitmap;	/* the current bitmap block's contents */
+
+	/* is this a valid HFS MDB? */
+	if (!mdb) {
+		return -3;
+	}
+
+	/* is there any actual work to be done? */
+	if (!count) {
+		return 0;
+	}
+
+	/* are all of the bits in range? */
+	if ((start + count) > mdb->fs_ablocks) {
+		return -2;
+	}
+
+	block_nr = start / HFS_BM_BPB;
+	u32_nr = (start % HFS_BM_BPB) / 32;
+	bit_nr = start % 32;
+
+	/* bitmap is always on a 32-bit boundary */
+	bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+	/* do any partial hfs_u32 at the start */
+	if (bit_nr != 0) {
+		while ((bit_nr < 32) && left) {
+			if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+				hfs_buffer_dirty(mdb->bitmap[block_nr]);
+				return -1;
+			}
+			++bit_nr;
+			--left;
+		}
+		bit_nr=0;
+
+		/* advance u32_nr and check for end of this block */
+		if (++u32_nr > 127) {
+			u32_nr = 0;
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			++block_nr;
+			/* bitmap is always on a 32-bit boundary */
+			bitmap = (hfs_u32 *)
+					hfs_buffer_data(mdb->bitmap[block_nr]);
+		}
+	}
+
+	/* do full hfs_u32s */
+	while (left > 31) {
+		if (bitmap[u32_nr] != ((hfs_u32)0)) {
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			return -1;
+		}
+		bitmap[u32_nr] = ~((hfs_u32)0);
+		left -= 32;
+
+		/* advance u32_nr and check for end of this block */
+		if (++u32_nr > 127) {
+			u32_nr = 0;
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			++block_nr;
+			/* bitmap is always on a 32-bit boundary */
+			bitmap = (hfs_u32 *)
+					hfs_buffer_data(mdb->bitmap[block_nr]);
+		}
+	}
+
+			
+	/* do any partial hfs_u32 at end */
+	while (left) {
+		if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			return -1;
+		}
+		++bit_nr;
+		--left;
+	}
+
+	hfs_buffer_dirty(mdb->bitmap[block_nr]);
+	mdb->free_ablocks -= count;
+
+	/* successful completion */
+	hfs_mdb_dirty(mdb->sys_mdb);
+	return 0;
+}
+
+/*
+ * hfs_clear_vbm_bits()
+ *
+ * Description:
+ *   Clear the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: The offset of the first bit
+ *   hfs_u16 count: The number of bits
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: One of the bits was already clear.  This is a strange
+ *	 error and when it happens, the filesystem must be repaired!
+ *   -2: One or more of the bits are out of range of the bitmap.
+ *   -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   Starting with bit number 'start', 'count' bits in the volume bitmap
+ *   are cleared. The affected bitmap blocks are marked "dirty", the free
+ *   block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_clear_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+	hfs_u16 block_nr;	/* index of the current bitmap block */
+	hfs_u16 u32_nr;		/* index of the current hfs_u32 in block */
+	hfs_u16 bit_nr;		/* index of the current bit in hfs_u32 */
+	hfs_u16 left = count;	/* number of bits left to be set */
+	hfs_u32 *bitmap;	/* the current bitmap block's contents */
+
+	/* is this a valid HFS MDB? */
+	if (!mdb) {
+		return -3;
+	}
+
+	/* is there any actual work to be done? */
+	if (!count) {
+		return 0;
+	}
+
+	/* are all of the bits in range? */
+	if ((start + count) > mdb->fs_ablocks) {
+		return -2;
+	}
+
+	block_nr = start / HFS_BM_BPB;
+	u32_nr = (start % HFS_BM_BPB) / 32;
+	bit_nr = start % 32;
+
+	/* bitmap is always on a 32-bit boundary */
+	bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+	/* do any partial hfs_u32 at the start */
+	if (bit_nr != 0) {
+		while ((bit_nr < 32) && left) {
+			if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+				hfs_buffer_dirty(mdb->bitmap[block_nr]);
+				return -1;
+			}
+			++bit_nr;
+			--left;
+		}
+		bit_nr=0;
+
+		/* advance u32_nr and check for end of this block */
+		if (++u32_nr > 127) {
+			u32_nr = 0;
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			++block_nr;
+			/* bitmap is always on a 32-bit boundary */
+			bitmap = (hfs_u32 *)
+					hfs_buffer_data(mdb->bitmap[block_nr]);
+		}
+	}
+
+	/* do full hfs_u32s */
+	while (left > 31) {
+		if (bitmap[u32_nr] != ~((hfs_u32)0)) {
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			return -1;
+		}
+		bitmap[u32_nr] = ((hfs_u32)0);
+		left -= 32;
+
+		/* advance u32_nr and check for end of this block */
+		if (++u32_nr > 127) {
+			u32_nr = 0;
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			++block_nr;
+			/* bitmap is always on a 32-bit boundary */
+			bitmap = (hfs_u32 *)
+					hfs_buffer_data(mdb->bitmap[block_nr]);
+		}
+	}
+
+			
+	/* do any partial hfs_u32 at end */
+	while (left) {
+		if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+			hfs_buffer_dirty(mdb->bitmap[block_nr]);
+			return -1;
+		}
+		++bit_nr;
+		--left;
+	}
+
+	hfs_buffer_dirty(mdb->bitmap[block_nr]);
+	mdb->free_ablocks += count;
+
+	/* successful completion */
+	hfs_mdb_dirty(mdb->sys_mdb);
+	return 0;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bitops.c linux/fs/hfs/bitops.c
--- v2.1.77/linux/fs/hfs/bitops.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bitops.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,124 @@
+/*
+ * linux/fs/hfs/bitops.c
+ *
+ * Copyright (C) 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions to handle bitmaps in "left-to-right"
+ * bit-order such that the MSB of a 32-bit big-endian word is bit 0.
+ * (This corresponds to bit 7 of a 32-bit little-endian word.)
+ *
+ * I have tested and confirmed that the results are identical on the
+ * Intel x86, PowerPC and DEC Alpha processors.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_find_zero_bit()
+ *
+ * Description:
+ *  Given a block of memory, its length in bits, and a starting bit number,
+ *  determine the number of the first zero bits (in left-to-right ordering)
+ *  in that range.
+ *
+ *  Returns >= 'size' if no zero bits are found in the range.
+ *
+ *  Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ *  may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_find_zero_bit(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+	const hfs_u32 *end   = start + ((size + 31) >> 5);
+	const hfs_u32 *curr  = start + (offset >> 5);
+	int bit = offset % 32;
+	
+	if (offset < size) {
+		/* scan the first partial hfs_u32 for zero bits */
+		if (bit != 0) {
+			do {
+				if (!hfs_test_bit(bit, curr)) {
+					goto done;
+				}
+				++bit;
+			} while (bit < 32);
+			bit = 0;
+			++curr;
+		}
+	
+		/* scan complete hfs_u32s for the first zero bit */
+		while (curr < end) {
+			if (*curr == ~((hfs_u32)0)) {
+				++curr;
+			} else {
+				while (hfs_test_bit(bit, curr)) {
+					++bit;
+				}
+				break;
+			}
+		}
+
+done:
+		bit |= (curr - start) << 5;
+		return bit;
+	} else {
+		return size;
+	}
+}
+
+/*
+ * hfs_count_zero_bits()
+ *
+ * Description:
+ *  Given a block of memory, its length in bits, and a starting bit number,
+ *  determine the number of consecutive zero bits (in left-to-right ordering)
+ *  in that range.
+ *
+ *  Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ *  may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_count_zero_bits(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+	const hfs_u32 *end   = start + ((size + 31) >> 5);
+	const hfs_u32 *curr  = start + (offset >> 5);
+	int bit = offset % 32;
+
+	if (offset < size) {
+		/* scan the first partial hfs_u32 for one bits */
+		if (bit != 0) {
+			do {
+				if (hfs_test_bit(bit, curr)) {
+					goto done;
+				}
+				++bit;
+			} while (bit < 32);
+			bit = 0;
+			++curr;
+		}
+	
+		/* scan complete hfs_u32s for the first one bit */
+		while (curr < end) {
+			if (*curr == ((hfs_u32)0)) {
+				++curr;
+			} else {
+				while (!hfs_test_bit(bit, curr)) {
+					++bit;
+				}
+				break;
+			}
+		}
+
+done:
+		bit |= (curr - start) << 5;
+		if (bit > size) {
+			bit = size;
+		}
+		return bit - offset;
+	} else {
+		return 0;
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/bnode.c linux/fs/hfs/bnode.c
--- v2.1.77/linux/fs/hfs/bnode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/bnode.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,540 @@
+/*
+ * linux/fs/hfs/bnode.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access nodes in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local variables ================*/
+ 
+/* debugging statistics */
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+int bnode_count = 0;
+#endif
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_delete()
+ *
+ * Description:
+ *   This function is called to remove a bnode from the cache and
+ *   release its resources.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: Pointer to the (struct hfs_bnode) to be
+ *   removed from the cache.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ *   The node 'bn' is removed from the cache, its memory freed and its
+ *   buffer (if any) released.
+ */
+void hfs_bnode_delete(struct hfs_bnode *bn)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	--bnode_count;
+#endif
+	/* join neighbors */
+	if (bn->next) {
+		bn->next->prev = bn->prev;
+	}
+	if (bn->prev) {
+		bn->prev->next = bn->next;
+	}
+	/* fix cache slot if necessary */
+	if (bhash(bn->tree, bn->node) == bn) {
+		bhash(bn->tree, bn->node) = bn->next;
+	}
+	/* release resources */
+	hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+	HFS_DELETE(bn);
+}
+
+
+/*
+ * hfs_bnode_read()
+ *
+ * Description: 
+ *   This function creates a (struct hfs_bnode) and, if appropriate,
+ *   inserts it in the cache.
+ * Input Variable(s):
+ *   struct hfs_bnode *bnode: pointer to the new bnode.
+ *   struct hfs_btree *tree: pointer to the (struct hfs_btree)
+ *    containing the desired node
+ *   hfs_u32 node: the number of the desired node.
+ *   int sticky: the value to assign to the 'sticky' field.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_bnode *) pointing to the newly created bnode or NULL.
+ * Preconditions:
+ *   'bnode' points to a "valid" (struct hfs_bnode).
+ *   'tree' points to a "valid" (struct hfs_btree).
+ *   'node' is an existing node number in the B-tree.
+ * Postconditions:
+ *   The following are true of 'bnode' upon return:
+ *    The 'magic' field is set to indicate a valid (struct hfs_bnode). 
+ *    The 'sticky', 'tree' and 'node' fields are initialized to the
+ *    values of the of the corresponding arguments.
+ *    If the 'sticky' argument is zero then the fields 'prev' and
+ *    'next' are initialized by inserting the (struct hfs_bnode) in the
+ *    linked list of the appropriate cache slot; otherwise they are
+ *    initialized to NULL.
+ *    The data is read from disk (or buffer cache) and the 'buf' field
+ *    points to the buffer for that data.
+ *    If no other processes tried to access this node while this
+ *    process was waiting on disk I/O (if necessary) then the
+ *    remaining fields are zero ('count', 'resrv', 'lock') or NULL
+ *    ('wqueue', 'rqueue') corresponding to no accesses.
+ *    If there were access attempts during I/O then they were blocked
+ *    until the I/O was complete, and the fields 'count', 'resrv',
+ *    'lock', 'wqueue' and 'rqueue' reflect the results of unblocking
+ *    those processes when the I/O was completed.
+ */
+void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree,
+		    hfs_u32 node, int sticky)
+{
+	struct NodeDescriptor *nd;
+	int block, lcv;
+	hfs_u16 curr, prev, limit;
+
+	/* Initialize the structure */
+	memset(bnode, 0, sizeof(*bnode));
+	bnode->magic = HFS_BNODE_MAGIC;
+	bnode->tree = tree;
+	bnode->node = node;
+	bnode->sticky = sticky;
+
+	if (sticky == HFS_NOT_STICKY) {
+		/* Insert it in the cache if appropriate */
+		if ((bnode->next = bhash(tree, node))) {
+			bnode->next->prev = bnode;
+		}
+		bhash(tree, node) = bnode;
+	}
+
+	/* Make the bnode look like it is being
+	   modified so other processes will wait for
+	   the I/O to complete */
+	bnode->count = bnode->resrv = bnode->lock = 1;
+
+	/* Read in the node, possibly causing a schedule()
+	   call.  If the I/O fails then emit a warning.	 Each
+	   process that was waiting on the bnode (including
+	   the current one) will notice the failure and
+	   hfs_bnode_relse() the node.	The last hfs_bnode_relse()
+	   will call hfs_bnode_delete() and discard the bnode.	*/
+
+	block = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+	if (!block) {
+		hfs_warn("hfs_bnode_read: bad node number 0x%08x\n", node);
+	} else if (hfs_buffer_ok(bnode->buf =
+				 hfs_buffer_get(tree->sys_mdb, block, 1))) {
+		/* read in the NodeDescriptor */
+		nd = (struct NodeDescriptor *)hfs_buffer_data(bnode->buf);
+		bnode->ndFLink    = hfs_get_hl(nd->ndFLink);
+		bnode->ndBLink    = hfs_get_hl(nd->ndBLink);
+		bnode->ndType     = nd->ndType;
+		bnode->ndNHeight  = nd->ndNHeight;
+		bnode->ndNRecs    = hfs_get_hs(nd->ndNRecs);
+
+		/* verify the integrity of the node */
+		prev = sizeof(struct NodeDescriptor);
+		limit = HFS_SECTOR_SIZE - sizeof(hfs_u16)*(bnode->ndNRecs + 1);
+		for (lcv=1; lcv <= (bnode->ndNRecs + 1); ++lcv) {
+			curr = hfs_get_hs(RECTBL(bnode, lcv));
+			if ((curr < prev) || (curr > limit)) {
+				hfs_warn("hfs_bnode_read: corrupt node "
+					 "number 0x%08x\n", node);
+				hfs_buffer_put(bnode->buf);
+				bnode->buf = NULL;
+				break;
+			}
+			prev = curr;
+		}
+	}
+
+	/* Undo our fakery with the lock state and
+	   hfs_wake_up() anyone who we managed to trick */
+	--bnode->count;
+	bnode->resrv = bnode->lock = 0;
+	hfs_wake_up(&bnode->rqueue);
+}
+
+/*
+ * hfs_bnode_lock()
+ *
+ * Description:
+ *   This function does the locking of a bnode.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to lock
+ *   int lock_type: the type of lock desired
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' points to a "valid" (struct hfs_bnode).
+ *   'lock_type' is a valid hfs_lock_t
+ * Postconditions:
+ *   The 'count' field of 'bn' is incremented by one.  If 'lock_type'
+ *   is HFS_LOCK_RESRV the 'resrv' field is also incremented.
+ */
+void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type)
+{
+	struct hfs_bnode *bn = bnr->bn;
+
+	if ((lock_type == bnr->lock_type) || !bn) {
+		return;
+	}
+
+	if (bnr->lock_type == HFS_LOCK_WRITE) {
+		hfs_bnode_commit(bnr->bn);
+	}
+
+	switch (lock_type) {
+	default:
+		goto bail;
+		break;
+
+	case HFS_LOCK_READ:
+		/* We may not obtain read access if any process is
+		   currently modifying or waiting to modify this node.
+		   If we can't obtain access we wait on the rqueue
+		   wait queue to be woken up by the modifying process
+		   when it relinquishes its lock. */
+		switch (bnr->lock_type) {
+		default:
+			goto bail;
+			break;
+
+		case HFS_LOCK_NONE:
+			while (bn->lock || bn->wqueue) {
+				hfs_sleep_on(&bn->rqueue);
+			}
+			++bn->count;
+			break;
+		}
+		break;
+			
+	case HFS_LOCK_RESRV:
+		/* We may not obtain a reservation (read access with
+		   an option to write later), if any process currently
+		   holds a reservation on this node.  That includes
+		   any process which is currently modifying this node.
+		   If we can't obtain access, then we wait on the
+		   rqueue wait queue to e woken up by the
+		   reservation-holder when it calls hfs_bnode_relse. */
+		switch (bnr->lock_type) {
+		default:
+			goto bail;
+			break;
+
+		case HFS_LOCK_NONE:
+			while (bn->resrv) {
+				hfs_sleep_on(&bn->rqueue);
+			}
+			bn->resrv = 1;
+			++bn->count;
+			break;
+
+		case HFS_LOCK_WRITE:
+			bn->lock = 0;
+			hfs_wake_up(&bn->rqueue);
+			break;
+		}
+		break;
+		
+	case HFS_LOCK_WRITE:
+		switch (bnr->lock_type) {
+		default:
+			goto bail;
+			break;
+
+		case HFS_LOCK_NONE:
+			while (bn->resrv) {
+				hfs_sleep_on(&bn->rqueue);
+			}
+			bn->resrv = 1;
+			++bn->count;
+		case HFS_LOCK_RESRV:
+			while (bn->count > 1) {
+				hfs_sleep_on(&bn->wqueue);
+			}
+			bn->lock = 1;
+			break;
+		}
+		break;
+
+	case HFS_LOCK_NONE:
+		switch (bnr->lock_type) {
+		default:
+			goto bail;
+			break;
+
+		case HFS_LOCK_READ:
+			/* This process was reading this node.	If
+			   there is now exactly one other process using
+			   the node then hfs_wake_up() a (potentially
+			   nonexistent) waiting process.  Note that I
+			   refer to "a" process since the reservation
+			   system ensures that only one process can
+			   get itself on the wait queue.  */
+			if (bn->count == 2) {
+				hfs_wake_up(&bn->wqueue);
+			}
+			break;
+
+		case HFS_LOCK_WRITE:
+			/* This process was modifying this node.
+			   Unlock the node and fall-through to the
+			   HFS_LOCK_RESRV case, since a 'reservation'
+			   is a prerequisite for HFS_LOCK_WRITE.  */
+			bn->lock = 0;
+		case HFS_LOCK_RESRV:
+			/* This process had placed a 'reservation' on
+			   this node, indicating an intention to
+			   possibly modify the node.  We can get to
+			   this spot directly (if the 'reservation'
+			   not converted to a HFS_LOCK_WRITE), or by
+			   falling through from the above case if the
+			   reservation was converted.
+			   Since HFS_LOCK_RESRV and HFS_LOCK_WRITE
+			   both block processes that want access
+			   (HFS_LOCK_RESRV blocks other processes that
+			   want reservations but allow HFS_LOCK_READ
+			   accesses, while HFS_LOCK_WRITE must have
+			   exclusive access and thus blocks both
+			   types) we hfs_wake_up() any processes that
+			   might be waiting for access.	 If multiple
+			   processes are waiting for a reservation
+			   then the magic of process scheduling will
+			   settle the dispute. */
+			bn->resrv = 0;
+			hfs_wake_up(&bn->rqueue);
+			break;
+		}
+		--bn->count;
+		break;
+	}
+	bnr->lock_type = lock_type;
+	return;
+
+bail:
+	hfs_warn("hfs_bnode_lock: invalid lock change: %d->%d.\n",
+		bnr->lock_type, lock_type);
+	return;
+}
+
+/*
+ * hfs_bnode_relse()
+ *
+ * Description:
+ *   This function is called when a process is done using a bnode.  If
+ *   the proper conditions are met then we call hfs_bnode_delete() to remove
+ *   it from the cache.	 If it is not deleted then we update its state
+ *   to reflect one less process using it.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to release.
+ *   int lock_type: The type of lock held by the process releasing this node.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' is NULL or points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ *   If 'bn' meets the appropriate conditions (see below) then it is
+ *   kept in the cache and all fields are set to consistent values
+ *   which reflect one less process using the node than upon entry.
+ *   If 'bn' does not meet the conditions then it is deleted (see
+ *   hfs_bnode_delete() for postconditions).
+ *   In either case, if 'lock_type' is HFS_LOCK_WRITE
+ *   then the corresponding buffer is dirtied.
+ */
+void hfs_bnode_relse(struct hfs_bnode_ref *bnr)
+{
+	struct hfs_bnode *bn;
+
+	if (!bnr || !(bn = bnr->bn)) {
+		return;
+	}
+
+	/* We update the lock state of the node if it is still in use
+	   or if it is "sticky" (such as the B-tree head and root).
+	   Otherwise we just delete it.	 */
+	if ((bn->count > 1) || (bn->rqueue) || (bn->sticky != HFS_NOT_STICKY)) {
+		hfs_bnode_lock(bnr, HFS_LOCK_NONE);
+	} else {
+		/* dirty buffer if we (might) have modified it */
+		if (bnr->lock_type == HFS_LOCK_WRITE) {
+			hfs_bnode_commit(bn);
+		}
+		hfs_bnode_delete(bn);
+		bnr->lock_type = HFS_LOCK_NONE;
+	}
+	bnr->bn = NULL;
+}
+
+/*
+ * hfs_bnode_find()
+ *
+ * Description:
+ *   This function is called to obtain a bnode.  The cache is
+ *   searched for the node.  If it not found there it is added to
+ *   the cache by hfs_bnode_read().  There are two special cases node=0
+ *   (the header node) and node='tree'->bthRoot (the root node), in
+ *   which the nodes are obtained from fields of 'tree' without
+ *   consulting or modifying the cache.
+ * Input Variable(s):
+ *   struct hfs_tree *tree: pointer to the (struct hfs_btree) from
+ *    which to get a node.
+ *   int node: the node number to get from 'tree'.
+ *   int lock_type: The kind of access (HFS_LOCK_READ, or
+ *    HFS_LOCK_RESRV) to obtain to the node
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_bnode_ref) Reference to the requested node.
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree).
+ * Postconditions:
+ *   If 'node' refers to a valid node in 'tree' and 'lock_type' has
+ *   one of the values listed above and no I/O errors occur then the
+ *   value returned refers to a valid (struct hfs_bnode) corresponding
+ *   to the requested node with the requested access type.  The node
+ *   is also added to the cache if not previously present and not the
+ *   root or header.
+ *   If the conditions given above are not met, the bnode in the
+ *   returned reference is NULL.
+ */
+struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *tree,
+				    hfs_u32 node, int lock_type)
+{
+	struct hfs_bnode *bn;
+	struct hfs_bnode *empty = NULL;
+	struct hfs_bnode_ref bnr;
+
+	bnr.lock_type = HFS_LOCK_NONE;
+	bnr.bn = NULL;
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	hfs_warn("hfs_bnode_find: %c %d:%d\n",
+		 lock_type==HFS_LOCK_READ?'R':
+			(lock_type==HFS_LOCK_RESRV?'V':'W'),
+		 (int)ntohl(tree->entry.cnid), node);
+#endif
+
+	/* check special cases */
+	if (!node) {
+		bn = &tree->head;
+		goto return_it;
+	} else if (node == tree->bthRoot) {
+		bn = tree->root;
+		goto return_it;
+	} 
+
+restart:
+	/* look for the node in the cache. */
+	bn = bhash(tree, node);
+	while (bn && (bn->magic == HFS_BNODE_MAGIC)) {
+		if (bn->node == node) {
+			goto found_it;
+		}
+		bn = bn->next;
+	}
+
+	if (!empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+		++bnode_count;
+#endif
+		if (HFS_NEW(empty)) {
+			goto restart;
+		}
+		return bnr;
+	}
+	bn = empty;
+	hfs_bnode_read(bn, tree, node, HFS_NOT_STICKY);
+	goto return_it;
+
+found_it:
+	/* check validity */
+	if (bn->magic != HFS_BNODE_MAGIC) {
+		/* If we find a corrupt bnode then we return
+		   NULL.  However, we don't try to remove it
+		   from the cache or release its resources
+		   since we have no idea what kind of trouble
+		   we could get into that way. */
+		hfs_warn("hfs_bnode_find: bnode cache is corrupt.\n");
+		return bnr;
+	} 
+	if (empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+		--bnode_count;
+#endif
+		HFS_DELETE(empty);
+	}
+	
+return_it:
+	/* Wait our turn */
+	bnr.bn = bn;
+	hfs_bnode_lock(&bnr, lock_type);
+
+	/* Check for failure to read the node from disk */
+	if (!hfs_buffer_ok(bn->buf)) {
+		hfs_bnode_relse(&bnr);
+	}
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	if (!bnr.bn) {
+		hfs_warn("hfs_bnode_find: failed\n");
+	} else {
+		hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count,
+			 bn->buf->b_count, bn->ndNHeight, bnode_count);
+	}
+#endif
+
+	return bnr;
+}
+
+/*
+ * hfs_bnode_commit()
+ *
+ * Called to write a possibly dirty bnode back to disk.
+ */
+void hfs_bnode_commit(struct hfs_bnode *bn)
+{
+	if (hfs_buffer_ok(bn->buf)) {
+		struct NodeDescriptor *nd;
+		nd = (struct NodeDescriptor *)hfs_buffer_data(bn->buf);
+
+		hfs_put_hl(bn->ndFLink, nd->ndFLink);
+		hfs_put_hl(bn->ndBLink, nd->ndBLink);
+		nd->ndType    = bn->ndType;
+		nd->ndNHeight = bn->ndNHeight;
+		hfs_put_hs(bn->ndNRecs, nd->ndNRecs);
+		hfs_buffer_dirty(bn->buf);
+
+		/* increment write count */
+		hfs_mdb_dirty(bn->tree->sys_mdb);
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/brec.c linux/fs/hfs/brec.c
--- v2.1.77/linux/fs/hfs/brec.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/brec.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,239 @@
+/*
+ * linux/fs/hfs/brec.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * first()
+ *
+ * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise
+ */
+static inline int first(const struct hfs_belem *elem)
+{
+	return (elem->record == 1) ? HFS_BPATH_FIRST : 0;
+}
+
+/*
+ * overflow()
+ *
+ * return HFS_BPATH_OVERFLOW if the node has no room for an 
+ * additional pointer record, 0 otherwise.
+ */
+static inline int overflow(const struct hfs_btree *tree,
+			   const struct hfs_bnode *bnode)
+{
+	/* there is some algebra involved in getting this form */
+	return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) <
+		 (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) +
+		  ROUND(tree->bthKeyLen+1))) ?  HFS_BPATH_OVERFLOW : 0;
+}
+
+/*
+ * underflow()
+ *
+ * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full
+ * upon removal of a pointer record, 0 otherwise.
+ */
+static inline int underflow(const struct hfs_btree *tree,
+			    const struct hfs_bnode *bnode)
+{
+	return ((bnode->ndNRecs * sizeof(hfs_u16) +
+		 bnode_offset(bnode, bnode->ndNRecs)) <
+		(HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ?
+		HFS_BPATH_UNDERFLOW : 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_next()
+ *
+ * Description:
+ *   Obtain access to a child of an internal node in a B-tree.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ *    add an element to.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_belem *: pointer to the new path element or NULL
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec), the last element of
+ *   which corresponds to a record in a bnode of type ndIndxNode and the
+ *   'record' field indicates the index record for the desired child.
+ * Postconditions:
+ *   If the call to hfs_bnode_find() fails then 'brec' is released
+ *   and a NULL is returned.
+ *   Otherwise:
+ *    Any ancestors in 'brec' that are not needed (as determined by the
+ *     'keep_flags' field of 'brec) are released from 'brec'.
+ *    A new element is added to 'brec' corresponding to the desired
+ *     child.
+ *    The child is obtained with the same 'lock_type' field as its
+ *     parent.
+ *    The 'record' field is initialized to the last record.
+ *    A pointer to the new path element is returned.
+ */
+struct hfs_belem *hfs_brec_next(struct hfs_brec *brec)
+{
+	struct hfs_belem *elem = brec->bottom;
+	hfs_u32 node;
+	int lock_type;
+
+	/* release unneeded ancestors */
+	elem->flags = first(elem) |
+		      overflow(brec->tree, elem->bnr.bn) |
+		      underflow(brec->tree, elem->bnr.bn);
+	if (!(brec->keep_flags & elem->flags)) {
+		hfs_brec_relse(brec, brec->bottom-1);
+	} else if ((brec->bottom-2 >= brec->top) &&
+		   !(elem->flags & (elem-1)->flags)) {
+		hfs_brec_relse(brec, brec->bottom-2);
+	}
+
+	node = hfs_get_hl(belem_record(elem));
+	lock_type = elem->bnr.lock_type;
+
+	if (!node || hfs_bnode_in_brec(node, brec)) {
+		hfs_warn("hfs_bfind: corrupt btree\n");
+		hfs_brec_relse(brec, NULL);
+		return NULL;
+	}
+
+	++elem;
+	++brec->bottom;
+
+	elem->bnr = hfs_bnode_find(brec->tree, node, lock_type);
+	if (!elem->bnr.bn) {
+		hfs_brec_relse(brec, NULL);
+		return NULL;
+	}
+	elem->record = elem->bnr.bn->ndNRecs;
+
+	return elem;
+}
+
+/*
+ * hfs_brec_lock()
+ *
+ * Description:
+ *   This function obtains HFS_LOCK_WRITE access to the bnode
+ *   containing this hfs_brec.	All descendents in the path from this
+ *   record to the leaf are given HFS_LOCK_WRITE access and all
+ *   ancestors in the path from the root to here are released.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the brec to obtain
+ *    HFS_LOCK_WRITE access to some of the nodes of.
+ *   struct hfs_belem *elem: the first node to lock or NULL for all
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions: 
+ *   All nodes between the indicated node and the beginning of the path
+ *    are released.  hfs_bnode_lock() is called in turn on each node
+ *    from the indicated node to the leaf node of the path, with a
+ *    lock_type argument of HFS_LOCK_WRITE.  If one of those calls
+ *    results in deadlock, then this function will never return.
+ */
+void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem) 
+{
+	if (!elem) {
+		elem = brec->top;
+	} else if (elem > brec->top) {
+		hfs_brec_relse(brec, elem-1);
+	}
+
+	while (elem <= brec->bottom) {
+		hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE);
+		++elem;
+	}
+}
+
+/*
+ * hfs_brec_init()
+ *
+ * Description:
+ *   Obtain access to the root node of a B-tree.
+ *   Note that this first must obtain access to the header node.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ *    initialize
+ *   struct hfs_btree *btree: pointer to the (struct hfs_btree)
+ *   int lock_type: the type of access to get to the nodes.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_belem *: pointer to the root path element or NULL
+ * Preconditions:
+ *   'brec' points to a (struct hfs_brec).
+ *   'tree' points to a valid (struct hfs_btree).
+ * Postconditions:
+ *   If the two calls to brec_bnode_find() succeed then the return value
+ *   points to a (struct hfs_belem) which corresponds to the root node
+ *   of 'brec->tree'.
+ *   Both the root and header nodes are obtained with the type of lock
+ *   given by (flags & HFS_LOCK_MASK).
+ *   The fields 'record' field of the root is set to its last record.
+ *   If the header node is not needed to complete the appropriate
+ *   operation (as determined by the 'keep_flags' field of 'brec') then
+ *   it is released before this function returns.
+ *   If either call to brec_bnode_find() fails, NULL is returned and the
+ *   (struct hfs_brec) pointed to by 'brec' is invalid.
+ */
+struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree,
+				int flags)
+{
+	struct hfs_belem *head = &brec->elem[0];
+	struct hfs_belem *root = &brec->elem[1];
+	int lock_type = flags & HFS_LOCK_MASK;
+
+	brec->tree = tree;
+
+	head->bnr = hfs_bnode_find(tree, 0, lock_type);
+	if (!head->bnr.bn) {
+		return NULL;
+	}
+
+	root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type);
+	if (!root->bnr.bn) {
+		hfs_bnode_relse(&head->bnr);
+		return NULL;
+	}
+
+	root->record = root->bnr.bn->ndNRecs;
+	
+	brec->top = head;
+	brec->bottom = root;
+	
+	brec->keep_flags = flags & HFS_BPATH_MASK;
+
+	/* HFS_BPATH_FIRST not applicable for root */
+	/* and HFS_BPATH_UNDERFLOW is different */
+	root->flags = overflow(tree, root->bnr.bn);
+	if (root->record < 3) {
+		root->flags |= HFS_BPATH_UNDERFLOW;
+	}
+
+	if (!(root->flags & brec->keep_flags)) {
+		hfs_brec_relse(brec, head);
+	}
+
+	return root;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/btree.c linux/fs/hfs/btree.c
--- v2.1.77/linux/fs/hfs/btree.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/btree.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,316 @@
+/*
+ * linux/fs/hfs/btree.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to manipulate the B-tree structure.
+ * The catalog and extents files are both B-trees.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_ditch() 
+ *
+ * Description:
+ *   This function deletes an entire linked list of bnodes, so it
+ *   does not need to keep the linked list consistent as
+ *   hfs_bnode_delete() does.
+ *   Called by hfs_btree_init() for error cleanup and by hfs_btree_free().
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in
+ *    the linked list to be deleted.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev'
+ *    field of NULL.
+ * Postconditions:
+ *   'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers
+ *   are deleted, freeing the associated memory and hfs_buffer_put()ing
+ *   the associated buffer.
+ */
+static void hfs_bnode_ditch(struct hfs_bnode *bn) {
+	struct hfs_bnode *tmp;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+	extern int bnode_count;
+#endif
+
+	while (bn != NULL) {
+		tmp = bn->next;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+		hfs_warn("deleting node %d from tree %d with count %d\n",
+		         bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count);
+		--bnode_count;
+#endif
+		hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+
+		/* free all but the header */
+		if (bn->node) {
+			HFS_DELETE(bn);
+		}
+		bn = tmp;
+	}
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_btree_free()
+ *
+ * Description:
+ *   This function frees a (struct hfs_btree) obtained from hfs_btree_init().
+ *   Called by hfs_put_super().
+ * Input Variable(s):
+ *   struct hfs_btree *bt: pointer to the (struct hfs_btree) to free
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bt' is NULL or points to a "valid" (struct hfs_btree)
+ * Postconditions:
+ *   If 'bt' points to a "valid" (struct hfs_btree) then all (struct
+ *    hfs_bnode)s associated with 'bt' are freed by calling
+ *    hfs_bnode_ditch() and the memory associated with the (struct
+ *    hfs_btree) is freed.
+ *   If 'bt' is NULL or not "valid" an error is printed and nothing
+ *    is changed.
+ */
+void hfs_btree_free(struct hfs_btree *bt)
+{
+	int lcv;
+
+	if (bt && (bt->magic == HFS_BTREE_MAGIC)) {
+		hfs_extent_free(&bt->entry.u.file.data_fork);
+
+		for (lcv=0; lcv<HFS_CACHELEN; ++lcv) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+			hfs_warn("deleting nodes from bucket %d:\n", lcv);
+#endif
+			hfs_bnode_ditch(bt->cache[lcv]);
+		}
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+		hfs_warn("deleting header and bitmap nodes\n");
+#endif
+		hfs_bnode_ditch(&bt->head);
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+		hfs_warn("deleting root node\n");
+#endif
+		hfs_bnode_ditch(bt->root);
+
+		HFS_DELETE(bt);
+	} else if (bt) {
+		hfs_warn("hfs_btree_free: corrupted hfs_btree.\n");
+	}
+}
+
+/*
+ * hfs_btree_init()
+ *
+ * Description:
+ *   Given some vital information from the MDB (HFS superblock),
+ *   initializes the fields of a (struct hfs_btree).
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: pointer to the MDB
+ *   ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree
+ *   hfs_u32 tsize: the size, in bytes, of the B-tree
+ *   hfs_u32 csize: the size, in bytes, of the clump size for the B-tree
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_btree *): pointer to the initialized hfs_btree on success,
+ *    or NULL on failure
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb)
+ * Postconditions:
+ *   Assuming the inputs are what they claim to be, no errors occur
+ *   reading from disk, and no inconsistencies are noticed in the data
+ *   read from disk, the return value is a pointer to a "valid"
+ *   (struct hfs_btree).  If there are errors reading from disk or
+ *   inconsistencies are noticed in the data read from disk, then and
+ *   all resources that were allocated are released and NULL is
+ *   returned.	If the inputs are not what they claim to be or if they
+ *   are unnoticed inconsistencies in the data read from disk then the
+ *   returned hfs_btree is probably going to lead to errors when it is
+ *   used in a non-trivial way.
+ */
+struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
+				  hfs_byte_t ext[12],
+				  hfs_u32 tsize, hfs_u32 csize)
+{
+	struct hfs_btree * bt;
+	struct BTHdrRec * th;
+	struct hfs_bnode * tmp;
+	unsigned int next;
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+	unsigned char *p, *q;
+#endif
+
+	if (!mdb || !ext || !HFS_NEW(bt)) {
+		goto bail3;
+	}
+
+	bt->magic = HFS_BTREE_MAGIC;
+	bt->sys_mdb = mdb->sys_mdb;
+	bt->reserved = 0;
+	bt->lock = 0;
+	bt->wait = NULL;
+	bt->dirt = 0;
+	memset(bt->cache, 0, sizeof(bt->cache));
+	bt->entry.mdb = mdb;
+	bt->entry.cnid = cnid;
+	bt->entry.type = HFS_CDR_FIL;
+	bt->entry.u.file.magic = HFS_FILE_MAGIC;
+	bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz)
+						>> HFS_SECTOR_SIZE_BITS;
+	bt->entry.u.file.data_fork.entry = &bt->entry;
+	bt->entry.u.file.data_fork.lsize = tsize;
+	bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS;
+	bt->entry.u.file.data_fork.fork = HFS_FK_DATA;
+	hfs_extent_in(&bt->entry.u.file.data_fork, ext);
+
+	hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY);
+	if (!hfs_buffer_ok(bt->head.buf)) {
+		goto bail2;
+	}
+	th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+						sizeof(struct NodeDescriptor));
+
+	/* read in the bitmap nodes (if any) */
+	tmp = &bt->head;
+	while ((next = tmp->ndFLink)) {
+		if (!HFS_NEW(tmp->next)) {
+			goto bail2;
+		}
+		hfs_bnode_read(tmp->next, bt, next, HFS_STICKY);
+		if (!hfs_buffer_ok(tmp->next->buf)) {
+			goto bail2;
+		}
+		tmp->next->prev = tmp;
+		tmp = tmp->next;
+	}
+
+	if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) {
+		hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n");
+		goto bail2;
+	}
+
+	if (cnid == htonl(HFS_CAT_CNID)) {
+		bt->compare = (hfs_cmpfn)hfs_cat_compare;
+	} else if (cnid == htonl(HFS_EXT_CNID)) {
+		bt->compare = (hfs_cmpfn)hfs_ext_compare;
+	} else {
+		goto bail2;
+	}
+	bt->bthDepth  = hfs_get_hs(th->bthDepth);
+	bt->bthRoot   = hfs_get_hl(th->bthRoot);
+	bt->bthNRecs  = hfs_get_hl(th->bthNRecs);
+	bt->bthFNode  = hfs_get_hl(th->bthFNode);
+	bt->bthLNode  = hfs_get_hl(th->bthLNode);
+	bt->bthNNodes = hfs_get_hl(th->bthNNodes);
+	bt->bthFree   = hfs_get_hl(th->bthFree);
+	bt->bthKeyLen = hfs_get_hs(th->bthKeyLen);
+
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+	hfs_warn("bthDepth %d\n", bt->bthDepth);
+	hfs_warn("bthRoot %d\n", bt->bthRoot);
+	hfs_warn("bthNRecs %d\n", bt->bthNRecs);
+	hfs_warn("bthFNode %d\n", bt->bthFNode);
+	hfs_warn("bthLNode %d\n", bt->bthLNode);
+	hfs_warn("bthKeyLen %d\n", bt->bthKeyLen);
+	hfs_warn("bthNNodes %d\n", bt->bthNNodes);
+	hfs_warn("bthFree %d\n", bt->bthFree);
+	p = (unsigned char *)hfs_buffer_data(bt->head.buf);
+	q = p + HFS_SECTOR_SIZE;
+	while (p < q) {
+		hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x "
+		         "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+			 *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++,
+			 *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++);
+	}
+#endif
+
+	/* Read in the root if it exists.
+	   The header always exists, but the root exists only if the
+	   tree is non-empty */
+	if (bt->bthDepth && bt->bthRoot) {
+		if (!HFS_NEW(bt->root)) {
+			goto bail2;
+		}
+		hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY);
+		if (!hfs_buffer_ok(bt->root->buf)) {
+			goto bail1;
+		}
+	} else {
+		bt->root = NULL;
+	}
+
+	return bt;
+
+ bail1:
+	hfs_bnode_ditch(bt->root);
+ bail2:
+	hfs_bnode_ditch(&bt->head);
+	HFS_DELETE(bt);
+ bail3:
+	return NULL;
+}
+
+/*
+ * hfs_btree_commit()
+ *
+ * Called to write a possibly dirty btree back to disk.
+ */
+void hfs_btree_commit(struct hfs_btree *bt, hfs_byte_t ext[12], hfs_lword_t size)
+{
+	if (bt->dirt) {
+		struct BTHdrRec *th;
+		th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+						 sizeof(struct NodeDescriptor));
+
+		hfs_put_hs(bt->bthDepth,  th->bthDepth);
+		hfs_put_hl(bt->bthRoot,   th->bthRoot);
+		hfs_put_hl(bt->bthNRecs,  th->bthNRecs);
+		hfs_put_hl(bt->bthFNode,  th->bthFNode);
+		hfs_put_hl(bt->bthLNode,  th->bthLNode);
+		hfs_put_hl(bt->bthNNodes, th->bthNNodes);
+		hfs_put_hl(bt->bthFree,   th->bthFree);
+		hfs_buffer_dirty(bt->head.buf);
+
+		/*
+		 * Commit the bnodes which are not cached.
+		 * The map nodes don't need to be committed here because
+		 * they are committed every time they are changed.
+		 */
+		hfs_bnode_commit(&bt->head);
+		if (bt->root) {
+			hfs_bnode_commit(bt->root);
+		}
+
+	
+		hfs_put_hl(bt->bthNNodes << HFS_SECTOR_SIZE_BITS, size);
+		hfs_extent_out(&bt->entry.u.file.data_fork, ext);
+		/* hfs_buffer_dirty(mdb->buf); (Done by caller) */
+
+		bt->dirt = 0;
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/catalog.c linux/fs/hfs/catalog.c
--- v2.1.77/linux/fs/hfs/catalog.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/catalog.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,1604 @@
+/*
+ * linux/fs/hfs/catalog.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the catalog B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * Cache code shamelessly stolen from 
+ *     linux/fs/inode.c Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures by calling
+ * memset(&foo, 0, sizeof(foo)).  This produces the desired behavior
+ * only due to the non-ANSI assumption that the machine representation
+ */
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+#define NUM_FREE_ENTRIES 8
+
+/* Number of hash table slots */
+#define CCACHE_NR 128
+
+/* Max number of entries in memory */
+#define CCACHE_MAX 1024
+
+/* Number of entries to fit in a single page on an i386 */
+#define CCACHE_INC ((4080-sizeof(void *))/sizeof(struct hfs_cat_entry))
+
+/*================ File-local data types ================*/
+
+/* The catalog record for a file */
+typedef struct {
+	hfs_byte_t	Flags;		/* Flags such as read-only */
+	hfs_byte_t	Typ;		/* file version number = 0 */
+	hfs_finfo_t	UsrWds;		/* data used by the Finder */
+	hfs_lword_t	FlNum;		/* The CNID */
+	hfs_word_t	StBlk;		/* obsolete */
+	hfs_lword_t	LgLen;		/* The logical EOF of the data fork*/
+	hfs_lword_t	PyLen;		/* The physical EOF of the data fork */
+	hfs_word_t	RStBlk;		/* obsolete */
+	hfs_lword_t	RLgLen;		/* The logical EOF of the rsrc fork */
+	hfs_lword_t	RPyLen;		/* The physical EOF of the rsrc fork */
+	hfs_lword_t	CrDat;		/* The creation date */
+	hfs_lword_t	MdDat;		/* The modified date */
+	hfs_lword_t	BkDat;		/* The last backup date */
+	hfs_fxinfo_t	FndrInfo;	/* more data for the Finder */
+	hfs_word_t	ClpSize;	/* number of bytes to allocate
+					   when extending files */
+	hfs_byte_t	ExtRec[12];	/* first extent record
+					   for the data fork */
+	hfs_byte_t	RExtRec[12];	/* first extent record
+					   for the resource fork */
+	hfs_lword_t	Resrv;		/* reserved by Apple */
+} FIL_REC;
+
+/* the catalog record for a directory */
+typedef struct {
+	hfs_word_t	Flags;		/* flags */
+	hfs_word_t	Val;		/* Valence: number of files and
+					   dirs in the directory */
+	hfs_lword_t	DirID;		/* The CNID */
+	hfs_lword_t	CrDat;		/* The creation date */
+	hfs_lword_t	MdDat;		/* The modification date */
+	hfs_lword_t	BkDat;		/* The last backup date */
+	hfs_dinfo_t	UsrInfo;	/* data used by the Finder */
+	hfs_dxinfo_t	FndrInfo;	/* more data used by Finder */
+	hfs_byte_t	Resrv[16];	/* reserved by Apple */
+} DIR_REC;
+
+/* the catalog record for a thread */
+typedef struct {
+	hfs_byte_t		Reserv[8];	/* reserved by Apple */
+	hfs_lword_t		ParID;		/* CNID of parent directory */
+	struct hfs_name		CName;		/* The name of this entry */
+} THD_REC;
+
+/* A catalog tree record */
+struct hfs_cat_rec {
+	hfs_byte_t		cdrType;	/* The type of entry */
+	hfs_byte_t		cdrResrv2;	/* padding */
+	union {
+		FIL_REC fil;
+		DIR_REC dir;
+		THD_REC thd;
+	} u;
+};
+
+typedef struct hfs_cat_entry *hfs_cat_entry_ptr;
+
+struct allocation_unit {
+	struct allocation_unit *next;
+	struct hfs_cat_entry entries[CCACHE_INC];
+};
+
+/*================ File-local variables ================*/
+ 
+static hfs_cat_entry_ptr hash_table[CCACHE_NR] = {NULL, };
+
+static struct hfs_cat_entry *first_entry = NULL;
+static hfs_wait_queue entry_wait;
+static int nr_entries = 0, nr_free_entries = 0;
+
+static struct allocation_unit *allocation = NULL;
+
+/*================ File-local functions ================*/
+
+/*
+ * brec_to_id
+ *
+ * Get the CNID from a brec
+ */
+static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
+{
+	struct hfs_cat_rec *rec = brec->data;
+
+	return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
+				rec->u.fil.FlNum : rec->u.dir.DirID);
+}
+
+/*
+ * hashfn()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
+ */
+static inline unsigned int hashfn(const struct hfs_mdb *mdb,
+				  const struct hfs_cat_key *key)
+{
+#define LSB(X) (((unsigned char *)(&X))[3])
+	return ((unsigned int)LSB(mdb->create_date) ^ 
+		(unsigned int)key->ParID[3] ^
+		hfs_strhash(&key->CName)) % CCACHE_NR;
+#undef LSB
+}
+
+/*
+ * hash()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *)
+ * to a pointer to a slot in the hash table.
+ */
+static inline struct hfs_cat_entry **hash(struct hfs_mdb *mdb,
+				          const struct hfs_cat_key *key)
+{
+	return hash_table + hashfn(mdb, key);
+}
+
+/*
+ * insert_free()
+ *
+ * Add an entry to the front of the free list.
+ */
+static inline void insert_free(struct hfs_cat_entry *entry)
+{
+	struct hfs_cat_entry * prev, * next = first_entry;
+
+	first_entry = entry;
+	prev = next->prev;
+	entry->next = next;
+	entry->prev = prev;
+	prev->next = entry;
+	next->prev = entry;
+}
+
+/*
+ * remove_free()
+ *
+ * Remove an entry from the free list.
+ */
+static inline void remove_free(struct hfs_cat_entry *entry)
+{
+	if (first_entry == entry) {
+		first_entry = first_entry->next;
+	}
+	if (entry->next) {
+		entry->next->prev = entry->prev;
+	}
+	if (entry->prev) {
+		entry->prev->next = entry->next;
+	}
+	entry->next = entry->prev = NULL;
+}
+
+/*
+ * insert_hash()
+ *
+ * Add an entry to the front of the appropriate hash list
+ */
+static void insert_hash(struct hfs_cat_entry *entry)
+{
+	struct hfs_cat_entry **h;
+	h = hash(entry->mdb, &entry->key);
+
+	entry->hash_next = *h;
+	entry->hash_prev = NULL;
+	if (entry->hash_next) {
+		entry->hash_next->hash_prev = entry;
+	}
+	*h = entry;
+}
+
+/*
+ * remove_hash
+ *
+ * Remove an entry from its hash list (if any).
+ */
+static void remove_hash(struct hfs_cat_entry *entry)
+{
+	struct hfs_cat_entry **h;
+
+	if (entry->mdb) {
+		h = hash(entry->mdb, &entry->key);
+
+		if (*h == entry) {
+			*h = entry->hash_next;
+		}
+		if (entry->hash_next) {
+			entry->hash_next->hash_prev = entry->hash_prev;
+		}
+		if (entry->hash_prev) {
+			entry->hash_prev->hash_next = entry->hash_next;
+		}
+		entry->hash_prev = entry->hash_next = NULL;
+	}
+}
+
+/*
+ * put_last_free()
+ *
+ * Move an entry to the end of the free list.
+ */
+static inline void put_last_free(struct hfs_cat_entry *entry)
+{
+	remove_free(entry);
+	entry->prev = first_entry->prev;
+	entry->prev->next = entry;
+	entry->next = first_entry;
+	entry->next->prev = entry;
+}
+
+/*
+ * grow_entries()
+ *
+ * Try to allocate more entries, adding them to the free list.
+ */
+static int grow_entries(void)
+{
+	struct allocation_unit *tmp;
+	struct hfs_cat_entry * entry;
+	int i;
+
+	if (!HFS_NEW(tmp)) {
+		return -ENOMEM;
+	}
+
+	memset(tmp, 0, sizeof(*tmp));
+
+	tmp->next = allocation;
+	allocation = tmp;
+	entry = tmp->entries;
+
+	i = CCACHE_INC;
+
+	nr_entries += i;
+	nr_free_entries += i;
+
+	if (!first_entry) {
+		entry->next = entry->prev = first_entry = entry++;
+		--i;
+	}
+
+	for ( i = CCACHE_INC - 1; i ; --i ) {
+		insert_free(entry++);
+	}
+	return 0;
+}
+
+/*
+ * wait_on_entry()
+ *
+ * Sleep until a locked entry is unlocked.
+ */
+static inline void wait_on_entry(struct hfs_cat_entry * entry)
+{
+	while (entry->lock) {
+		hfs_sleep_on(&entry->wait);
+	}
+}
+
+/*
+ * lock_entry()
+ *
+ * Obtain an exclusive lock on an entry.
+ */
+static void lock_entry(struct hfs_cat_entry * entry)
+{
+	wait_on_entry(entry);
+	entry->lock = 1;
+}
+
+/*
+ * lock_entry()
+ *
+ * Relinquish an exclusive lock on an entry.
+ */
+static void unlock_entry(struct hfs_cat_entry * entry)
+{
+	entry->lock = 0;
+	hfs_wake_up(&entry->wait);
+}
+
+/*
+ * clear_entry()
+ *
+ * Zero all the fields of an entry and place it on the free list.
+ */
+static void clear_entry(struct hfs_cat_entry * entry)
+{
+	wait_on_entry(entry);
+	remove_hash(entry);
+	remove_free(entry);
+	if (entry->count) {
+		nr_free_entries++;
+	}
+	/* zero all but the wait queue */
+	memset(&entry->next, 0,
+	       sizeof(*entry) - offsetof(struct hfs_cat_entry, next));
+	insert_free(entry);
+}
+
+/*
+ * __read_entry()
+ *
+ * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
+ */
+static void __read_entry(struct hfs_cat_entry *entry,
+			 const struct hfs_cat_rec *cat)
+{
+	entry->type = cat->cdrType;
+
+	if (cat->cdrType == HFS_CDR_DIR) {
+		struct hfs_dir *dir = &entry->u.dir;
+
+		entry->cnid = hfs_get_nl(cat->u.dir.DirID);
+
+		dir->magic = HFS_DIR_MAGIC;
+		dir->flags = hfs_get_ns(cat->u.dir.Flags);
+		memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16);
+		memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16);
+		entry->create_date = hfs_get_nl(cat->u.dir.CrDat);
+		entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
+		entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
+		dir->dirs = dir->files = 0;
+	} else if (cat->cdrType == HFS_CDR_FIL) {
+		struct hfs_file *fil = &entry->u.file;
+
+		entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
+
+		fil->magic = HFS_FILE_MAGIC;
+
+		fil->data_fork.fork = HFS_FK_DATA;
+		fil->data_fork.entry = entry;
+		fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen);
+		fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >>
+						     HFS_SECTOR_SIZE_BITS;
+		hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec);
+
+		fil->rsrc_fork.fork = HFS_FK_RSRC;
+		fil->rsrc_fork.entry = entry;
+		fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen);
+		fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >>
+						     HFS_SECTOR_SIZE_BITS;
+		hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec);
+
+		memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
+		memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
+
+		entry->create_date = hfs_get_nl(cat->u.fil.CrDat);
+		entry->modify_date = hfs_get_nl(cat->u.fil.MdDat);
+		entry->backup_date = hfs_get_nl(cat->u.fil.BkDat);
+		fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize)
+					/ entry->mdb->alloc_blksz)
+						>> HFS_SECTOR_SIZE_BITS;
+		fil->flags = cat->u.fil.Flags;
+	} else {
+		hfs_warn("hfs_fs: entry is neither file nor directory!\n");
+	}
+}
+
+/*
+ * count_dir_entries()
+ *
+ * Count the number of files and directories in a given directory.
+ */
+static inline void count_dir_entries(struct hfs_cat_entry *entry,
+				     struct hfs_brec *brec)
+{
+	int error = 0;
+	hfs_u32 cnid;
+	hfs_u8 type;
+
+	if (!hfs_cat_open(entry, brec)) {
+		while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) {
+			if (type == HFS_CDR_FIL) {
+				++entry->u.dir.files;
+			} else if (type == HFS_CDR_DIR) {
+				++entry->u.dir.dirs;
+			}
+		} /* -ENOENT is normal termination */
+	}
+	if (error != -ENOENT) {
+		entry->cnid = 0;
+	}
+}
+
+/*
+ * read_entry()
+ *
+ * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
+ */
+static inline void read_entry(struct hfs_cat_entry *entry,
+			      struct hfs_brec *brec)
+{
+	int need_count;
+	struct hfs_cat_rec *rec = brec->data;
+
+	__read_entry(entry, rec);
+
+	need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
+
+	hfs_brec_relse(brec, NULL);
+
+	if (need_count) {
+		count_dir_entries(entry, brec);
+	}
+}
+
+/*
+ * __write_entry()
+ *
+ * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
+ */
+static void __write_entry(const struct hfs_cat_entry *entry,
+			  struct hfs_cat_rec *cat)
+{
+	if (entry->type == HFS_CDR_DIR) {
+		const struct hfs_dir *dir = &entry->u.dir;
+
+		hfs_put_ns(dir->flags,             cat->u.dir.Flags);
+		hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val);
+		hfs_put_nl(entry->cnid,            cat->u.dir.DirID);
+		hfs_put_nl(entry->create_date,     cat->u.dir.CrDat);
+		hfs_put_nl(entry->modify_date,     cat->u.dir.MdDat);
+		hfs_put_nl(entry->backup_date,     cat->u.dir.BkDat);
+		memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16);
+		memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16);
+	} else if (entry->type == HFS_CDR_FIL) {
+		const struct hfs_file *fil = &entry->u.file;
+
+		cat->u.fil.Flags = fil->flags;
+		hfs_put_nl(entry->cnid,            cat->u.fil.FlNum);
+		memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16);
+		hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen);
+		hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS,
+ 							cat->u.fil.PyLen);
+		hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
+		hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
+ 							cat->u.fil.RPyLen);
+		hfs_put_nl(entry->create_date,     cat->u.fil.CrDat);
+		hfs_put_nl(entry->modify_date,     cat->u.fil.MdDat);
+		hfs_put_nl(entry->backup_date,     cat->u.fil.BkDat);
+		memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16);
+		hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz)
+				<< HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize);
+		hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec);
+		hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec);
+	} else {
+		hfs_warn("__write_entry: invalid entry\n");
+	}
+}
+
+/*
+ * write_entry()
+ *
+ * Write a modified entry back to the catalog B-tree.
+ */
+static void write_entry(struct hfs_cat_entry * entry)
+{
+	struct hfs_brec brec;
+	int error;
+
+	wait_on_entry(entry);
+	if (!entry->dirt) {
+		return;
+	}
+	if (!entry->deleted) {
+		entry->lock = 1;	
+		error = hfs_bfind(&brec, entry->mdb->cat_tree,
+				  HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
+		if (!error) {
+			if (entry->key_dirt) {
+				/* key may have changed case due to a rename */
+				entry->key_dirt = 0;
+				if (brec.key->KeyLen != entry->key.KeyLen) {
+					hfs_warn("hfs_write_entry: key length "
+						 "changed!\n");
+					error = 1;
+				} else {
+					memcpy(brec.key, &entry->key,
+					       entry->key.KeyLen);
+				}
+			} else if (entry->cnid != brec_to_id(&brec)) {
+				hfs_warn("hfs_write_entry: CNID "
+					 "changed unexpectedly!\n");
+				error = 1;
+			}
+			if (!error) {
+				__write_entry(entry, brec.data);
+			}
+			hfs_brec_relse(&brec, NULL);
+		}
+		if (error) {
+			hfs_warn("hfs_write_entry: unable to write "
+				 "entry %08x\n", entry->cnid);
+		}
+		unlock_entry(entry);
+	}
+	entry->dirt = 0;
+}
+
+/*
+ */
+#define CAN_UNUSE(tmp) ((tmp)->count < 3 && !((tmp)->lock || (tmp)->dirt))
+static inline int try_to_free_entries(const int goal)
+{
+	hfs_prune_entry(first_entry);
+	return 1;
+}
+
+
+/*
+ * get_empty_entry()
+ *
+ * Allocate an unused entry.
+ */
+static inline struct hfs_cat_entry *get_empty_entry(void)
+{
+	struct hfs_cat_entry * entry, * best;
+	int i, try = 0;
+
+	if ((nr_entries < CCACHE_MAX) &&
+	    (nr_free_entries <= (nr_entries >> 1))) {
+		grow_entries();
+	}
+repeat:
+	entry = first_entry;
+	best = NULL;
+	for (i = nr_entries/2; i > 0; i--,entry = entry->next) {
+		if (!entry->count) {
+			if (!best) {
+				best = entry;
+			}
+			if (!entry->dirt && !entry->lock) {
+				best = entry;
+				break;
+			}
+
+		}
+	}
+	if (!best || best->dirt || best->lock) {
+		if (nr_entries < CCACHE_MAX) {
+			if (grow_entries() == 0) {
+				goto repeat;
+			}
+		}
+	}
+	entry = best;
+	if (!entry) {
+	        if (try++ < 4 && try_to_free_entries(NUM_FREE_ENTRIES)) {
+		        goto repeat;
+		}
+
+		hfs_warn("hfs_cat_get: No free entries\n");
+		hfs_sleep_on(&entry_wait);
+		goto repeat;
+	}
+	if (entry->lock) {
+		wait_on_entry(entry);
+		goto repeat;
+	}
+	if (entry->dirt) {
+		write_entry(entry);
+		goto repeat;
+	}
+	if (entry->count) {
+		goto repeat;
+	}
+
+	clear_entry(entry);
+	entry->count = 1;
+	entry->dirt = 0;
+	entry->deleted = 1;	/* so it gets cleared if discarded */
+
+	nr_free_entries--;
+	if (nr_free_entries < 0) {
+		hfs_warn ("hfs_get_empty_entry: bad free entry count.\n");
+		nr_free_entries = 0;
+	}
+	return entry;
+}
+
+/*
+ * get_entry()
+ *
+ * Try to return an entry for the indicated file or directory.
+ * If ('read' == 0) then no attempt will be made to read it from disk
+ * and a locked, but uninitialized, entry is returned.
+ */
+static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
+				       const struct hfs_cat_key *key, int read)
+{
+	struct hfs_cat_entry **h;
+	struct hfs_cat_entry * entry;
+	struct hfs_cat_entry * empty = NULL;
+	struct hfs_brec brec;
+
+	h = hash(mdb, key);
+repeat:
+	for (entry = *h; entry ; entry = entry->hash_next) {
+		if (entry->mdb == mdb && !hfs_cat_compare(&entry->key, key)) {
+			goto found_it;
+		}
+	}
+	if (!empty) {
+		empty = get_empty_entry();
+		if (empty && read &&
+		    hfs_bfind(&brec, mdb->cat_tree,
+			      HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
+			/* If we get here then:
+				1) We got an empty entry.
+				2) We want to read the record.
+				3) We failed to find the record. */
+			hfs_cat_put(empty);
+			empty = NULL;
+		}
+		if (empty) {
+			goto repeat;
+		}
+		return NULL;
+	}
+	entry = empty;
+	entry->deleted = 0;
+	entry->mdb = mdb;
+	memcpy(&entry->key, key, sizeof(*key));
+	put_last_free(entry);
+	insert_hash(entry);
+
+	entry->lock = 1;
+	if (!read) {
+		/* Return a locked but incomplete entry.  Note that the
+		   caller can tell it is incomplete since entry->cnid = 0. */
+		return entry;
+	}
+	read_entry(entry, &brec);
+	unlock_entry(entry);
+
+	goto return_it;
+
+found_it:
+	if (!entry->count) {
+		nr_free_entries--;
+	}
+	entry->count++;
+	if (empty) {
+		if (read) {
+			hfs_brec_relse(&brec, NULL);
+		}
+		hfs_cat_put(empty);
+	}
+	wait_on_entry(entry);
+	if (entry->deleted) {
+		/* The entry was deleted while we slept */
+		hfs_cat_put(entry);
+		hfs_relinquish();
+		goto repeat;
+	}
+
+return_it:
+	if (!entry->cnid) {
+		/* There was an error reading the entry */
+		hfs_cat_put(entry);
+		entry = NULL;
+	}
+	return entry;
+}
+
+/* 
+ * new_cnid()
+ *
+ * Allocate a CNID to use for a new file or directory.
+ */
+static inline hfs_u32 new_cnid(struct hfs_mdb *mdb)
+{
+	/* If the create succeeds then the mdb will get dirtied */
+	return htonl(mdb->next_id++);
+}
+
+/*
+ * update_dir()
+ *
+ * Update counts, times and dirt on a changed directory
+ */
+static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
+		       int is_dir, int count)
+{
+	/* update counts */
+	if (is_dir) {
+		mdb->dir_count += count;
+		dir->u.dir.dirs += count;
+		if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+			mdb->root_dirs += count;
+		}
+	} else {
+		mdb->file_count += count;
+		dir->u.dir.files += count;
+		if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+			mdb->root_files += count;
+		}
+	}
+	
+	/* update times and dirt */
+	dir->modify_date = hfs_time();
+	dir->dirt = 1;
+}
+
+/*
+ * Add a writer to dir, excluding readers.
+ */
+static inline void start_write(struct hfs_cat_entry *dir)
+{
+	if (dir->u.dir.readers || dir->u.dir.read_wait) {
+		hfs_sleep_on(&dir->u.dir.write_wait);
+	}
+	++dir->u.dir.writers;
+}
+
+/*
+ * Add a reader to dir, excluding writers.
+ */
+static inline void start_read(struct hfs_cat_entry *dir)
+{
+	if (dir->u.dir.writers || dir->u.dir.write_wait) {
+		hfs_sleep_on(&dir->u.dir.read_wait);
+	}
+	++dir->u.dir.readers;
+}
+
+/*
+ * Remove a writer from dir, possibly admitting readers.
+ */
+static inline void end_write(struct hfs_cat_entry *dir)
+{
+	if (!(--dir->u.dir.writers)) {
+		hfs_wake_up(&dir->u.dir.read_wait);
+	}
+}
+
+/*
+ * Remove a reader from dir, possibly admitting writers.
+ */
+static inline void end_read(struct hfs_cat_entry *dir)
+{
+	if (!(--dir->u.dir.readers)) {
+		hfs_wake_up(&dir->u.dir.write_wait);
+	}
+}
+
+/*
+ * create_entry()
+ *
+ * Add a new file or directory to the catalog B-tree and
+ * return a (struct hfs_cat_entry) for it in '*result'.
+ */
+static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+			const struct hfs_cat_rec *record, int is_dir,
+			hfs_u32 cnid, struct hfs_cat_entry **result)
+{
+	struct hfs_mdb *mdb = parent->mdb;
+	struct hfs_cat_entry *entry;
+	struct hfs_cat_key thd_key;
+	struct hfs_cat_rec thd_rec;
+	int error, has_thread;
+
+	if (result) {
+		*result = NULL;
+	}
+
+	/* keep readers from getting confused by changing dir size */
+	start_write(parent);
+
+	/* create a locked entry in the cache */
+	entry = get_entry(mdb, key, 0);
+	if (!entry) {
+		/* The entry exists but can't be read */
+		error = -EIO;
+		goto done;
+	}
+	if (entry->cnid) {
+		/* The (unlocked) entry exists in the cache */
+		error = -EEXIST;
+		goto bail2;
+	}
+
+	/* limit directory valence to signed 16-bit integer */
+        if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) {
+		error = -ENOSPC;
+		goto bail1;
+	}
+
+	has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD);
+
+	if (has_thread) {
+		/* init some fields for the thread record */
+		memset(&thd_rec, 0, sizeof(thd_rec));
+		thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH;
+		memcpy(&thd_rec.u.thd.ParID, &key->ParID,
+		       sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+		/* insert the thread record */
+		hfs_cat_build_key(cnid, NULL, &thd_key);
+		error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key),
+				    &thd_rec, 2 + sizeof(THD_REC));
+		if (error) {
+			goto bail1;
+		}
+	}
+
+	/* insert the record */
+	error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record,
+				is_dir ?  2 + sizeof(DIR_REC) :
+					  2 + sizeof(FIL_REC));
+	if (error) {
+		if (has_thread && (error != -EIO)) {
+			/* at least TRY to remove the thread record */
+			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+		}
+		goto bail1;
+	}
+
+	/* update the parent directory */
+	update_dir(mdb, parent, is_dir, 1);
+
+	/* complete the cache entry and return success */
+	__read_entry(entry, record);
+	unlock_entry(entry);
+	if (result) {
+		*result = entry;
+	} else {
+		hfs_cat_put(entry);
+	}
+	goto done;
+
+bail1:
+	entry->deleted = 1;
+	remove_hash(entry);
+	unlock_entry(entry);
+bail2:
+	hfs_cat_put(entry);
+done:
+	end_write(parent);
+	return error;
+}
+
+/*================ Global functions ================*/
+
+/* 
+ * hfs_cat_put()
+ *
+ * Release an entry we aren't using anymore.
+ *
+ * NOTE: We must be careful any time we sleep on a non-deleted
+ * entry that the entry is in a consistent state, since another
+ * process may get the entry while we sleep. That is why we
+ * 'goto repeat' after each operation that might sleep.
+ */
+void hfs_cat_put(struct hfs_cat_entry * entry)
+{
+	if (!entry) {
+		return;
+	}
+	wait_on_entry(entry);
+	if (!entry->count) {
+		hfs_warn("hfs_cat_put: trying to free free entry\n");
+		return;
+	}
+repeat:
+	if (entry->count > 1) {
+		entry->count--;
+		return;
+	}
+
+	if (!entry->cnid) {
+		clear_entry(entry);
+		return;
+	}
+
+	if (entry->type == HFS_CDR_FIL) {
+		if (entry->deleted) {
+			/* free all extents */
+			entry->u.file.data_fork.lsize = 0;
+			hfs_extent_adj(&entry->u.file.data_fork);
+			entry->u.file.rsrc_fork.lsize = 0;
+			hfs_extent_adj(&entry->u.file.rsrc_fork);
+		} else {
+			/* clear out any cached extents */
+			if (entry->u.file.data_fork.first.next) {
+				hfs_extent_free(&entry->u.file.data_fork);
+				wait_on_entry(entry);
+				goto repeat;
+			}
+			if (entry->u.file.rsrc_fork.first.next) {
+				hfs_extent_free(&entry->u.file.rsrc_fork);
+				wait_on_entry(entry);
+				goto repeat;
+			}
+		}
+	}
+
+	if (entry->deleted) {
+		clear_entry(entry);
+		return;
+	}
+
+	if (entry->dirt) {
+		write_entry(entry);
+		goto repeat;
+	}
+
+	entry->count--;
+	nr_free_entries++;
+
+	/* get_empty_entry() could be blocked waiting for more entries */
+	hfs_wake_up(&entry_wait);
+
+	return;
+}
+
+/* 
+ * hfs_cat_get()
+ *
+ * Wrapper for get_entry() which always calls with ('read'==1).
+ * Used for access to get_entry() from outside this file.
+ */
+struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb,
+				  const struct hfs_cat_key *key)
+{
+	return get_entry(mdb, key, 1);
+}
+
+/* 
+ * hfs_cat_invalidate()
+ *
+ * Called by hfs_mdb_put() to remove all the entries
+ * in the cache which are associated with a given MDB.
+ */
+void hfs_cat_invalidate(struct hfs_mdb *mdb)
+{
+	struct hfs_cat_entry * entry, * next;
+	int i;
+
+	next = first_entry;
+	for (i = nr_entries ; i > 0 ; i--) {
+		entry = next;
+		next = entry->next;	/* clear_entry() changes the queues.. */
+		if (entry->mdb != mdb) {
+			continue;
+		}
+		if (entry->count || entry->dirt || entry->lock) {
+			hfs_warn("hfs_fs: entry busy on removed device %s.\n",
+			         hfs_mdb_name(entry->mdb->sys_mdb));
+			continue;
+		}
+		clear_entry(entry);
+	}
+}
+
+/*
+ * hfs_cat_commit()
+ *
+ * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
+ */
+void hfs_cat_commit(struct hfs_mdb *mdb)
+{
+	int i;
+	struct hfs_cat_entry * entry;
+
+	entry = first_entry;
+	for(i = 0; i < nr_entries*2; i++, entry = entry->next) {
+		if (mdb && entry->mdb != mdb) {
+			continue;
+		}
+		if (!entry->cnid || entry->deleted) {
+			continue;
+		}
+		wait_on_entry(entry);
+		if (entry->dirt) {
+			write_entry(entry);
+		}
+	}
+}
+
+/*
+ * hfs_cat_free()
+ *
+ * Releases all the memory allocated in grow_entries().
+ * Must call hfs_cat_invalidate() on all MDBs before calling this.
+ */
+void hfs_cat_free(void)
+{
+	struct allocation_unit *tmp;
+
+	while (allocation) {
+		tmp = allocation->next;
+		HFS_DELETE(allocation);
+		allocation = tmp;
+	}
+}
+
+/*
+ * hfs_cat_compare()
+ *
+ * Description:
+ *   This is the comparison function used for the catalog B-tree.  In
+ *   comparing catalog B-tree entries, the parent id is the most
+ *   significant field (compared as unsigned ints).  The name field is
+ *   the least significant (compared in "Macintosh lexical order",
+ *   see hfs_strcmp() in string.c)
+ * Input Variable(s):
+ *   struct hfs_cat_key *key1: pointer to the first key to compare
+ *   struct hfs_cat_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ *   key1 and key2 point to "valid" (struct hfs_cat_key)s.
+ * Postconditions:
+ *   This function has no side-effects
+ */
+int hfs_cat_compare(const struct hfs_cat_key *key1,
+		    const struct hfs_cat_key *key2)
+{
+	unsigned int parents;
+	int retval;
+
+	parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID);
+	if (parents != 0) {
+		retval = (int)parents;
+	} else {
+		retval = hfs_strcmp(&key1->CName, &key2->CName);
+	}
+	return retval;
+}
+
+/*
+ * hfs_cat_build_key()
+ *
+ * Given the ID of the parent and the name build a search key.
+ */
+void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname,
+		       struct hfs_cat_key *key)
+{
+	hfs_put_nl(parent, key->ParID);
+
+	if (cname) {
+		key->KeyLen = 6 + cname->Len;
+		memcpy(&key->CName, cname, sizeof(*cname));
+	} else {
+		key->KeyLen = 6;
+		memset(&key->CName, 0, sizeof(*cname));
+	}
+}
+
+/*
+ * hfs_cat_open()
+ *
+ * Given a directory on an HFS filesystem get its thread and
+ * lock the directory against insertions and deletions.
+ * Return 0 on success or an error code on failure.
+ */
+int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+	struct hfs_cat_key key;
+	int error;
+
+	if (dir->type != HFS_CDR_DIR) {
+		return -EINVAL;
+	}
+	
+	/* Block writers */
+	start_read(dir);
+
+	/* Find the directory */
+	hfs_cat_build_key(dir->cnid, NULL, &key);
+	error = hfs_bfind(brec, dir->mdb->cat_tree,
+			  HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+
+	if (error) {
+		end_read(dir);
+	}
+
+	return error;
+}
+
+/*
+ * hfs_cat_next()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ * The directory is "closed" on an error.
+ */
+int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
+		 hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
+{
+	int error;
+
+	if (!dir || !brec) {
+		return -EINVAL;
+	}
+
+	/* Get the count'th next catalog tree entry */
+	error = hfs_bsucc(brec, count);
+	if (!error) {
+		struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key;
+		if (hfs_get_nl(key->ParID) != dir->cnid) {
+			hfs_brec_relse(brec, NULL);
+			error = -ENOENT;
+		}
+	}
+	if (!error) {
+		*type = ((struct hfs_cat_rec *)brec->data)->cdrType;
+		*cnid = brec_to_id(brec);
+	} else {
+		end_read(dir);
+	}
+	return error;
+}
+
+/*
+ * hfs_cat_close()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ */
+void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+	if (dir && brec) {
+		hfs_brec_relse(brec, NULL);
+		end_read(dir);
+	}
+}
+
+/*
+ * hfs_cat_parent()
+ *
+ * Given a catalog entry, return the entry for its parent.
+ * Uses catalog key for the entry to get its parent's ID
+ * and then uses the parent's thread record to locate the
+ * parent's actual catalog entry.
+ */
+struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
+{
+	struct hfs_cat_entry *retval = NULL;
+	struct hfs_mdb *mdb = entry->mdb;
+	struct hfs_brec brec;
+	struct hfs_cat_key key;
+	int error;
+
+	lock_entry(entry);
+	if (!entry->deleted) {
+		hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
+		error = hfs_bfind(&brec, mdb->cat_tree,
+				  HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+		if (!error) {
+			/* convert thread record to key */
+			struct hfs_cat_rec *rec = brec.data;
+			key.KeyLen = 6 + rec->u.thd.CName.Len;
+			memcpy(&key.ParID, &rec->u.thd.ParID,
+                       	       sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+                	hfs_brec_relse(&brec, NULL);
+
+			retval = hfs_cat_get(mdb, &key);
+		}
+	}
+	unlock_entry(entry);
+	return retval;
+}
+	
+/*
+ * hfs_cat_create()
+ *
+ * Create a new file with the indicated name in the indicated directory.
+ * The file will have the indicated flags, type and creator.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+		   hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
+		   struct hfs_cat_entry **result)
+{
+	struct hfs_cat_rec record;
+	hfs_u32 id = new_cnid(parent->mdb);
+	hfs_u32 mtime = hfs_time();
+
+	/* init some fields for the file record */
+	memset(&record, 0, sizeof(record));
+	record.cdrType = HFS_CDR_FIL;
+	record.u.fil.Flags = flags | HFS_FIL_USED;
+	hfs_put_nl(id,      record.u.fil.FlNum);
+	hfs_put_nl(mtime,   record.u.fil.CrDat);
+	hfs_put_nl(mtime,   record.u.fil.MdDat);
+	hfs_put_nl(0,       record.u.fil.BkDat);
+	hfs_put_nl(type,    record.u.fil.UsrWds.fdType);
+	hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator);
+
+	return create_entry(parent, key, &record, 0, id, result);
+}
+
+/*
+ * hfs_cat_mkdir()
+ *
+ * Create a new directory with the indicated name in the indicated directory.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+		  struct hfs_cat_entry **result)
+{
+	struct hfs_cat_rec record;
+	hfs_u32 id = new_cnid(parent->mdb);
+	hfs_u32 mtime = hfs_time();
+
+	/* init some fields for the directory record */
+	memset(&record, 0, sizeof(record));
+	record.cdrType = HFS_CDR_DIR;
+	hfs_put_nl(id,     record.u.dir.DirID);
+	hfs_put_nl(mtime, record.u.dir.CrDat);
+	hfs_put_nl(mtime, record.u.dir.MdDat);
+	hfs_put_nl(0,     record.u.dir.BkDat);
+	hfs_put_hs(0xff,  record.u.dir.UsrInfo.frView);
+
+	return create_entry(parent, key, &record, 1, id, result);
+}
+
+/*
+ * hfs_cat_delete()
+ *
+ * Delete the indicated file or directory.
+ * The associated thread is also removed unless ('with_thread'==0).
+ */
+int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
+		   int with_thread)
+{
+	struct hfs_cat_key key;
+	struct hfs_mdb *mdb = parent->mdb;
+	int is_dir, error = 0;
+
+	if (parent->mdb != entry->mdb) {
+		return -EINVAL;
+	}
+
+	if (entry->type == HFS_CDR_FIL) {
+		with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
+		is_dir = 0;
+	} else {
+		is_dir = 1;
+	}
+
+	/* keep readers from getting confused by changing dir size */
+	start_write(parent);
+
+	/* don't delete a busy directory */
+	if (entry->type == HFS_CDR_DIR) {
+		start_read(entry);
+
+		if (entry->u.dir.files || entry->u.dir.dirs) {
+			error = -ENOTEMPTY;
+		}
+	}
+
+	/* try to delete the file or directory */
+	if (!error) {
+		lock_entry(entry);
+		if (entry->deleted) {
+			/* somebody beat us to it */
+			error = -ENOENT;
+		} else {
+			error = hfs_bdelete(mdb->cat_tree,
+					    HFS_BKEY(&entry->key));
+		}
+		unlock_entry(entry);
+	}
+
+	if (!error) {
+		/* Mark the entry deleted and remove it from the cache */
+		entry->deleted = 1;
+		remove_hash(entry);
+
+		/* try to delete the thread entry if it exists */
+		if (with_thread) {
+			hfs_cat_build_key(entry->cnid, NULL, &key);
+			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
+		}
+
+		update_dir(mdb, parent, is_dir, -1);
+	}
+
+	if (entry->type == HFS_CDR_DIR) {
+		end_read(entry);
+	}
+	end_write(parent);
+	return error;
+}
+
+/*
+ * hfs_cat_move()
+ *
+ * Rename a file or directory, possibly to a new directory.
+ * If the destination exists it is removed and a
+ * (struct hfs_cat_entry) for it is returned in '*result'.
+ */
+int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
+		 struct hfs_cat_entry *entry, struct hfs_cat_key *new_key,
+		 struct hfs_cat_entry **removed)
+{
+	struct hfs_cat_entry *dest;
+	struct hfs_mdb *mdb;
+	int error = 0;
+	int is_dir, has_thread;
+
+	if (removed) {
+		*removed = NULL;
+	}
+
+	/* sanity checks */
+	if (!old_dir || !new_dir) {
+		return -EINVAL;
+	}
+	mdb = old_dir->mdb;
+	if (mdb != new_dir->mdb) {
+		return -EXDEV;
+	}
+
+	/* precompute a few things */
+	if (entry->type == HFS_CDR_DIR) {
+		is_dir = 1;
+		has_thread = 1;
+	} else if (entry->type == HFS_CDR_FIL) {
+		is_dir = 0;
+		has_thread = entry->u.file.flags & HFS_FIL_THD;
+	} else {
+		return -EINVAL;
+	}
+
+	while (mdb->rename_lock) {
+		hfs_sleep_on(&mdb->rename_wait);
+	}
+	mdb->rename_lock = 1;
+
+	/* keep readers from getting confused by changing dir size */
+	start_write(new_dir);
+	if (old_dir != new_dir) {
+		start_write(old_dir);
+	}
+
+	/* Don't move a directory inside itself */
+	if (is_dir) {
+		struct hfs_cat_key thd_key;
+		struct hfs_brec brec;
+
+		hfs_u32 id = new_dir->cnid;
+		while (id != htonl(HFS_ROOT_CNID)) {
+			if (id == entry->cnid) {
+				error = -EINVAL;
+			} else {
+				hfs_cat_build_key(id, NULL, &thd_key);
+				error = hfs_bfind(&brec, mdb->cat_tree,
+						  HFS_BKEY(&thd_key),
+						  HFS_BFIND_READ_EQ);
+			}
+			if (error) {
+				goto done;
+			} else {
+				struct hfs_cat_rec *rec = brec.data;
+				id = hfs_get_nl(rec->u.thd.ParID);
+				hfs_brec_relse(&brec, NULL);
+			}
+		}
+	}
+
+restart:
+	/* see if the destination exists, getting it if it does */
+	dest = hfs_cat_get(mdb, new_key);
+
+	if (!dest) {
+		/* destination doesn't exist, so create it */
+		struct hfs_cat_rec new_record;
+
+		/* create a locked entry in the cache */
+		dest = get_entry(mdb, new_key, 0);
+		if (!dest) {
+			error = -EIO;
+			goto done;
+		}
+		if (dest->cnid) {
+			/* The (unlocked) entry exists in the cache */
+			goto have_distinct;
+		}
+
+		/* limit directory valence to signed 16-bit integer */
+        	if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
+							HFS_MAX_VALENCE) {
+			error = -ENOSPC;
+			goto bail3;
+		}
+
+		/* build the new record */
+		new_record.cdrType = entry->type;
+		__write_entry(entry, &new_record);
+
+		/* insert the new record */
+		error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key),
+				    &new_record, is_dir ? 2 + sizeof(DIR_REC) :
+							  2 + sizeof(FIL_REC));
+		if (error == -EEXIST) {
+			dest->deleted = 1;
+			remove_hash(dest);
+			unlock_entry(dest);
+			hfs_cat_put(dest);
+			goto restart;
+		} else if (error) {
+			goto bail3;
+		}
+
+		/* update the destination directory */
+		update_dir(mdb, new_dir, is_dir, 1);
+	} else if (entry != dest) {
+have_distinct:
+		/* The destination exists and is not same as source */
+		lock_entry(dest);
+		if (dest->deleted) {
+			unlock_entry(dest);
+			hfs_cat_put(dest);
+			goto restart;
+		}
+		if (dest->type != entry->type) {
+			/* can't move a file on top
+			   of a dir nor vice versa. */
+			error = is_dir ? -ENOTDIR : -EISDIR;
+		} else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) {
+			/* directory to replace is not empty */
+			error = -ENOTEMPTY;
+		}
+
+		if (error) {
+			goto bail2;
+		}
+	} else {
+		/* The destination exists but is same as source */
+	  /*--entry->count;*/
+		hfs_cat_put(dest);
+		dest = NULL;
+	}
+
+	/* lock the entry */
+	lock_entry(entry);
+	if (entry->deleted) {
+		error = -ENOENT;
+		goto bail1;
+	}
+
+	if (dest) {
+		/* remove the old entry */
+		error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
+
+		if (error) {
+			/* We couldn't remove the entry for the
+			   original file, so nothing has changed. */
+			goto bail1;
+		}
+		update_dir(mdb, old_dir, is_dir, -1);
+	}
+
+	/* update the thread of the dir/file we're moving */
+	if (has_thread) {
+		struct hfs_cat_key thd_key;
+		struct hfs_brec brec;
+
+		hfs_cat_build_key(entry->cnid, NULL, &thd_key);
+		error = hfs_bfind(&brec, mdb->cat_tree,
+				  HFS_BKEY(&thd_key), HFS_BFIND_WRITE);
+		if (error == -ENOENT) {
+			if (is_dir) {
+				/* directory w/o a thread! */
+				error = -EIO;
+			} else {
+				/* We were lied to! */
+				entry->u.file.flags &= ~HFS_FIL_THD;
+				entry->dirt = 1;
+			}
+		}
+		if (!error) {
+			struct hfs_cat_rec *rec = brec.data;
+			memcpy(&rec->u.thd.ParID, &new_key->ParID,
+			       sizeof(hfs_u32) + sizeof(struct hfs_name));
+			hfs_brec_relse(&brec, NULL);
+		} else if (error == -ENOENT) {
+			error = 0;
+		} else if (!dest) {
+			/* Nothing was changed */
+			unlock_entry(entry);
+			goto done;
+		} else {
+			/* Something went seriously wrong.
+			   The dir/file has been deleted. */
+			/* XXX try some recovery? */
+			entry->deleted = 1;
+			remove_hash(entry);
+			goto bail1;
+		}
+	}
+
+	/* TRY to remove the thread for the pre-existing entry */
+	if (dest && dest->cnid &&
+	    (is_dir || (dest->u.file.flags & HFS_FIL_THD))) {
+		struct hfs_cat_key thd_key;
+
+		hfs_cat_build_key(dest->cnid, NULL, &thd_key);
+		(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+	}
+
+	/* update directories */
+	new_dir->modify_date = hfs_time();
+	new_dir->dirt = 1;
+
+	/* update key */
+	remove_hash(entry);
+	memcpy(&entry->key, new_key, sizeof(*new_key));
+	entry->key_dirt = 1; /* Since case might differ */
+	entry->dirt = 1;
+	insert_hash(entry);
+	unlock_entry(entry);
+
+	/* delete any pre-existing or place-holder entry */
+	if (dest) {
+		dest->deleted = 1;
+		remove_hash(dest);
+		unlock_entry(dest);
+		if (removed && dest->cnid) {
+			*removed = dest;
+		} else {
+			hfs_cat_put(dest);
+		}
+	}
+	goto done;
+
+bail1:
+	unlock_entry(entry);
+bail2:
+	if (dest) {
+		if (!dest->cnid) {
+			/* TRY to remove the new entry */
+			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
+			update_dir(mdb, new_dir, is_dir, -1);
+bail3:
+			dest->deleted = 1;
+			remove_hash(dest);
+		}
+		unlock_entry(dest);
+		hfs_cat_put(dest);
+	}
+done:
+	if (new_dir != old_dir) {
+		end_write(old_dir);
+	}
+	end_write(new_dir);
+	mdb->rename_lock = 0;
+	hfs_wake_up(&mdb->rename_wait);
+
+	return error;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/dir.c linux/fs/hfs/dir.c
--- v2.1.77/linux/fs/hfs/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/dir.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,400 @@
+/*
+ * linux/fs/hfs/dir.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains directory-related functions independent of which
+ * scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key()
+ *
+ * Build a key for a file by the given name in the given directory.
+ * If the name matches one of the reserved names returns 1 otherwise 0.
+ */
+static int build_key(struct hfs_cat_key *key, struct inode *dir,
+		     const char *name, int len)
+{
+	struct hfs_name cname;
+	const struct hfs_name *reserved;
+
+	/* mangle the name */
+	hfs_nameout(dir, &cname, name, len);
+
+	/* check against reserved names */
+	reserved = HFS_SB(dir->i_sb)->s_reserved1;
+	while (reserved->Len) {
+		if (hfs_streq(reserved, &cname)) {
+			return 1;
+		}
+		++reserved;
+	}
+
+	/* check against the names reserved only in the root directory */
+	if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
+		reserved = HFS_SB(dir->i_sb)->s_reserved2;
+		while (reserved->Len) {
+			if (hfs_streq(reserved, &cname)) {
+				return 1;
+			}
+			++reserved;
+		}
+	}
+
+	/* build the key */
+	hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
+
+	return 0;
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
+ */
+static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
+{
+	int i;
+
+	for (i = 0; i < 4; ++i) {
+		struct dentry *de = dir->sys_entry[i];
+		if (de) {
+		        struct inode *tmp = de->d_inode;
+			if (S_ISDIR(tmp->i_mode)) {
+				if (is_dir &&
+				    (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+					/* In "normal" directory only */
+					++(tmp->i_nlink);
+				}
+				tmp->i_size += HFS_I(tmp)->dir_size;
+				tmp->i_version = ++event;
+			}
+			tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+			mark_inode_dirty(tmp);
+		}
+	}
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
+ */
+static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
+{
+	int i;
+
+	for (i = 0; i < 4; ++i) {
+		struct dentry *de = dir->sys_entry[i];
+		if (de) {
+		        struct inode *tmp = de->d_inode;
+			if (S_ISDIR(tmp->i_mode)) {
+				if (is_dir &&
+				    (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+					/* In "normal" directory only */
+					--(tmp->i_nlink);
+				}
+				tmp->i_size -= HFS_I(tmp)->dir_size;
+				tmp->i_version = ++event;
+			}
+			tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+			mark_inode_dirty(tmp);
+		}
+	}
+}
+
+/*
+ * mark_inodes_deleted()
+ *
+ * Update inodes associated with a deleted entry to reflect its deletion.
+ * Well, we really just drop the dentry.
+ */
+static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, 
+				       struct dentry *dentry)
+{
+	struct dentry *de;
+	int i;
+
+	for (i = 0; i < 4; ++i) {
+		if ((de = entry->sys_entry[i]) && (dentry != de)) {
+		    entry->sys_entry[i] = NULL;
+		    dget(de);
+		    d_delete(de);
+		    dput(de);
+		}
+	}
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_dir_read()
+ *
+ * This is the read() entry in the file_operations structure for HFS
+ * directories.	 It simply returns an error code, since reading is not
+ * supported.
+ */
+hfs_rwret_t hfs_dir_read(struct file * filp, char *buf,
+			 hfs_rwarg_t count, loff_t *ppos)
+{
+	return -EISDIR;
+}
+
+/*
+ * hfs_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
+{
+	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+	struct hfs_cat_entry *new;
+	struct hfs_cat_key key;
+	struct inode *inode;
+	int error;
+
+	/* build the key, checking against reserved names */
+	if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) {
+		error = -EEXIST;
+	} else {
+		/* try to create the file */
+		error = hfs_cat_create(entry, &key, 
+				       (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
+				       HFS_SB(dir->i_sb)->s_type,
+				       HFS_SB(dir->i_sb)->s_creator, &new);
+	}
+
+	if (!error) {
+		update_dirs_plus(entry, 0);
+
+		/* create an inode for the new file */
+		inode = hfs_iget(new, HFS_I(dir)->file_type, dentry);
+		if (!inode) {
+		        /* XXX correct error? */
+		        error = -EIO;
+   		} else {
+		  if (HFS_I(dir)->d_drop_op)
+		    HFS_I(dir)->d_drop_op(HFS_I(dir)->file_type, dentry);
+		  d_instantiate(dentry, inode);
+		}
+	}
+
+	return error;
+}
+
+/*
+ * hfs_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
+{
+	struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+	struct hfs_cat_entry *new;
+	struct hfs_cat_key key;
+	struct inode *inode;
+	int error;
+
+	/* build the key, checking against reserved names */
+	if (build_key(&key, parent, dentry->d_name.name, 
+		      dentry->d_name.len)) {
+		error = -EEXIST;
+	} else {
+		/* try to create the directory */
+		error = hfs_cat_mkdir(entry, &key, &new);
+	}
+
+	if (!error) {
+	        update_dirs_plus(entry, 1);
+	        inode = hfs_iget(new, HFS_I(parent)->file_type, dentry);
+		if (!inode) {
+		        error = -EIO;
+		} else
+		  d_instantiate(dentry, inode);
+	}
+
+	return error;
+}
+
+/*
+ * hfs_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ *
+ * HFS only supports regular files and directories and Linux disallows
+ * using mknod() to create directories.  Thus we just check the arguments
+ * and call hfs_create().
+ */
+int hfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+	int error;
+
+	if (!dir) {
+		error = -ENOENT;
+	} else if (S_ISREG(mode)) {
+		error = hfs_create(dir, dentry, mode);
+	} else {
+		error = -EPERM;
+	}
+	return error;
+}
+
+/*
+ * hfs_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+int hfs_unlink(struct inode * dir, struct dentry *dentry)
+{
+	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+	struct hfs_cat_entry *victim = NULL;
+	struct hfs_cat_key key;
+	int error;
+
+	if (build_key(&key, dir, dentry->d_name.name, 
+		      dentry->d_name.len)) {
+		error = -EPERM;
+	} else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+		error = -ENOENT;
+	} else if (victim->type != HFS_CDR_FIL) {
+		error = -EPERM;
+	} else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+		mark_inodes_deleted(victim, dentry);
+		d_delete(dentry);
+		update_dirs_minus(entry, 0);
+	}
+
+	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
+	return error;
+}
+
+/*
+ * hfs_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+int hfs_rmdir(struct inode * parent, struct dentry *dentry)
+{
+	struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+	struct hfs_cat_entry *victim = NULL;
+	struct hfs_cat_key key;
+	int error;
+
+	if (build_key(&key, parent, dentry->d_name.name,
+		      dentry->d_name.len)) {
+		error = -EPERM;
+	} else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+		error = -ENOENT;
+	} else if (victim->type != HFS_CDR_DIR) {
+		error = -ENOTDIR;
+	} else if (/* we only have to worry about 2 and 3 for mount points */
+		   (victim->sys_entry[2] &&
+		    (victim->sys_entry[2] !=
+		     victim->sys_entry[2]->d_mounts)) ||
+		   (victim->sys_entry[3] &&
+		    (victim->sys_entry[3] != 
+		     victim->sys_entry[3]->d_mounts))
+		   ) {
+		error = -EBUSY;
+	} else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+		mark_inodes_deleted(victim, dentry);
+		d_delete(dentry);
+		update_dirs_minus(entry, 1);
+	}
+	 
+	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
+	return error;
+}
+
+/*
+ * hfs_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ * XXX: how do you handle must_be dir?
+ */
+int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	       struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
+	struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
+	struct hfs_cat_entry *victim = NULL;
+	struct hfs_cat_entry *deleted;
+	struct hfs_cat_key key;
+	int error;
+
+	if (build_key(&key, old_dir, old_dentry->d_name.name,
+		      old_dentry->d_name.len) ||
+	    (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) {
+		error = -EPERM;
+	} else if (!(victim = hfs_cat_get(old_parent->mdb, &key))) {
+		error = -ENOENT;
+	} else if (build_key(&key, new_dir, new_dentry->d_name.name,
+			     new_dentry->d_name.len)) {
+		error = -EPERM;
+	} else if (!(error = hfs_cat_move(old_parent, new_parent,
+					  victim, &key, &deleted))) {
+		int is_dir = (victim->type == HFS_CDR_DIR);
+
+		/* drop the old dentries */
+		mark_inodes_deleted(victim, old_dentry);
+		update_dirs_minus(old_parent, is_dir);
+		if (deleted) {
+		  mark_inodes_deleted(deleted, new_dentry);
+		  hfs_cat_put(deleted);
+		} else {
+		  /* no existing inodes. just drop negative dentries */
+		  if (HFS_I(new_dir)->d_drop_op) 
+		    HFS_I(new_dir)->d_drop_op(HFS_I(new_dir)->file_type,
+						   new_dentry);
+		  update_dirs_plus(new_parent, is_dir);
+		}
+
+		/* update dcache */
+		d_move(old_dentry, new_dentry);
+	}
+	 
+	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
+	return error;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/dir_cap.c linux/fs/hfs/dir_cap.c
--- v2.1.77/linux/fs/hfs/dir_cap.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/dir_cap.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,402 @@
+/*
+ * linux/fs/hfs/dir_cap.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories under the CAP scheme.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int cap_lookup(struct inode *, struct dentry *);
+static int cap_readdir(struct file *, void *, filldir_t);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN			1
+#define DOT_DOT_LEN		2
+#define DOT_RESOURCE_LEN	9
+#define DOT_FINDERINFO_LEN	11
+#define DOT_ROOTINFO_LEN	9
+
+const struct hfs_name hfs_cap_reserved1[] = {
+	{DOT_LEN,		"."},
+	{DOT_DOT_LEN,		".."},
+	{DOT_RESOURCE_LEN,	".resource"},
+	{DOT_FINDERINFO_LEN,	".finderinfo"},
+	{0,			""},
+};
+
+const struct hfs_name hfs_cap_reserved2[] = {
+	{DOT_ROOTINFO_LEN,	".rootinfo"},
+	{0,			""},
+};
+
+#define DOT		(&hfs_cap_reserved1[0])
+#define DOT_DOT		(&hfs_cap_reserved1[1])
+#define DOT_RESOURCE	(&hfs_cap_reserved1[2])
+#define DOT_FINDERINFO	(&hfs_cap_reserved1[3])
+#define DOT_ROOTINFO	(&hfs_cap_reserved2[0])
+
+static struct file_operations hfs_cap_dir_operations = {
+	NULL,			/* lseek - default */
+	hfs_dir_read,		/* read - invalid */
+	NULL,			/* write - bad */
+	cap_readdir,		/* readdir */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap - none */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync,		/* fsync - default */
+	NULL,			/* fasync - default */
+	NULL,			/* check_media_change - none */
+	NULL			/* revalidate - none */
+};
+
+struct inode_operations hfs_cap_ndir_inode_operations = {
+	&hfs_cap_dir_operations,/* default directory file-ops */
+	hfs_create,		/* create */
+	cap_lookup,		/* lookup */
+	NULL,			/* link */
+	hfs_unlink,		/* unlink */
+	NULL,			/* symlink */
+	hfs_mkdir,		/* mkdir */
+	hfs_rmdir,		/* rmdir */
+	hfs_mknod,		/* mknod */
+	hfs_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+struct inode_operations hfs_cap_fdir_inode_operations = {
+	&hfs_cap_dir_operations,/* default directory file-ops */
+	NULL,			/* create */
+	cap_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 */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+struct inode_operations hfs_cap_rdir_inode_operations = {
+	&hfs_cap_dir_operations,/* default directory file-ops */
+	hfs_create,		/* create */
+	cap_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 */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the CAP scheme.  The purpose is to generate the
+ * inode corresponding to an entry in a directory, given the inode for
+ * the directory and the name (and its length) of the entry.
+ */
+static int cap_lookup(struct inode * dir, struct dentry *dentry)
+{
+	ino_t dtype;
+	struct hfs_name cname;
+	struct hfs_cat_entry *entry;
+	struct hfs_cat_key key;
+	struct inode *inode = NULL;
+	
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		goto done;
+	}
+
+	entry = HFS_I(dir)->entry;
+	dtype = HFS_ITYPE(dir->i_ino);
+
+	/* Perform name-mangling */
+	hfs_nameout(dir, &cname, dentry->d_name.name, 
+		    dentry->d_name.len);
+
+	/* Check for "." */
+	if (hfs_streq(&cname, DOT)) {
+		/* this little trick skips the iget and iput */
+		d_add(dentry, dir);
+		return 0;
+	}
+
+	/* Check for "..". */
+	if (hfs_streq(&cname, DOT_DOT)) {
+		struct hfs_cat_entry *parent;
+
+		if (dtype != HFS_CAP_NDIR) {
+			/* Case for ".." in ".finderinfo" or ".resource" */
+			parent = entry;
+			++entry->count; /* __hfs_iget() eats one */
+		} else {
+			/* Case for ".." in a normal directory */
+			parent = hfs_cat_parent(entry);
+		}
+		inode = hfs_iget(parent, HFS_CAP_NDIR, dentry);
+		goto done;
+	}
+
+	/* Check for special directories if in a normal directory.
+	   Note that cap_dupdir() does an iput(dir). */
+	if (dtype==HFS_CAP_NDIR) {
+		/* Check for ".resource", ".finderinfo" and ".rootinfo" */
+		if (hfs_streq(&cname, DOT_RESOURCE)) {
+			++entry->count; /* __hfs_iget() eats one */
+			inode = hfs_iget(entry, HFS_CAP_RDIR, dentry);
+			goto done;
+		} else if (hfs_streq(&cname, DOT_FINDERINFO)) {
+			++entry->count; /* __hfs_iget() eats one */
+			inode = hfs_iget(entry, HFS_CAP_FDIR, dentry);
+			goto done;
+		} else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+			   hfs_streq(&cname, DOT_ROOTINFO)) {
+			++entry->count; /* __hfs_iget() eats one */
+			inode = hfs_iget(entry, HFS_CAP_FNDR, dentry);
+			goto done;
+		}
+	}
+
+	/* Do an hfs_iget() on the mangled name. */
+	hfs_cat_build_key(entry->cnid, &cname, &key);
+	inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+			 HFS_I(dir)->file_type, dentry);
+
+	/* Don't return a resource fork for a directory */
+	if (inode && (dtype == HFS_CAP_RDIR) &&
+	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+		inode = NULL;
+	}
+
+done:
+	d_add(dentry, inode);
+	return 0;
+}
+
+/*
+ * cap_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the CAP scheme.  The purpose is to enumerate the
+ * entries in a directory, given the inode of the directory and a
+ * (struct file *), the 'f_pos' field of which indicates the location
+ * in the directory.  The (struct file *) is updated so that the next
+ * call with the same 'dir' and 'filp' arguments will produce the next
+ * directory entry.  The entries are returned in 'dirent', which is
+ * "filled-in" by calling filldir().  This allows the same readdir()
+ * function be used for different dirent formats.  We try to read in
+ * as many entries as we can before filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory.	 However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead.	Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int cap_readdir(struct file * filp,
+		       void * dirent, filldir_t filldir)
+{
+	ino_t type;
+	int skip_dirs;
+	struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+	struct inode *dir = filp->f_dentry->d_inode;
+
+	if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+		return -EBADF;
+	}
+
+        entry = HFS_I(dir)->entry;
+	type = HFS_ITYPE(dir->i_ino);
+	skip_dirs = (type == HFS_CAP_RDIR);
+
+	if (filp->f_pos == 0) {
+		/* Entry 0 is for "." */
+		if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+			return 0;
+		}
+		filp->f_pos = 1;
+	}
+
+	if (filp->f_pos == 1) {
+		/* Entry 1 is for ".." */
+		hfs_u32 cnid;
+
+		if (type == HFS_CAP_NDIR) {
+			cnid = hfs_get_nl(entry->key.ParID);
+		} else {
+			cnid = entry->cnid;
+		}
+
+		if (filldir(dirent, DOT_DOT->Name,
+			    DOT_DOT_LEN, 1, ntohl(cnid))) {
+			return 0;
+		}
+		filp->f_pos = 2;
+	}
+
+	if (filp->f_pos < (dir->i_size - 3)) {
+		hfs_u32 cnid;
+		hfs_u8 type;
+
+	    	if (hfs_cat_open(entry, &brec) ||
+	    	    hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+			return 0;
+		}
+		while (filp->f_pos < (dir->i_size - 3)) {
+			if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+				return 0;
+			}
+			if (!skip_dirs || (type != HFS_CDR_DIR)) {
+				ino_t ino;
+				unsigned int len;
+				unsigned char tmp_name[HFS_NAMEMAX];
+
+				ino = ntohl(cnid) | HFS_I(dir)->file_type;
+				len = hfs_namein(dir, tmp_name,
+				    &((struct hfs_cat_key *)brec.key)->CName);
+				if (filldir(dirent, tmp_name, len,
+					    filp->f_pos, ino)) {
+					hfs_cat_close(entry, &brec);
+					return 0;
+				}
+			}
+			++filp->f_pos;
+		}
+		hfs_cat_close(entry, &brec);
+	}
+
+	if (filp->f_pos == (dir->i_size - 3)) {
+		if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+		    (type == HFS_CAP_NDIR)) {
+			/* In root dir last-2 entry is for ".rootinfo" */
+			if (filldir(dirent, DOT_ROOTINFO->Name,
+				    DOT_ROOTINFO_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_CAP_FNDR)) {
+				return 0;
+			}
+		}
+		++filp->f_pos;
+	}
+
+	if (filp->f_pos == (dir->i_size - 2)) {
+		if (type == HFS_CAP_NDIR) {
+			/* In normal dirs last-1 entry is for ".finderinfo" */
+			if (filldir(dirent, DOT_FINDERINFO->Name,
+				    DOT_FINDERINFO_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_CAP_FDIR)) {
+				return 0;
+			}
+		}
+		++filp->f_pos;
+	}
+
+	if (filp->f_pos == (dir->i_size - 1)) {
+		if (type == HFS_CAP_NDIR) {
+			/* In normal dirs last entry is for ".resource" */
+			if (filldir(dirent, DOT_RESOURCE->Name,
+				    DOT_RESOURCE_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_CAP_RDIR)) {
+				return 0;
+			}
+		}
+		++filp->f_pos;
+	}
+
+	return 0;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry 
+ * upon successful completion. */
+void hfs_cap_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+  if (type == HFS_CAP_DATA) { /* given name */
+    hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry);
+    hfs_drop_special(DOT_RESOURCE, dentry->d_parent, dentry);
+  } else {
+    struct dentry *de;
+
+    /* look for name */
+    if ((de = hfs_lookup_dentry(dentry->d_name.name,
+			       dentry->d_name.len,
+			       dentry->d_parent->d_parent))) {
+      if (!de->d_inode)
+	d_drop(de);
+      dput(de);
+    }
+    
+    switch (type) {
+    case HFS_CAP_RSRC: /* given .resource/name */
+       /* look for .finderinfo/name */
+      hfs_drop_special(DOT_FINDERINFO, dentry->d_parent->d_parent,
+		       dentry);
+      break;
+    case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this 
+			* happens. */
+      /* look for .resource/name */
+      hfs_drop_special(DOT_RESOURCE, dentry->d_parent->d_parent,
+		       dentry);
+      break;
+    }
+  }
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/dir_dbl.c linux/fs/hfs/dir_dbl.c
--- v2.1.77/linux/fs/hfs/dir_dbl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/dir_dbl.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,463 @@
+/*
+ * linux/fs/hfs/dir_dbl.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int dbl_lookup(struct inode *, struct dentry *);
+static int dbl_readdir(struct file *, void *, filldir_t);
+static int dbl_create(struct inode *, struct dentry *, int);
+static int dbl_mkdir(struct inode *, struct dentry *, int);
+static int dbl_mknod(struct inode *, struct dentry *, int, int);
+static int dbl_unlink(struct inode *, struct dentry *);
+static int dbl_rmdir(struct inode *, struct dentry *);
+static int dbl_rename(struct inode *, struct dentry *,
+		      struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN			1
+#define DOT_DOT_LEN		2
+#define ROOTINFO_LEN		8
+#define PCNT_ROOTINFO_LEN	9
+
+const struct hfs_name hfs_dbl_reserved1[] = {
+	{DOT_LEN,		"."},
+	{DOT_DOT_LEN,		".."},
+	{0,			""},
+};
+
+const struct hfs_name hfs_dbl_reserved2[] = {
+	{ROOTINFO_LEN,		"RootInfo"},
+	{PCNT_ROOTINFO_LEN,	"%RootInfo"},
+	{0,			""},
+};
+
+#define DOT		(&hfs_dbl_reserved1[0])
+#define DOT_DOT		(&hfs_dbl_reserved1[1])
+#define ROOTINFO	(&hfs_dbl_reserved2[0])
+#define PCNT_ROOTINFO	(&hfs_dbl_reserved2[1])
+
+static struct file_operations hfs_dbl_dir_operations = {
+	NULL,			/* lseek - default */
+	hfs_dir_read,		/* read - invalid */
+	NULL,			/* write - bad */
+	dbl_readdir,		/* readdir */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap - none */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync,		/* fsync - default */
+        NULL,			/* fasync - default */
+        NULL,			/* check_media_change - none */
+        NULL			/* revalidate - none */
+};
+
+struct inode_operations hfs_dbl_dir_inode_operations = {
+	&hfs_dbl_dir_operations,/* default directory file-ops */
+	dbl_create,		/* create */
+	dbl_lookup,		/* lookup */
+	NULL,			/* link */
+	dbl_unlink,		/* unlink */
+	NULL,			/* symlink */
+	dbl_mkdir,		/* mkdir */
+	dbl_rmdir,		/* rmdir */
+	dbl_mknod,		/* mknod */
+	dbl_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+
+/*================ File-local functions ================*/
+
+/*
+ * is_hdr()
+ */
+static int is_hdr(struct inode *dir, const char *name, int len)
+{
+	int retval = 0;
+
+	if (name[0] == '%') {
+		struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+		struct hfs_cat_entry *victim;
+		struct hfs_name cname;
+		struct hfs_cat_key key;
+
+		hfs_nameout(dir, &cname, name+1, len-1);
+		hfs_cat_build_key(entry->cnid, &cname, &key);
+		if ((victim = hfs_cat_get(entry->mdb, &key))) {
+			hfs_cat_put(victim);
+			retval = 1;
+		}
+	}
+	return retval;
+}
+
+/*
+ * dbl_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the AppleDouble scheme.  The purpose is to
+ * generate the inode corresponding to an entry in a directory, given
+ * the inode for the directory and the name (and its length) of the
+ * entry.
+ */
+static int dbl_lookup(struct inode * dir, struct dentry *dentry)
+{
+	struct hfs_name cname;
+	struct hfs_cat_entry *entry;
+	struct hfs_cat_key key;
+	struct inode *inode = NULL;
+	
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		goto done;
+	}
+
+	entry = HFS_I(dir)->entry;
+	
+	/* Perform name-mangling */
+	hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+ 
+	/* Check for "." */
+	if (hfs_streq(&cname, DOT)) {
+		/* this little trick skips the iget and iput */
+		d_add(dentry, dir);
+		return 0;
+	}
+
+	/* Check for "..". */
+	if (hfs_streq(&cname, DOT_DOT)) {
+		inode = hfs_iget(hfs_cat_parent(entry), HFS_DBL_DIR, dentry);
+		goto done;
+	}
+
+	/* Check for "%RootInfo" if in the root directory. */
+	if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+	    hfs_streq(&cname, PCNT_ROOTINFO)) {
+		++entry->count; /* __hfs_iget() eats one */
+		inode = hfs_iget(entry, HFS_DBL_HDR, dentry);
+		goto done;
+	}
+
+	/* Do an hfs_iget() on the mangled name. */
+	hfs_cat_build_key(entry->cnid, &cname, &key);
+	inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_NORM, dentry);
+
+	/* Try as a header if not found and first character is '%' */
+	if (!inode && (dentry->d_name.name[0] == '%')) {
+		hfs_nameout(dir, &cname, dentry->d_name.name+1,
+			    dentry->d_name.len-1);
+		hfs_cat_build_key(entry->cnid, &cname, &key);
+		inode = hfs_iget(hfs_cat_get(entry->mdb, &key), 
+				 HFS_DBL_HDR, dentry);
+	}
+	
+done:
+	d_add(dentry, inode);
+	return 0;
+}
+
+/*
+ * dbl_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the AppleDouble scheme.  The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a (struct file *), the 'f_pos' field of which
+ * indicates the location in the directory.  The (struct file *) is
+ * updated so that the next call with the same 'dir' and 'filp'
+ * arguments will produce the next directory entry.  The entries are
+ * returned in 'dirent', which is "filled-in" by calling filldir().
+ * This allows the same readdir() function be used for different
+ * formats.  We try to read in as many entries as we can before
+ * filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory.	 However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead.	Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int dbl_readdir(struct file * filp,
+		       void * dirent, filldir_t filldir)
+{
+	struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+	struct inode *dir = filp->f_dentry->d_inode;
+
+	if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+		return -EBADF;
+	}
+
+        entry = HFS_I(dir)->entry;
+
+	if (filp->f_pos == 0) {
+		/* Entry 0 is for "." */
+		if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+			return 0;
+		}
+		filp->f_pos = 1;
+	}
+
+	if (filp->f_pos == 1) {
+		/* Entry 1 is for ".." */
+		if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1,
+			    hfs_get_hl(entry->key.ParID))) {
+			return 0;
+		}
+		filp->f_pos = 2;
+	}
+
+	if (filp->f_pos < (dir->i_size - 1)) {
+                hfs_u32 cnid;
+                hfs_u8 type;
+
+		if (hfs_cat_open(entry, &brec) ||
+		    hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1,
+				 &cnid, &type)) {
+			return 0;
+		}
+
+		while (filp->f_pos < (dir->i_size - 1)) {
+			unsigned char tmp_name[HFS_NAMEMAX + 1];
+			ino_t ino;
+			int is_hdr = (filp->f_pos & 1);
+			unsigned int len;
+
+			if (is_hdr) {
+				ino = ntohl(cnid) | HFS_DBL_HDR;
+				tmp_name[0] = '%';
+				len = 1 + hfs_namein(dir, tmp_name + 1,
+				    &((struct hfs_cat_key *)brec.key)->CName);
+			} else {
+				if (hfs_cat_next(entry, &brec, 1,
+							&cnid, &type)) {
+					return 0;
+				}
+				ino = ntohl(cnid);
+				len = hfs_namein(dir, tmp_name,
+				    &((struct hfs_cat_key *)brec.key)->CName);
+			}
+
+			if (filldir(dirent, tmp_name, len, filp->f_pos, ino)) {
+				hfs_cat_close(entry, &brec);
+				return 0;
+			}
+			++filp->f_pos;
+		}
+		hfs_cat_close(entry, &brec);
+	}
+
+	if (filp->f_pos == (dir->i_size - 1)) {
+		if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+			/* In root dir last entry is for "%RootInfo" */
+			if (filldir(dirent, PCNT_ROOTINFO->Name,
+				    PCNT_ROOTINFO_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_DBL_HDR)) {
+				return 0;
+			}
+		}
+		++filp->f_pos;
+	}
+
+	return 0;
+}
+
+/*
+ * dbl_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+static int dbl_create(struct inode * dir, struct dentry *dentry,
+		      int mode)
+{
+	int error;
+
+	if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
+		error = -EEXIST;
+	} else {
+		error = hfs_create(dir, dentry, mode);
+	}
+	return error;
+}
+
+/*
+ * dbl_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+static int dbl_mkdir(struct inode * parent, struct dentry *dentry,
+		     int mode)
+{
+	int error;
+
+	if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) {
+		error = -EEXIST;
+	} else {
+		error = hfs_mkdir(parent, dentry, mode);
+	}
+	return error;
+}
+
+/*
+ * dbl_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ */
+static int dbl_mknod(struct inode *dir, struct dentry *dentry,
+		     int mode, int rdev)
+{
+	int error;
+
+	if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
+		error = -EEXIST;
+	} else {
+		error = hfs_mknod(dir, dentry, mode, rdev);
+	}
+	return error;
+}
+
+/*
+ * dbl_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+static int dbl_unlink(struct inode * dir, struct dentry *dentry)
+{
+	int error;
+
+	error = hfs_unlink(dir, dentry);
+	if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name,
+					 dentry->d_name.len)) {
+		error = -EPERM;
+	}
+	return error;
+}
+
+/*
+ * dbl_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+static int dbl_rmdir(struct inode * parent, struct dentry *dentry)
+{
+	int error;
+
+	error = hfs_rmdir(parent, dentry);
+	if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name,
+					 dentry->d_name.len)) {
+		error = -ENOTDIR;
+	}
+	return error;
+}
+
+/*
+ * dbl_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ * 
+ * XXX: how do we handle must_be_dir?
+ */
+static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry,
+		      struct inode *new_dir, struct dentry *new_dentry)
+{
+	int error;
+
+	if (is_hdr(new_dir, new_dentry->d_name.name, 
+		   new_dentry->d_name.len)) {
+		error = -EPERM;
+	} else {
+		error = hfs_rename(old_dir, old_dentry,
+				   new_dir, new_dentry);
+		if ((error == -ENOENT) /*&& !must_be_dir*/ &&
+		    is_hdr(old_dir, old_dentry->d_name.name, 
+			   old_dentry->d_name.len)) {
+			error = -EPERM;
+		}
+	}
+	return error;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry 
+ * upon successful completion. this allocates an array for %name
+ * on the first attempt to access it. */
+void hfs_dbl_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+  unsigned char tmp_name[HFS_NAMEMAX + 1];
+  struct dentry *de = NULL;
+
+  switch (type) {
+  case HFS_DBL_HDR:
+   /* given %name, look for name. i don't think this happens. */
+   de = hfs_lookup_dentry(dentry->d_name.name + 1, dentry->d_name.len - 1,
+			   dentry->d_parent);
+    break;
+  case HFS_DBL_DATA:
+    /* given name, look for %name */
+    tmp_name[0] = '%';
+    strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1);
+    de = hfs_lookup_dentry(tmp_name, dentry->d_name.len + 1,
+			     dentry->d_parent);
+  }
+
+  if (de) {
+    if (!de->d_inode)
+      d_drop(de);
+    dput(de);
+  }
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/dir_nat.c linux/fs/hfs/dir_nat.c
--- v2.1.77/linux/fs/hfs/dir_nat.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/dir_nat.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,486 @@
+/*
+ * linux/fs/hfs/dir_nat.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int nat_lookup(struct inode *, struct dentry *);
+static int nat_readdir(struct file *, void *, filldir_t);
+static int nat_rmdir(struct inode *, struct dentry *);
+static int nat_hdr_unlink(struct inode *, struct dentry *);
+static int nat_hdr_rename(struct inode *, struct dentry *,
+			  struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN			1
+#define DOT_DOT_LEN		2
+#define DOT_APPLEDOUBLE_LEN	12
+#define DOT_PARENT_LEN		7
+
+const struct hfs_name hfs_nat_reserved1[] = {
+	{DOT_LEN,		"."},
+	{DOT_DOT_LEN,		".."},
+	{DOT_APPLEDOUBLE_LEN,	".AppleDouble"},
+	{DOT_PARENT_LEN,	".Parent"},
+	{0,			""},
+};
+
+const struct hfs_name hfs_nat_reserved2[] = {
+	{0,			""},
+};
+
+#define DOT		(&hfs_nat_reserved1[0])
+#define DOT_DOT		(&hfs_nat_reserved1[1])
+#define DOT_APPLEDOUBLE	(&hfs_nat_reserved1[2])
+#define DOT_PARENT	(&hfs_nat_reserved1[3])
+
+static struct file_operations hfs_nat_dir_operations = {
+	NULL,			/* lseek - default */
+	hfs_dir_read,		/* read - invalid */
+	NULL,			/* write - bad */
+	nat_readdir,		/* readdir */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap - none */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync,		/* fsync - default */
+        NULL,			/* fasync - default */
+        NULL,			/* check_media_change - none */
+        NULL,			/* revalidate - none */
+	NULL                    /* lock - none */
+};
+
+struct inode_operations hfs_nat_ndir_inode_operations = {
+	&hfs_nat_dir_operations,/* default directory file-ops */
+	hfs_create,		/* create */
+	nat_lookup,		/* lookup */
+	NULL,			/* link */
+	hfs_unlink,		/* unlink */
+	NULL,			/* symlink */
+	hfs_mkdir,		/* mkdir */
+	nat_rmdir,		/* rmdir */
+	hfs_mknod,		/* mknod */
+	hfs_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,                   /* updatepage */
+	NULL                    /* revalidate */
+};
+
+struct inode_operations hfs_nat_hdir_inode_operations = {
+	&hfs_nat_dir_operations,/* default directory file-ops */
+	hfs_create,		/* create */
+	nat_lookup,		/* lookup */
+	NULL,			/* link */
+	nat_hdr_unlink,		/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	nat_hdr_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,                   /* updatepage */
+	NULL                    /* revalidate */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * nat_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the Netatalk scheme.  The purpose is to generate
+ * the inode corresponding to an entry in a directory, given the inode
+ * for the directory and the name (and its length) of the entry.
+ */
+static int nat_lookup(struct inode * dir, struct dentry *dentry)
+{
+	ino_t dtype;
+	struct hfs_name cname;
+	struct hfs_cat_entry *entry;
+	struct hfs_cat_key key;
+	struct inode *inode = NULL;
+
+	if (!dir || !S_ISDIR(dir->i_mode)) {
+		goto done;
+	}
+
+	entry = HFS_I(dir)->entry;
+	dtype = HFS_ITYPE(dir->i_ino);
+
+	/* Perform name-mangling */
+	hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+
+	/* Check for "." */
+	if (hfs_streq(&cname, DOT)) {
+		/* this little trick skips the iget and iput */
+	        d_add(dentry, dir);
+		return 0;
+	}
+
+	/* Check for "..". */
+	if (hfs_streq(&cname, DOT_DOT)) {
+		struct hfs_cat_entry *parent;
+
+		if (dtype != HFS_NAT_NDIR) {
+			/* Case for ".." in ".AppleDouble" */
+			parent = entry;
+			++entry->count; /* __hfs_iget() eats one */
+		} else {
+			/* Case for ".." in a normal directory */
+			parent = hfs_cat_parent(entry);
+		}
+		inode = hfs_iget(parent, HFS_NAT_NDIR, dentry);
+		goto done;
+	}
+
+	/* Check for ".AppleDouble" if in a normal directory,
+	   and for ".Parent" in ".AppleDouble". */
+	if (dtype==HFS_NAT_NDIR) {
+		/* Check for ".AppleDouble" */
+		if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+			++entry->count; /* __hfs_iget() eats one */
+			inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
+			goto done;
+		}
+	} else if (dtype==HFS_NAT_HDIR) {
+		if (hfs_streq(&cname, DOT_PARENT)) {
+			++entry->count; /* __hfs_iget() eats one */
+			inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
+			goto done;
+		}
+	}
+
+	/* Do an hfs_iget() on the mangled name. */
+	hfs_cat_build_key(entry->cnid, &cname, &key);
+	inode = hfs_iget(hfs_cat_get(entry->mdb, &key), 
+			 HFS_I(dir)->file_type, dentry);
+
+	/* Don't return a header file for a directory other than .Parent */
+	if (inode && (dtype == HFS_NAT_HDIR) &&
+	    (HFS_I(inode)->entry != entry) &&
+	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+	        iput(inode);
+		inode = NULL;
+	}
+
+done:
+	d_add(dentry, inode);
+	return 0;
+}
+
+/*
+ * nat_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the netatalk scheme.  The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a struct file which indicates the location in the
+ * directory.  The struct file is updated so that the next call with
+ * the same dir and filp will produce the next directory entry.	 The
+ * entries are returned in dirent, which is "filled-in" by calling
+ * filldir().  This allows the same readdir() function be used for
+ * different dirent formats.  We try to read in as many entries as we
+ * can before filldir() refuses to take any more.
+ *
+ * Note that the Netatalk format doesn't have the problem with
+ * metadata for covered directories that exists in the other formats,
+ * since the metadata is contained within the directory.
+ */
+static int nat_readdir(struct file * filp,
+		       void * dirent, filldir_t filldir)
+{
+	ino_t type;
+	int skip_dirs;
+	struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+	struct inode *dir = filp->f_dentry->d_inode;
+
+	if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+		return -EBADF;
+	}
+
+        entry = HFS_I(dir)->entry;
+	type = HFS_ITYPE(dir->i_ino);
+	skip_dirs = (type == HFS_NAT_HDIR);
+
+	if (filp->f_pos == 0) {
+		/* Entry 0 is for "." */
+		if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+			return 0;
+		}
+		filp->f_pos = 1;
+	}
+
+	if (filp->f_pos == 1) {
+		/* Entry 1 is for ".." */
+		hfs_u32 cnid;
+
+		if (type == HFS_NAT_NDIR) {
+			cnid = hfs_get_nl(entry->key.ParID);
+		} else {
+			cnid = entry->cnid;
+		}
+
+		if (filldir(dirent, DOT_DOT->Name,
+			    DOT_DOT_LEN, 1, ntohl(cnid))) {
+			return 0;
+		}
+		filp->f_pos = 2;
+	}
+
+	if (filp->f_pos < (dir->i_size - 1)) {
+		hfs_u32 cnid;
+		hfs_u8 type;
+
+	    	if (hfs_cat_open(entry, &brec) ||
+		    hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+			return 0;
+		}
+		while (filp->f_pos < (dir->i_size - 1)) {
+			if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+				return 0;
+			}
+			if (!skip_dirs || (type != HFS_CDR_DIR)) {
+				ino_t ino;
+				unsigned int len;
+				unsigned char tmp_name[HFS_NAMEMAX];
+
+				ino = ntohl(cnid) | HFS_I(dir)->file_type;
+				len = hfs_namein(dir, tmp_name,
+				    &((struct hfs_cat_key *)brec.key)->CName);
+				if (filldir(dirent, tmp_name, len,
+					    filp->f_pos, ino)) {
+					hfs_cat_close(entry, &brec);
+					return 0;
+				}
+			}
+			++filp->f_pos;
+		}
+		hfs_cat_close(entry, &brec);
+	}
+
+	if (filp->f_pos == (dir->i_size - 1)) {
+		if (type == HFS_NAT_NDIR) {
+			/* In normal dirs entry 2 is for ".AppleDouble" */
+			if (filldir(dirent, DOT_APPLEDOUBLE->Name,
+				    DOT_APPLEDOUBLE_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_NAT_HDIR)) {
+				return 0;
+			}
+		} else if (type == HFS_NAT_HDIR) {
+			/* In .AppleDouble entry 2 is for ".Parent" */
+			if (filldir(dirent, DOT_PARENT->Name,
+				    DOT_PARENT_LEN, filp->f_pos,
+				    ntohl(entry->cnid) | HFS_NAT_HDR)) {
+				return 0;
+			}
+		}
+		++filp->f_pos;
+	}
+
+	return 0;
+}
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry 
+ * upon successful completion. */
+void hfs_nat_drop_dentry(const ino_t type, struct dentry *dentry)
+{
+  struct dentry *de;
+  
+  switch (type) {
+  case HFS_NAT_HDR: /* given .AppleDouble/name */
+    /* look for name */
+    de = hfs_lookup_dentry(dentry->d_name.name,
+			   dentry->d_name.len,
+			   dentry->d_parent->d_parent);
+    if (de) {
+      if (!de->d_inode)
+	d_drop(de);
+      dput(de);
+    }
+    break;
+  case HFS_NAT_DATA: /* given name */
+    /* look for .AppleDouble/name */
+    hfs_drop_special(DOT_APPLEDOUBLE, dentry->d_parent, dentry);
+    break;
+  }
+}
+
+/*
+ * nat_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * Netatalk directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ *
+ * We handle .AppleDouble and call hfs_rmdir() for all other cases.
+ */
+static int nat_rmdir(struct inode *parent, struct dentry *dentry)
+{
+	struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+	struct hfs_name cname;
+	int error;
+
+	hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
+	if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+		if (!HFS_SB(parent->i_sb)->s_afpd) {
+			/* Not in AFPD compatibility mode */
+			error = -EPERM;
+		} else if (entry->u.dir.files || entry->u.dir.dirs) {
+			/* AFPD compatible, but the directory is not empty */
+			error = -ENOTEMPTY;
+		} else {
+			/* AFPD compatible, so pretend to succeed */
+			error = 0;
+		}
+	} else {
+		error = hfs_rmdir(parent, dentry);
+	}
+	return error;
+}
+
+/*
+ * nat_hdr_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * Netatalk .AppleDouble directories.  The purpose is to delete an
+ * existing file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ *
+ * WE DON'T ACTUALLY DELETE HEADER THE FILE.
+ * In non-afpd-compatible mode:
+ *   We return -EPERM.
+ * In afpd-compatible mode:
+ *   We return success if the file exists or is .Parent.
+ *   Otherwise we return -ENOENT.
+ */
+static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+	int error = 0;
+
+	if (!HFS_SB(dir->i_sb)->s_afpd) {
+		/* Not in AFPD compatibility mode */
+		error = -EPERM;
+	} else {
+		struct hfs_name cname;
+
+		hfs_nameout(dir, &cname, dentry->d_name.name, 
+			    dentry->d_name.len);
+		if (!hfs_streq(&cname, DOT_PARENT)) {
+			struct hfs_cat_entry *victim;
+			struct hfs_cat_key key;
+
+			hfs_cat_build_key(entry->cnid, &cname, &key);
+			victim = hfs_cat_get(entry->mdb, &key);
+
+			if (victim) {
+				/* pretend to succeed */
+				hfs_cat_put(victim);
+			} else {
+				error = -ENOENT;
+			}
+		}
+	}
+	return error;
+}
+
+/*
+ * nat_hdr_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * Netatalk header directories.  The purpose is to rename an existing
+ * file given the inode for the current directory and the name 
+ * (and its length) of the existing file and the inode for the new
+ * directory and the name (and its length) of the new file/directory.
+ *
+ * WE NEVER MOVE ANYTHING.
+ * In non-afpd-compatible mode:
+ *   We return -EPERM.
+ * In afpd-compatible mode:
+ *   If the source header doesn't exist, we return -ENOENT.
+ *   If the destination is not a header directory we return -EPERM.
+ *   We return success if the destination is also a header directory
+ *    and the header exists or is ".Parent".
+ */
+static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
+			  struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
+	int error = 0;
+
+	if (!HFS_SB(old_dir->i_sb)->s_afpd) {
+		/* Not in AFPD compatibility mode */
+		error = -EPERM;
+	} else {
+		struct hfs_name cname;
+
+		hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
+			    old_dentry->d_name.len);
+		if (!hfs_streq(&cname, DOT_PARENT)) {
+			struct hfs_cat_entry *victim;
+			struct hfs_cat_key key;
+
+			hfs_cat_build_key(entry->cnid, &cname, &key);
+			victim = hfs_cat_get(entry->mdb, &key);
+
+			if (victim) {
+				/* pretend to succeed */
+				hfs_cat_put(victim);
+			} else {
+				error = -ENOENT;
+			}
+		}
+
+		if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
+			error = -EPERM;
+		}
+	}
+	return error;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/extent.c linux/fs/hfs/extent.c
--- v2.1.77/linux/fs/hfs/extent.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/extent.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,811 @@
+/*
+ * linux/fs/hfs/extent.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the extents B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data type ================*/
+
+/* An extent record on disk*/
+struct hfs_raw_extent {
+	hfs_word_t	block1;
+	hfs_word_t	length1;
+	hfs_word_t	block2;
+	hfs_word_t	length2;
+	hfs_word_t	block3;
+	hfs_word_t	length3;
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key
+ */
+static inline void build_key(struct hfs_ext_key *key,
+			     const struct hfs_fork *fork, hfs_u16 block)
+{
+	key->KeyLen = 7;
+	key->FkType = fork->fork;
+	hfs_put_nl(fork->entry->cnid, key->FNum);
+	hfs_put_hs(block,             key->FABN);
+}
+
+
+/*
+ * lock_bitmap()
+ *
+ * Get an exclusive lock on the B-tree bitmap.
+ */
+static inline void lock_bitmap(struct hfs_mdb *mdb) {
+	while (mdb->bitmap_lock) {
+		hfs_sleep_on(&mdb->bitmap_wait);
+	}
+	mdb->bitmap_lock = 1;
+}
+
+/*
+ * unlock_bitmap()
+ *
+ * Relinquish an exclusive lock on the B-tree bitmap.
+ */
+static inline void unlock_bitmap(struct hfs_mdb *mdb) {
+	mdb->bitmap_lock = 0;
+	hfs_wake_up(&mdb->bitmap_wait);
+}
+
+/*
+ * dump_ext()
+ *
+ * prints the content of a extent for debugging purposes.
+ */
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+static void dump_ext(const char *msg, const struct hfs_extent *e) {
+	if (e) {
+		hfs_warn("%s (%d-%d) (%d-%d) (%d-%d)\n", msg,
+			 e->start,
+			 e->start + e->length[0] - 1,
+			 e->start + e->length[0],
+			 e->start + e->length[0] + e->length[1] - 1,
+			 e->start + e->length[0] + e->length[1],
+			 e->end);
+	} else {
+		hfs_warn("%s NULL\n", msg);
+	}
+}
+#else
+#define dump_ext(A,B) {}
+#endif
+
+/*
+ * read_extent()
+ * 
+ * Initializes a (struct hfs_extent) from a (struct hfs_raw_extent) and
+ * the number of the starting block for the extent.
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void read_extent(struct hfs_extent *to,
+			const struct hfs_raw_extent *from,
+			hfs_u16 start)
+{
+	to->start = start;
+	to->block[0]  = hfs_get_hs(from->block1);
+	to->length[0] = hfs_get_hs(from->length1);
+	to->block[1]  = hfs_get_hs(from->block2);
+	to->length[1] = hfs_get_hs(from->length2);
+	to->block[2]  = hfs_get_hs(from->block3);
+	to->length[2] = hfs_get_hs(from->length3);
+	to->end = start + to->length[0] + to->length[1] + to->length[2] - 1;
+	to->next = to->prev = NULL;
+	to->count = 0;
+}
+
+/*
+ * write_extent()
+ * 
+ * Initializes a (struct hfs_raw_extent) from a (struct hfs_extent).
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void write_extent(struct hfs_raw_extent *to,
+			 const struct hfs_extent *from)
+{
+	hfs_put_hs(from->block[0], to->block1);
+	hfs_put_hs(from->length[0], to->length1);
+	hfs_put_hs(from->block[1], to->block2);
+	hfs_put_hs(from->length[1], to->length2);
+	hfs_put_hs(from->block[2], to->block3);
+	hfs_put_hs(from->length[2], to->length3);
+}
+
+/*
+ * decode_extent()
+ *
+ * Given an extent record and allocation block offset into the file,
+ * return the number of the corresponding allocation block on disk,
+ * or -1 if the desired block is not mapped by the given extent.
+ *
+ * Note that callers must check that extent != NULL
+ */
+static int decode_extent(const struct hfs_extent * extent, int block)
+{
+	if (!extent || (block < extent->start) || (block > extent->end) ||
+	    (extent->end == (hfs_u16)(extent->start - 1))) {
+		return -1;
+	}
+	block -= extent->start;
+	if (block < extent->length[0]) {
+		return block + extent->block[0];
+	}
+	block -= extent->length[0];
+	if (block < extent->length[1]) {
+		return block + extent->block[1];
+	}
+	return block + extent->block[2] - extent->length[1];
+}
+
+/*
+ * relse_ext()
+ *
+ * Reduce the reference count of an in-core extent record by one,
+ * removing it from memory if the count falls to zero.
+ */
+static void relse_ext(struct hfs_extent *ext)
+{
+	if (--ext->count || !ext->start) {
+		return;
+	}
+	ext->prev->next = ext->next;
+	if (ext->next) {
+		ext->next->prev = ext->prev;
+	}
+	HFS_DELETE(ext);
+}
+
+/*
+ * set_cache()
+ * 
+ * Changes the 'cache' field of the fork.
+ */
+static inline void set_cache(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+	struct hfs_extent *tmp = fork->cache;
+
+	++ext->count;
+	fork->cache = ext;
+	relse_ext(tmp);
+}
+
+/*
+ * find_ext()
+ *
+ * Given a pointer to a (struct hfs_file) and an allocation block
+ * number in the file, find the extent record containing that block.
+ * Returns a pointer to the extent record on success or NULL on failure.
+ * The 'cache' field of 'fil' also points to the extent so it has a
+ * reference count of at least 2.
+ *
+ * Callers must check that fil != NULL
+ */
+static struct hfs_extent * find_ext(struct hfs_fork *fork, int alloc_block)
+{
+        struct hfs_cat_entry *entry = fork->entry;
+	struct hfs_btree *tr= entry->mdb->ext_tree;
+	struct hfs_ext_key target, *key;
+	struct hfs_brec brec;
+	struct hfs_extent *ext, *ptr;
+	int tmp;
+
+	if (alloc_block < 0) {
+		ext = &fork->first;
+		goto found;
+	}
+
+	ext = fork->cache;
+	if (!ext || (alloc_block < ext->start)) {
+		ext = &fork->first;
+	}
+	while (ext->next && (alloc_block > ext->end)) {
+		ext = ext->next;
+	}
+	if ((alloc_block <= ext->end) && (alloc_block >= ext->start)) {
+		goto found;
+	}
+
+	/* time to read more extents */
+	if (!HFS_NEW(ext)) {
+		goto bail3;
+	}
+
+	build_key(&target, fork, alloc_block);
+
+	tmp = hfs_bfind(&brec, tr, HFS_BKEY(&target), HFS_BFIND_READ_LE);
+	if (tmp < 0) {
+		goto bail2;
+	}
+
+	key = (struct hfs_ext_key *)brec.key;
+	if ((hfs_get_nl(key->FNum) != hfs_get_nl(target.FNum)) ||
+	    (key->FkType != fork->fork)) {
+		goto bail1;
+	}
+		
+	read_extent(ext, brec.data, hfs_get_hs(key->FABN));
+	hfs_brec_relse(&brec, NULL);
+
+	if ((alloc_block > ext->end) && (alloc_block < ext->start)) {
+		/* something strange happened */
+		goto bail2;
+	}
+
+	ptr = fork->cache;
+	if (!ptr || (alloc_block < ptr->start)) {
+		ptr = &fork->first;
+	}
+	while (ptr->next && (alloc_block > ptr->end)) {
+		ptr = ptr->next;
+	}
+	if (ext->start == ptr->start) {
+		/* somebody beat us to it. */
+		HFS_DELETE(ext);
+		ext = ptr;
+	} else if (ext->start < ptr->start) {
+		/* insert just before ptr */
+		ptr->prev->next = ext;
+		ext->prev = ptr->prev;
+		ext->next = ptr;
+		ptr->prev = ext;
+	} else {
+		/* insert at end */
+		ptr->next = ext;
+		ext->prev = ptr;
+	}
+ found:
+	++ext->count; /* for return value */
+	set_cache(fork, ext);
+	return ext;
+
+ bail1:
+	hfs_brec_relse(&brec, NULL);
+ bail2:
+	HFS_DELETE(ext);
+ bail3:
+	return NULL;
+}
+
+/*
+ * delete_extent()
+ *
+ * Description:
+ *   Deletes an extent record from a fork, reducing its physical length.
+ * Input Variable(s):
+ *   struct hfs_fork *fork: the fork
+ *   struct hfs_extent *ext: the current last extent for 'fork'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'fork' points to a valid (struct hfs_fork)
+ *   'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ *    and which is not also the first extent in 'fork'.
+ * Postconditions:
+ *   The extent record has been removed if possible, and a warning has been
+ *   printed otherwise.
+ */
+static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+	struct hfs_mdb *mdb = fork->entry->mdb;
+	struct hfs_ext_key key;
+	int error;
+
+	if (fork->cache == ext) {
+		set_cache(fork, ext->prev);
+	}
+	ext->prev->next = NULL;
+	if (ext->count != 1) {
+		hfs_warn("hfs_truncate: extent has count %d.\n", ext->count);
+	}
+
+	lock_bitmap(mdb);
+	error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]);
+	if (error) {
+		hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+	}
+	error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]);
+	if (error) {
+		hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+	}
+	error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]);
+	if (error) {
+		hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+	}
+	unlock_bitmap(mdb);
+
+	build_key(&key, fork, ext->start);
+
+	error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key));
+	if (error) {
+		hfs_warn("hfs_truncate: error %d deleting an extent.\n", error);
+	}
+	HFS_DELETE(ext);
+}
+
+/*
+ * new_extent()
+ *
+ * Description:
+ *   Adds a new extent record to a fork, extending its physical length.
+ * Input Variable(s):
+ *   struct hfs_fork *fork: the fork to extend
+ *   struct hfs_extent *ext: the current last extent for 'fork'
+ *   hfs_u16 ablock: the number of allocation blocks in 'fork'.
+ *   hfs_u16 start: first allocation block to add to 'fork'.
+ *   hfs_u16 len: the number of allocation blocks to add to 'fork'.
+ *   hfs_u16 ablksz: number of sectors in an allocation block.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_extent *) the new extent or NULL
+ * Preconditions:
+ *   'fork' points to a valid (struct hfs_fork)
+ *   'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ *   'ablock', 'start', 'len' and 'ablksz' are what they claim to be.
+ * Postconditions:
+ *   If NULL is returned then no changes have been made to 'fork'.
+ *   If the return value is non-NULL that it is the extent that has been
+ *   added to 'fork' both in memory and on disk.  The 'psize' field of
+ *   'fork' has been updated to reflect the new physical size.
+ */
+static struct hfs_extent *new_extent(struct hfs_fork *fork,
+				     struct hfs_extent *ext,
+				     hfs_u16 ablock, hfs_u16 start,
+				     hfs_u16 len, hfs_u16 ablksz)
+{
+	struct hfs_raw_extent raw;
+	struct hfs_ext_key key;
+	int error;
+
+	if (fork->entry->cnid == htonl(HFS_EXT_CNID)) {
+		/* Limit extents tree to the record in the MDB */
+		return NULL;
+	}
+
+	if (!HFS_NEW(ext->next)) {
+		return NULL;
+	}
+	ext->next->prev = ext;
+	ext->next->next = NULL;
+	ext = ext->next;
+	relse_ext(ext->prev);
+
+	ext->start = ablock;
+	ext->block[0] = start;
+	ext->length[0] = len;
+	ext->block[1] = 0;
+	ext->length[1] = 0;
+	ext->block[2] = 0;
+	ext->length[2] = 0;
+	ext->end = ablock + len - 1;
+	ext->count = 1;
+
+	write_extent(&raw, ext);
+	
+	build_key(&key, fork, ablock);
+
+	error = hfs_binsert(fork->entry->mdb->ext_tree, 
+			    HFS_BKEY(&key), &raw, sizeof(raw));
+	if (error) {
+		ext->prev->next = NULL;
+		HFS_DELETE(ext);
+		return NULL;
+	}
+	set_cache(fork, ext);
+	return ext;
+}
+
+/*
+ * update_ext()
+ *
+ * Given a (struct hfs_fork) write an extent record back to disk.
+ */
+static void update_ext(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+	struct hfs_ext_key target;
+	struct hfs_brec brec;
+
+	if (ext->start) {
+		build_key(&target, fork, ext->start);
+
+		if (!hfs_bfind(&brec, fork->entry->mdb->ext_tree,
+			       HFS_BKEY(&target), HFS_BFIND_WRITE)) {
+			write_extent(brec.data, ext);
+			hfs_brec_relse(&brec, NULL);
+		}
+	}
+}
+
+/*
+ * zero_blocks()
+ * 
+ * Zeros-out 'num' allocation blocks beginning with 'start'.
+ */
+static int zero_blocks(struct hfs_mdb *mdb, int start, int num) {
+	hfs_buffer buf;
+	int end;
+	int j;
+
+	start = mdb->fs_start + start * mdb->alloc_blksz;
+	end = start + num * mdb->alloc_blksz;
+
+	for (j=start; j<end; ++j) {
+		if (hfs_buffer_ok(buf = hfs_buffer_get(mdb->sys_mdb, j, 0))) {
+			memset(hfs_buffer_data(buf), 0, HFS_SECTOR_SIZE);
+			hfs_buffer_dirty(buf);
+			hfs_buffer_put(buf);
+		}
+	}
+	return 0;
+}
+
+/*
+ * shrink_fork()
+ *
+ * Try to remove enough allocation blocks from 'fork'
+ * so that it is 'ablocks' allocation blocks long. 
+ */
+static void shrink_fork(struct hfs_fork *fork, int ablocks)
+{
+	struct hfs_mdb *mdb = fork->entry->mdb;
+	struct hfs_extent *ext;
+	int i, error, next, count;
+	hfs_u16 ablksz = mdb->alloc_blksz;
+
+	next =  (fork->psize / ablksz) - 1;
+	ext = find_ext(fork, next);
+	while (ext && ext->start && (ext->start >= ablocks)) {
+		next = ext->start - 1;
+		delete_extent(fork, ext);
+		ext = find_ext(fork, next);
+	}
+	if (!ext) {
+		fork->psize = (next + 1) * ablksz;
+		return;
+	}
+
+	if ((count = next + 1 - ablocks) > 0) {
+		for (i=2; (i>=0) && !ext->length[i]; --i) {};
+		while (count && (ext->length[i] <= count)) {
+			ext->end -= ext->length[i];
+			count -= ext->length[i];
+			error = hfs_clear_vbm_bits(mdb, ext->block[i],
+						   ext->length[i]);
+			if (error) {
+				hfs_warn("hfs_truncate: error %d freeing "
+				       "blocks.\n", error);
+			}
+			ext->block[i] = ext->length[i] = 0;
+			--i;
+		}
+		if (count) {
+			ext->end -= count;
+			ext->length[i] -= count;
+			error = hfs_clear_vbm_bits(mdb, ext->block[i] +
+						       ext->length[i], count);
+			if (error) {
+				hfs_warn("hfs_truncate: error %d freeing "
+				       "blocks.\n", error);
+			}
+		}
+		update_ext(fork, ext);
+	}
+
+	fork->psize = ablocks * ablksz;
+}
+
+/*
+ * grow_fork()
+ *
+ * Try to add enough allocation blocks to 'fork'
+ * so that it is 'ablock' allocation blocks long. 
+ */
+static void grow_fork(struct hfs_fork *fork, int ablocks)
+{
+	struct hfs_cat_entry *entry = fork->entry;
+	struct hfs_mdb *mdb = entry->mdb;
+	struct hfs_extent *ext;
+	int i, start, err;
+	hfs_u16 need, len=0;
+	hfs_u16 ablksz = mdb->alloc_blksz;
+	hfs_u32 blocks, clumpablks;
+
+	blocks = fork->psize;
+	need = ablocks - blocks/ablksz;
+	if (need < 1) {
+		return;
+	}
+
+	/* round up to clumpsize */
+	if (entry->u.file.clumpablks) {
+		clumpablks = entry->u.file.clumpablks;
+	} else {
+		clumpablks = mdb->clumpablks;
+	}
+	need = ((need + clumpablks - 1) / clumpablks) * clumpablks;
+
+	/* find last extent record and try to extend it */
+	if (!(ext = find_ext(fork, blocks/ablksz - 1))) {
+		/* somehow we couldn't find the end of the file! */
+		return;
+	}
+
+	/* determine which is the last used extent in the record */
+	/* then try to allocate the blocks immediately following it */
+	for (i=2; (i>=0) && !ext->length[i]; --i) {};
+	if (i>=0) {
+		/* try to extend the last extent */
+		start = ext->block[i] + ext->length[i];
+
+		err = 0;
+		lock_bitmap(mdb);
+		len = hfs_vbm_count_free(mdb, start);
+		if (!len) {
+			unlock_bitmap(mdb);
+			goto more_extents;
+		}
+		if (need < len) {
+			len = need;
+		}
+		err = hfs_set_vbm_bits(mdb, start, len);
+		unlock_bitmap(mdb);
+		if (err) {
+			relse_ext(ext);
+			return;
+		}
+	
+		zero_blocks(mdb, start, len);
+	
+		ext->length[i] += len;
+		ext->end += len;
+		blocks = (fork->psize += len * ablksz);
+		need -= len;
+		update_ext(fork, ext);
+	}
+
+more_extents:
+	/* add some more extents */
+	while (need) {
+		len = need;
+		err = 0;
+		lock_bitmap(mdb);
+		start = hfs_vbm_search_free(mdb, &len);
+		if (need < len) {
+			len = need;
+		}
+		err = hfs_set_vbm_bits(mdb, start, len);
+		unlock_bitmap(mdb);
+		if (!len || err) {
+			relse_ext(ext);
+			return;
+		}
+		zero_blocks(mdb, start, len);
+
+		/* determine which is the first free extent in the record */
+		for (i=0; (i<3) && ext->length[i]; ++i) {};
+		if (i < 3) {
+			ext->block[i] = start;
+			ext->length[i] = len;
+			ext->end += len;
+			update_ext(fork, ext);
+		} else {
+			if (!(ext = new_extent(fork, ext, blocks/ablksz,
+					       start, len, ablksz))) {
+				hfs_clear_vbm_bits(mdb, start, len);
+				return;
+			}
+		}
+		blocks = (fork->psize += len * ablksz);
+		need -= len;
+	}
+	set_cache(fork, ext);
+	relse_ext(ext);
+	return;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_ext_compare()
+ *
+ * Description:
+ *   This is the comparison function used for the extents B-tree.  In
+ *   comparing extent B-tree entries, the file id is the most
+ *   significant field (compared as unsigned ints); the fork type is
+ *   the second most significant field (compared as unsigned chars);
+ *   and the allocation block number field is the least significant
+ *   (compared as unsigned ints).
+ * Input Variable(s):
+ *   struct hfs_ext_key *key1: pointer to the first key to compare
+ *   struct hfs_ext_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
+ * Postconditions:
+ *   This function has no side-effects */
+int hfs_ext_compare(const struct hfs_ext_key *key1,
+		    const struct hfs_ext_key *key2)
+{
+	unsigned int tmp;
+	int retval;
+
+	tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum);
+	if (tmp != 0) {
+		retval = (int)tmp;
+	} else {
+		tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType;
+		if (tmp != 0) {
+			retval = (int)tmp;
+		} else {
+			retval = (int)(hfs_get_hs(key1->FABN)
+				       - hfs_get_hs(key2->FABN));
+		}
+	}
+	return retval;
+}
+
+/*
+ * hfs_extent_adj()
+ *
+ * Given an hfs_fork shrink or grow the fork to hold the
+ * forks logical size.
+ */
+void hfs_extent_adj(struct hfs_fork *fork)
+{
+	if (fork) {
+		hfs_u32 blks, ablocks;
+		hfs_u16 ablksz;
+
+		if (fork->lsize > HFS_FORK_MAX) {
+			fork->lsize = HFS_FORK_MAX;
+		}
+
+		blks = (fork->lsize+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+		ablksz = fork->entry->mdb->alloc_blksz;
+		ablocks = (blks + ablksz - 1) / ablksz;
+
+		if (blks > fork->psize) {
+			grow_fork(fork, ablocks);
+			if (blks > fork->psize) {
+				fork->lsize =
+					fork->psize >> HFS_SECTOR_SIZE_BITS;
+			}
+			fork->entry->dirt = 1;
+		} else if (blks < fork->psize) {
+			shrink_fork(fork, ablocks);
+			fork->entry->dirt = 1;
+		}
+	}
+}
+
+/*
+ * hfs_extent_map()
+ *
+ * Given an hfs_fork and a block number within the fork, return the
+ * number of the corresponding physical block on disk, or zero on
+ * error.
+ */
+int hfs_extent_map(struct hfs_fork *fork, int block, int create) 
+{
+	int ablksz, ablock, offset, tmp;
+	struct hfs_extent *ext;
+
+	if (!fork || !fork->entry || !fork->entry->mdb) {
+		return 0;
+	}
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+	hfs_warn("hfs_extent_map: ablock %d of file %d, fork %d\n",
+		 block, fork->entry->cnid, fork->fork);
+#endif
+
+	if (block < 0) {
+		hfs_warn("hfs_extent_map: block < 0\n");
+		return 0;
+	}
+	if (block > (HFS_FORK_MAX >> HFS_SECTOR_SIZE_BITS)) {
+		hfs_warn("hfs_extent_map: block(0x%08x) > big; cnid=%d "
+			 "fork=%d\n", block, fork->entry->cnid, fork->fork);
+		return 0;
+	}
+	ablksz = fork->entry->mdb->alloc_blksz;
+	offset = fork->entry->mdb->fs_start + (block % ablksz);
+	ablock = block / ablksz;
+	
+	if (block >= fork->psize) {
+		if (create) {
+			grow_fork(fork, ablock + 1);
+			fork->entry->dirt = 1;
+		} else {
+			return 0;
+		}
+	}
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+	hfs_warn("(lblock %d offset %d)\n", ablock, offset);
+#endif
+
+	if ((ext = find_ext(fork, ablock))) {
+		dump_ext("trying new: ", ext);
+		tmp = decode_extent(ext, ablock);
+		relse_ext(ext);
+		if (tmp >= 0) {
+			return tmp*ablksz + offset;
+		}
+	} 
+
+	return 0;
+}
+
+/*
+ * hfs_extent_out()
+ *
+ * Copy the first extent record from a (struct hfs_fork) to a (struct
+ * raw_extent), record (normally the one in the catalog entry).
+ */
+void hfs_extent_out(const struct hfs_fork *fork, hfs_byte_t dummy[12])
+{
+	struct hfs_raw_extent *ext = (struct hfs_raw_extent *)dummy;
+
+	if (fork && ext) {
+		write_extent(ext, &fork->first);
+		dump_ext("extent out: ", &fork->first);
+	}
+}
+
+/*
+ * hfs_extent_in()
+ *
+ * Copy an raw_extent to the 'first' and 'cache' fields of an hfs_fork.
+ */
+void hfs_extent_in(struct hfs_fork *fork, const hfs_byte_t dummy[12])
+{
+	const struct hfs_raw_extent *ext =
+		(const struct hfs_raw_extent *)dummy;
+
+	if (fork && ext) {
+		read_extent(&fork->first, ext, 0);
+		fork->cache = &fork->first;
+		fork->first.count = 2;
+		dump_ext("extent in: ", &fork->first);
+	}
+}
+
+/* 
+ * hfs_extent_free()
+ *
+ * Removes from memory all extents associated with 'fil'.
+ */
+void hfs_extent_free(struct hfs_fork *fork)
+{
+	if (fork) {
+		set_cache(fork, &fork->first);
+
+	        if (fork->first.next) {
+		        hfs_warn("hfs_extent_free: extents in use!\n");
+		}
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/file.c linux/fs/hfs/file.c
--- v2.1.77/linux/fs/hfs/file.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/file.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,525 @@
+/*
+ * linux/fs/hfs/file.c
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file-related functions which are independent of
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hfs_file_read(struct file *, char *, hfs_rwarg_t,
+				 loff_t *);
+static hfs_rwret_t hfs_file_write(struct file *, const char *, hfs_rwarg_t,
+				  loff_t *);
+static void hfs_file_truncate(struct inode *);
+static int hfs_bmap(struct inode *, int);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_file_operations = {
+	NULL,			/* lseek - default */
+	hfs_file_read,		/* read */
+	hfs_file_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	generic_file_mmap,	/* mmap */
+	NULL,			/* open */
+	NULL,			/* release */
+	file_fsync,		/* fsync - default */
+        NULL,			/* fasync - default */
+        NULL,			/* check_media_change - none */
+        NULL,			/* revalidate - none */
+        NULL			/* lock - none */
+};
+
+struct inode_operations hfs_file_inode_operations = {
+	&hfs_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 */
+	hfs_bmap,		/* bmap */
+	hfs_file_truncate,	/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
+};
+
+/*================ Variable-like macros ================*/
+
+/* maximum number of blocks to try to read in at once */
+#define NBUF 32
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_getblk()
+ *
+ * Given an hfs_fork and a block number return the buffer_head for
+ * that block from the fork.  If 'create' is non-zero then allocate
+ * the necessary block(s) to the fork.
+ */
+struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
+{
+	int tmp;
+	kdev_t dev = fork->entry->mdb->sys_mdb->s_dev;
+
+	tmp = hfs_extent_map(fork, block, create);
+
+	if (create) {
+		/* If writing the block, then we have exclusive access
+		   to the file until we return, so it can't have moved.
+		*/
+		return tmp ? getblk(dev, tmp, HFS_SECTOR_SIZE) : NULL;
+	} else {
+		/* If reading the block, then retry since the
+		   location on disk could have changed while
+		   we waited on the I/O in getblk to complete.
+		*/
+		do {
+			struct buffer_head *bh =
+					getblk(dev, tmp, HFS_SECTOR_SIZE);
+			int tmp2 = hfs_extent_map(fork, block, 0);
+
+			if (tmp2 == tmp) {
+				return bh;
+			} else {
+				/* The block moved or no longer exists. */
+				brelse(bh);
+				tmp = tmp2;
+			}
+		} while (tmp != 0);
+
+		/* The block no longer exists. */
+		return NULL;
+	}
+}
+
+/*
+ * hfs_bmap()
+ *
+ * This is the bmap() field in the inode_operations structure for
+ * "regular" (non-header) files.  The purpose is to translate an inode
+ * and a block number within the corresponding file into a physical
+ * block number.  This function just calls hfs_extent_map() to do the
+ * real work.
+ */
+static int hfs_bmap(struct inode * inode, int block)
+{
+	return hfs_extent_map(HFS_I(inode)->fork, block, 0);
+}
+
+/*
+ * hfs_file_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * "regular" (non-header) files.  The purpose is to transfer up to
+ * 'count' bytes from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file.	The data is transfered to
+ * user-space at the address 'buf'.  Returns the number of bytes
+ * successfully transfered.  This function checks the arguments, does
+ * some setup and then calls hfs_do_read() to do the actual transfer.
+ */
+static hfs_rwret_t hfs_file_read(struct file * filp, char * buf, 
+				 hfs_rwarg_t count, loff_t *ppos)
+{
+        struct inode *inode = filp->f_dentry->d_inode;
+	hfs_s32 read, left, pos, size;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_file_read: mode = %07o\n",inode->i_mode);
+		return -EINVAL;
+	}
+	pos = *ppos;
+	if (pos >= HFS_FORK_MAX) {
+		return 0;
+	}
+	size = inode->i_size;
+	if (pos > size) {
+		left = 0;
+	} else {
+		left = size - pos;
+	}
+	if (left > count) {
+		left = count;
+	}
+	if (left <= 0) {
+		return 0;
+	}
+	if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos,
+				buf, left, filp->f_reada != 0)) > 0) {
+	        *ppos += read;
+		filp->f_reada = 1;
+	}
+
+	return read;
+}
+
+/*
+ * hfs_file_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * "regular" files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'.  The return
+ * value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf,
+				  hfs_rwarg_t count, loff_t *ppos)
+{
+        struct inode    *inode = filp->f_dentry->d_inode;
+	struct hfs_fork *fork = HFS_I(inode)->fork;
+	hfs_s32 written, pos;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+		return -EINVAL;
+	}
+
+	pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+	if (pos >= HFS_FORK_MAX) {
+		return 0;
+	}
+	if (count > HFS_FORK_MAX) {
+		count = HFS_FORK_MAX;
+	}
+	if ((written = hfs_do_write(inode, fork, pos, buf, count)) > 0)
+	        pos += written;
+
+	*ppos = pos;
+	if (*ppos > inode->i_size) 
+	        inode->i_size = *ppos;
+
+	return written;
+}
+
+/*
+ * hfs_file_truncate()
+ *
+ * This is the truncate() entry in the file_operations structure for
+ * "regular" files.  The purpose is to change the length of the file
+ * corresponding to the given inode.  Changes can either lengthen or
+ * shorten the file.
+ */
+static void hfs_file_truncate(struct inode * inode)
+{
+	struct hfs_fork *fork = HFS_I(inode)->fork;
+
+	fork->lsize = inode->i_size;
+	hfs_extent_adj(fork);
+
+	inode->i_size = fork->lsize;
+	inode->i_blocks = fork->psize;
+}
+
+/*
+ * xlate_to_user()
+ *
+ * Like copy_to_user() while translating CR->NL.
+ */
+static inline void xlate_to_user(char *buf, const char *data, int count)
+{
+	char ch;
+
+	while (count--) {
+		ch = *(data++);
+		put_user((ch == '\r') ? '\n' : ch, buf++);
+	}
+}
+
+/*
+ * xlate_from_user()
+ *
+ * Like copy_from_user() while translating NL->CR;
+ */
+static inline void xlate_from_user(char *data, const char *buf, int count)
+{
+	copy_from_user(data, buf, count);
+	while (count--) {
+		if (*data == '\n') {
+			*data = '\r';
+		}
+		++data;
+	}
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_do_read()
+ *
+ * This function transfers actual data from disk to user-space memory,
+ * returning the number of bytes successfully transfered.  'fork' tells
+ * which file on the disk to read from.  'pos' gives the offset into
+ * the Linux file at which to begin the transfer.  Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file.  'count' tells how many
+ * bytes to transfer.  'buf' gives an address in user-space to transfer
+ * the data to.
+ * 
+ * This is based on Linus's minix_file_read().
+ * It has been changed to take into account that HFS files have no holes.
+ */
+hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+		    char * buf, hfs_u32 count, int reada)
+{
+	kdev_t dev = inode->i_dev;
+	hfs_s32 size, chars, offset, block, blocks, read = 0;
+	int bhrequest, uptodate;
+	int convert = HFS_I(inode)->convert;
+	struct buffer_head ** bhb, ** bhe;
+	struct buffer_head * bhreq[NBUF];
+	struct buffer_head * buflist[NBUF];
+
+	/* split 'pos' in to block and (byte) offset components */
+	block = pos >> HFS_SECTOR_SIZE_BITS;
+	offset = pos & (HFS_SECTOR_SIZE-1);
+
+	/* compute the logical size of the fork in blocks */
+	size = (fork->lsize + (HFS_SECTOR_SIZE-1)) >> HFS_SECTOR_SIZE_BITS;
+
+	/* compute the number of physical blocks to be transferred */
+	blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+
+	bhb = bhe = buflist;
+	if (reada) {
+		if (blocks < read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9)) {
+			blocks = read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9);
+		}
+		if (block + blocks > size) {
+			blocks = size - block;
+		}
+	}
+
+	/* We do this in a two stage process.  We first try and
+	   request as many blocks as we can, then we wait for the
+	   first one to complete, and then we try and wrap up as many
+	   as are actually done.
+	   
+	   This routine is optimized to make maximum use of the
+	   various buffers and caches. */
+
+	do {
+		bhrequest = 0;
+		uptodate = 1;
+		while (blocks) {
+			--blocks;
+			*bhb = hfs_getblk(fork, block++, 0);
+
+			if (!(*bhb)) {
+				/* Since there are no holes in HFS files
+				   we must have encountered an error.
+				   So, stop adding blocks to the queue. */
+				blocks = 0;
+				break;
+			}
+
+			if (!buffer_uptodate(*bhb)) {
+				uptodate = 0;
+				bhreq[bhrequest++] = *bhb;
+			}
+
+			if (++bhb == &buflist[NBUF]) {
+				bhb = buflist;
+			}
+
+			/* If the block we have on hand is uptodate,
+			   go ahead and complete processing. */
+			if (uptodate) {
+				break;
+			}
+			if (bhb == bhe) {
+				break;
+			}
+		}
+
+		/* If the only block in the queue is bad then quit */
+		if (!(*bhe)) {
+			break;
+		}
+
+		/* Now request them all */
+		if (bhrequest) {
+			ll_rw_block(READ, bhrequest, bhreq);
+		}
+
+		do {  /* Finish off all I/O that has actually completed */
+			char *p;
+
+			wait_on_buffer(*bhe);
+
+			if (!buffer_uptodate(*bhe)) {
+				/* read error? */
+				brelse(*bhe);
+				if (++bhe == &buflist[NBUF]) {
+					bhe = buflist;
+				}
+				count = 0;
+				break;
+			}
+
+			if (count < HFS_SECTOR_SIZE - offset) {
+				chars = count;
+			} else {
+				chars = HFS_SECTOR_SIZE - offset;
+			}
+			count -= chars;
+			read += chars;
+			p = (*bhe)->b_data + offset;
+			if (convert) {
+				xlate_to_user(buf, p, chars);
+			} else {
+				copy_to_user(buf, p, chars);
+			}
+			brelse(*bhe);
+			buf += chars;
+			offset = 0;
+			if (++bhe == &buflist[NBUF]) {
+				bhe = buflist;
+			}
+		} while (count && (bhe != bhb) && !buffer_locked(*bhe));
+	} while (count);
+
+	/* Release the read-ahead blocks */
+	while (bhe != bhb) {
+		brelse(*bhe);
+		if (++bhe == &buflist[NBUF]) {
+			bhe = buflist;
+		}
+	}
+	if (!read) {
+		return -EIO;
+	}
+	return read;
+}
+ 
+/*
+ * hfs_do_write()
+ *
+ * This function transfers actual data from user-space memory to disk,
+ * returning the number of bytes successfully transfered.  'fork' tells
+ * which file on the disk to write to.  'pos' gives the offset into
+ * the Linux file at which to begin the transfer.  Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file.  'count' tells how many
+ * bytes to transfer.  'buf' gives an address in user-space to transfer
+ * the data from.
+ * 
+ * This is just a minor edit of Linus's minix_file_write().
+ */
+hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+		     const char * buf, hfs_u32 count)
+{
+	hfs_s32 written, c;
+	struct buffer_head * bh;
+	char * p;
+	int convert = HFS_I(inode)->convert;
+
+	written = 0;
+	while (written < count) {
+		bh = hfs_getblk(fork, pos/HFS_SECTOR_SIZE, 1);
+		if (!bh) {
+			if (!written) {
+				written = -ENOSPC;
+			}
+			break;
+		}
+		c = HFS_SECTOR_SIZE - (pos % HFS_SECTOR_SIZE);
+		if (c > count - written) {
+			c = count - written;
+		}
+		if (c != HFS_SECTOR_SIZE && !buffer_uptodate(bh)) {
+			ll_rw_block(READ, 1, &bh);
+			wait_on_buffer(bh);
+			if (!buffer_uptodate(bh)) {
+				brelse(bh);
+				if (!written) {
+					written = -EIO;
+				}
+				break;
+			}
+		}
+		p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
+		if (convert) {
+			xlate_from_user(p, buf, c);
+		} else {
+			copy_from_user(p, buf, c);
+		}
+		update_vm_cache(inode,pos,p,c);
+		pos += c;
+		written += c;
+		buf += c;
+		mark_buffer_uptodate(bh, 1);
+		mark_buffer_dirty(bh, 0);
+		brelse(bh);
+	}
+	if (written > 0) {
+		struct hfs_cat_entry *entry = fork->entry;
+
+		inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+		if (pos > fork->lsize) {
+			fork->lsize = pos;
+		}
+		entry->modify_date = hfs_u_to_mtime(CURRENT_TIME);
+		entry->dirt = 1;
+	}
+	return written;
+}
+
+/*
+ * hfs_file_fix_mode()
+ *
+ * Fixes up the permissions on a file after changing the write-inhibit bit.
+ */
+void hfs_file_fix_mode(struct hfs_cat_entry *entry)
+{
+	struct dentry **de = entry->sys_entry;
+	int i;
+
+	if (entry->u.file.flags & HFS_FIL_LOCK) {
+		for (i = 0; i < 4; ++i) {
+			if (de[i]) {
+				de[i]->d_inode->i_mode &= ~S_IWUGO;
+			}
+		}
+	} else {
+		for (i = 0; i < 4; ++i) {
+			if (de[i]) {
+			        struct inode *inode = de[i]->d_inode;
+				inode->i_mode |= S_IWUGO;
+				inode->i_mode &= 
+				  ~HFS_SB(inode->i_sb)->s_umask;
+			}
+		}
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c
--- v2.1.77/linux/fs/hfs/file_cap.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/file_cap.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,297 @@
+/*
+ * linux/fs/hfs/file_cap.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the CAP representation.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t cap_info_read(struct file *, char *,
+				 hfs_rwarg_t, loff_t *);
+static hfs_rwret_t cap_info_write(struct file *, const char *,
+				  hfs_rwarg_t, loff_t *);
+static void cap_info_truncate(struct inode *);
+
+/*================ Function-like macros ================*/
+
+/*
+ * OVERLAPS()
+ *
+ * Determines if a given range overlaps the specified structure member
+ */
+#define OVERLAPS(START, END, TYPE, MEMB) \
+	((END > offsetof(TYPE, MEMB)) && \
+	 (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_cap_info_operations = {
+	NULL,			/* lseek - default */
+	cap_info_read,		/* read */
+	cap_info_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap - not yet */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync,		/* fsync - default */
+	NULL,			/* fasync - default */
+	NULL,			/* check_media_change - none */
+	NULL,			/* revalidate - none */
+	NULL			/* lock - none */
+};
+
+struct inode_operations hfs_cap_info_inode_operations = {
+	&hfs_cap_info_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 */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	NULL,				/* bmap - none */
+	cap_info_truncate,		/* truncate */
+	NULL,				/* permission */
+	NULL,				/* smap */
+	NULL,				/* updatepage */
+	NULL				/* revalidata */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_build_meta()
+ *
+ * Build the metadata structure.
+ */
+static void cap_build_meta(struct hfs_cap_info *meta,
+			   struct hfs_cat_entry *entry)
+{
+	memset(meta, 0, sizeof(*meta));
+	memcpy(meta->fi_fndr, &entry->info, 32);
+	if ((entry->type == HFS_CDR_FIL) &&
+	    (entry->u.file.flags & HFS_FIL_LOCK)) {
+		/* Couple the locked bit of the file to the
+		   AFP {write,rename,delete} inhibit bits. */
+		hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
+	}
+	meta->fi_magic1 = HFS_CAP_MAGIC1;
+	meta->fi_version = HFS_CAP_VERSION;
+	meta->fi_magic = HFS_CAP_MAGIC;
+	meta->fi_bitmap = HFS_CAP_LONGNAME;
+	memcpy(meta->fi_macfilename, entry->key.CName.Name,
+	       entry->key.CName.Len);
+	meta->fi_datemagic = HFS_CAP_DMAGIC;
+	meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
+	hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
+	hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
+	hfs_put_nl(CURRENT_TIME,                       meta->fi_utime);
+}
+
+/*
+ * cap_info_read()
+ *
+ * This is the read() entry in the file_operations structure for CAP
+ * metadata files.  The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' to user-space at the address 'buf'.  The return value
+ * is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
+				 hfs_rwarg_t count, loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+	hfs_s32 left, size, read = 0;
+	hfs_u32 pos;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
+		return -EINVAL;
+	}
+
+	pos = *ppos;
+	if (pos > HFS_FORK_MAX) {
+		return 0;
+	}
+	size = inode->i_size;
+	if (pos > size) {
+		left = 0;
+	} else {
+		left = size - pos;
+	}
+	if (left > count) {
+		left = count;
+	}
+	if (left <= 0) {
+		return 0;
+	}
+
+	if (pos < sizeof(struct hfs_cap_info)) {
+		int memcount = sizeof(struct hfs_cap_info) - pos;
+		struct hfs_cap_info meta;
+
+		if (memcount > left) {
+			memcount = left;
+		}
+		cap_build_meta(&meta, entry);
+		/* is copy_to_user guaranteed to write memcount? */
+		copy_to_user(buf, ((char *)&meta) + pos, memcount);
+		left -= memcount;
+		read += memcount;
+		pos += memcount;
+		buf += memcount;
+	}
+
+	if (left > 0) {
+		clear_user(buf, left);
+	        pos += left;
+	}
+
+	if (read) {
+		inode->i_atime = CURRENT_TIME;
+		*ppos = pos;
+	}
+
+	return read;
+}
+
+/*
+ * cap_info_write()
+ *
+ * This is the write() entry in the file_operations structure for CAP
+ * metadata files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_write(struct file *filp, const char *buf, 
+				  hfs_rwarg_t count, loff_t *ppos)
+{
+        struct inode *inode = filp->f_dentry->d_inode;
+	hfs_u32 pos;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+		return -EINVAL;
+	}
+	if (count <= 0) {
+		return 0;
+	}
+	
+	pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+	if (pos > HFS_FORK_MAX) {
+		return 0;
+	}
+
+	*ppos += count;
+	if (*ppos > HFS_FORK_MAX) {
+		*ppos = HFS_FORK_MAX;
+		count = HFS_FORK_MAX - pos;
+	}
+
+	if (*ppos > inode->i_size)
+	        inode->i_size = *ppos;
+
+	/* Only deal with the part we store in memory */
+	if (pos < sizeof(struct hfs_cap_info)) {
+		int end, mem_count;
+		struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+		struct hfs_cap_info meta;
+
+		mem_count = sizeof(struct hfs_cap_info) - pos;
+		if (mem_count > count) {
+			mem_count = count;
+		}
+		end = pos + mem_count;
+
+		cap_build_meta(&meta, entry);
+		copy_from_user(((char *)&meta) + pos, buf, mem_count);
+
+		/* Update finder attributes if changed */
+		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
+			memcpy(&entry->info, meta.fi_fndr, 32);
+			entry->dirt = 1;
+		}
+
+		/* Update file flags if changed */
+		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
+		    (entry->type == HFS_CDR_FIL)) {
+			int locked = hfs_get_ns(&meta.fi_attr) &
+							htons(HFS_AFP_WRI);
+			hfs_u8 new_flags;
+
+			if (locked) {
+				new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+			} else {
+				new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+			}
+
+			if (new_flags != entry->u.file.flags) {
+				entry->u.file.flags = new_flags;
+				entry->dirt = 1;
+				hfs_file_fix_mode(entry);
+			}
+		}
+
+		/* Update CrDat if changed */
+		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
+			entry->create_date =
+				hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
+			entry->dirt = 1;
+		}
+
+		/* Update MdDat if changed */
+		if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
+			entry->modify_date =
+				hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
+			entry->dirt = 1;
+		}
+	}
+
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	return count;
+}
+
+/*
+ * cap_info_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * CAP metadata files.
+ */
+static void cap_info_truncate(struct inode *inode)
+{
+	if (inode->i_size > HFS_FORK_MAX) {
+		inode->i_size = HFS_FORK_MAX;
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
--- v2.1.77/linux/fs/hfs/file_hdr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/file_hdr.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,937 @@
+/*
+ * linux/fs/hfs/file_hdr.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the AppleDouble and Netatalk representations.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * XXX: Note the reason that there is not bmap() for AppleDouble
+ * header files is that dynamic nature of their structure make it
+ * very difficult to safely mmap them.  Maybe in the distant future
+ * I'll get bored enough to implement it.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *);
+static hfs_rwret_t hdr_write(struct file *, const char *,
+			     hfs_rwarg_t, loff_t *);
+static void hdr_truncate(struct inode *);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_hdr_operations = {
+	NULL,			/* lseek - default */
+	hdr_read,		/* read */
+	hdr_write,		/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap - XXX: not yet */
+	NULL,			/* no special open code */
+	NULL,			/* no special release code */
+	file_fsync,		/* fsync - default */
+        NULL,			/* fasync - default */
+        NULL,			/* check_media_change - none */
+        NULL,			/* revalidate - none */
+        NULL			/* lock - none */
+};
+
+struct inode_operations hfs_hdr_inode_operations = {
+	&hfs_hdr_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 */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap - XXX: not available since
+				   header part has no disk block */
+	hdr_truncate,		/* truncate */
+	NULL,			/* permission */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
+};
+
+const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
+	__constant_htonl(HFS_DBL_MAGIC),	/* magic   */
+	__constant_htonl(HFS_HDR_VERSION_2),	/* version */
+	5,					/* entries */
+	{					/* descr[] */
+		{HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+		{HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo),  32},
+		{HFS_HDR_MACI,  offsetof(struct hfs_dbl_hdr, fileinfo),    4},
+		{HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name),   ~0},
+		{HFS_HDR_RSRC,  HFS_DBL_HDR_LEN,                           ~0},
+	},
+	{					/* order[] */
+		(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
+		(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1],
+		(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
+		(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
+		(struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+	}
+};
+
+const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
+	__constant_htonl(HFS_DBL_MAGIC),	/* magic   */
+	__constant_htonl(HFS_HDR_VERSION_2),	/* version */
+	4,					/* entries */
+	{					/* descr[] */
+		{HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+		{HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo),  32},
+		{HFS_HDR_MACI,  offsetof(struct hfs_dbl_hdr, fileinfo),    4},
+		{HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name),   ~0},
+	},
+	{					/* order[] */
+		(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
+		(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
+		(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
+		(struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+	}
+};
+
+const struct hfs_hdr_layout hfs_nat_hdr_layout = {
+	__constant_htonl(HFS_DBL_MAGIC),	/* magic   */
+	__constant_htonl(HFS_HDR_VERSION_1),	/* version */
+	5,					/* entries */
+	{					/* descr[] */
+		{HFS_HDR_RSRC,  HFS_NAT_HDR_LEN,                           ~0},
+		{HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name),   ~0},
+		{HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment),     0},
+		{HFS_HDR_OLDI,  offsetof(struct hfs_nat_hdr, create_time), 16},
+		{HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo),  32},
+	},
+	{					/* order[] */
+		(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+		(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+		(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+		(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+		(struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+	}
+};
+
+/*================ File-local variables ================*/
+
+static const char fstype[16] =
+	{'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '};
+
+/*================ File-local data types ================*/
+
+struct hdr_hdr {
+        hfs_lword_t	magic;
+        hfs_lword_t	version;
+        hfs_byte_t	filler[16];
+        hfs_word_t	entries;
+        hfs_byte_t	descrs[12*HFS_HDR_MAX];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dlength()
+ */
+static int dlength(const struct hfs_hdr_descr *descr,
+		   const struct hfs_cat_entry *entry)
+{
+	hfs_u32 length = descr->length;
+
+	/* handle auto-sized entries */
+	if (length == ~0) {
+		switch (descr->id) {
+		case HFS_HDR_DATA:
+			if (entry->type == HFS_CDR_FIL) {
+				length = entry->u.file.data_fork.lsize;
+			} else {
+				length = 0;
+			}
+			break;
+
+		case HFS_HDR_RSRC:
+			if (entry->type == HFS_CDR_FIL) {
+				length = entry->u.file.rsrc_fork.lsize;
+			} else {
+				length = 0;
+			}
+			break;
+
+		case HFS_HDR_FNAME:
+			length = entry->key.CName.Len;
+			break;
+
+		default:
+			length = 0;
+		}
+	}
+	return length;
+}
+
+/*
+ * hdr_build_meta()
+ */
+static void hdr_build_meta(struct hdr_hdr *meta,
+			   const struct hfs_hdr_layout *layout,
+			   const struct hfs_cat_entry *entry)
+{
+	const struct hfs_hdr_descr *descr;
+	hfs_byte_t *ptr;
+	int lcv;
+
+	hfs_put_nl(layout->magic,   meta->magic);
+	hfs_put_nl(layout->version, meta->version);
+	if (layout->version == htonl(HFS_HDR_VERSION_1)) {
+		memcpy(meta->filler, fstype, 16);
+	} else {
+		memset(meta->filler, 0, 16);
+	}
+	hfs_put_hs(layout->entries, meta->entries);
+	memset(meta->descrs, 0, sizeof(meta->descrs));
+	for (lcv = 0, descr = layout->descr, ptr = meta->descrs;
+	     lcv < layout->entries; ++lcv, ++descr, ptr += 12) {
+		hfs_put_hl(descr->id,             ptr);
+		hfs_put_hl(descr->offset,         ptr + 4);
+		hfs_put_hl(dlength(descr, entry), ptr + 8);
+	}
+}
+
+/*
+ * dup_layout ()
+ */
+static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old)
+{
+	struct hfs_hdr_layout *new;
+	int lcv;
+
+	if (HFS_NEW(new)) {
+		memcpy(new, old, sizeof(*new));
+		for (lcv = 0; lcv < new->entries; ++lcv) {
+			(char *)(new->order[lcv]) += (char *)new - (char *)old;
+		}
+	}
+	return new;
+}
+
+/*
+ * init_layout()
+ */
+static inline void init_layout(struct hfs_hdr_layout *layout,
+			       const hfs_byte_t *descrs)
+{
+	struct hfs_hdr_descr **base, **p, **q, *tmp;
+	int lcv, entries = layout->entries;
+
+	for (lcv = 0; lcv < entries; ++lcv, descrs += 12) {
+		layout->order[lcv] = &layout->descr[lcv];
+		layout->descr[lcv].id     = hfs_get_hl(descrs);
+		layout->descr[lcv].offset = hfs_get_hl(descrs + 4);
+		layout->descr[lcv].length = hfs_get_hl(descrs + 8);
+	}
+	for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) {
+		layout->order[lcv] = NULL;
+		layout->descr[lcv].id     = 0;
+		layout->descr[lcv].offset = 0;
+		layout->descr[lcv].length = 0;
+	}
+
+	/* Sort the 'order' array using an insertion sort */
+	base = &layout->order[0];
+	for (p = (base+1); p < (base+entries); ++p) {
+		q=p;
+		while ((*q)->offset < (*(q-1))->offset) {
+			tmp = *q;
+			*q = *(q-1);
+			*(--q) = tmp;
+			if (q == base) break;
+		}
+	}
+}
+
+/*
+ * adjust_forks()
+ */
+static inline void adjust_forks(struct hfs_cat_entry *entry,
+				const struct hfs_hdr_layout *layout)
+{
+	int lcv;
+
+	for (lcv = 0; lcv < layout->entries; ++lcv) {
+		const struct hfs_hdr_descr *descr = &layout->descr[lcv];
+
+		if ((descr->id == HFS_HDR_DATA) &&
+		    (descr->length != entry->u.file.data_fork.lsize)) {
+			entry->u.file.data_fork.lsize = descr->length;
+			hfs_extent_adj(&entry->u.file.data_fork);
+		} else if ((descr->id == HFS_HDR_RSRC) &&
+			   (descr->length != entry->u.file.rsrc_fork.lsize)) {
+			entry->u.file.rsrc_fork.lsize = descr->length;
+			hfs_extent_adj(&entry->u.file.rsrc_fork);
+		}
+	}
+}
+
+/*
+ * get_dates()
+ */
+static void get_dates(const struct hfs_cat_entry *entry,
+		      const struct inode *inode,  hfs_u32 dates[3])
+{
+	if (HFS_SB(inode->i_sb)->s_afpd) {
+		/* AFPD compatible: use un*x times */
+		dates[0] = htonl(hfs_m_to_utime(entry->create_date));
+		dates[1] = htonl(hfs_m_to_utime(entry->modify_date));
+		dates[2] = htonl(hfs_m_to_utime(entry->backup_date));
+	} else {
+		dates[0] = hfs_m_to_htime(entry->create_date);
+		dates[1] = hfs_m_to_htime(entry->modify_date);
+		dates[2] = hfs_m_to_htime(entry->backup_date);
+	}
+}
+
+/*
+ * set_dates()
+ */
+static void set_dates(struct hfs_cat_entry *entry, struct inode *inode,
+		      const hfs_u32 *dates)
+{
+	hfs_u32 tmp;
+	if (HFS_SB(inode->i_sb)->s_afpd) {
+		/* AFPD compatible: use un*x times */
+		tmp = hfs_u_to_mtime(ntohl(dates[0]));
+		if (entry->create_date != tmp) {
+			entry->create_date = tmp;
+			entry->dirt = 1;
+		}
+		tmp = hfs_u_to_mtime(ntohl(dates[1]));
+		if (entry->modify_date != tmp) {
+			entry->modify_date = tmp;
+			inode->i_ctime = inode->i_atime = inode->i_mtime =
+				ntohl(dates[1]);
+			entry->dirt = 1;
+		}
+		tmp = hfs_u_to_mtime(ntohl(dates[2]));
+		if (entry->backup_date != tmp) {
+			entry->backup_date = tmp;
+			entry->dirt = 1;
+		}
+	} else {
+		tmp = hfs_h_to_mtime(dates[0]);
+		if (entry->create_date != tmp) {
+			entry->create_date = tmp;
+			entry->dirt = 1;
+		}
+		tmp = hfs_h_to_mtime(dates[1]);
+		if (entry->modify_date != tmp) {
+			entry->modify_date = tmp;
+			inode->i_ctime = inode->i_atime = inode->i_mtime = 
+				hfs_h_to_utime(dates[1]);
+			entry->dirt = 1;
+		}
+		tmp = hfs_h_to_mtime(dates[2]);
+		if (entry->backup_date != tmp) {
+			entry->backup_date = tmp;
+			entry->dirt = 1;
+		}
+	}
+}
+
+/*
+ * hdr_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * header files.  The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file.	The data is transfered to
+ * user-space at the address 'buf'.  Returns the number of bytes
+ * successfully transfered.
+ */
+/* XXX: what about the entry count changing on us? */
+static hfs_rwret_t hdr_read(struct file * filp, char * buf, 
+			    hfs_rwarg_t count, loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+	const struct hfs_hdr_layout *layout;
+	off_t start, length, offset;
+	off_t pos = *ppos;
+	int left, lcv, read = 0;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode);
+		return -EINVAL;
+	}
+
+	if (HFS_I(inode)->layout) {
+		layout = HFS_I(inode)->layout;
+	} else {
+		layout = HFS_I(inode)->default_layout;
+	}
+
+	/* Adjust count to fit within the bounds of the file */
+	if ((pos >= inode->i_size) || (count <= 0)) {
+		return 0;
+	} else if (count > inode->i_size - pos) {
+		count = inode->i_size - pos;
+	}
+
+	/* Handle the fixed-location portion */
+	length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 +
+		 sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32));
+	if (pos < length) {
+		struct hdr_hdr meta;
+
+		left = length - pos;
+		if (left > count) {
+			left = count;
+		}
+
+		hdr_build_meta(&meta, layout, entry);
+		copy_to_user(buf, ((char *)&meta) + pos, left);
+		count -= left;
+		read += left;
+		pos += left;
+		buf += left;
+	}
+	if (!count) {
+		goto done;
+	}
+
+	/* Handle the actual data */
+	for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+		const struct hfs_hdr_descr *descr = layout->order[lcv];
+		struct hfs_fork *fork;
+		char tmp[16], *p;
+		off_t limit;
+
+		/* stop reading if we run out of descriptors early */
+		if (!descr) {
+			break;
+		}
+
+		/* find start and length of this entry */
+		start = descr->offset;
+		length = dlength(descr, entry);
+
+		/* Skip to next entry if this one is empty or isn't needed */
+		if (!length || (pos >= start + length)) {
+			continue;
+		}
+
+		/* Pad with zeros to the start of this entry if needed */
+		if (pos < start) {
+			left = start - pos;
+			if (left > count) {
+				left = count;
+			}
+			clear_user(buf, left);
+			count -= left;
+			read += left;
+			pos += left;
+			buf += left;
+		}
+		if (!count) {
+			goto done;
+		}
+
+		/* locate and/or construct the data for this entry */
+		fork = NULL;
+		p = NULL;
+		switch (descr->id) {
+		case HFS_HDR_DATA:
+			fork = &entry->u.file.data_fork;
+			limit = fork->lsize;
+			break;
+
+		case HFS_HDR_RSRC:
+			fork = &entry->u.file.rsrc_fork;
+			limit = fork->lsize;
+			break;
+
+		case HFS_HDR_FNAME:
+			p = entry->key.CName.Name;
+			limit = entry->key.CName.Len;
+			break;
+
+		case HFS_HDR_OLDI:
+		case HFS_HDR_DATES:
+			get_dates(entry, inode, (hfs_u32 *)tmp);
+			if (descr->id == HFS_HDR_DATES) {
+				memcpy(tmp + 12, tmp + 4, 4);
+			} else if ((entry->type == HFS_CDR_FIL) &&
+				   (entry->u.file.flags & HFS_FIL_LOCK)) {
+				hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+			} else {
+				hfs_put_nl(0, tmp + 12);
+			}
+			p = tmp;
+			limit = 16;
+			break;
+
+		case HFS_HDR_FINFO:
+			p = (char *)&entry->info;
+			limit = 32;
+			break;
+
+		case HFS_HDR_MACI:
+			hfs_put_ns(0, tmp);
+			if (entry->type == HFS_CDR_FIL) {
+				hfs_put_hs(entry->u.file.flags, tmp + 2);
+			} else {
+				hfs_put_ns(entry->u.dir.flags, tmp + 2);
+			}
+			p = tmp;
+			limit = 4;
+			break;
+
+		default:
+			limit = 0;
+		}
+		
+		/* limit the transfer to the available data
+		   of to the stated length of the entry. */
+		if (length > limit) {
+			length = limit;
+		}
+		offset = pos - start;
+		left = length - offset;
+		if (left > count) {
+			left = count;
+		}
+		if (left <= 0) {
+			continue;
+		}
+
+		/* transfer the data */
+		if (p) {
+			copy_to_user(buf, p + offset, left);
+		} else if (fork) {
+			left = hfs_do_read(inode, fork, offset, buf, left,
+					   filp->f_reada != 0);
+			if (left > 0) {
+				filp->f_reada = 1;
+			} else if (!read) {
+				return left;
+			} else {
+				goto done;
+			}
+		}
+		count -= left;
+		read += left;
+		pos += left;
+		buf += left;
+	}
+
+	/* Pad the file out with zeros */
+	if (count) {
+		clear_user(buf, count);
+		read += count;
+		pos += count;
+	}
+		
+done:
+	if (read) {
+		inode->i_atime = CURRENT_TIME;
+		*ppos = pos;
+	}
+	return read;
+}
+
+/*
+ * hdr_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * header files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
+			     hfs_rwarg_t count, loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+        struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+        struct hfs_hdr_layout *layout;
+        off_t start, length, offset;
+        int left, lcv, written = 0;
+	struct hdr_hdr meta;
+	int built_meta = 0;
+        off_t pos;
+
+	if (!S_ISREG(inode->i_mode)) {
+		hfs_warn("hfs_hdr_write: mode = %07o\n", inode->i_mode);
+		return -EINVAL;
+	}
+	if (count <= 0) {
+		return 0;
+	}
+
+	pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+	if (!HFS_I(inode)->layout) {
+		HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+	}
+	layout = HFS_I(inode)->layout;
+
+	/* Handle the 'magic', 'version', 'filler' and 'entries' fields */
+	length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16);
+	if (pos < length) {
+		hdr_build_meta(&meta, layout, entry);
+		built_meta = 1;
+
+		left = length - pos;
+		if (left > count) {
+			left = count;
+		}
+
+		copy_from_user(((char *)&meta) + pos, buf, left);
+		layout->magic   = hfs_get_nl(meta.magic);
+		layout->version = hfs_get_nl(meta.version);
+		layout->entries = hfs_get_hs(meta.entries);
+		if (layout->entries > HFS_HDR_MAX) {
+			/* XXX: should allocate slots dynamically */
+			hfs_warn("hfs_hdr_write: TRUNCATING TO %d "
+				 "DESCRIPTORS\n", HFS_HDR_MAX);
+			layout->entries = HFS_HDR_MAX;
+		}
+
+		count -= left;
+		written += left;
+		pos += left;
+		buf += left;
+	}
+	if (!count) {
+		goto done;
+	}
+
+	/* We know for certain how many entries we have, so process them */
+	length += layout->entries * 3 * sizeof(hfs_u32);
+	if (pos < length) {
+		if (!built_meta) {
+			hdr_build_meta(&meta, layout, entry);
+		}
+
+		left = length - pos;
+		if (left > count) {
+			left = count;
+		}
+
+		copy_from_user(((char *)&meta) + pos, buf, left);
+		init_layout(layout, meta.descrs);
+
+		count -= left;
+		written += left;
+		pos += left;
+		buf += left;
+
+		/* Handle possible size changes for the forks */
+		if (entry->type == HFS_CDR_FIL) {
+			adjust_forks(entry, layout);
+		}
+	}
+
+	/* Handle the actual data */
+	for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+		struct hfs_hdr_descr *descr = layout->order[lcv];
+		struct hfs_fork *fork;
+		char tmp[16], *p;
+		off_t limit;
+
+		/* stop writing if we run out of descriptors early */
+		if (!descr) {
+			break;
+		}
+
+		/* find start and length of this entry */
+		start = descr->offset;
+		if ((descr->id == HFS_HDR_DATA) ||
+		    (descr->id == HFS_HDR_RSRC)) {
+			if (entry->type == HFS_CDR_FIL) {
+				length = 0x7fffffff - start;
+			} else {
+				continue;
+			}
+		} else {
+			length = dlength(descr, entry);
+		}
+
+		/* Trim length to avoid overlap with the next entry */
+		if (layout->order[lcv+1] &&
+		    ((start + length) > layout->order[lcv+1]->offset)) {
+			length = layout->order[lcv+1]->offset - start;
+		}
+
+		/* Skip to next entry if this one is empty or isn't needed */
+		if (!length || (pos >= start + length)) {
+			continue;
+		}
+
+		/* Skip any padding that may exist between entries */
+		if (pos < start) {
+			left = start - pos;
+			if (left > count) {
+				left = count;
+			}
+			count -= left;
+			written += left;
+			pos += left;
+			buf += left;
+		}
+		if (!count) {
+			goto done;
+		}
+
+		/* locate and/or construct the data for this entry */
+		fork = NULL;
+		p = NULL;
+		switch (descr->id) {
+		case HFS_HDR_DATA:
+#if 0
+/* Can't yet write to the data fork via a header file, since there is the
+ * possibility to write via the data file, and the only locking is at the
+ * inode level.
+ */
+			fork = &entry->u.file.data_fork;
+			limit = length;
+#else
+			limit = 0;
+#endif
+			break;
+
+		case HFS_HDR_RSRC:
+			fork = &entry->u.file.rsrc_fork;
+			limit = length;
+			break;
+
+		case HFS_HDR_OLDI:
+		case HFS_HDR_DATES:
+			get_dates(entry, inode, (hfs_u32 *)tmp);
+			if (descr->id == HFS_HDR_DATES) {
+				memcpy(tmp + 12, tmp + 4, 4);
+			} else if ((entry->type == HFS_CDR_FIL) &&
+				   (entry->u.file.flags & HFS_FIL_LOCK)) {
+				hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+			} else {
+				hfs_put_nl(0, tmp + 12);
+			}
+			p = tmp;
+			limit = 16;
+			break;
+
+		case HFS_HDR_FINFO:
+			p = (char *)&entry->info;
+			limit = 32;
+			break;
+
+		case HFS_HDR_MACI:
+			hfs_put_ns(0, tmp);
+			if (entry->type == HFS_CDR_FIL) {
+				hfs_put_hs(entry->u.file.flags, tmp + 2);
+			} else {
+				hfs_put_ns(entry->u.dir.flags, tmp + 2);
+			}
+			p = tmp;
+			limit = 4;
+			break;
+
+		case HFS_HDR_FNAME:	/* Can't rename a file this way */
+		default:
+			limit = 0;
+		}
+		
+		/* limit the transfer to the available data
+		   of to the stated length of the entry. */
+		if (length > limit) {
+			length = limit;
+		}
+		offset = pos - start;
+		left = length - offset;
+		if (left > count) {
+			left = count;
+		}
+		if (left <= 0) {
+			continue;
+		}
+
+		/* transfer the data from user space */
+		if (p) {
+			copy_from_user(p + offset, buf, left);
+		} else if (fork) {
+			left = hfs_do_write(inode, fork, offset, buf, left);
+		}
+
+		/* process the data */
+		switch (descr->id) {
+		case HFS_HDR_OLDI:
+			set_dates(entry, inode, (hfs_u32 *)tmp);
+			if (entry->type == HFS_CDR_FIL) {
+				hfs_u8 new_flags = entry->u.file.flags;
+
+				if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) {
+					new_flags |= HFS_FIL_LOCK;
+				} else {
+					new_flags &= ~HFS_FIL_LOCK;
+				}
+
+				if (new_flags != entry->u.file.flags) {
+					entry->u.file.flags = new_flags;
+					entry->dirt = 1;
+					hfs_file_fix_mode(entry);
+				}
+			}
+			break;
+
+		case HFS_HDR_DATES:
+			set_dates(entry, inode, (hfs_u32 *)tmp);
+			break;
+
+		case HFS_HDR_FINFO:
+			entry->dirt = 1;
+			break;
+
+		case HFS_HDR_MACI:
+			if (entry->type == HFS_CDR_DIR) {
+				hfs_u16 new_flags = hfs_get_ns(tmp + 2);
+
+				if (entry->u.dir.flags != new_flags) {
+					entry->u.dir.flags = new_flags;
+					entry->dirt = 1;
+				}
+			} else {
+				hfs_u8 new_flags = tmp[3];
+				hfs_u8 changed = entry->u.file.flags^new_flags;
+				
+				if (changed) {
+					entry->u.file.flags = new_flags;
+					entry->dirt = 1;
+					if (changed & HFS_FIL_LOCK) {
+						hfs_file_fix_mode(entry);
+					}
+				}
+			}
+			break;
+
+		case HFS_HDR_DATA:
+		case HFS_HDR_RSRC:
+			if (left <= 0) {
+				if (!written) {
+					return left;
+				} else {
+					goto done;
+				}
+			} else if (fork->lsize > descr->length) {
+				descr->length = fork->lsize;
+			}
+			break;
+
+		case HFS_HDR_FNAME:	/* Can't rename a file this way */
+		default:
+			break;
+		}
+
+		count -= left;
+		written += left;
+		pos += left;
+		buf += left;
+	}
+
+	/* Skip any padding at the end */
+	if (count) {
+		written += count;
+		pos += count;
+	}
+		
+done:
+	*ppos = pos;
+	if (written > 0) {
+	        if (pos > inode->i_size)
+		        inode->i_size = pos;
+	        inode->i_mtime = inode->i_atime = CURRENT_TIME;
+	}
+	return written;
+}
+
+/*
+ * hdr_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * header files.  The purpose is to allocate or release blocks as needed
+ * to satisfy a change in file length.
+ */
+static void hdr_truncate(struct inode *inode)
+{
+	struct hfs_hdr_layout *layout;
+	size_t size = inode->i_size;
+	int lcv, last;
+
+	if (!HFS_I(inode)->layout) {
+		HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+	}
+	layout = HFS_I(inode)->layout;
+
+	last = layout->entries - 1;
+	for (lcv = 0; lcv <= last; ++lcv) {
+		struct hfs_hdr_descr *descr = layout->order[lcv];
+		struct hfs_fork *fork;
+		hfs_u32 offset;
+
+		if (!descr) {
+			break;
+		}
+
+		if (descr->id == HFS_HDR_RSRC) {
+			fork = &HFS_I(inode)->entry->u.file.rsrc_fork;
+#if 0
+/* Can't yet truncate the data fork via a header file, since there is the
+ * possibility to truncate via the data file, and the only locking is at
+ * the inode level.
+ */
+		} else if (descr->id == HFS_HDR_DATA) {
+			fork = &HFS_I(inode)->entry->u.file.data_fork;
+#endif
+		} else {
+			continue;
+		}
+
+		offset = descr->offset;
+
+		if ((lcv != last) && ((offset + descr->length) <= size)) {
+			continue;
+		}
+
+		if (offset < size) {
+			descr->length = size - offset;
+		} else {
+			descr->length = 0;
+		}
+		if (fork->lsize != descr->length) {
+			fork->lsize = descr->length;
+			hfs_extent_adj(fork);
+		}
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/hfs.h linux/fs/hfs/hfs.h
--- v2.1.77/linux/fs/hfs/hfs.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/hfs.h	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,490 @@
+/* 
+ * linux/fs/hfs/hfs.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_H
+#define _HFS_H
+
+#include <linux/hfs_sysdep.h>
+
+#define HFS_NEW(X)	((X) = hfs_malloc(sizeof(*(X))))
+#define HFS_DELETE(X)	{ hfs_free((X), sizeof(*(X))); (X) = NULL; }
+ 
+/* offsets to various blocks */
+#define HFS_DD_BLK		0 /* Driver Descriptor block */
+#define HFS_PMAP_BLK		1 /* First block of partition map */
+#define HFS_MDB_BLK		2 /* Block (w/i partition) of MDB */
+
+/* magic numbers for various disk blocks */
+#define HFS_DRVR_DESC_MAGIC	0x4552 /* "ER": driver descriptor map */
+#define HFS_OLD_PMAP_MAGIC	0x5453 /* "TS": old-type partition map */
+#define HFS_NEW_PMAP_MAGIC	0x504D /* "PM": new-type partition map */
+#define HFS_SUPER_MAGIC		0x4244 /* "BD": HFS MDB (super block) */
+#define HFS_MFS_SUPER_MAGIC	0xD2D7 /* MFS MDB (super block) */
+
+/* magic numbers for various internal structures */
+#define HFS_FILE_MAGIC		0x4801
+#define HFS_DIR_MAGIC		0x4802
+#define HFS_MDB_MAGIC		0x4803
+#define HFS_EXT_MAGIC		0x4804 /* XXX currently unused */
+#define HFS_BREC_MAGIC		0x4811 /* XXX currently unused */
+#define HFS_BTREE_MAGIC		0x4812
+#define HFS_BNODE_MAGIC		0x4813
+
+/* various FIXED size parameters */
+#define HFS_SECTOR_SIZE		512    /* size of an HFS sector */
+#define HFS_SECTOR_SIZE_BITS	9      /* log_2(HFS_SECTOR_SIZE) */
+#define HFS_NAMELEN		31     /* maximum length of an HFS filename */
+#define HFS_NAMEMAX		(3*31) /* max size of ENCODED filename */
+#define HFS_BM_MAXBLOCKS	(16)   /* max number of bitmap blocks */
+#define HFS_BM_BPB (8*HFS_SECTOR_SIZE) /* number of bits per bitmap block */
+#define HFS_MAX_VALENCE		32767U
+#define HFS_FORK_MAX		(0x7FFFFFFF)
+
+/* Meanings of the drAtrb field of the MDB,
+ * Reference: _Inside Macintosh: Files_ p. 2-61
+ */
+#define HFS_SB_ATTRIB_HLOCK 0x0080
+#define HFS_SB_ATTRIB_CLEAN 0x0100
+#define HFS_SB_ATTRIB_SPARED 0x0200
+#define HFS_SB_ATTRIB_SLOCK 0x8000
+
+/* 2**16 - 1 */
+#define HFS_USHRT_MAX	65535
+
+/* Some special File ID numbers */
+#define HFS_POR_CNID	1	/* Parent Of the Root */
+#define HFS_ROOT_CNID	2	/* ROOT directory */
+#define HFS_EXT_CNID	3	/* EXTents B-tree */
+#define HFS_CAT_CNID	4	/* CATalog B-tree */
+#define HFS_BAD_CNID	5	/* BAD blocks file */
+
+/* values for hfs_cat_rec.cdrType */
+#define HFS_CDR_DIR	0x01
+#define HFS_CDR_FIL	0x02
+#define HFS_CDR_THD	0x03
+#define HFS_CDR_FTH	0x04
+
+/* legal values for hfs_ext_key.FkType and hfs_file.fork */
+#define HFS_FK_DATA	0x00
+#define HFS_FK_RSRC	0xFF
+
+/* bits in hfs_fil_entry.Flags */
+#define HFS_FIL_LOCK	0x01
+#define HFS_FIL_THD	0x02
+#define HFS_FIL_USED	0x80
+
+/* Access types used when requesting access to a B-node */
+#define HFS_LOCK_NONE	0x0000	/* Illegal */
+#define HFS_LOCK_READ	0x0001	/* read-only access */
+#define HFS_LOCK_RESRV	0x0002	/* might potentially modify */
+#define HFS_LOCK_WRITE	0x0003	/* will modify now (exclusive access) */
+#define HFS_LOCK_MASK	0x000f
+
+/* Flags field of the hfs_path_elem */
+#define HFS_BPATH_FIRST		0x0100
+#define HFS_BPATH_OVERFLOW	0x0200
+#define HFS_BPATH_UNDERFLOW	0x0400
+#define HFS_BPATH_MASK		0x0f00
+
+/* Flags for hfs_bfind() */
+#define HFS_BFIND_EXACT		0x0010
+#define HFS_BFIND_LOCK		0x0020
+
+/* Modes for hfs_bfind() */
+#define HFS_BFIND_WRITE   (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BFIND_LOCK)
+#define HFS_BFIND_READ_EQ (HFS_LOCK_READ|HFS_BFIND_EXACT)
+#define HFS_BFIND_READ_LE (HFS_LOCK_READ)
+#define HFS_BFIND_INSERT  (HFS_LOCK_RESRV|HFS_BPATH_FIRST|HFS_BPATH_OVERFLOW)
+#define HFS_BFIND_DELETE \
+	 (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BPATH_FIRST|HFS_BPATH_UNDERFLOW)
+
+/*======== HFS structures as they appear on the disk ========*/
+
+/* Pascal-style string of up to 31 characters */
+struct hfs_name {
+	hfs_byte_t	Len;
+	hfs_byte_t	Name[31];
+};
+
+typedef struct {
+	hfs_word_t	v;
+	hfs_word_t	h;
+} hfs_point_t;
+
+typedef struct {
+	hfs_word_t	top;
+	hfs_word_t	left;
+	hfs_word_t	bottom;
+	hfs_word_t	right;
+} hfs_rect_t;
+
+typedef struct {
+	hfs_lword_t	 fdType;
+	hfs_lword_t	 fdCreator;
+	hfs_word_t	 fdFlags;
+	hfs_point_t	 fdLocation;
+	hfs_word_t	 fdFldr;
+} hfs_finfo_t;
+
+typedef struct {
+	hfs_word_t	fdIconID;
+	hfs_byte_t	fdUnused[8];
+	hfs_word_t	fdComment;
+	hfs_lword_t	fdPutAway;
+} hfs_fxinfo_t;
+
+typedef struct {
+	hfs_rect_t	frRect;
+	hfs_word_t	frFlags;
+	hfs_point_t	frLocation;
+	hfs_word_t	frView;
+} hfs_dinfo_t;
+
+typedef struct {
+	hfs_point_t	frScroll;
+	hfs_lword_t	frOpenChain;
+	hfs_word_t	frUnused;
+	hfs_word_t	frComment;
+	hfs_lword_t	frPutAway;
+} hfs_dxinfo_t;
+
+union hfs_finder_info {
+	struct {
+		hfs_finfo_t	finfo;
+		hfs_fxinfo_t	fxinfo;
+	} file;
+	struct {
+		hfs_dinfo_t	dinfo;
+		hfs_dxinfo_t	dxinfo;
+	} dir;
+};
+
+/* A btree record key on disk */
+struct hfs_bkey {
+	hfs_byte_t	KeyLen;		/* number of bytes in the key */
+	hfs_byte_t	value[1];	/* (KeyLen) bytes of key */
+};
+
+/* Cast to a pointer to a generic bkey */
+#define	HFS_BKEY(X)	(((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
+
+/* The key used in the catalog b-tree: */
+struct hfs_cat_key {
+	hfs_byte_t	KeyLen;	/* number of bytes in the key */
+	hfs_byte_t	Resrv1;	/* padding */
+	hfs_lword_t	ParID;	/* CNID of the parent dir */
+	struct hfs_name	CName;	/* The filename of the entry */
+};
+
+/* The key used in the extents b-tree: */
+struct hfs_ext_key {
+	hfs_byte_t	KeyLen;	/* number of bytes in the key */
+	hfs_byte_t	FkType;	/* HFS_FK_{DATA,RSRC} */
+	hfs_lword_t	FNum;	/* The File ID of the file */
+	hfs_word_t	FABN;	/* allocation blocks number*/
+};
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * struct hfs_mdb
+ *
+ * The fields from the MDB of an HFS filesystem
+ */
+struct hfs_mdb {
+	int			magic;		/* A magic number */
+	unsigned char		vname[28];	/* The volume name */
+	hfs_sysmdb		sys_mdb;	/* superblock */
+	hfs_buffer		buf;		/* The hfs_buffer
+						   holding the real
+						   superblock (aka VIB
+						   or MDB) */
+	hfs_buffer		alt_buf;	/* The hfs_buffer holding
+						   the alternate superblock */
+	hfs_buffer		bitmap[16];	/* The hfs_buffer holding the
+						   allocation bitmap */
+	struct hfs_btree *	ext_tree;	/* Information about
+						   the extents b-tree */
+	struct hfs_btree *	cat_tree;	/* Information about
+						   the catalog b-tree */
+	hfs_u32			file_count;	/* The number of
+						   regular files in
+						   the filesystem */
+	hfs_u32			dir_count;	/* The number of
+						   directories in the
+						   filesystem */
+	hfs_u32			next_id;	/* The next available
+						   file id number */
+	hfs_u32			clumpablks;	/* The number of allocation
+						   blocks to try to add when
+						   extending a file */
+	hfs_u32			write_count;	/* The number of MDB
+						   writes (a sort of
+						   version number) */
+	hfs_u32			fs_start;	/* The first 512-byte
+						   block represented
+						   in the bitmap */
+	hfs_u32			create_date;	/* In network byte-order */
+	hfs_u32			modify_date;	/* In network byte-order */
+	hfs_u32			backup_date;	/* In network byte-order */
+	hfs_u16			root_files;	/* The number of
+						   regular
+						   (non-directory)
+						   files in the root
+						   directory */
+	hfs_u16			root_dirs;	/* The number of
+						   directories in the
+						   root directory */
+	hfs_u16			fs_ablocks;	/* The number of
+						   allocation blocks
+						   in the filesystem */
+	hfs_u16			free_ablocks;	/* The number of unused
+						   allocation blocks
+						   in the filesystem */
+	hfs_u16			alloc_blksz;	/* The number of
+						   512-byte blocks per
+						   "allocation block" */
+	hfs_u16			attrib;		/* Attribute word */
+	hfs_wait_queue		rename_wait;
+	int			rename_lock;
+	hfs_wait_queue		bitmap_wait;
+	int			bitmap_lock;
+};
+
+/*
+ * struct hfs_extent
+ *
+ * The offset to allocation block mapping for a given file is
+ * contained in a series of these structures.  Each (struct
+ * hfs_extent) records up to three runs of contiguous allocation
+ * blocks.  An allocation block is a contiguous group of physical
+ * blocks.
+ */
+struct hfs_extent {
+	int		   magic;     /* A magic number */
+	unsigned short	   start;     /* Where in the file this record
+					 begins (in allocation blocks) */
+	unsigned short	   end;	      /* Where in the file this record
+					 ends (in allocation blocks) */
+	unsigned short	   block[3];  /* The allocation block on disk which
+					 begins this extent */
+	unsigned short	   length[3]; /* The number of allocation blocks
+					 in this extent */
+	struct hfs_extent  *next;     /* Next extent record for this file */
+	struct hfs_extent  *prev;     /* Previous extent record for this file */
+	int		   count;     /* Number of times it is used */
+};
+
+/*
+ * struct hfs_dir 
+ *
+ * This structure holds information specific
+ * to a directory in an HFS filesystem.
+ */
+struct hfs_dir {		
+	int		magic;		/* A magic number */
+	hfs_u16		flags;
+	hfs_u16		dirs;		/* Number of directories in this one */
+	hfs_u16		files;		/* Number of files in this directory */
+	int		readers;
+	hfs_wait_queue	read_wait;
+	int		writers;
+	hfs_wait_queue	write_wait;
+};
+
+/*
+ * struct hfs_fork
+ *
+ * This structure holds the information
+ * specific to a single fork of a file.
+ */
+struct hfs_fork {
+	struct hfs_cat_entry	*entry;    /* The file this fork is part of */
+	struct hfs_extent	first;     /* The first extent record for
+						 this fork */
+	struct hfs_extent	*cache;    /* The most-recently accessed
+						 extent record for this fork */
+	hfs_u32			lsize;     /* The logical size in bytes */
+	hfs_u32			psize;     /* The phys size (512-byte blocks) */
+        hfs_u8			fork;      /* Which fork is this? */
+};
+
+/*
+ * struct hfs_file 
+ *
+ * This structure holds information specific
+ * to a file in an HFS filesystem.
+ */
+struct hfs_file {
+	int		   magic;
+	struct hfs_fork    data_fork;
+	struct hfs_fork    rsrc_fork;
+	hfs_u16		   clumpablks;
+	hfs_u8		   flags;
+};
+
+/*
+ * struct hfs_file 
+ *
+ * This structure holds information about a
+ * file or directory in an HFS filesystem.
+ *
+ * 'wait' must remain 1st and 'next' 2nd since we do some pointer arithmetic.
+ */
+struct hfs_cat_entry {
+	hfs_wait_queue		wait;
+	struct hfs_cat_entry	*next;
+	struct hfs_cat_entry	*prev;
+	struct hfs_cat_entry	*hash_next;
+	struct hfs_cat_entry	*hash_prev;
+	struct hfs_mdb		*mdb;
+	hfs_sysentry		sys_entry;
+	struct hfs_cat_key	key;
+	union hfs_finder_info	info;
+	hfs_u32			cnid;		/* In network byte-order */
+	hfs_u32			create_date;	/* In network byte-order */
+	hfs_u32			modify_date;	/* In network byte-order */
+	hfs_u32			backup_date;	/* In network byte-order */
+	unsigned short		count;
+	unsigned char		lock;
+	unsigned char		dirt;
+	unsigned char		key_dirt;
+	unsigned char		deleted;
+	hfs_u8			type;
+	union {
+		struct hfs_dir	dir;
+		struct hfs_file file;
+	} u;
+};
+
+/* 
+ * struct hfs_bnode_ref
+ *
+ * A pointer to a (struct hfs_bnode) and the type of lock held on it.
+ */
+struct hfs_bnode_ref {
+        struct hfs_bnode *bn;
+        int lock_type;
+};
+
+/*
+ * struct hfs_belem
+ *
+ * An element of the path from the root of a B-tree to a leaf.
+ * Includes the reference to a (struct hfs_bnode), the index of
+ * the appropriate record in that node, and some flags.
+ */
+struct hfs_belem {
+	struct hfs_bnode_ref	bnr;
+	int			record;
+	int			flags;
+};
+
+/*
+ * struct hfs_brec
+ *
+ * The structure returned by hfs_bfind() to describe the requested record.
+ */
+struct hfs_brec {
+	int			keep_flags;
+	struct hfs_btree	*tree;
+	struct hfs_belem	*top;
+	struct hfs_belem	*bottom;
+	struct hfs_belem	elem[9];
+	struct hfs_bkey		*key;
+	void			*data;	/* The actual data */
+};
+
+/*================ Function prototypes ================*/
+
+/* bdelete.c */
+extern int hfs_bdelete(struct hfs_btree *, const struct hfs_bkey *);
+
+/* bfind.c */
+extern void hfs_brec_relse(struct hfs_brec *, struct hfs_belem *);
+extern int hfs_bsucc(struct hfs_brec *, int);
+extern int hfs_bfind(struct hfs_brec *, struct hfs_btree *,
+		     const struct hfs_bkey *, int);
+ 
+/* binsert.c */
+extern int hfs_binsert(struct hfs_btree *, const struct hfs_bkey *,
+		       const void *, hfs_u16);
+
+/* bitmap.c */
+extern hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *, hfs_u16);
+extern hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *, hfs_u16 *);
+extern int hfs_set_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+extern int hfs_clear_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+
+/* bitops.c */
+extern hfs_u32 hfs_find_zero_bit(const hfs_u32 *, hfs_u32, hfs_u32);
+extern hfs_u32 hfs_count_zero_bits(const hfs_u32 *, hfs_u32, hfs_u32);
+
+/* btree.c */
+extern struct hfs_btree *hfs_btree_init(struct hfs_mdb *, ino_t,
+				        hfs_byte_t *, hfs_u32, hfs_u32);
+extern void hfs_btree_free(struct hfs_btree *);
+extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t);
+
+/* catalog.c */
+extern void hfs_cat_put(struct hfs_cat_entry *);
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+					 const struct hfs_cat_key *);
+
+extern void hfs_cat_invalidate(struct hfs_mdb *);
+extern void hfs_cat_commit(struct hfs_mdb *);
+extern void hfs_cat_free(void);
+
+extern int hfs_cat_compare(const struct hfs_cat_key *,
+			   const struct hfs_cat_key *);
+extern void hfs_cat_build_key(hfs_u32, const struct hfs_name *,
+			      struct hfs_cat_key *);
+extern struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *);
+
+extern int hfs_cat_open(struct hfs_cat_entry *, struct hfs_brec *);
+extern int hfs_cat_next(struct hfs_cat_entry *, struct hfs_brec *,
+			hfs_u16, hfs_u32 *, hfs_u8 *);
+extern void hfs_cat_close(struct hfs_cat_entry *, struct hfs_brec *);
+
+extern int hfs_cat_create(struct hfs_cat_entry *, struct hfs_cat_key *,
+			  hfs_u8, hfs_u32, hfs_u32, struct hfs_cat_entry **);
+extern int hfs_cat_mkdir(struct hfs_cat_entry *, struct hfs_cat_key *,
+			 struct hfs_cat_entry **);
+extern int hfs_cat_delete(struct hfs_cat_entry *, struct hfs_cat_entry *, int);
+extern int hfs_cat_move(struct hfs_cat_entry *, struct hfs_cat_entry *,
+			struct hfs_cat_entry *, struct hfs_cat_key *,
+			struct hfs_cat_entry **);
+
+/* extent.c */
+extern int hfs_ext_compare(const struct hfs_ext_key *,
+			   const struct hfs_ext_key *);
+extern void hfs_extent_in(struct hfs_fork *, const hfs_byte_t *);
+extern void hfs_extent_out(const struct hfs_fork *, hfs_byte_t *);
+extern int hfs_extent_map(struct hfs_fork *, int, int);
+extern void hfs_extent_adj(struct hfs_fork *);
+extern void hfs_extent_free(struct hfs_fork *);
+
+/* mdb.c */
+extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, int, hfs_s32);
+extern void hfs_mdb_commit(struct hfs_mdb *, int);
+extern void hfs_mdb_put(struct hfs_mdb *, int);
+
+/* part_tbl.c */
+extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
+
+/* string.c */
+extern unsigned int hfs_strhash(const struct hfs_name *);
+extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *);
+extern int hfs_streq(const struct hfs_name *, const struct hfs_name *);
+extern void hfs_tolower(unsigned char *, int);
+
+/* sysdep.c */
+extern int hfs_prune_entry(struct hfs_cat_entry *);
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/hfs_btree.h linux/fs/hfs/hfs_btree.h
--- v2.1.77/linux/fs/hfs/hfs_btree.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/hfs_btree.h	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hfs/hfs_btree.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the declarations of the private B-tree
+ * structures and functions.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_BTREE_H
+#define _HFS_BTREE_H
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+/* The stickiness of a (struct hfs_bnode) */
+#define HFS_NOT_STICKY	0
+#define HFS_STICKY	1
+
+/* The number of hash buckets in a B-tree's bnode cache */
+#define HFS_CACHELEN	17	/* primes are best? */
+
+/*
+ * Legal values for the 'ndType' field of a (struct NodeDescriptor)
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-65
+ */
+#define ndIndxNode	0x00	/* An internal (index) node */
+#define ndHdrNode	0x01	/* The tree header node (node 0) */
+#define ndMapNode	0x02	/* Holds part of the bitmap of used nodes */
+#define ndLeafNode	0xFF	/* A leaf (ndNHeight==1) node */
+
+/*================ Function-like macros ================*/
+
+/* Access the cache slot which should contain the desired node */
+#define bhash(tree, node) ((tree)->cache[(node) % HFS_CACHELEN])
+
+/* round up to multiple of sizeof(hfs_u16) */
+#define ROUND(X) ((X + sizeof(hfs_u16) - 1) & ~(sizeof(hfs_u16)-1))
+
+/* Refer to the (base-1) array of offsets in a bnode */
+#define RECTBL(X,N) \
+	(((hfs_u16 *)(hfs_buffer_data((X)->buf)+HFS_SECTOR_SIZE))-(N))
+
+/*================ Private data types ================*/
+
+/*
+ * struct BTHdrRec
+ *
+ * The B-tree header record
+ *
+ * This data structure is stored in the first node (512-byte block) of
+ * each B-tree file.  It contains important information about the
+ * B-tree.  Most fields vary over the life of the tree and are
+ * indicated by a 'V' in the comments.	The other fields are fixed for
+ * the life of the tree and are indicated by a 'F'.
+ *
+ * Reference: _Inside Macintosh: Files_ pp. 2-68 through 2-69 */
+struct BTHdrRec {
+	hfs_word_t  bthDepth;	/* (V) The number of levels in this B-tree */
+	hfs_lword_t bthRoot;	/* (V) The node number of the root node */
+	hfs_lword_t bthNRecs;	/* (V) The number of leaf records */
+	hfs_lword_t bthFNode;	/* (V) The number of the first leaf node */
+	hfs_lword_t bthLNode;	/* (V) The number of the last leaf node */
+	hfs_word_t  bthNodeSize;	/* (F) The number of bytes in a node (=512) */
+	hfs_word_t  bthKeyLen;	/* (F) The length of a key in an index node */
+	hfs_lword_t bthNNodes;	/* (V) The total number of nodes */
+	hfs_lword_t bthFree;	/* (V) The number of unused nodes */
+	hfs_byte_t  bthResv[76];	/* Reserved */
+};
+
+/*
+ * struct NodeDescriptor
+ *
+ * The B-tree node descriptor.
+ *
+ * This structure begins each node in the B-tree file.	It contains
+ * important information about the node's contents.  'V' and 'F' in
+ * the comments indicate fields that are variable or fixed over the
+ * life of a node, where the 'life' of a node is defined as the period
+ * between leaving and reentering the free pool.
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-64
+ */
+struct NodeDescriptor {
+	hfs_lword_t ndFLink;	/* (V) Number of the next node at this level */
+	hfs_lword_t ndBLink;	/* (V) Number of the prev node at this level */
+	hfs_byte_t  ndType;	/* (F) The type of node */
+	hfs_byte_t  ndNHeight;	/* (F) The level of this node (leaves=1) */
+	hfs_word_t  ndNRecs;	/* (V) The number of records in this node */
+	hfs_word_t  ndResv2;	/* Reserved */
+};
+
+/*
+ * typedef hfs_cmpfn
+ *
+ * The type 'hfs_cmpfn' is a comparison function taking 2 keys and
+ * returning a positive, negative or zero integer according to the
+ * ordering of the two keys (just like strcmp() does for strings).
+ */
+typedef int (*hfs_cmpfn)(const void *, const void *);
+
+/*
+ * struct hfs_bnode
+ *
+ * An in-core B-tree node
+ *
+ * This structure holds information from the NodeDescriptor in native
+ * byte-order, a pointer to the buffer which contains the actual
+ * node and fields necessary for locking access to the node during
+ * updates.  The use of the locking fields is explained with the
+ * locking functions.
+ */
+struct hfs_bnode {
+	int		    magic;   /* Magic number to guard against
+					wild pointers */
+	hfs_buffer	    buf;     /* The buffer containing the
+					actual node */
+	struct hfs_btree    *tree;   /* The tree to which this node
+					belongs */
+	struct hfs_bnode    *prev;   /* Next node in this hash bucket */
+	struct hfs_bnode    *next;   /* Previous node in this hash
+					bucket */
+	int		    sticky;  /* Boolean: non-zero means keep
+					this node in-core (set for
+					root and head) */
+	hfs_u32		    node;    /* Node number */
+	/* locking related fields: */
+	hfs_wait_queue	    wqueue;  /* Wait queue for write access */
+	hfs_wait_queue	    rqueue;  /* Wait queue for read or reserve
+					access */
+	int		    count;   /* Number of processes accessing
+					this node */
+	int		    resrv;   /* Boolean, true means a process
+					had placed a 'reservation' on
+					this node */
+	int		    lock;    /* Boolean, true means some
+					process has exclusive access,
+					so KEEP OUT */
+	/* fields from the NodeDescriptor in native byte-order: */
+	hfs_u32		    ndFLink;
+	hfs_u32		    ndBLink;
+	hfs_u16		    ndNRecs;
+	hfs_u8		    ndType;
+	hfs_u8		    ndNHeight;
+};
+
+/*
+ * struct hfs_btree
+ *
+ * An in-core B-tree.
+ *
+ * This structure holds information from the BTHdrRec, MDB
+ * (superblock) and other information needed to work with the B-tree.
+ */
+struct hfs_btree {
+	int			magic;	       /* Magic number to
+						  guard against wild
+						  pointers */
+	hfs_cmpfn		compare;       /* Comparison function
+						  for this tree */
+	struct hfs_bnode	head;	       /* in-core copy of node 0 */
+	struct hfs_bnode	*root;	       /* Pointer to the in-core
+						  copy of the root node */
+	hfs_sysmdb		sys_mdb;       /* The "device" holding
+						  the filesystem */
+	int			reserved;      /* bnodes claimed but
+						  not yet used */
+	struct hfs_bnode		       /* The bnode cache */
+				*cache[HFS_CACHELEN];
+	struct hfs_cat_entry	entry;	       /* Fake catalog entry */
+	int			lock;
+	hfs_wait_queue		wait;
+	int			dirt;
+	/* Fields from the BTHdrRec in native byte-order: */
+	hfs_u32			bthRoot;
+	hfs_u32			bthNRecs;
+	hfs_u32			bthFNode;
+	hfs_u32			bthLNode;
+	hfs_u32			bthNNodes;
+	hfs_u32			bthFree;
+	hfs_u16			bthKeyLen;
+	hfs_u16			bthDepth;
+};
+
+/*================ Global functions ================*/
+
+/* Convert a (struct hfs_bnode *) and an index to the value of the
+   n-th offset in the bnode (N >= 1) to the offset */
+extern inline hfs_u16 bnode_offset(const struct hfs_bnode *bnode, int n)
+{ return hfs_get_hs(RECTBL(bnode,n)); }
+
+/* Convert a (struct hfs_bnode *) and an index to the size of the
+   n-th record in the bnode (N >= 1) */
+extern inline hfs_u16 bnode_rsize(const struct hfs_bnode *bnode, int n)
+{ return bnode_offset(bnode, n+1) - bnode_offset(bnode, n); }
+
+/* Convert a (struct hfs_bnode *) to the offset of the empty part */
+extern inline hfs_u16 bnode_end(const struct hfs_bnode *bnode)
+{ return bnode_offset(bnode, bnode->ndNRecs + 1); }
+
+/* Convert a (struct hfs_bnode *) to the number of free bytes it contains */
+extern inline hfs_u16 bnode_freespace(const struct hfs_bnode *bnode)
+{ return HFS_SECTOR_SIZE - bnode_end(bnode)
+	  - (bnode->ndNRecs + 1)*sizeof(hfs_u16); }
+
+/* Convert a (struct hfs_bnode *) X and an index N to
+   the address of the record N in the bnode (N >= 1) */
+extern inline void *bnode_datastart(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf)+sizeof(struct NodeDescriptor)); }
+
+/* Convert a (struct hfs_bnode *) to the address of the empty part */
+extern inline void *bnode_dataend(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_end(bnode)); }
+
+/* Convert various pointers to address of record's key */
+extern inline void *bnode_key(const struct hfs_bnode *bnode, int n)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_offset(bnode, n)); }
+extern inline void *belem_key(const struct hfs_belem *elem)
+{ return bnode_key(elem->bnr.bn, elem->record); }
+extern inline void *brec_key(const struct hfs_brec *brec)
+{ return belem_key(brec->bottom); }
+
+/* Convert various pointers to the address of a record */
+extern inline void *bkey_record(const struct hfs_bkey *key)
+{ return (void *)key + ROUND(key->KeyLen + 1); }
+extern inline void *bnode_record(const struct hfs_bnode *bnode, int n)
+{ return bkey_record(bnode_key(bnode, n)); }
+extern inline void *belem_record(const struct hfs_belem *elem)
+{ return bkey_record(belem_key(elem)); }
+extern inline void *brec_record(const struct hfs_brec *brec)
+{ return bkey_record(brec_key(brec)); }
+
+/*================ Function Prototypes ================*/
+
+/* balloc.c */
+extern int hfs_bnode_bitop(struct hfs_btree *, hfs_u32, int);
+extern struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *);
+extern int hfs_bnode_free(struct hfs_bnode_ref *);
+extern void hfs_btree_extend(struct hfs_btree *);
+
+/* bins_del.c */
+extern void hfs_bnode_update_key(struct hfs_brec *, struct hfs_belem *,
+				 struct hfs_bnode *, int);
+extern void hfs_bnode_shift_right(struct hfs_bnode *, struct hfs_bnode *, int);
+extern void hfs_bnode_shift_left(struct hfs_bnode *, struct hfs_bnode *, int);
+extern int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec);
+
+/* bnode.c */
+extern void hfs_bnode_read(struct hfs_bnode *, struct hfs_btree *,
+			   hfs_u32, int);
+extern void hfs_bnode_relse(struct hfs_bnode_ref *);
+extern struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *, hfs_u32, int);
+extern void hfs_bnode_lock(struct hfs_bnode_ref *, int);
+extern void hfs_bnode_delete(struct hfs_bnode *);
+extern void hfs_bnode_commit(struct hfs_bnode *);
+
+/* brec.c */
+extern void hfs_brec_lock(struct hfs_brec *, struct hfs_belem *);
+extern struct hfs_belem *hfs_brec_init(struct hfs_brec *, struct hfs_btree *,
+				       int);
+extern struct hfs_belem *hfs_brec_next(struct hfs_brec *);
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/inode.c linux/fs/hfs/inode.c
--- v2.1.77/linux/fs/hfs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/inode.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,423 @@
+/*
+ * linux/fs/hfs/inode.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains inode-related functions which do not depend on
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Variable-like macros ================*/
+
+#define HFS_VALID_MODE_BITS  (S_IFREG | S_IFDIR | S_IRWXUGO)
+
+/*================ File-local functions ================*/
+
+/*
+ * init_file_inode()
+ *
+ * Given an HFS catalog entry initialize an inode for a file.
+ */
+static void init_file_inode(struct inode *inode, hfs_u8 fork)
+{
+	struct hfs_fork *fk;
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+	if (!IS_NOEXEC(inode) && (fork == HFS_FK_DATA)) {
+		inode->i_mode = S_IRWXUGO | S_IFREG;
+	} else {
+		inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+	}
+
+	if (fork == HFS_FK_DATA) {
+		hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType);
+
+		fk = &entry->u.file.data_fork;
+		HFS_I(inode)->convert =
+			((HFS_SB(inode->i_sb)->s_conv == 't') ||
+			 ((HFS_SB(inode->i_sb)->s_conv == 'a') &&
+			  ((type == htonl(0x54455854)) ||   /* "TEXT" */
+			   (type == htonl(0x7474726f)))));  /* "ttro" */
+	} else {
+		fk = &entry->u.file.rsrc_fork;
+		HFS_I(inode)->convert = 0;
+	}
+	HFS_I(inode)->fork = fk;
+	inode->i_size = fk->lsize;
+	inode->i_blocks = fk->psize;
+	inode->i_nlink = 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_put_inode()
+ *
+ * This is the put_inode() entry in the super_operations for HFS
+ * filesystems.  The purpose is to perform any filesystem-dependent 
+ * cleanup necessary when the use-count of an inode falls to zero.
+ */
+void hfs_put_inode(struct inode * inode)
+{
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+	entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
+	hfs_cat_put(entry);
+
+	if (inode->i_count == 1) {
+		struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
+		if (tmp) {
+			HFS_I(inode)->layout = NULL;
+			HFS_DELETE(tmp);
+		}
+	}
+}
+
+/*
+ * hfs_notify_change()
+ *
+ * Based very closely on fs/msdos/inode.c by Werner Almesberger
+ *
+ * This is the notify_change() field in the super_operations structure
+ * for HFS file systems.  The purpose is to take that changes made to
+ * an inode and apply then in a filesystem-dependent manner.  In this
+ * case the process has a few of tasks to do:
+ *  1) prevent changes to the i_uid and i_gid fields.
+ *  2) map file permissions to the closest allowable permissions
+ *  3) Since multiple Linux files can share the same on-disk inode under
+ *     HFS (for instance the data and resource forks of a file) a change
+ *     to permissions must be applied to all other in-core inodes which 
+ *     correspond to the same HFS file.
+ */
+int hfs_notify_change(struct inode * inode, struct iattr * attr)
+{
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+	struct dentry **de = entry->sys_entry;
+	struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
+	int error, i;
+
+	error = inode_change_ok(inode, attr); /* basic permission checks */
+	if (error) {
+		/* Let netatalk's afpd think chmod() always succeeds */
+		if (hsb->s_afpd &&
+		    (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) {
+			return 0;
+		} else {
+			return error;
+		}
+	}
+
+	/* no uig/gid changes and limit which mode bits can be set */
+	if (((attr->ia_valid & ATTR_UID) && 
+	     (attr->ia_uid != hsb->s_uid)) ||
+	    ((attr->ia_valid & ATTR_GID) && 
+	     (attr->ia_gid != hsb->s_gid)) ||
+	    ((attr->ia_valid & ATTR_MODE) &&
+	     (((entry->type == HFS_CDR_DIR) &&
+	       (attr->ia_mode != inode->i_mode))||
+	      (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) {
+		return hsb->s_quiet ? 0 : error;
+	}
+	
+	if (entry->type == HFS_CDR_DIR) {
+		attr->ia_valid &= ~ATTR_MODE;
+	} else if (attr->ia_valid & ATTR_MODE) {
+		/* Only the 'w' bits can ever change and only all together. */
+		if (attr->ia_mode & S_IWUSR) {
+			attr->ia_mode = inode->i_mode | S_IWUGO;
+		} else {
+			attr->ia_mode = inode->i_mode & ~S_IWUGO;
+		}
+		attr->ia_mode &= ~hsb->s_umask;
+	}
+	inode_setattr(inode, attr);
+
+	/* We wouldn't want to mess with the sizes of the other fork */
+	attr->ia_valid &= ~ATTR_SIZE;
+
+	/* We must change all in-core inodes corresponding to this file. */
+	for (i = 0; i < 4; ++i) {
+	  if (de[i] && (de[i]->d_inode != inode)) {
+			inode_setattr(de[i]->d_inode, attr);
+	  }
+	}
+
+	/* Change the catalog entry if needed */
+	if (attr->ia_valid & ATTR_MTIME) {
+		entry->modify_date = hfs_u_to_mtime(inode->i_mtime);
+		entry->dirt = 1;
+	}
+	if (attr->ia_valid & ATTR_MODE) {
+		hfs_u8 new_flags;
+
+		if (inode->i_mode & S_IWUSR) {
+			new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+		} else {
+			new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+		}
+
+		if (new_flags != entry->u.file.flags) {
+			entry->u.file.flags = new_flags;
+			entry->dirt = 1;
+		}
+	}
+	/* size changes handled in hfs_extent_adj() */
+
+	return 0;
+}
+
+/*
+ * __hfs_iget()
+ *
+ * Given the MDB for a HFS filesystem, a 'key' and an 'entry' in
+ * the catalog B-tree and the 'type' of the desired file return the
+ * inode for that file/directory or NULL.  Note that 'type' indicates
+ * whether we want the actual file or directory, or the corresponding
+ * metadata (AppleDouble header file or CAP metadata file).
+ *
+ * In an ideal world we could call iget() and would not need this
+ * function.  However, since there is no way to even know the inode
+ * number until we've found the file/directory in the catalog B-tree
+ * that simply won't happen.
+ *
+ * The main idea here is to look in the catalog B-tree to get the
+ * vital info about the file or directory (including the file id which
+ * becomes the inode number) and then to call iget() and return the
+ * inode if it is complete.  If it is not then we use the catalog
+ * entry to fill in the missing info, by calling the appropriate
+ * 'fillin' function.  Note that these fillin functions are
+ * essentially hfs_*_read_inode() functions, but since there is no way
+ * to pass the catalog entry through iget() to such a read_inode()
+ * function, we have to call them after iget() returns an incomplete
+ * inode to us.	 This is pretty much the same problem faced in the NFS
+ * code, and pretty much the same solution. The SMB filesystem deals
+ * with this in a different way: by using the address of the
+ * kmalloc()'d space which holds the data as the inode number.
+ *
+ * XXX: Both this function and NFS's corresponding nfs_fhget() would
+ * benefit from a way to pass an additional (void *) through iget() to
+ * the VFS read_inode() function.
+ */
+struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
+		       struct dentry *dentry)
+{
+	struct dentry **sys_entry;
+	struct super_block *sb;
+	struct inode *inode;
+
+	if (!entry) {
+		return NULL;
+	}
+
+	/* If there are several processes all calling __iget() for
+	   the same inode then they will all get the same one back.
+	   The first one to return from __iget() will notice that the
+	   i_mode field of the inode is blank and KNOW that it is
+	   the first to return.  Therefore, it will set the appropriate
+	   'sys_entry' field in the entry and initialize the inode.
+	   All the initialization must be done without sleeping,
+	   or else other processes could end up using a partially
+	   initialized inode.				*/
+
+	sb = entry->mdb->sys_mdb;
+	sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)];
+
+	if (*sys_entry && (inode = (*sys_entry)->d_inode)) {
+		/* There is an existing inode for this file/dir.  Use it. */
+		++inode->i_count;
+		hfs_cat_put(entry);
+	} else if (!(inode = iget(sb, ntohl(entry->cnid) | type)) ||
+		   (inode->i_dev != sb->s_dev)) {
+		/* Something went wrong */
+		hfs_cat_put(entry);
+	} else if (inode->i_mode) {
+		/* The inode has been initialized by another process.
+		   Note that if hfs_put_inode() is sleeping in hfs_cat_put()
+		   then we still need to attach it to the entry. */
+		if (*sys_entry) {
+			hfs_cat_put(entry);
+		} else {
+			*sys_entry = dentry; /* cache dentry */
+		}
+	} else {
+		/* Initialize the inode */
+		struct hfs_sb_info *hsb = HFS_SB(sb);
+
+		inode->i_rdev = 0;
+		inode->i_ctime = inode->i_atime = inode->i_mtime =
+					hfs_m_to_utime(entry->modify_date);
+		inode->i_blksize = HFS_SECTOR_SIZE;
+		inode->i_uid = hsb->s_uid;
+		inode->i_gid = hsb->s_gid;
+
+		memset(HFS_I(inode), 0, sizeof(struct hfs_inode_info));
+		HFS_I(inode)->magic = HFS_INO_MAGIC;
+		HFS_I(inode)->entry = entry;
+
+		hsb->s_ifill(inode, type);
+		if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) &&
+		    (entry->u.file.flags & HFS_FIL_LOCK)) {
+			inode->i_mode &= ~S_IWUGO;
+		}
+		inode->i_mode &= ~hsb->s_umask;
+
+		if (!inode->i_mode) {
+			clear_inode(inode);
+			inode = NULL;
+		} 
+
+		*sys_entry = dentry; /* cache dentry */
+	}
+
+	return inode;
+}
+
+/*================ Scheme-specific functions ================*/
+
+/* 
+ * hfs_cap_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the CAP scheme.
+ */
+void hfs_cap_ifill(struct inode * inode, ino_t type)
+{
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+	HFS_I(inode)->d_drop_op = hfs_cap_drop_dentry;
+	if (type == HFS_CAP_FNDR) {
+		inode->i_size = sizeof(struct hfs_cap_info);
+		inode->i_blocks = 0;
+		inode->i_nlink = 1;
+		inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+		inode->i_op = &hfs_cap_info_inode_operations;
+	} else if (entry->type == HFS_CDR_FIL) {
+		init_file_inode(inode, (type == HFS_CAP_DATA) ?
+						HFS_FK_DATA : HFS_FK_RSRC);
+		inode->i_op = &hfs_file_inode_operations;
+	} else { /* Directory */
+		struct hfs_dir *hdir = &entry->u.dir;
+
+		inode->i_blocks = 0;
+		inode->i_size = hdir->files + hdir->dirs + 5;
+		HFS_I(inode)->dir_size = 1;
+		if (type == HFS_CAP_NDIR) {
+			inode->i_mode = S_IRWXUGO | S_IFDIR;
+			inode->i_nlink = hdir->dirs + 4;
+			inode->i_op = &hfs_cap_ndir_inode_operations;
+			HFS_I(inode)->file_type = HFS_CAP_NORM;
+		} else if (type == HFS_CAP_FDIR) {
+			inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+			inode->i_nlink = 2;
+			inode->i_op = &hfs_cap_fdir_inode_operations;
+			HFS_I(inode)->file_type = HFS_CAP_FNDR;
+		} else if (type == HFS_CAP_RDIR) {
+			inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+			inode->i_nlink = 2;
+			inode->i_op = &hfs_cap_rdir_inode_operations;
+			HFS_I(inode)->file_type = HFS_CAP_RSRC;
+		}
+	}
+}
+
+/* 
+ * hfs_dbl_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the AppleDouble
+ * scheme.
+ */
+void hfs_dbl_ifill(struct inode * inode, ino_t type)
+{
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+	HFS_I(inode)->d_drop_op = hfs_dbl_drop_dentry;
+	if (type == HFS_DBL_HDR) {
+		if (entry->type == HFS_CDR_FIL) {
+			init_file_inode(inode, HFS_FK_RSRC);
+			inode->i_size += HFS_DBL_HDR_LEN;
+			HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout;
+		} else {
+			inode->i_size = HFS_DBL_HDR_LEN;
+			inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+			inode->i_nlink = 1;
+			HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout;
+		}
+		inode->i_op = &hfs_hdr_inode_operations;
+	} else if (entry->type == HFS_CDR_FIL) {
+		init_file_inode(inode, HFS_FK_DATA);
+		inode->i_op = &hfs_file_inode_operations;
+	} else { /* Directory */
+		struct hfs_dir *hdir = &entry->u.dir;
+
+		inode->i_blocks = 0;
+		inode->i_nlink = hdir->dirs + 2;
+		inode->i_size = 3 + 2 * (hdir->dirs + hdir->files);
+		inode->i_mode = S_IRWXUGO | S_IFDIR;
+		inode->i_op = &hfs_dbl_dir_inode_operations;
+		HFS_I(inode)->file_type = HFS_DBL_NORM;
+		HFS_I(inode)->dir_size = 2;
+	}
+}
+
+/* 
+ * hfs_nat_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the Netatalk
+ * scheme.
+ */
+void hfs_nat_ifill(struct inode * inode, ino_t type)
+{
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+	HFS_I(inode)->d_drop_op = hfs_nat_drop_dentry;
+	if (type == HFS_NAT_HDR) {
+		if (entry->type == HFS_CDR_FIL) {
+			init_file_inode(inode, HFS_FK_RSRC);
+			inode->i_size += HFS_NAT_HDR_LEN;
+		} else {
+			inode->i_size = HFS_NAT_HDR_LEN;
+			inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+			inode->i_nlink = 1;
+		}
+		inode->i_op = &hfs_hdr_inode_operations;
+		HFS_I(inode)->default_layout = &hfs_nat_hdr_layout;
+	} else if (entry->type == HFS_CDR_FIL) {
+		init_file_inode(inode, HFS_FK_DATA);
+		inode->i_op = &hfs_file_inode_operations;
+	} else { /* Directory */
+		struct hfs_dir *hdir = &entry->u.dir;
+
+		inode->i_blocks = 0;
+		inode->i_size = hdir->files + hdir->dirs + 3;
+		inode->i_mode = S_IRWXUGO | S_IFDIR;
+		HFS_I(inode)->dir_size = 1;
+		if (type == HFS_NAT_NDIR) {
+			inode->i_nlink = hdir->dirs + 3;
+			inode->i_op = &hfs_nat_ndir_inode_operations;
+			HFS_I(inode)->file_type = HFS_NAT_NORM;
+		} else if (type == HFS_NAT_HDIR) {
+			inode->i_nlink = 2;
+			inode->i_op = &hfs_nat_hdir_inode_operations;
+			HFS_I(inode)->file_type = HFS_NAT_HDR;
+		}
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/mdb.c linux/fs/hfs/mdb.c
--- v2.1.77/linux/fs/hfs/mdb.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/mdb.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,298 @@
+/*
+ * linux/fs/hfs/mdb.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions for reading/writing the MDB.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/* 
+ * The HFS Master Directory Block (MDB).
+ *
+ * Also known as the Volume Information Block (VIB), this structure is
+ * the HFS equivalent of a superblock.
+ *
+ * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ */
+struct raw_mdb {
+	hfs_word_t	drSigWord;	/* Signature word indicating fs type */
+	hfs_lword_t	drCrDate;	/* fs creation date/time */
+	hfs_lword_t	drLsMod;	/* fs modification date/time */
+	hfs_word_t	drAtrb;		/* fs attributes */
+	hfs_word_t	drNmFls;	/* number of files in root directory */
+	hfs_word_t	drVBMSt;	/* location (in 512-byte blocks)
+					   of the volume bitmap */
+	hfs_word_t	drAllocPtr;	/* location (in allocation blocks)
+					   to begin next allocation search */
+	hfs_word_t	drNmAlBlks;	/* number of allocation blocks */
+	hfs_lword_t	drAlBlkSiz;	/* bytes in an allocation block */
+	hfs_lword_t	drClpSiz;	/* clumpsize, the number of bytes to
+					   allocate when extending a file */
+	hfs_word_t	drAlBlSt;	/* location (in 512-byte blocks)
+					   of the first allocation block */
+	hfs_lword_t	drNxtCNID;	/* CNID to assign to the next
+					   file or directory created */
+	hfs_word_t	drFreeBks;	/* number of free allocation blocks */
+	hfs_byte_t	drVN[28];	/* the volume label */
+	hfs_lword_t	drVolBkUp;	/* fs backup date/time */
+	hfs_word_t	drVSeqNum;	/* backup sequence number */
+	hfs_lword_t	drWrCnt;	/* fs write count */
+	hfs_lword_t	drXTClpSiz;	/* clumpsize for the extents B-tree */
+	hfs_lword_t	drCTClpSiz;	/* clumpsize for the catalog B-tree */
+	hfs_word_t	drNmRtDirs;	/* number of directories in
+					   the root directory */
+	hfs_lword_t	drFilCnt;	/* number of files in the fs */
+	hfs_lword_t	drDirCnt;	/* number of directories in the fs */
+	hfs_byte_t	drFndrInfo[32];	/* data used by the Finder */
+	hfs_word_t	drVCSize;	/* MacOS caching parameter */
+	hfs_word_t	drVCBMSize;	/* MacOS caching parameter */
+	hfs_word_t	drCtlCSize;	/* MacOS caching parameter */
+	hfs_lword_t	drXTFlSize;	/* bytes in the extents B-tree */
+	hfs_byte_t	drXTExtRec[12];	/* extents B-tree's first 3 extents */
+	hfs_lword_t	drCTFlSize;	/* bytes in the catalog B-tree */
+	hfs_byte_t	drCTExtRec[12];	/* catalog B-tree's first 3 extents */
+};
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mdb_get()
+ *
+ * Build the in-core MDB for a filesystem, including
+ * the B-trees and the volume bitmap.
+ */
+struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
+			    hfs_s32 part_start)
+{
+	struct hfs_mdb *mdb;
+	hfs_buffer buf;
+	struct raw_mdb *raw;
+	unsigned int bs, block;
+	int lcv, limit;
+	hfs_buffer *bmbuf;
+
+	if (!HFS_NEW(mdb)) {
+		hfs_warn("hfs_fs: out of memory\n");
+		return NULL;
+	}
+
+	memset(mdb, 0, sizeof(*mdb));
+	mdb->magic = HFS_MDB_MAGIC;
+	mdb->sys_mdb = sys_mdb;
+
+	/* See if this is an HFS filesystem */
+	buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
+	if (!hfs_buffer_ok(buf)) {
+		hfs_warn("hfs_fs: Unable to read superblock\n");
+		goto bail2;
+	}
+	raw = (struct raw_mdb *)hfs_buffer_data(buf);
+	if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
+		hfs_buffer_put(buf);
+		goto bail2;
+	}
+	mdb->buf = buf;
+
+	bs = hfs_get_hl(raw->drAlBlkSiz);
+	if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) {
+		hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
+		goto bail1;
+	}
+	mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
+
+	/* These parameters are read from the MDB, and never written */
+	mdb->create_date = hfs_get_hl(raw->drCrDate);
+	mdb->fs_ablocks  = hfs_get_hs(raw->drNmAlBlks);
+	mdb->fs_start    = hfs_get_hs(raw->drAlBlSt) + part_start;
+	mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
+	mdb->clumpablks  = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
+						 >> HFS_SECTOR_SIZE_BITS;
+	memcpy(mdb->vname, raw->drVN, 28);
+
+	/* These parameters are read from and written to the MDB */
+	mdb->modify_date  = hfs_get_nl(raw->drLsMod);
+	mdb->attrib       = hfs_get_ns(raw->drAtrb);
+	mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
+	mdb->next_id      = hfs_get_hl(raw->drNxtCNID);
+	mdb->write_count  = hfs_get_hl(raw->drWrCnt);
+	mdb->root_files   = hfs_get_hs(raw->drNmFls);
+	mdb->root_dirs    = hfs_get_hs(raw->drNmRtDirs);
+	mdb->file_count   = hfs_get_hl(raw->drFilCnt);
+	mdb->dir_count    = hfs_get_hl(raw->drDirCnt);
+	
+	/* TRY to get the alternate (backup) MDB */
+	lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
+	limit = lcv + mdb->alloc_blksz;
+	for (; lcv < limit; ++lcv) {
+		buf = hfs_buffer_get(sys_mdb, lcv, 1);
+		if (hfs_buffer_ok(buf)) {
+			struct raw_mdb *tmp =
+				(struct raw_mdb *)hfs_buffer_data(buf);
+
+			if (hfs_get_ns(tmp->drSigWord) ==
+						htons(HFS_SUPER_MAGIC)) {
+				mdb->alt_buf = buf;
+				break;
+			}
+			hfs_buffer_put(buf);
+		}
+	}
+	if (mdb->alt_buf == NULL) {
+		hfs_warn("hfs_fs: unable to locate alternate MDB\n");
+		hfs_warn("hfs_fs: continuing without an alternate MDB\n");
+	}
+
+	/* read in the bitmap */
+	block = hfs_get_hs(raw->drVBMSt) + part_start;
+	bmbuf = mdb->bitmap;
+	lcv = (mdb->fs_ablocks + 4095) / 4096;
+	for ( ; lcv; --lcv, ++bmbuf, ++block) {
+		if (!hfs_buffer_ok(*bmbuf =
+				   hfs_buffer_get(sys_mdb, block, 1))) {
+			hfs_warn("hfs_fs: unable to read volume bitmap\n");
+			goto bail1;
+		}
+	}
+
+	if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
+					     raw->drXTExtRec,
+					     hfs_get_hl(raw->drXTFlSize),
+					     hfs_get_hl(raw->drXTClpSiz))) ||
+	    !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
+					     raw->drCTExtRec,
+					     hfs_get_hl(raw->drCTFlSize),
+					     hfs_get_hl(raw->drCTClpSiz)))) {
+		hfs_warn("hfs_fs: unable to initialize data structures\n");
+		goto bail1;
+	}
+
+	if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) {
+		hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n");
+	} else if (!readonly) {
+		/* Mark the volume uncleanly unmounted in case we crash */
+		hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN),
+			   raw->drAtrb);
+		hfs_buffer_dirty(mdb->buf);
+		hfs_buffer_sync(mdb->buf);
+	}
+
+	return mdb;
+
+bail1:
+	hfs_mdb_put(mdb, readonly);
+bail2:
+	return NULL;
+}
+
+/*
+ * hfs_mdb_commit()
+ *
+ * Description:
+ *   This updates the MDB on disk (look also at hfs_write_super()).
+ *   It does not check, if the superblock has been modified, or
+ *   if the filesystem has been mounted read-only. It is mainly
+ *   called by hfs_write_super() and hfs_btree_extend().
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   int backup;
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   The HFS MDB and on disk will be updated, by copying the possibly
+ *   modified fields from the in memory MDB (in native byte order) to
+ *   the disk block buffer.
+ *   If 'backup' is non-zero then the alternate MDB is also written
+ *   and the function doesn't return until it is actually on disk.
+ */
+void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
+{
+	struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+
+	/* Commit catalog entries to buffers */
+	hfs_cat_commit(mdb);
+
+	/* Commit B-tree data to buffers */
+	hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
+	hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
+
+	/* Update write_count and modify_date */
+	++mdb->write_count;
+	mdb->modify_date = hfs_time();
+
+	/* These parameters may have been modified, so write them back */
+	hfs_put_nl(mdb->modify_date,   raw->drLsMod);
+	hfs_put_hs(mdb->free_ablocks,  raw->drFreeBks);
+	hfs_put_hl(mdb->next_id,       raw->drNxtCNID);
+	hfs_put_hl(mdb->write_count,   raw->drWrCnt);
+	hfs_put_hs(mdb->root_files,    raw->drNmFls);
+	hfs_put_hs(mdb->root_dirs,     raw->drNmRtDirs);
+	hfs_put_hl(mdb->file_count,    raw->drFilCnt);
+	hfs_put_hl(mdb->dir_count,     raw->drDirCnt);
+
+	/* write MDB to disk */
+	hfs_buffer_dirty(mdb->buf);
+
+       	/* write the backup MDB, not returning until it is written */
+        if (backup && hfs_buffer_ok(mdb->alt_buf)) {
+                memcpy(hfs_buffer_data(mdb->alt_buf),
+		       hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
+                hfs_buffer_dirty(mdb->alt_buf);
+                hfs_buffer_sync(mdb->alt_buf);
+        }
+}
+
+/*
+ * hfs_mdb_put()
+ *
+ * Release the resources associated with the in-core MDB.
+ */
+void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
+	int lcv;
+
+	/* invalidate cached catalog entries */
+	hfs_cat_invalidate(mdb);
+
+	/* free the B-trees */
+	hfs_btree_free(mdb->ext_tree);
+	hfs_btree_free(mdb->cat_tree);
+
+	/* free the volume bitmap */
+	for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
+		hfs_buffer_put(mdb->bitmap[lcv]);
+	}
+
+	/* update volume attributes */
+	if (!readonly) {
+		struct raw_mdb *raw =
+				(struct raw_mdb *)hfs_buffer_data(mdb->buf);
+		hfs_put_ns(mdb->attrib, raw->drAtrb);
+		hfs_buffer_dirty(mdb->buf);
+	}
+
+	/* free the buffers holding the primary and alternate MDBs */
+	hfs_buffer_put(mdb->buf);
+	hfs_buffer_put(mdb->alt_buf);
+
+	/* free the MDB */
+	HFS_DELETE(mdb);
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/part_tbl.c linux/fs/hfs/part_tbl.c
--- v2.1.77/linux/fs/hfs/part_tbl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/part_tbl.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,244 @@
+/*
+ * linux/fs/hfs/part_tbl.c
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Original code to handle the new style Mac partition table based on
+ * a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/*
+ * The Macintosh Driver Descriptor Block
+ *
+ * On partitioned Macintosh media this is block 0.
+ * We really only need the "magic number" to check for partitioned media.
+ */
+struct hfs_drvr_desc {
+	hfs_word_t	ddSig;		/* The signature word */
+	/* a bunch more stuff we don't need */
+};
+
+/* 
+ * The new style Mac partition map
+ *
+ * For each partition on the media there is a physical block (512-byte
+ * block) containing one of these structures.  These blocks are
+ * contiguous starting at block 1.
+ */
+struct new_pmap {
+	hfs_word_t	pmSig;		/* Signature bytes to verify
+					   that this is a partition
+					   map block */
+	hfs_word_t	reSigPad;	/* padding */
+	hfs_lword_t	pmMapBlkCnt;	/* (At least in block 1) this
+					   is the number of partition
+					   map blocks */
+	hfs_lword_t	pmPyPartStart;	/* The physical block number
+					   of the first block in this
+					   partition */
+	hfs_lword_t	pmPartBlkCnt;	/* The number of physical
+					   blocks in this partition */
+	hfs_byte_t	pmPartName[32];	/* (null terminated?) string
+					   giving the name of this
+					   partition */
+	hfs_byte_t	pmPartType[32];	/* (null terminated?) string
+					   giving the type of this
+					   partition */
+	/* a bunch more stuff we don't need */
+};
+
+/* 
+ * The old style Mac partition map
+ *
+ * The partition map consists for a 2-byte signature followed by an
+ * array of these structures.  The map is terminated with an all-zero
+ * one of these.
+ */
+struct old_pmap {
+	hfs_word_t		pdSig;	/* Signature bytes */
+	struct 	old_pmap_entry {
+		hfs_lword_t	pdStart;
+		hfs_lword_t	pdSize;
+		hfs_lword_t	pdFSID;
+	}	pdEntry[42];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * parse_new_part_table()
+ *
+ * Parse a new style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+				int part, hfs_s32 *size, hfs_s32 *start)
+{
+	struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
+	hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
+	int hfs_part = 0;
+	int entry;
+
+	for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
+		if (entry) {
+			/* read the next partition map entry */
+			buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
+			if (!hfs_buffer_ok(buf)) {
+				hfs_warn("hfs_fs: unable to "
+				         "read partition map.\n");
+				goto bail;
+			}
+			pm = (struct new_pmap *)hfs_buffer_data(buf);
+			if (hfs_get_ns(pm->pmSig) !=
+						htons(HFS_NEW_PMAP_MAGIC)) {
+				hfs_warn("hfs_fs: invalid "
+				         "entry in partition map\n");
+				hfs_buffer_put(buf);
+				goto bail;
+			}
+		}
+
+		/* look for an HFS partition */
+		if (!memcmp(pm->pmPartType,"Apple_HFS",9) && 
+		    ((hfs_part++) == part)) {
+			/* Found it! */
+			*start = hfs_get_hl(pm->pmPyPartStart);
+			*size = hfs_get_hl(pm->pmPartBlkCnt);
+		}
+
+		hfs_buffer_put(buf);
+	}
+
+	return 0;
+
+bail:
+	return 1;
+}
+
+/*
+ * parse_old_part_table()
+ *
+ * Parse a old style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+				int part, hfs_s32 *size, hfs_s32 *start)
+{
+	struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
+	struct old_pmap_entry *p = &pm->pdEntry[0];
+	int hfs_part = 0;
+
+	while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
+		/* look for an HFS partition */
+		if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
+		    ((hfs_part++) == part)) {
+			/* Found it! */
+			*start = hfs_get_hl(p->pdStart);
+			*size = hfs_get_hl(p->pdSize);
+		}
+		++p;
+	}
+	hfs_buffer_put(buf);
+
+	return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_part_find()
+ *
+ * Parse the partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
+		  hfs_s32 *size, hfs_s32 *start)
+{
+	hfs_buffer buf;
+	hfs_u16 sig;
+	int dd_found = 0;
+	int retval = 1;
+
+	/* Read block 0 to see if this media is partitioned */
+	buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
+	if (!hfs_buffer_ok(buf)) {
+		hfs_warn("hfs_fs: Unable to read block 0.\n");
+		goto done;
+	}
+	sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
+	hfs_buffer_put(buf);
+
+        if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
+		/* We are definitely on partitioned media. */
+		dd_found = 1;
+	}
+
+	buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
+	if (!hfs_buffer_ok(buf)) {
+		hfs_warn("hfs_fs: Unable to read block 1.\n");
+		goto done;
+	}
+
+	*size = *start = 0;
+
+	switch (hfs_get_ns(hfs_buffer_data(buf))) {
+	case __constant_htons(HFS_OLD_PMAP_MAGIC):
+		retval = parse_old_part_table(sys_mdb, buf, part, size, start);
+		break;
+
+	case __constant_htons(HFS_NEW_PMAP_MAGIC):
+		retval = parse_new_part_table(sys_mdb, buf, part, size, start);
+		break;
+
+	default:
+		if (dd_found) {
+			/* The media claimed to have a partition map */
+			if (!silent) {
+				hfs_warn("hfs_fs: This disk has an "
+					 "unrecognized partition map type.\n");
+			}
+		} else {
+			/* Conclude that the media is not partitioned */
+			retval = 0;
+		}
+		goto done;
+	}
+
+	if (!retval) {
+		if (*start == 0) {
+			if (part) {
+				hfs_warn("hfs_fs: unable to locate "
+				         "HFS partition number %d.\n", part);
+			} else {
+				hfs_warn("hfs_fs: unable to locate any "
+					 "HFS partitions.\n");
+			}
+			retval = 1;
+		} else if (*size < 0) {
+			hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
+			retval = 1;
+		} else if (*start < 0) {
+			hfs_warn("hfs_fs: Partition begins beyond 1 "
+				 "Terabyte.\n");
+			retval = 1;
+		}
+	}
+done:
+	return retval;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/string.c linux/fs/hfs/string.c
--- v2.1.77/linux/fs/hfs/string.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/string.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,152 @@
+/*
+ * linux/fs/hfs/string.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the string comparison function for the
+ * Macintosh character set.
+ *
+ * The code in this file is derived from code which is copyright
+ * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI)
+ * It is used here by the permission of ARDI's president Cliff Matthews.
+ *
+ * If you discover bugs in this code please notify both the author of the
+ * Linux HFS file system: hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ * and the author of ARDI's HFS code: ctm@ardi.com (Clifford T. Matthews)
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ File-local variables ================*/
+
+/*
+ * unsigned char caseorder[]
+ *
+ * Defines the lexical ordering of characters on the Macintosh
+ *
+ * Composition of the 'casefold' and 'order' tables from ARDI's code
+ * with the entry for 0x20 changed to match that for 0xCA to remove
+ * special case for those two characters.
+ */
+static unsigned char caseorder[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD,
+0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3,
+0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63,
+0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98,
+0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81,
+0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81,
+0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82,
+0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*
+ * unsigned char casefold[]
+ *
+ * Defines the mapping to lowercase characters on the Macintosh
+ *
+ * "Inverse" of the 'casefold' from ARDI's code.
+ */
+static unsigned char casefold[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x41,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0x8A,0x8C,0x8D,0x8E,0x96,0x9A,0x9F,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xBE,0xBF,
+0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0x88,0x8B,0x9B,0xCF,0xCF,
+0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*================ Global functions ================*/
+
+/*
+ * Hash a string to an integer in a case-independent way
+ */
+unsigned int hfs_strhash(const struct hfs_name *cname)
+{
+	/* Currently just sum of the 'order' of first and last characters */
+	return ((unsigned int)caseorder[cname->Name[0]] +
+		(unsigned int)caseorder[cname->Name[cname->Len - 1]]);
+}
+
+/*
+ * Compare two strings in the HFS filename character ordering
+ * Returns positive, negative, or zero, not just 0 or (+/-)1
+ *
+ * Equivalent to ARDI's call:
+ *	ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0])
+ */
+int hfs_strcmp(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+	int len, tmp;
+	const unsigned char *p1, *p2;
+
+	if (!s1 || !s2) {
+		return 0;
+	}
+
+	len = (s1->Len > s2->Len) ? s2->Len : s1->Len;
+	p1 = s1->Name;
+	p2 = s2->Name;
+
+	while (len--) {
+		if ((tmp = (int)caseorder[*(p1++)]-(int)caseorder[*(p2++)])) {
+			return tmp;
+		}
+	}
+	return s1->Len - s2->Len;
+}
+
+/*
+ * Test for equality of two strings in the HFS filename character ordering.
+ */
+int hfs_streq(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+	int len;
+	const unsigned char *p1, *p2;
+
+	if (!s1 || !s2 || (s1->Len != s2->Len)) {
+		return 0;
+	}
+
+	len = s1->Len;
+	p1 = s1->Name;
+	p2 = s2->Name;
+
+	while (len--) {
+		if (caseorder[*(p1++)] != caseorder[*(p2++)]) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * Convert a string to the Macintosh version of lower case.
+ */
+void hfs_tolower(unsigned char *p, int len)
+{
+	while (len--) {
+		*p = casefold[*p];
+		++p;
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.1.77/linux/fs/hfs/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/super.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,517 @@
+/*
+ * linux/fs/hfs/super.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains hfs_read_super() some of the the super_ops and
+ * init_module() and cleanup_module().	The remaining super_ops are in
+ * inode.c since they deal with inodes.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+/*================ Forward declarations ================*/
+
+static void hfs_read_inode(struct inode *inode);
+static void hfs_put_super(struct super_block *);
+static int hfs_statfs(struct super_block *, struct statfs *, int);
+static void hfs_write_super(struct super_block *);
+
+/*================ Global variables ================*/
+
+static struct super_operations hfs_super_operations = { 
+	hfs_read_inode,		/* read_inode */
+	NULL,			/* write_inode */
+	hfs_put_inode,		/* put_inode     - in inode.c */
+	NULL,                   /* delete inode */
+	hfs_notify_change,	/* notify_change - in inode.c */
+	hfs_put_super,		/* put_super */
+	hfs_write_super,	/* write_super */
+	hfs_statfs,		/* statfs */
+	NULL			/* remount_fs */
+};
+
+/*================ File-local variables ================*/
+
+static struct file_system_type hfs_fs = {
+        "hfs",
+	FS_REQUIRES_DEV,
+	hfs_read_super, 
+	NULL};
+
+/*================ File-local functions ================*/
+
+/* 
+ * hfs_read_inode()
+ *
+ * this doesn't actually do much. hfs_iget actually fills in the 
+ * necessary inode information.
+ */
+static void hfs_read_inode(struct inode *inode)
+{
+  inode->i_mode = 0;
+  inode->i_op = NULL;
+}
+
+
+/*
+ * hfs_write_super()
+ *
+ * Description:
+ *   This function is called by the VFS only. When the filesystem
+ *   is mounted r/w it updates the MDB on disk.
+ * Input Variable(s):
+ *   struct super_block *sb: Pointer to the hfs superblock
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'sb' points to a "valid" (struct super_block).
+ * Postconditions:
+ *   The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb
+ *   (hfs_put_super() must set this flag!). Some MDB fields are updated
+ *   and the MDB buffer is written to disk by calling hfs_mdb_commit().
+ */
+static void hfs_write_super(struct super_block *sb)
+{
+	struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+	/* is this a valid hfs superblock? */
+	if (!sb || sb->s_magic != HFS_SUPER_MAGIC) {
+		return;
+	}
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		/* sync everything to the buffers */
+		hfs_mdb_commit(mdb, 0);
+	}
+	sb->s_dirt = 0;
+}
+
+/*
+ * hfs_put_super()
+ *
+ * This is the put_super() entry in the super_operations structure for
+ * HFS filesystems.  The purpose is to release the resources
+ * associated with the superblock sb.
+ */
+static void hfs_put_super(struct super_block *sb)
+{
+	struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+ 
+	lock_super(sb);
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		hfs_mdb_commit(mdb, 0);
+		sb->s_dirt = 0;
+	}
+
+	/* release the MDB's resources */
+	hfs_mdb_put(mdb, sb->s_flags & MS_RDONLY);
+
+	/* restore default blocksize for the device */
+	set_blocksize(sb->s_dev, BLOCK_SIZE);
+
+	/* invalidate the superblock */
+	sb->s_dev = 0;
+
+	MOD_DEC_USE_COUNT;
+
+	unlock_super(sb);
+	return;
+}
+
+/*
+ * hfs_statfs()
+ *
+ * This is the statfs() entry in the super_operations structure for
+ * HFS filesystems.  The purpose is to return various data about the
+ * filesystem.
+ */
+static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
+{
+	struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+	struct statfs tmp;
+
+	tmp.f_type = HFS_SUPER_MAGIC;
+	tmp.f_bsize = HFS_SECTOR_SIZE;
+	tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
+	tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
+	tmp.f_bavail = tmp.f_bfree;
+	tmp.f_files = -1; /* According to the statfs manual page, -1 is the  */
+	tmp.f_ffree = -1; /* correct value when the meaning is undefined.    */
+	tmp.f_namelen = HFS_NAMELEN;
+
+	return copy_to_user(buf, &tmp, len) ? -EFAULT : 0;
+}
+
+/*
+ * parse_options()
+ * 
+ * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger
+ * This function is called by hfs_read_super() to parse the mount options.
+ */
+static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
+{
+	char *this_char, *value;
+	char names, fork;
+
+	/* initialize the sb with defaults */
+	memset(hsb, 0, sizeof(*hsb));
+	hsb->magic = HFS_SB_MAGIC;
+	hsb->s_uid   = current->uid;
+	hsb->s_gid   = current->gid;
+	hsb->s_umask = current->fs->umask;
+	hsb->s_type    = 0x3f3f3f3f;	/* == '????' */
+	hsb->s_creator = 0x3f3f3f3f;	/* == '????' */
+	hsb->s_lowercase = 0;
+	hsb->s_quiet     = 0;
+	hsb->s_afpd      = 0;
+	hsb->s_conv = 'b';
+	names = '?';
+	fork = '?';
+	*part = 0;
+
+	if (!options) {
+		goto done;
+	}
+	for (this_char = strtok(options,","); this_char;
+	     this_char = strtok(NULL,",")) {
+		if ((value = strchr(this_char,'=')) != NULL) {
+			*value++ = 0;
+		}
+	/* Numeric-valued options */
+		if (!strcmp(this_char,"uid")) {
+			if (!value || !*value) {
+				return 0;
+			}
+			hsb->s_uid = simple_strtoul(value,&value,0);
+			if (*value) {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"gid")) {
+			if (!value || !*value) {
+				return 0;
+			}
+			hsb->s_gid = simple_strtoul(value,&value,0);
+			if (*value) {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"umask")) {
+			if (!value || !*value) {
+				return 0;
+			}
+			hsb->s_umask = simple_strtoul(value,&value,8);
+			if (*value) {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"part")) {
+			if (!value || !*value) {
+				return 0;
+			}
+			*part = simple_strtoul(value,&value,0);
+			if (*value) {
+				return 0;
+			}
+	/* String-valued options */
+		} else if (!strcmp(this_char,"type") && value) {
+			if (strlen(value) != 4) {
+				return 0;
+			}
+			hsb->s_type = hfs_get_nl(value);
+		} else if (!strcmp(this_char,"creator") && value) {
+			if (strlen(value) != 4) {
+				return 0;
+			}
+			hsb->s_creator = hfs_get_nl(value);
+	/* Boolean-valued options */
+		} else if (!strcmp(this_char,"quiet")) {
+			if (value) {
+				return 0;
+			}
+			hsb->s_quiet = 1;
+		} else if (!strcmp(this_char,"afpd")) {
+			if (value) {
+				return 0;
+			}
+			hsb->s_afpd = 1;
+	/* Multiple choice options */
+		} else if (!strcmp(this_char,"names") && value) {
+			if ((*value && !value[1] && strchr("ntal78c",*value)) ||
+			    !strcmp(value,"netatalk") ||
+			    !strcmp(value,"trivial") ||
+			    !strcmp(value,"alpha") ||
+			    !strcmp(value,"latin") ||
+			    !strcmp(value,"7bit") ||
+			    !strcmp(value,"8bit") ||
+			    !strcmp(value,"cap")) {
+				names = *value;
+			} else {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"fork") && value) {
+			if ((*value && !value[1] && strchr("nsdc",*value)) ||
+			    !strcmp(value,"netatalk") ||
+			    !strcmp(value,"single") ||
+			    !strcmp(value,"double") ||
+			    !strcmp(value,"cap")) {
+				fork = *value;
+			} else {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"case") && value) {
+			if ((*value && !value[1] && strchr("la",*value)) ||
+			    !strcmp(value,"lower") ||
+			    !strcmp(value,"asis")) {
+				hsb->s_lowercase = (*value == 'l');
+			} else {
+				return 0;
+			}
+		} else if (!strcmp(this_char,"conv") && value) {
+			if ((*value && !value[1] && strchr("bta",*value)) ||
+			    !strcmp(value,"binary") ||
+			    !strcmp(value,"text") ||
+			    !strcmp(value,"auto")) {
+				hsb->s_conv = *value;
+			} else {
+				return 0;
+			}
+		} else {
+			return 0;
+		}
+	}
+
+done:
+	/* Parse the "fork" and "names" options */
+	if (fork == '?') {
+		fork = hsb->s_afpd ? 'n' : 'c';
+	}
+	switch (fork) {
+	default:
+	case 'c':
+		hsb->s_ifill = hfs_cap_ifill;
+		hsb->s_reserved1 = hfs_cap_reserved1;
+		hsb->s_reserved2 = hfs_cap_reserved2;
+		break;
+
+	case 's':
+		hfs_warn("hfs_fs: AppleSingle not yet implemented.\n");
+		return 0;
+		/* break; */
+	
+	case 'd':
+		hsb->s_ifill = hfs_dbl_ifill;
+		hsb->s_reserved1 = hfs_dbl_reserved1;
+		hsb->s_reserved2 = hfs_dbl_reserved2;
+		break;
+
+	case 'n':
+		hsb->s_ifill = hfs_nat_ifill;
+		hsb->s_reserved1 = hfs_nat_reserved1;
+		hsb->s_reserved2 = hfs_nat_reserved2;
+		break;
+	}
+
+	if (names == '?') {
+		names = fork;
+	}
+	switch (names) {
+	default:
+	case 'n':
+		hsb->s_nameout = hfs_colon2mac;
+		hsb->s_namein = hfs_mac2nat;
+		break;
+
+	case 'c':
+		hsb->s_nameout = hfs_colon2mac;
+		hsb->s_namein = hfs_mac2cap;
+		break;
+
+	case 't':
+		hsb->s_nameout = hfs_triv2mac;
+		hsb->s_namein = hfs_mac2triv;
+		break;
+
+	case '7':
+		hsb->s_nameout = hfs_prcnt2mac;
+		hsb->s_namein = hfs_mac2seven;
+		break;
+
+	case '8':
+		hsb->s_nameout = hfs_prcnt2mac;
+		hsb->s_namein = hfs_mac2eight;
+		break;
+
+	case 'l':
+		hsb->s_nameout = hfs_latin2mac;
+		hsb->s_namein = hfs_mac2latin;
+		break;
+
+ 	case 'a':	/* 's' and 'd' are unadvertised aliases for 'alpha', */
+ 	case 's':	/* since 'alpha' is the default if fork=s or fork=d. */
+ 	case 'd':	/* (It is also helpful for poor typists!)           */
+		hsb->s_nameout = hfs_prcnt2mac;
+		hsb->s_namein = hfs_mac2alpha;
+		break;
+	}
+
+	return 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_read_super()
+ *
+ * This is the function that is responsible for mounting an HFS
+ * filesystem.	It performs all the tasks necessary to get enough data
+ * from the disk to read the root inode.  This includes parsing the
+ * mount options, dealing with Macintosh partitions, reading the
+ * superblock and the allocation bitmap blocks, calling
+ * hfs_btree_init() to get the necessary data about the extents and
+ * catalog B-trees and, finally, reading the root inode into memory.
+ */
+struct super_block *hfs_read_super(struct super_block *s, void *data,
+				   int silent)
+{
+	struct hfs_mdb *mdb;
+	struct hfs_cat_key key;
+	kdev_t dev = s->s_dev;
+#ifndef CONFIG_MAC_PARTITION
+	hfs_s32 part_size, part_start;
+#endif
+	struct inode *root_inode;
+	int part;
+
+	if (!parse_options((char *)data, HFS_SB(s), &part)) {
+		hfs_warn("hfs_fs: unable to parse mount options.\n");
+		goto bail3;
+	}
+
+	/* in case someone tries to unload the module while we wait on I/O */
+	MOD_INC_USE_COUNT;
+
+	lock_super(s);
+
+	/* set the device driver to 512-byte blocks */
+	set_blocksize(dev, HFS_SECTOR_SIZE);
+
+	/* look for a partition table and find the correct partition */
+#ifndef CONFIG_MAC_PARTITION
+	if (hfs_part_find(s, part, silent, &part_size, &part_start)) {
+		goto bail2;
+	}
+
+	mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start);
+#else
+	mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0);
+#endif
+	if (!mdb) {
+		if (!silent) {
+			printk("VFS: Can't find a HFS filesystem on dev %s.\n",
+			       kdevname(dev));
+		}
+		goto bail2;
+	}
+	HFS_SB(s)->s_mdb = mdb;
+
+	if (HFS_ITYPE(mdb->next_id) != 0) {
+		hfs_warn("hfs_fs: too many files.\n");
+		goto bail1;
+	}
+
+	s->s_magic = HFS_SUPER_MAGIC;
+	s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
+	s->s_blocksize = HFS_SECTOR_SIZE;
+	s->s_op = &hfs_super_operations;
+
+	/* try to get the root inode */
+	hfs_cat_build_key(htonl(HFS_POR_CNID),
+			  (struct hfs_name *)(mdb->vname), &key);
+
+	root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL);
+	if (!root_inode) 
+		goto bail_no_root;
+	  
+	/* cache the dentry in the inode */
+	s->s_root = 
+	  HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] =
+	  d_alloc_root(root_inode, NULL);
+	if (!s->s_root) 
+		goto bail_no_root;
+
+	/* everything's okay */
+	unlock_super(s);
+	return s;
+
+bail_no_root: 
+	hfs_warn("hfs_fs: get root inode failed.\n");
+	iput(root_inode);
+bail1:
+	hfs_mdb_put(mdb, s->s_flags & MS_RDONLY);
+bail2:
+	set_blocksize(dev, BLOCK_SIZE);
+	MOD_DEC_USE_COUNT;
+	unlock_super(s);
+bail3:
+	s->s_dev = 0;
+	return NULL;	
+}
+
+__initfunc(int init_hfs_fs(void))
+{
+	return register_filesystem(&hfs_fs);
+}
+
+#ifdef MODULE
+int init_module(void) {
+	int error;
+
+#if defined(DEBUG_SIZES) || defined(DEBUG_ALL)
+	hfs_warn("HFS inode: %d bytes available\n",
+		 sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info));
+	hfs_warn("HFS super_block: %d bytes available\n",
+		 sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info));
+	if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) ||
+	    (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) {
+		return -ENOMEM; /* well sort of */
+	}
+#endif
+	error = init_hfs_fs();
+	if (!error) {
+		/* register_symtab(NULL); */
+	}
+	return error;
+}
+
+void cleanup_module(void) {
+	hfs_cat_free();
+	unregister_filesystem(&hfs_fs);
+}
+#endif
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+long int hfs_alloc = 0;
+#endif
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/sysdep.c linux/fs/hfs/sysdep.c
--- v2.1.77/linux/fs/hfs/sysdep.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/sysdep.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,52 @@
+/*
+ * linux/fs/hfs/sysdep.c
+ *
+ * Copyright (C) 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to do various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*
+ * hfs_buffer_get()
+ *
+ * Return a buffer for the 'block'th block of the media.
+ * If ('read'==0) then the buffer is not read from disk.
+ */
+hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) {
+	hfs_buffer tmp = HFS_BAD_BUFFER;
+
+	if (read) {
+		tmp = bread(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+	} else {
+		tmp = getblk(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+		if (tmp) {
+			mark_buffer_uptodate(tmp, 1);
+		}
+	}
+	if (!tmp) {
+		hfs_error("hfs_fs: unable to read block 0x%08x from dev %s\n",
+			  block, hfs_mdb_name(sys_mdb));
+	}
+
+	return tmp;
+}
+
+/* catalog.c needs a re-write. as a kludge, we throw away all of 
+ * the dcache if we need more entries. */
+int hfs_prune_entry(struct hfs_cat_entry *entry)
+{
+        shrink_dcache_sb(entry->mdb->sys_mdb);
+	return 1;
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/trans.c linux/fs/hfs/trans.c
--- v2.1.77/linux/fs/hfs/trans.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/trans.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,556 @@
+/*
+ * linux/fs/hfs/trans.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains routines for converting between the Macintosh
+ * character set and various other encodings.  This includes dealing
+ * with ':' vs. '/' as the path-element separator.
+ *
+ * Latin-1 translation based on code contributed by Holger Schemel
+ * (aeglos@valinor.owl.de).
+ *
+ * The '8-bit', '7-bit ASCII' and '7-bit alphanumeric' encodings are
+ * implementations of the three encodings recommended by Apple in the
+ * document "AppleSingle/AppleDouble Formats: Developer's Note
+ * (9/94)".  This document is available from Apple's Technical
+ * Information Library from the World Wide Web server
+ * www.info.apple.com.
+ *
+ * The 'CAP' encoding is an implementation of the naming scheme used
+ * by the Columbia AppleTalk Package, available for anonymous FTP from
+ * ????.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local variables ================*/
+
+/* int->ASCII map for a single hex digit */
+static char hex[16] = {'0','1','2','3','4','5','6','7',
+		       '8','9','a','b','c','d','e','f'};
+/*
+ * Latin-1 to Mac character set map
+ *
+ * For the sake of consistency this map is generated from the Mac to
+ * Latin-1 map the first time it is needed.  This means there is just
+ * one map to maintain.
+ */
+static unsigned char latin2mac_map[128]; /* initially all zero */
+
+/*
+ * Mac to Latin-1 map for the upper 128 characters (both have ASCII in
+ * the lower 128 positions)
+ */
+static unsigned char mac2latin_map[128] = {
+	0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+	0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+	0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+	0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+	0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+	0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+	0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00, 
+	0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+	0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+	0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00, 
+	0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00, 
+	0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+	0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+	0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dehex()
+ *
+ * Given a hexadecimal digit in ASCII, return the integer representation.
+ */
+static inline const unsigned char dehex(char c) {
+	if ((c>='0')&&(c<='9')) {
+		return c-'0';
+	}
+	if ((c>='a')&&(c<='f')) {
+		return c-'a'+10;
+	}
+	if ((c>='A')&&(c<='F')) {
+		return c-'A'+10;
+	}
+	return 0xff;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mac2nat()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the Netatalk name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' and any initial '.' are passed
+ * unchanged from input to output.  The remaining characters are replaced
+ * by three characters: ':xx' where xx is the hexadecimal representation
+ * of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2nat(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	/* Special case for .AppleDesktop which in the
+	   distant future may be a pseudodirectory. */
+	if (strncmp(".AppleDesktop", p, len) == 0) {
+		strncpy(out, p, 13);
+		return 13;
+	}
+
+	while (len--) {
+		c = *p++;
+		if ((c<32) || (c=='/') || (c>126) || (!count && (c=='.'))) {
+			*out++ = ':';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		} else {
+			*out++ = c;
+			count++;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2cap()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the CAP name-mangling scheme, returning the length of the mangled
+ * filename.  Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' are passed unchanged from
+ * input to output.  The remaining characters are replaced by three
+ * characters: ':xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2cap(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	while (len--) {
+		c = *p++;
+		if ((c<32) || (c=='/') || (c>126)) {
+			*out++ = ':';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		} else {
+			*out++ = c;
+			count++;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2eight()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '8-bit' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%' and '/' are replaced by three characters: '%xx'
+ * where xx is the hexadecimal representation of the character, using
+ * lowercase 'a' through 'f'.  All other characters are passed
+ * unchanged from input to output.  Note that this format is mainly
+ * implemented for completeness and is rather hard to read.
+ */
+int hfs_mac2eight(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	while (len--) {
+		c = *p++;
+		if (!c || (c=='/') || (c=='%')) {
+			*out++ = '%';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		} else {
+			*out++ = c;
+			count++;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2seven()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit ASCII' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%', '/' and 128-255 are replaced by three
+ * characters: '%xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'.	All other characters
+ * are passed unchanged from input to output.  Note that control
+ * characters (including newline) and space are unchanged make reading
+ * these filenames difficult.
+ */
+int hfs_mac2seven(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	while (len--) {
+		c = *p++;
+		if (!c || (c=='/') || (c=='%') || (c&0x80)) {
+			*out++ = '%';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		} else {
+			*out++ = c;
+			count++;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2alpha()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit alphanumeric' name-mangling scheme, returning the length
+ * of the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * The characters 'a'-'z', 'A'-'Z', '0'-'9', '_' and the last '.' in
+ * the filename are passed unchanged from input to output.  All
+ * remaining characters (including any '.'s other than the last) are
+ * replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2alpha(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+	const unsigned char *lp;	/* last period */
+
+	/* strrchr() would be good here, but 'in' is not null-terminated */
+	for (lp=p+len-1; (lp>=p)&&(*lp!='.'); --lp) {}
+	++lp;
+
+	while (len--) {
+		c = *p++;
+		if ((p==lp) || ((c>='0')&&(c<='9')) || ((c>='A')&&(c<='Z')) ||
+				((c>='a')&&(c<='z')) || (c=='_')) {
+			*out++ = c;
+			count++;
+		} else {
+			*out++ = '%';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2triv()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'trivial' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * The name-mangling works as follows:
+ * The character '/', which is illegal in Linux filenames is replaced
+ * by ':' which never appears in HFS filenames.	 All other characters
+ * are passed unchanged from input to output.
+ */
+int hfs_mac2triv(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	while (len--) {
+		c = *p++;
+		if (c=='/') {
+			*out++ = ':';
+		} else {
+			*out++ = c;
+		}
+		count++;
+	}
+	return count;
+}
+
+/*
+ * hfs_mac2latin()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'Latin-1' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * The Macintosh character set and Latin-1 are both extensions of the
+ * ASCII character set.	 Some, but certainly not all, of the characters
+ * in the Macintosh character set are also in Latin-1 but not with the
+ * same encoding.  This name-mangling scheme replaces the characters in
+ * the Macintosh character set that have Latin-1 equivalents by those
+ * equivalents; the characters 32-126, excluding '/' and '%', are
+ * passed unchanged from input to output.  The remaining characters
+ * are replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ *
+ * The array mac2latin_map[] indicates the correspondence between the
+ * two character sets.	The byte in element x-128 gives the Latin-1
+ * encoding of the character with encoding x in the Macintosh
+ * character set.  A value of zero indicates Latin-1 has no
+ * corresponding character.
+ */
+int hfs_mac2latin(char *out, const struct hfs_name *in) {
+	unsigned char c;
+	const unsigned char *p = in->Name;
+	int len = in->Len;
+	int count = 0;
+
+	while (len--) {
+		c = *p++;
+
+		if ((c & 0x80) && mac2latin_map[c & 0x7f]) {
+			*out++ = mac2latin_map[c & 0x7f];
+			count++;
+		} else if ((c>=32) && (c<=126) && (c!='/') && (c!='%')) {
+			*out++ =  c;
+			count++;
+		} else {
+			*out++ = '%';
+			*out++ = hex[(c>>4) & 0xf];
+			*out++ = hex[c & 0xf];
+			count += 3;
+		}
+	}
+	return count;
+}
+
+/*
+ * hfs_colon2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'CAP' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2cap() and hfs_mac2nat().
+ * A ':' not followed by a 2-digit hexadecimal number (or followed
+ * by the codes for NULL or ':') is replaced by a '|'.
+ */
+void hfs_colon2mac(struct hfs_name *out, const char *in, int len) {
+	int hi, lo;
+	unsigned char code, c, *count;
+	unsigned char *p = out->Name;
+
+	out->Len = 0;
+	count = &out->Len;
+	while (len-- && (*count < HFS_NAMELEN)) {
+		c = *in++;
+		(*count)++;
+		if (c!=':') {
+			*p++ = c;
+		} else if ((len<2) ||
+			   ((hi=dehex(in[0])) & 0xf0) ||
+			   ((lo=dehex(in[1])) & 0xf0) ||
+			   !(code = (hi << 4) | lo) ||
+			   (code == ':')) {
+			*p++ = '|';
+		} else {
+			*p++ = code;
+			len -= 2;
+			in += 2;
+		}
+	}
+}
+
+/*
+ * hfs_prcnt2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using Apple's three recommended name-mangling schemes, returning
+ * the length of the mangled filename.	Note that the output string is
+ * not NULL terminated.
+ *
+ * This routine is a inverse to hfs_mac2alpha(), hfs_mac2seven() and
+ * hfs_mac2eight().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ */
+void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) {
+	int hi, lo;
+	unsigned char code, c, *count;
+	unsigned char *p = out->Name;
+
+	out->Len = 0;
+	count = &out->Len;
+	while (len-- && (*count < HFS_NAMELEN)) {
+		c = *in++;
+		(*count)++;
+		if (c==':') {
+			*p++ = '|';
+		} else if (c!='%') {
+			*p++ = c;
+		} else if ((len<2) ||
+			   ((hi=dehex(in[0])) & 0xf0) ||
+			   ((lo=dehex(in[1])) & 0xf0) ||
+			   !(code = (hi << 4) | lo) ||
+			   (code == ':')) {
+			*p++ = '%';
+		} else {
+			*p++ = code;
+			len -= 2;
+			in += 2;
+		}
+	}
+}
+
+/*
+ * hfs_triv2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'trivial' name-mangling scheme, returning the length of
+ * the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2triv().
+ * A ':' is replaced by a '/'.
+ */
+void hfs_triv2mac(struct hfs_name *out, const char *in, int len) {
+	unsigned char c, *count;
+	unsigned char *p = out->Name;
+
+	out->Len = 0;
+	count = &out->Len;
+	while (len-- && (*count < HFS_NAMELEN)) {
+		c = *in++;
+		(*count)++;
+		if (c==':') {
+			*p++ = '/';
+		} else {
+			*p++ = c;
+		}
+	}
+}
+
+/*
+ * hfs_latin2mac()
+ *
+ * Given an Latin-1 string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'Latin-1' name-mangling scheme, returning the length of
+ * the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_latin2cap().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ *
+ * Note that the character map is built the first time it is needed.
+ */
+void hfs_latin2mac(struct hfs_name *out, const char *in, int len)
+{
+	int hi, lo;
+	unsigned char code, c, *count;
+	unsigned char *p = out->Name;
+	static int map_initialized = 0;
+
+	if (!map_initialized) {
+		int i;
+
+		/* build the inverse mapping at run time */
+		for (i = 0; i < 128; i++) {
+			if ((c = mac2latin_map[i])) {
+				latin2mac_map[(int)c - 128] = i + 128;
+			}
+		}
+		map_initialized = 1;
+	}
+
+	out->Len = 0;
+	count = &out->Len;
+	while (len-- && (*count < HFS_NAMELEN)) {
+		c = *in++;
+		(*count)++;
+
+		if (c==':') {
+			*p++ = '|';
+		} else if (c!='%') {
+			if (c<128 || !(*p = latin2mac_map[c-128])) {
+				*p = c;
+			}
+			p++;
+		} else if ((len<2) ||
+			   ((hi=dehex(in[0])) & 0xf0) ||
+			   ((lo=dehex(in[1])) & 0xf0) ||
+			   !(code = (hi << 4) | lo) ||
+			   (code == ':')) {
+			*p++ = '%';
+		} else {
+			*p++ = code;
+			len -= 2;
+			in += 2;
+		}
+	}
+}
diff -u --recursive --new-file v2.1.77/linux/fs/hfs/version.c linux/fs/hfs/version.c
--- v2.1.77/linux/fs/hfs/version.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/hfs/version.c	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,10 @@
+/*
+ * linux/fs/hfs/version.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the version string for this release.
+ */
+
+const char hfs_version[]="0.96";
diff -u --recursive --new-file v2.1.77/linux/fs/inode.c linux/fs/inode.c
--- v2.1.77/linux/fs/inode.c	Tue Nov 18 17:22:08 1997
+++ linux/fs/inode.c	Sun Jan  4 10:52:42 1998
@@ -215,7 +215,7 @@
 /*
  * This is called by the filesystem to tell us
  * that the inode is no longer useful. We just
- * terminate it with extreme predjudice.
+ * terminate it with extreme prejudice.
  */
 void clear_inode(struct inode *inode)
 {
diff -u --recursive --new-file v2.1.77/linux/fs/isofs/symlink.c linux/fs/isofs/symlink.c
--- v2.1.77/linux/fs/isofs/symlink.c	Mon Nov 17 18:47:21 1997
+++ linux/fs/isofs/symlink.c	Sun Jan  4 00:53:41 1998
@@ -18,8 +18,8 @@
 
 #include <asm/uaccess.h>
 
-static int isofs_readlink(struct inode *, char *, int);
-static struct dentry * isofs_follow_link(struct inode * inode, struct dentry *base);
+static int isofs_readlink(struct dentry *, char *, int);
+static struct dentry * isofs_follow_link(struct dentry *, struct dentry *);
 
 /*
  * symlinks can't do much...
@@ -44,14 +44,14 @@
 	NULL			/* permission */
 };
 
-static int isofs_readlink(struct inode * inode, char * buffer, int buflen)
+static int isofs_readlink(struct dentry * dentry, char * buffer, int buflen)
 {
         char * pnt;
 	int i;
 
 	if (buflen > 1023)
 		buflen = 1023;
-	pnt = get_rock_ridge_symlink(inode);
+	pnt = get_rock_ridge_symlink(dentry->d_inode);
 
 	if (!pnt)
 		return 0;
@@ -65,12 +65,12 @@
 	return i;
 }
 
-static struct dentry * isofs_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry * isofs_follow_link(struct dentry * dentry,
+					struct dentry *base)
 {
 	char * pnt;
 
-	pnt = get_rock_ridge_symlink(inode);
-
+	pnt = get_rock_ridge_symlink(dentry->d_inode);
 	if(!pnt) {
 		dput(base);
 		return ERR_PTR(-ELOOP);
diff -u --recursive --new-file v2.1.77/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c
--- v2.1.77/linux/fs/lockd/clntproc.c	Mon Dec  1 12:04:14 1997
+++ linux/fs/lockd/clntproc.c	Sun Jan  4 00:53:43 1998
@@ -42,7 +42,7 @@
 	memset(argp, 0, sizeof(*argp));
 	argp->cookie  = nlm_cookie++;
 	argp->state   = nsm_local_state;
-	lock->fh      = *NFS_FH(fl->fl_file->f_dentry->d_inode);
+	lock->fh      = *NFS_FH(fl->fl_file->f_dentry);
 	lock->caller  = system_utsname.nodename;
 	lock->oh.data = req->a_owner;
 	lock->oh.len  = sprintf(req->a_owner, "%d@%s",
diff -u --recursive --new-file v2.1.77/linux/fs/minix/symlink.c linux/fs/minix/symlink.c
--- v2.1.77/linux/fs/minix/symlink.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/minix/symlink.c	Sun Jan  4 00:53:41 1998
@@ -14,8 +14,8 @@
 
 #include <asm/uaccess.h>
 
-static int minix_readlink(struct inode *, char *, int);
-static struct dentry *minix_follow_link(struct inode *, struct dentry *);
+static int minix_readlink(struct dentry *, char *, int);
+static struct dentry *minix_follow_link(struct dentry *, struct dentry *);
 
 /*
  * symlinks can't do much...
@@ -40,8 +40,10 @@
 	NULL			/* permission */
 };
 
-static struct dentry * minix_follow_link(struct inode * inode, struct dentry * base)
+static struct dentry * minix_follow_link(struct dentry * dentry,
+					struct dentry * base)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh;
 
 	bh = minix_bread(inode, 0, 0);
@@ -55,7 +57,7 @@
 	return base;
 }
 
-static int minix_readlink(struct inode * inode, char * buffer, int buflen)
+static int minix_readlink(struct dentry * dentry, char * buffer, int buflen)
 {
 	struct buffer_head * bh;
 	int i;
@@ -63,7 +65,7 @@
 
 	if (buflen > 1023)
 		buflen = 1023;
-	bh = minix_bread(inode, 0, 0);
+	bh = minix_bread(dentry->d_inode, 0, 0);
 	if (!bh)
 		return 0;
 	i = 0;
diff -u --recursive --new-file v2.1.77/linux/fs/namei.c linux/fs/namei.c
--- v2.1.77/linux/fs/namei.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/namei.c	Sun Jan  4 00:53:41 1998
@@ -221,33 +221,28 @@
 }
 
 /*
- * This is called when everything else fails, and we actually have
- * to go to the low-level filesystem to find out what we should do..
- *
- * We get the directory semaphore, and after getting that we also
- * make sure that nobody added the entry to the dcache in the meantime..
+ * "." and ".." are special - ".." especially so because it has to be able
+ * to know about the current root directory and parent relationships
  */
-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
+static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name)
 {
-	struct dentry * result;
-	struct inode *dir = parent->d_inode;
+	struct dentry *result = NULL;
+	if (name->name[0] == '.') {
+		switch (name->len) {
+		default:
+			break;
+		case 2:	
+			if (name->name[1] != '.')
+				break;
 
-	down(&dir->i_sem);
-	result = d_lookup(parent, name);
-	if (!result) {
-		struct dentry * dentry = d_alloc(parent, name);
-		result = ERR_PTR(-ENOMEM);
-		if (dentry) {
-			int error = dir->i_op->lookup(dir, dentry);
-			result = dentry;
-			if (error) {
-				dput(dentry);
-				result = ERR_PTR(error);
-			}
+			if (parent != current->fs->root)
+				parent = parent->d_covers->d_parent;
+			/* fallthrough */
+		case 1:
+			result = parent;
 		}
 	}
-	up(&dir->i_sem);
-	return result;
+	return dget(result);
 }
 
 /*
@@ -274,28 +269,40 @@
 }
 
 /*
- * "." and ".." are special - ".." especially so because it has to be able
- * to know about the current root directory and parent relationships
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
  */
-static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * name)
+static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
 {
-	struct dentry *result = NULL;
-	if (name->name[0] == '.') {
-		switch (name->len) {
-		default:
-			break;
-		case 2:	
-			if (name->name[1] != '.')
-				break;
+	struct dentry * result;
+	struct inode *dir = parent->d_inode;
 
-			if (parent != current->fs->root)
-				parent = parent->d_covers->d_parent;
-			/* fallthrough */
-		case 1:
-			result = parent;
+	down(&dir->i_sem);
+	/*
+	 * First re-do the cached lookup just in case it was created
+	 * while we waited for the directory semaphore..
+	 *
+	 * FIXME! This could use version numbering or similar to
+	 * avoid unnecessary cache lookups.
+	 */
+	result = cached_lookup(parent, name);
+	if (!result) {
+		struct dentry * dentry = d_alloc(parent, name);
+		result = ERR_PTR(-ENOMEM);
+		if (dentry) {
+			int error = dir->i_op->lookup(dir, dentry);
+			result = dentry;
+			if (error) {
+				dput(dentry);
+				result = ERR_PTR(error);
+			}
 		}
 	}
-	return dget(result);
+	up(&dir->i_sem);
+	return result;
 }
 
 static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
@@ -308,7 +315,7 @@
 
 			current->link_count++;
 			/* This eats the base */
-			result = inode->i_op->follow_link(inode, base);
+			result = inode->i_op->follow_link(dentry, base);
 			current->link_count--;
 			dput(dentry);
 			return result;
@@ -632,7 +639,7 @@
 			if (inode->i_sb && inode->i_sb->dq_op)
 				inode->i_sb->dq_op->initialize(inode, -1);
 			
-			error = do_truncate(inode, 0);
+			error = do_truncate(dentry, 0);
 		}
 		put_write_access(inode);
 		if (error)
@@ -1076,7 +1083,7 @@
 
 	if (dir->d_inode->i_sb && dir->d_inode->i_sb->dq_op)
 		dir->d_inode->i_sb->dq_op->initialize(dir->d_inode, -1);
-	error = dir->d_inode->i_op->link(inode, dir->d_inode, new_dentry);
+	error = dir->d_inode->i_op->link(old_dentry, dir->d_inode, new_dentry);
 
 exit_lock:
 	unlock_dir(dir);
diff -u --recursive --new-file v2.1.77/linux/fs/ncpfs/Makefile linux/fs/ncpfs/Makefile
--- v2.1.77/linux/fs/ncpfs/Makefile	Thu Feb 22 05:20:19 1996
+++ linux/fs/ncpfs/Makefile	Mon Jan  5 01:41:01 1998
@@ -12,10 +12,8 @@
 M_OBJS   := $(O_TARGET)
 
 # If you want debugging output, please uncomment the following line
-
 # EXTRA_CFLAGS += -DDEBUG_NCP=1
 
-include $(TOPDIR)/Rules.make
+CFLAGS_ncplib_kernel.o := -finline-functions
 
-ncplib_kernel.o: ncplib_kernel.c ncplib_kernel.h
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -finline-functions -c -o $@ $<
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.77/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.1.77/linux/fs/ncpfs/dir.c	Mon Nov 17 18:47:21 1997
+++ linux/fs/ncpfs/dir.c	Sun Jan  4 00:53:42 1998
@@ -105,10 +105,10 @@
 /*
  * Dentry operations routines
  */
-static int  ncp_lookup_validate(struct dentry *);
-static void ncp_delete_dentry(struct dentry *);
+static int ncp_lookup_validate(struct dentry *);
 static int ncp_hash_dentry(struct dentry *, struct qstr *);
 static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+static void ncp_delete_dentry(struct dentry *);
 
 static struct dentry_operations ncp_dentry_operations =
 {
@@ -125,19 +125,23 @@
  */
 #define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
 
-
+/*
+ * Note: leave the hash unchanged if the directory
+ * is case-sensitive.
+ */
 static int 
 ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
 {
-  unsigned long hash;
-  int i;
+	unsigned long hash;
+	int i;
 
-  hash = init_name_hash();
-  for (i=0; i<this->len ; i++)
-    hash = partial_name_hash(tolower(this->name[i]),hash);
-  this->hash = end_name_hash(hash);
-  
-  return 0;
+	if (!ncp_case_sensitive(dentry->d_inode)) {
+		hash = init_name_hash();
+		for (i=0; i<this->len ; i++)
+			hash = partial_name_hash(tolower(this->name[i]),hash);
+		this->hash = end_name_hash(hash);
+	}
+	return 0;
 }
 
 static int
@@ -202,42 +206,14 @@
  */
 ino_t ncp_invent_inos(unsigned long n)
 {
-	static ino_t ino = 1;
+	static ino_t ino = 2;
 
 	if (ino + 2*n < ino)
 	{
 		/* wrap around */
-		ino += n;
+		ino = 2;
 	}
 	ino += n;
-	return ino;
-}
-
-/*
- * Check whether a dentry already exists for the given name,
- * and return the inode number if it has an inode.  This is
- * needed to keep getcwd() working.
- */
-static ino_t
-find_inode_number(struct dentry *dir, struct qstr *name)
-{
-        unsigned long hash;
-        int i;
-	struct dentry * dentry;
-	ino_t ino = 0;
-        
-        hash = init_name_hash();
-        for (i=0; i<name->len ; i++)
-                hash = partial_name_hash(tolower(name->name[i]),hash);
-        name->hash = end_name_hash(hash);
-        
-	dentry = d_lookup(dir, name);
-	if (dentry)
-	{
-		if (dentry->d_inode)
-			ino = dentry->d_inode->i_ino;
-		dput(dentry);
-	}
 	return ino;
 }
 
diff -u --recursive --new-file v2.1.77/linux/fs/ncpfs/file.c linux/fs/ncpfs/file.c
--- v2.1.77/linux/fs/ncpfs/file.c	Wed Nov 12 13:34:27 1997
+++ linux/fs/ncpfs/file.c	Sun Jan  4 00:53:42 1998
@@ -15,11 +15,12 @@
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
-#include <linux/ncp_fs.h>
 #include <linux/locks.h>
-#include "ncplib_kernel.h"
 #include <linux/malloc.h>
 
+#include <linux/ncp_fs.h>
+#include "ncplib_kernel.h"
+
 static inline int min(int a, int b)
 {
 	return a < b ? a : b;
@@ -65,7 +66,7 @@
 		result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
 					NULL, NULL, OC_MODE_OPEN,
 					0, AR_READ, &finfo);
-		if (!result) {
+		if (result) {
 #ifdef NCPFS_PARANOIA
 printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result);
 #endif
@@ -82,11 +83,7 @@
 #ifdef NCPFS_PARANOIA
 printk(KERN_DEBUG "ncp_make_open: file open, access=%x\n", access);
 #endif
-	if (((right == O_RDONLY) && ((access == O_RDONLY)
-				     || (access == O_RDWR)))
-	    || ((right == O_WRONLY) && ((access == O_WRONLY)
-					|| (access == O_RDWR)))
-	    || ((right == O_RDWR) && (access == O_RDWR)))
+	if (access == right || access == O_RDWR)
 		error = 0;
 
 out_unlock:
@@ -231,7 +228,7 @@
 		}
 	}
 
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	inode->i_mtime = inode->i_atime = CURRENT_TIME;
 	
 	file->f_pos = pos;
 
diff -u --recursive --new-file v2.1.77/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.1.77/linux/fs/ncpfs/inode.c	Wed Nov 12 13:34:27 1997
+++ linux/fs/ncpfs/inode.c	Sun Jan  4 00:53:42 1998
@@ -37,7 +37,7 @@
 static void ncp_read_inode(struct inode *);
 static void ncp_put_inode(struct inode *);
 static void ncp_delete_inode(struct inode *);
-static int  ncp_notify_change(struct inode *, struct iattr *);
+static int  ncp_notify_change(struct dentry *, struct iattr *);
 static void ncp_put_super(struct super_block *);
 static int  ncp_statfs(struct super_block *, struct statfs *, int);
 
@@ -210,7 +210,7 @@
 	i->entryName[0]  = '\0';
 
 	root->finfo.opened= 0;
-	info->ino = 1;
+	info->ino = 2; /* tradition */
 	info->nw_info = root->finfo;
 }
 
@@ -354,7 +354,7 @@
 	ncp_unlock_server(server);
 
 	close_fp(server->ncp_filp);
-	kill_proc(server->m.wdog_pid, SIGTERM, 0);
+	kill_proc(server->m.wdog_pid, SIGTERM, 1);
 
 	ncp_kfree_s(server->packet, server->packet_size);
 
@@ -387,30 +387,34 @@
 	return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
-static int ncp_notify_change(struct inode *inode, struct iattr *attr)
+static int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *inode = dentry->d_inode;
 	int result = 0;
 	int info_mask;
 	struct nw_modify_dos_info info;
 
-	if (!ncp_conn_valid(NCP_SERVER(inode))) {
-		return -EIO;
-	}
-	if ((result = inode_change_ok(inode, attr)) < 0)
-		return result;
+	result = -EIO;
+	if (!ncp_conn_valid(NCP_SERVER(inode)))
+		goto out;
+
+	result = inode_change_ok(inode, attr);
+	if (result < 0)
+		goto out;
 
+	result = -EPERM;
 	if (((attr->ia_valid & ATTR_UID) &&
 	     (attr->ia_uid != NCP_SERVER(inode)->m.uid)))
-		return -EPERM;
+		goto out;
 
 	if (((attr->ia_valid & ATTR_GID) &&
 	     (attr->ia_uid != NCP_SERVER(inode)->m.gid)))
-		return -EPERM;
+		goto out;
 
 	if (((attr->ia_valid & ATTR_MODE) &&
 	     (attr->ia_mode &
 	      ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
-		return -EPERM;
+		goto out;
 
 	info_mask = 0;
 	memset(&info, 0, sizeof(info));
@@ -455,7 +459,8 @@
 	if ((attr->ia_valid & ATTR_SIZE) != 0) {
 		int written;
 
-		DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size);
+		DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n",
+			attr->ia_size);
 
 		if ((result = ncp_make_open(inode, O_RDWR)) < 0) {
 			return -EACCES;
@@ -467,11 +472,8 @@
 		   closing the file */
 		result = ncp_make_closed(inode);
 	}
-	/*
-	 * We need a dentry here ...
-	 */
-	/* ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); */
-
+	ncp_invalid_dir_cache(dentry->d_parent->d_inode);
+out:
 	return result;
 }
 
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.77/linux/fs/nfs/dir.c	Fri Jan  2 14:37:02 1998
+++ linux/fs/nfs/dir.c	Sun Jan  4 01:33:18 1998
@@ -64,7 +64,7 @@
 static int nfs_rmdir(struct inode *, struct dentry *);
 static int nfs_unlink(struct inode *, struct dentry *);
 static int nfs_symlink(struct inode *, struct dentry *, const char *);
-static int nfs_link(struct inode *, struct inode *, struct dentry *);
+static int nfs_link(struct dentry *, struct inode *, struct dentry *);
 static int nfs_mknod(struct inode *, struct dentry *, int, int);
 static int nfs_rename(struct inode *, struct dentry *,
 		      struct inode *, struct dentry *);
@@ -108,8 +108,11 @@
 static int
 nfs_dir_open(struct inode *dir, struct file *file)
 {
-	dfprintk(VFS, "NFS: nfs_dir_open(%x/%ld)\n", dir->i_dev, dir->i_ino);
-	return nfs_revalidate_inode(NFS_SERVER(dir), dir);
+	struct dentry *dentry = file->f_dentry;
+
+	dfprintk(VFS, "NFS: nfs_dir_open(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 }
 
 static ssize_t
@@ -133,7 +136,8 @@
 
 static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
-	struct inode 		*inode = filp->f_dentry->d_inode;
+	struct dentry 		*dentry = filp->f_dentry;
+	struct inode 		*inode = dentry->d_inode;
 	static struct wait_queue *readdir_wait = NULL;
 	struct wait_queue	**waitp = NULL;
 	struct nfs_dirent	*cache, *free;
@@ -144,14 +148,17 @@
 	__u32			*entry;
 	char			*name, *start;
 
-	dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "NFS: nfs_readdir(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
+	result = -EBADF;
 	if (!inode || !S_ISDIR(inode->i_mode)) {
 		printk("nfs_readdir: inode is NULL or not a directory\n");
-		return -EBADF;
+		goto out;
 	}
 
-	if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
-		return result;
+	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
+	if (result < 0)
+		goto out;
 
 	/*
 	 * Try to find the entry in the cache
@@ -250,7 +257,7 @@
 				goto done;
 		}
 
-		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode),
+		result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(dentry),
 					cookie, PAGE_SIZE, cache->entry);
 		if (result <= 0)
 			goto done;
@@ -300,6 +307,7 @@
 	wake_up(&cache->wait);
 	wake_up(&readdir_wait);
 
+out:
 	return result;
 }
 
@@ -449,11 +457,21 @@
 #endif
 }
 
-static struct dentry_operations nfs_dentry_operations = {
+/*
+ * Called when the dentry is being freed to release private memory.
+ */
+static void nfs_dentry_release(struct dentry *dentry)
+{
+	if (dentry->d_fsdata)
+		kfree(dentry->d_fsdata);
+}
+
+struct dentry_operations nfs_dentry_operations = {
 	nfs_lookup_revalidate,	/* d_validate(struct dentry *) */
-	0,			/* d_hash */
-	0,			/* d_compare */
-	nfs_dentry_delete	/* d_delete(struct dentry *) */
+	NULL,			/* d_hash */
+	NULL,			/* d_compare */
+	nfs_dentry_delete,	/* d_delete(struct dentry *) */
+	nfs_dentry_release	/* d_release(struct dentry *) */
 };
 
 /*
@@ -470,16 +488,20 @@
 	}
 }
 
+static void nfs_set_fh(struct dentry *dentry, struct nfs_fh *fhandle)
+{
+	*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
+}
+
 static int nfs_lookup(struct inode *dir, struct dentry * dentry)
 {
-	int len = dentry->d_name.len;
 	struct inode *inode;
 	int error;
 	struct nfs_fh fhandle;
 	struct nfs_fattr fattr;
 
-	dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
-			dir->i_dev, dir->i_ino, len, dentry->d_name.name);
+	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_lookup: inode is NULL or not a directory\n");
@@ -487,17 +509,26 @@
 	}
 
 	error = -ENAMETOOLONG;
-	if (len > NFS_MAXNAMLEN)
+	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
 
-	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), 
+	error = -ENOMEM;
+	if (!dentry->d_fsdata) {
+		dentry->d_fsdata = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+		if (!dentry->d_fsdata)
+			goto out;
+	}
+	dentry->d_op = &nfs_dentry_operations;
+
+	error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dentry->d_parent), 
 				dentry->d_name.name, &fhandle, &fattr);
 	inode = NULL;
 	if (error == -ENOENT)
 		goto no_entry;
 	if (!error) {
 		error = -EACCES;
-		inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+		nfs_set_fh(dentry, &fhandle);
+		inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
 		if (inode) {
 #ifdef NFS_PARANOIA
 if (inode->i_count > (S_ISDIR(inode->i_mode) ? 1 : inode->i_nlink))
@@ -506,7 +537,6 @@
 inode->i_ino, inode->i_count, inode->i_nlink);
 #endif
 	    no_entry:
-			dentry->d_op = &nfs_dentry_operations;
 			d_add(dentry, inode);
 			nfs_renew_times(dentry);
 			error = 0;
@@ -530,6 +560,7 @@
 	struct inode *inode;
 	int error = -EACCES;
 
+	nfs_set_fh(dentry, fhandle);
 	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
 	if (inode) {
 #ifdef NFS_PARANOIA
@@ -578,7 +609,7 @@
 	 * Invalidate the dir cache before the operation to avoid a race.
 	 */
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 			dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
@@ -616,7 +647,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 	if (!error)
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
@@ -652,7 +683,7 @@
 	sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
 
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, &sattr, &fhandle, &fattr);
 
 	/*
@@ -717,8 +748,8 @@
 		dentry->d_inode->i_nlink --;
 	d_delete(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_rmdir(NFS_SERVER(dir),
-				NFS_FH(dir), dentry->d_name.name);
+	error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+				dentry->d_name.name);
 	if (!error) {
 		if (rehash)
 			d_add(dentry, NULL);
@@ -839,8 +870,8 @@
 
 	nfs_invalidate_dircache(dir);
 	error = nfs_proc_rename(NFS_SERVER(dir),
-				NFS_FH(dir), dentry->d_name.name,
-				NFS_FH(dir), silly);
+				NFS_FH(dentry->d_parent), dentry->d_name.name,
+				NFS_FH(dentry->d_parent), silly);
 	if (!error) {
 		nfs_renew_times(dentry);
 		d_move(dentry, sdentry);
@@ -914,8 +945,8 @@
 		d_delete(dentry);
 	}
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_remove(NFS_SERVER(dir),
-					NFS_FH(dir), dentry->d_name.name);
+	error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
+				dentry->d_name.name);
 	/*
 	 * Rehash the negative dentry if the operation succeeded.
 	 */
@@ -998,7 +1029,7 @@
 	 */
 	d_drop(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
+	error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
 				dentry->d_name.name, symname, &sattr);
 	if (!error) {
 		nfs_renew_times(dentry->d_parent);
@@ -1012,13 +1043,14 @@
 }
 
 static int 
-nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
+	struct inode *inode = old_dentry->d_inode;
 	int error;
 
-	dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
-				inode->i_dev, inode->i_ino,
-				dir->i_dev, dir->i_ino, dentry->d_name.name);
+	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
+		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
 		printk("nfs_link: dir is NULL or not a directory\n");
@@ -1029,13 +1061,21 @@
 	if (dentry->d_name.len > NFS_MAXNAMLEN)
 		goto out;
 
+	/*
+	 * Drop the dentry in advance to force a new lookup.
+	 * Since nfs_proc_link doesn't return a filehandle,
+	 * we can't use the existing dentry.
+	 */
+	d_drop(dentry);
 	nfs_invalidate_dircache(dir);
-	error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
-				dentry->d_name.name);
+	error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
+				NFS_FH(dentry->d_parent), dentry->d_name.name);
 	if (!error) {
-		inode->i_count ++;
-		inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
-		d_instantiate(dentry, inode);
+ 		/*
+		 * Update the link count immediately, as some apps
+		 * (e.g. pine) test this after making a link.
+		 */
+		inode->i_nlink++;
 	}
 out:
 	return error;
@@ -1181,9 +1221,9 @@
 
 	nfs_invalidate_dircache(new_dir);
 	nfs_invalidate_dircache(old_dir);
-	error = nfs_proc_rename(NFS_SERVER(old_dir),
-				NFS_FH(old_dir), old_dentry->d_name.name,
-				NFS_FH(new_dir), new_dentry->d_name.name);
+	error = nfs_proc_rename(NFS_DSERVER(old_dentry),
+			NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
+			NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
 	if (!error) {
 		/* Update the dcache if needed */
 		if (rehash)
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.1.77/linux/fs/nfs/file.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/nfs/file.c	Sun Jan  4 00:53:41 1998
@@ -109,15 +109,14 @@
 static ssize_t
 nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
 {
-	struct inode * inode = file->f_dentry->d_inode;
+	struct dentry * dentry = file->f_dentry;
 	ssize_t result;
 
-	dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
-			inode->i_dev, inode->i_ino,
-			(unsigned long) count,
-			(unsigned long) *ppos);
+	dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		(unsigned long) count, (unsigned long) *ppos);
 
-	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 	if (!result)
 		result = generic_file_read(file, buf, count, ppos);
 	return result;
@@ -126,12 +125,13 @@
 static int
 nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
-	struct inode *inode = file->f_dentry->d_inode;
+	struct dentry *dentry = file->f_dentry;
 	int	status;
 
-	dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "nfs: mmap(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	status = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 	if (!status)
 		status = generic_file_mmap(file, vma);
 	return status;
@@ -163,31 +163,33 @@
 static ssize_t
 nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	struct inode * inode = file->f_dentry->d_inode;
+	struct dentry * dentry = file->f_dentry;
+	struct inode * inode = dentry->d_inode;
 	ssize_t result;
 
-	dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
-			inode->i_dev, inode->i_ino, inode->i_count,
-			(unsigned long) count, (unsigned long) *ppos);
+	dfprintk(VFS, "nfs: write(%s/%s (%d), %lu@%lu)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		inode->i_count, (unsigned long) count, (unsigned long) *ppos);
 
 	if (!inode) {
 		printk("nfs_file_write: inode = NULL\n");
 		return -EINVAL;
 	}
-	if (IS_SWAPFILE(inode)) {
-		printk("NFS: attempt to write to active swap file!\n");
-		return -EBUSY;
-	}
-	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	result = -EBUSY;
+	if (IS_SWAPFILE(inode))
+		goto out_swapfile;
+	result = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 	if (result)
 		goto out;
 
-	/* N.B. This should be impossible now -- inodes can't change mode */
-	if (!S_ISREG(inode->i_mode)) {
-		printk("nfs_file_write: write to non-file, mode %07o\n",
-			inode->i_mode);
-		return -EINVAL;
-	}
+#ifdef NFS_PARANOIA
+/* N.B. This should be impossible now -- inodes can't change mode */
+if (!S_ISREG(inode->i_mode)) {
+	printk("nfs_file_write: write to non-file, mode %07o\n",
+		inode->i_mode);
+	return -EINVAL;
+}
+#endif
 	result = count;
 	if (!count)
 		goto out;
@@ -198,6 +200,10 @@
 		result = generic_file_write(file, buf, count, ppos);
 out:
 	return result;
+
+out_swapfile:
+	printk("NFS: attempt to write to active swap file!\n");
+	goto out;
 }
 
 /*
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.77/linux/fs/nfs/inode.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/nfs/inode.c	Sun Jan  4 00:53:41 1998
@@ -38,7 +38,7 @@
 static void nfs_read_inode(struct inode *);
 static void nfs_put_inode(struct inode *);
 static void nfs_delete_inode(struct inode *);
-static int  nfs_notify_change(struct inode *, struct iattr *);
+static int  nfs_notify_change(struct dentry *, struct iattr *);
 static void nfs_put_super(struct super_block *);
 static int  nfs_statfs(struct super_block *, struct statfs *, int);
 
@@ -180,15 +180,15 @@
 nfs_read_super(struct super_block *sb, void *raw_data, int silent)
 {
 	struct nfs_mount_data	*data = (struct nfs_mount_data *) raw_data;
-	struct sockaddr_in	srvaddr;
 	struct nfs_server	*server;
-	struct rpc_timeout	timeparms;
 	struct rpc_xprt		*xprt;
 	struct rpc_clnt		*clnt;
+	struct nfs_fh		*root_fh;
+	struct inode		*root_inode;
 	unsigned int		authflavor;
 	int			tcp;
-	kdev_t			dev = sb->s_dev;
-	struct inode		*root_inode;
+	struct sockaddr_in	srvaddr;
+	struct rpc_timeout	timeparms;
 
 	MOD_INC_USE_COUNT;
 	if (!data)
@@ -211,7 +211,6 @@
 	lock_super(sb);
 
 	sb->s_magic      = NFS_SUPER_MAGIC;
-	sb->s_dev        = dev;
 	sb->s_op         = &nfs_sops;
 	sb->s_blocksize  = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
 	sb->u.nfs_sb.s_root = data->root;
@@ -234,21 +233,19 @@
 	timeparms.to_maxval  = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
 	timeparms.to_exponential = 1;
 
-	/* Choose authentication flavor */
-	if (data->flags & NFS_MOUNT_SECURE) {
-		authflavor = RPC_AUTH_DES;
-	} else if (data->flags & NFS_MOUNT_KERBEROS) {
-		authflavor = RPC_AUTH_KRB;
-	} else {
-		authflavor = RPC_AUTH_UNIX;
-	}
-
 	/* Now create transport and client */
 	xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
 						&srvaddr, &timeparms);
 	if (xprt == NULL)
 		goto out_no_xprt;
 
+	/* Choose authentication flavor */
+	authflavor = RPC_AUTH_UNIX;
+	if (data->flags & NFS_MOUNT_SECURE)
+		authflavor = RPC_AUTH_DES;
+	else if (data->flags & NFS_MOUNT_KERBEROS)
+		authflavor = RPC_AUTH_KRB;
+
 	clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
 						NFS_VERSION, authflavor);
 	if (clnt == NULL)
@@ -267,12 +264,20 @@
 	 * Keep the super block locked while we try to get 
 	 * the root fh attributes.
 	 */
+	root_fh = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL);
+	if (!root_fh)
+		goto out_no_fh;
+	*root_fh = data->root;
+
 	root_inode = nfs_fhget(sb, &data->root, NULL);
 	if (!root_inode)
 		goto out_no_root;
 	sb->s_root = d_alloc_root(root_inode, NULL);
 	if (!sb->s_root)
 		goto out_no_root;
+	sb->s_root->d_op = &nfs_dentry_operations;
+	sb->s_root->d_fsdata = root_fh;
+
 	/* We're airborne */
 	unlock_super(sb);
 
@@ -285,6 +290,8 @@
 out_no_root:
 	printk("nfs_read_super: get root inode failed\n");
 	iput(root_inode);
+	kfree(root_fh);
+out_no_fh:
 	rpciod_down();
 	goto out_shutdown;
 
@@ -352,36 +359,28 @@
 nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle,
 				  struct nfs_fattr *fattr)
 {
-	struct nfs_fattr newfattr;
 	int error;
-	struct inode *inode;
+	struct inode *inode = NULL;
+	struct nfs_fattr newfattr;
 
-	if (!sb) {
-		printk("nfs_fhget: super block is NULL\n");
-		return NULL;
-	}
+	if (!sb)
+		goto out_bad_args;
 	if (!fattr) {
-		error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,
-			&newfattr);
-		if (error) {
-			printk("nfs_fhget: getattr error = %d\n", -error);
-			return NULL;
-		}
 		fattr = &newfattr;
-	}
-	if (!(inode = iget(sb, fattr->fileid))) {
-		printk("nfs_fhget: iget failed\n");
-		return NULL;
-	}
+		error = nfs_proc_getattr(&sb->u.nfs_sb.s_server, fhandle,fattr);
+		if (error)
+			goto out_bad_attr;
+	}
+	inode = iget(sb, fattr->fileid);
+	if (!inode)
+		goto out_no_inode;
 #ifdef NFS_PARANOIA
 if (inode->i_dev != sb->s_dev)
 printk("nfs_fhget: impossible\n");
 #endif
 
-	if (inode->i_ino != fattr->fileid) {
-		printk("nfs_fhget: unexpected inode from iget\n");
-		return inode;
-	}
+	if (inode->i_ino != fattr->fileid)
+		goto out_bad_id;
 
 	/*
 	 * Check whether the mode has been set, as we only want to
@@ -412,29 +411,41 @@
 		inode->i_size  = fattr->size;
 		inode->i_mtime = fattr->mtime.seconds;
 		NFS_OLDMTIME(inode) = fattr->mtime.seconds;
-		*NFS_FH(inode) = *fhandle;
 	}
-	if (memcmp(NFS_FH(inode), fhandle, sizeof(struct nfs_fh)))
-		printk("nfs_fhget: fhandle changed!\n");
 	nfs_refresh_inode(inode, fattr);
 	dprintk("NFS: fhget(%x/%ld ct=%d)\n",
 		inode->i_dev, inode->i_ino,
 		inode->i_count);
 
+out:
 	return inode;
+
+out_bad_args:
+	printk("nfs_fhget: super block is NULL\n");
+	goto out;
+out_bad_attr:
+	printk("nfs_fhget: getattr error = %d\n", -error);
+	goto out;
+out_no_inode:
+	printk("nfs_fhget: iget failed\n");
+	goto out;
+out_bad_id:
+	printk("nfs_fhget: unexpected inode from iget\n");
+	goto out;
 }
 
 int
-nfs_notify_change(struct inode *inode, struct iattr *attr)
+nfs_notify_change(struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *inode = dentry->d_inode;
+	int error;
 	struct nfs_sattr sattr;
 	struct nfs_fattr fattr;
-	int error;
 
 	/*
 	 * Make sure the inode is up-to-date.
 	 */
-	error = nfs_revalidate(inode);
+	error = nfs_revalidate(dentry);
 	if (error) {
 #ifdef NFS_PARANOIA
 printk("nfs_notify_change: revalidate failed, error=%d\n", error);
@@ -470,7 +481,7 @@
 		sattr.atime.useconds = 0;
 	}
 
-	error = nfs_proc_setattr(NFS_SERVER(inode), NFS_FH(inode),
+	error = nfs_proc_setattr(NFS_DSERVER(dentry), NFS_FH(dentry),
 				&sattr, &fattr);
 	if (error)
 		goto out;
@@ -497,9 +508,9 @@
  * Externally visible revalidation function
  */
 int
-nfs_revalidate(struct inode *inode)
+nfs_revalidate(struct dentry *dentry)
 {
-	return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 }
 
 /*
@@ -507,38 +518,43 @@
  * the cached attributes have to be refreshed.
  */
 int
-_nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+_nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
 {
-	struct nfs_fattr fattr;
+	struct inode	*inode = dentry->d_inode;
 	int		 status = 0;
+	struct nfs_fattr fattr;
 
 	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
 		goto out;
 
-	dfprintk(PAGECACHE, "NFS: revalidating %x/%ld inode\n",
-			inode->i_dev, inode->i_ino);
-	status = nfs_proc_getattr(server, NFS_FH(inode), &fattr);
+	dfprintk(PAGECACHE, "NFS: revalidating %s/%s, ino=%ld\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		inode->i_ino);
+	status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
 	if (status) {
 #ifdef NFS_PARANOIA
-printk("nfs_revalidate_inode: getattr failed, error=%d\n", status);
+printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
 #endif
-		goto done;
+		goto out;
 	}
 
 	status = nfs_refresh_inode(inode, &fattr);
-	if (status)
-		goto done;
+	if (status) {
+#ifdef NFS_PARANOIA
+printk("nfs_revalidate_inode: %s/%s refresh failed, ino=%ld, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
+#endif
+		goto out;
+	}
 	if (fattr.mtime.seconds == NFS_OLDMTIME(inode)) {
 		/* Update attrtimeo value */
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
 			NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
 	}
 	NFS_OLDMTIME(inode) = fattr.mtime.seconds;
-
-done:
-	dfprintk(PAGECACHE,
-		"NFS: inode %x/%ld revalidation complete (status %d).\n",
-				inode->i_dev, inode->i_ino, status);
+	dfprintk(PAGECACHE, "NFS: %s/%s revalidation complete\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 out:
 	return status;
 }
@@ -569,7 +585,8 @@
 		goto out;
 	}
 	if (inode->i_ino != fattr->fileid) {
-		printk("nfs_refresh_inode: inode number mismatch\n");
+		printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
+			inode->i_ino, fattr->fileid);
 		goto out;
 	}
 
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/read.c linux/fs/nfs/read.c
--- v2.1.77/linux/fs/nfs/read.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/nfs/read.c	Sun Jan  4 00:53:41 1998
@@ -65,8 +65,8 @@
 /*
  * Read a page synchronously.
  */
-int
-nfs_readpage_sync(struct inode *inode, struct page *page)
+static int
+nfs_readpage_sync(struct dentry *dentry, struct inode *inode, struct page *page)
 {
 	struct nfs_rreq	rqst;
 	unsigned long	offset = page->offset;
@@ -83,12 +83,13 @@
 		if (count < rsize)
 			rsize = count;
 
-		dprintk("NFS: nfs_proc_read(%s, (%x,%lx), %ld, %d, %p)\n",
-			NFS_SERVER(inode)->hostname, inode->i_dev,
-			inode->i_ino, offset, rsize, buffer);
+		dprintk("NFS: nfs_proc_read(%s, (%s/%s), %ld, %d, %p)\n",
+			NFS_SERVER(inode)->hostname,
+			dentry->d_parent->d_name.name, dentry->d_name.name,
+			offset, rsize, buffer);
 
 		/* Set up arguments and perform rpc call */
-		nfs_readreq_setup(&rqst, NFS_FH(inode), offset, buffer, rsize);
+		nfs_readreq_setup(&rqst, NFS_FH(dentry), offset, buffer, rsize);
 		result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ,
 					&rqst.ra_args, &rqst.ra_res, flags);
 
@@ -160,7 +161,8 @@
 }
 
 static inline int
-nfs_readpage_async(struct inode *inode, struct page *page)
+nfs_readpage_async(struct dentry *dentry, struct inode *inode,
+			struct page *page)
 {
 	struct nfs_rreq	*req;
 	int		result, flags;
@@ -175,7 +177,7 @@
 	}
 
 	/* Initialize request */
-	nfs_readreq_setup(req, NFS_FH(inode), page->offset,
+	nfs_readreq_setup(req, NFS_FH(dentry), page->offset,
 				(void *) page_address(page), PAGE_SIZE);
 	req->ra_inode = inode;
 	req->ra_page = page;
@@ -209,8 +211,9 @@
  *  -	The server is congested.
  */
 int
-nfs_readpage(struct inode *inode, struct page *page)
+nfs_readpage(struct dentry *dentry, struct page *page)
 {
+	struct inode *inode = dentry->d_inode;
 	unsigned long	address;
 	int		error = -1;
 
@@ -218,11 +221,11 @@
 	set_bit(PG_locked, &page->flags);
 	address = page_address(page);
 	atomic_inc(&page->count);
-	if (!IS_SWAPFILE(inode) && !PageError(page)
-	 && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
-		error = nfs_readpage_async(inode, page);
+	if (!IS_SWAPFILE(inode) && !PageError(page) &&
+	    NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+		error = nfs_readpage_async(dentry, inode, page);
 	if (error < 0)		/* couldn't enqueue */
-		error = nfs_readpage_sync(inode, page);
+		error = nfs_readpage_sync(dentry, inode, page);
 	if (error < 0 && IS_SWAPFILE(inode))
 		printk("Aiee.. nfs swap-in of page failed!\n");
 	free_page(address);
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.1.77/linux/fs/nfs/symlink.c	Mon Aug 18 18:19:46 1997
+++ linux/fs/nfs/symlink.c	Sun Jan  4 00:53:41 1998
@@ -18,8 +18,8 @@
 
 #include <asm/uaccess.h>
 
-static int nfs_readlink(struct inode *, char *, int);
-static struct dentry *nfs_follow_link(struct inode *, struct dentry *);
+static int nfs_readlink(struct dentry *, char *, int);
+static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);
 
 /*
  * symlinks can't do much...
@@ -44,19 +44,20 @@
 	NULL			/* permission */
 };
 
-static int nfs_readlink(struct inode *inode, char *buffer, int buflen)
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	int error;
 	unsigned int len;
 	char *res;
 	void *mem;
 
-	dfprintk(VFS, "nfs: readlink(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "nfs: readlink(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
 	if (buflen > NFS_MAXPATHLEN)
 		buflen = NFS_MAXPATHLEN;
-	error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
-		&res, &len, buflen);
+	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+					&mem, &res, &len, buflen);
 	if (! error) {
 		copy_to_user(buffer, res, len);
 		put_user('\0', buffer + len);
@@ -66,34 +67,41 @@
 	return error;
 }
 
-static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base)
+static struct dentry *
+nfs_follow_link(struct dentry * dentry, struct dentry *base)
 {
 	int error;
 	unsigned int len;
 	char *res;
 	void *mem;
 	char *path;
+	struct dentry *result;
 
-	dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino);
+	dfprintk(VFS, "nfs: follow_link(%s/%s)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
-		&res, &len, NFS_MAXPATHLEN);
+	error = nfs_proc_readlink(NFS_DSERVER(dentry), NFS_FH(dentry),
+				 &mem, &res, &len, NFS_MAXPATHLEN);
+	result = ERR_PTR(error);
+	if (error)
+		goto out_dput;
 
-	if (error) {
-		dput(base);
-		return ERR_PTR(error);
-	}
+	result = ERR_PTR(-ENOMEM);
 	path = kmalloc(len + 1, GFP_KERNEL);
-	if (!path) {
-		dput(base);
-		kfree(mem);
-		return ERR_PTR(-ENOMEM);
-	}
+	if (!path)
+		goto out_mem;
 	memcpy(path, res, len);
 	path[len] = 0;
 	kfree(mem);
 
-	base = lookup_dentry(path, base, 1);
+	result = lookup_dentry(path, base, 1);
 	kfree(path);
-	return base;
+out:
+	return result;
+
+out_mem:
+	kfree(mem);
+out_dput:
+	dput(base);
+	goto out;
 }
diff -u --recursive --new-file v2.1.77/linux/fs/nfs/write.c linux/fs/nfs/write.c
--- v2.1.77/linux/fs/nfs/write.c	Sun Dec 21 22:36:16 1997
+++ linux/fs/nfs/write.c	Sun Jan  4 00:53:41 1998
@@ -79,6 +79,7 @@
 struct nfs_wreq {
 	struct rpc_listitem	wb_list;	/* linked list of req's */
 	struct rpc_task		wb_task;	/* RPC task */
+	struct dentry *		wb_dentry;	/* dentry referenced */
 	struct inode *		wb_inode;	/* inode referenced */
 	struct page *		wb_page;	/* page to be written */
 	unsigned int		wb_offset;	/* offset within page */
@@ -169,17 +170,17 @@
  * Offset is the data offset within the page.
  */
 static int
-nfs_writepage_sync(struct inode *inode, struct page *page,
-				unsigned long offset, unsigned int count)
+nfs_writepage_sync(struct dentry *dentry, struct inode *inode,
+		struct page *page, unsigned long offset, unsigned int count)
 {
-	struct nfs_fattr fattr;
 	unsigned int	wsize = NFS_SERVER(inode)->wsize;
 	int		result, refresh = 0, written = 0;
 	u8		*buffer;
+	struct nfs_fattr fattr;
 
-	dprintk("NFS:      nfs_writepage_sync(%x/%ld %d@%ld)\n",
-				inode->i_dev, inode->i_ino,
-				count, page->offset + offset);
+	dprintk("NFS:      nfs_writepage_sync(%s/%s %d@%ld)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		count, page->offset + offset);
 
 	buffer = (u8 *) page_address(page) + offset;
 	offset += page->offset;
@@ -188,7 +189,7 @@
 		if (count < wsize && !IS_SWAPFILE(inode))
 			wsize = count;
 
-		result = nfs_proc_write(NFS_SERVER(inode), NFS_FH(inode),
+		result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
 					IS_SWAPFILE(inode), offset, wsize,
 					buffer, &fattr);
 
@@ -380,16 +381,16 @@
  * Create and initialize a writeback request
  */
 static inline struct nfs_wreq *
-create_write_request(struct inode *inode, struct page *page,
-			unsigned int offset, unsigned int bytes)
+create_write_request(struct dentry *dentry, struct inode *inode,
+		struct page *page, unsigned int offset, unsigned int bytes)
 {
 	struct nfs_wreq *wreq;
 	struct rpc_clnt	*clnt = NFS_CLIENT(inode);
 	struct rpc_task	*task;
 
-	dprintk("NFS:      create_write_request(%x/%ld, %ld+%d)\n",
-				inode->i_dev, inode->i_ino,
-				page->offset + offset, bytes);
+	dprintk("NFS:      create_write_request(%s/%s, %ld+%d)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		page->offset + offset, bytes);
 
 	/* FIXME: Enforce hard limit on number of concurrent writes? */
 
@@ -408,6 +409,7 @@
 		goto out_req;
 
 	/* Put the task on inode's writeback request list. */
+	wreq->wb_dentry = dentry;
 	wreq->wb_inode  = inode;
 	wreq->wb_pid    = current->pid;
 	wreq->wb_page   = page;
@@ -504,9 +506,9 @@
  * (for now), and we currently do this synchronously only.
  */
 int
-nfs_writepage(struct inode *inode, struct page *page)
+nfs_writepage(struct dentry *dentry, struct page *page)
 {
-	return nfs_writepage_sync(inode, page, 0, PAGE_SIZE);
+	return nfs_writepage_sync(dentry, dentry->d_inode, page, 0, PAGE_SIZE);
 }
 
 /*
@@ -516,16 +518,17 @@
  * things with a page scheduled for an RPC call (e.g. invalidate it).
  */
 int
-nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
+nfs_updatepage(struct dentry *dentry, struct page *page, const char *buffer,
 			unsigned long offset, unsigned int count, int sync)
 {
+	struct inode *inode = dentry->d_inode;
 	struct nfs_wreq	*req;
 	int		status = 0, page_locked = 1;
 	u8		*page_addr;
 
-	dprintk("NFS:      nfs_updatepage(%x/%ld %d@%ld, sync=%d)\n",
-				inode->i_dev, inode->i_ino,
-				count, page->offset+offset, sync);
+	dprintk("NFS:      nfs_updatepage(%s/%s %d@%ld, sync=%d)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+		count, page->offset+offset, sync);
 
 	set_bit(PG_locked, &page->flags);
 	page_addr = (u8 *) page_address(page);
@@ -535,7 +538,7 @@
 	 */
 	if (NFS_SERVER(inode)->wsize < PAGE_SIZE) {
 		copy_from_user(page_addr + offset, buffer, count);
-		return nfs_writepage_sync(inode, page, offset, count);
+		return nfs_writepage_sync(dentry, inode, page, offset, count);
 	}
 
 	/*
@@ -560,7 +563,7 @@
 
 	/* Create the write request. */
 	status = -ENOBUFS;
-	req = create_write_request(inode, page, offset, count);
+	req = create_write_request(dentry, inode, page, offset, count);
 	if (!req)
 		goto done;
 
@@ -823,7 +826,7 @@
 {
 	struct nfs_wreq	*req = (struct nfs_wreq *) task->tk_calldata;
 	struct page	*page = req->wb_page;
-	struct inode	*inode = req->wb_inode;
+	struct dentry	*dentry = req->wb_dentry;
 
 	dprintk("NFS: %4d nfs_wback_lock (status %d flags %x)\n",
 			task->tk_pid, task->tk_status, req->wb_flags);
@@ -856,7 +859,7 @@
 	}
 
 	/* Setup the task struct for a writeback call */
-	req->wb_args->fh = NFS_FH(inode);
+	req->wb_args->fh = NFS_FH(dentry);
 	req->wb_args->offset = page->offset + req->wb_offset;
 	req->wb_args->count  = req->wb_bytes;
 	req->wb_args->buffer = (void *) (page_address(page) + req->wb_offset);
@@ -873,16 +876,12 @@
 nfs_wback_result(struct rpc_task *task)
 {
 	struct nfs_wreq *req = (struct nfs_wreq *) task->tk_calldata;
-	struct inode	*inode;
-	struct page	*page;
-	int		status;
+	struct inode	*inode = req->wb_inode;
+	struct page	*page  = req->wb_page;
+	int		status = task->tk_status;
 
 	dprintk("NFS: %4d nfs_wback_result (status %d)\n",
-				task->tk_pid, task->tk_status);
-
-	inode  = req->wb_inode;
-	page   = req->wb_page;
-	status = task->tk_status;
+		task->tk_pid, status);
 
 	if (status < 0) {
 		/*
diff -u --recursive --new-file v2.1.77/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.1.77/linux/fs/nfsd/vfs.c	Tue Dec 23 16:31:00 1997
+++ linux/fs/nfsd/vfs.c	Sun Jan  4 00:53:43 1998
@@ -230,7 +230,7 @@
 	if (iap->ia_valid) {
 		iap->ia_valid |= ATTR_CTIME;
 		iap->ia_ctime = CURRENT_TIME;
-		err = notify_change(inode, iap);
+		err = notify_change(dentry, iap);
 		if (err)
 			return nfserrno(-err);
 		if (EX_ISSYNC(fhp->fh_export))
@@ -475,7 +475,7 @@
 
 		ia.ia_valid = ATTR_MODE;
 		ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
-		notify_change(inode, &ia);
+		notify_change(dentry, &ia);
 	}
 
 	fh_unlock(fhp);			/* unlock inode */
@@ -668,7 +668,7 @@
 	fh_lock(fhp);
 	newattrs.ia_size = size;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
-	err = notify_change(inode, &newattrs);
+	err = notify_change(dentry, &newattrs);
 	if (!err) {
 		vmtruncate(inode, size);
 		if (inode->i_op && inode->i_op->truncate)
@@ -710,7 +710,7 @@
 	UPDATE_ATIME(inode);
 	/* N.B. Why does this call need a get_fs()?? */
 	oldfs = get_fs(); set_fs(KERNEL_DS);
-	err = inode->i_op->readlink(inode, buf, *lenp);
+	err = inode->i_op->readlink(dentry, buf, *lenp);
 	set_fs(oldfs);
 
 	if (err < 0)
@@ -789,7 +789,7 @@
 nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
 				char *fname, int len, struct svc_fh *tfhp)
 {
-	struct dentry	*ddir, *dnew;
+	struct dentry	*ddir, *dnew, *dold;
 	struct inode	*dirp, *dest;
 	int		err;
 
@@ -811,12 +811,14 @@
 	err = -EEXIST;
 	if (dnew->d_inode)
 		goto dput_and_out;
-	dest = tfhp->fh_dentry->d_inode;
 
 	err = -EPERM;
 	if (!len)
 		goto dput_and_out;
 
+	dold = tfhp->fh_dentry;
+	dest = dold->d_inode;
+
 	err = -EACCES;
 	if (nfsd_iscovered(ddir, ffhp->fh_export))
 		goto dput_and_out;
@@ -830,7 +832,7 @@
 		goto dput_and_out;
 
 	fh_lock(ffhp);
-	err = dirp->i_op->link(dest, dirp, dnew);
+	err = dirp->i_op->link(dold, dirp, dnew);
 	fh_unlock(ffhp);
 
 	if (!err && EX_ISSYNC(ffhp->fh_export)) {
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp437.c linux/fs/nls/nls_cp437.c
--- v2.1.77/linux/fs/nls/nls_cp437.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp437.c	Sun Jan  4 10:55:09 1998
@@ -355,6 +355,7 @@
 	page20, NULL,   page22, page23, NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,6 +390,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp737.c linux/fs/nls/nls_cp737.c
--- v2.1.77/linux/fs/nls/nls_cp737.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp737.c	Sun Jan  4 10:55:09 1998
@@ -283,6 +283,7 @@
 	page20, NULL,   page22, NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,6 +318,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp775.c linux/fs/nls/nls_cp775.c
--- v2.1.77/linux/fs/nls/nls_cp775.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp775.c	Sun Jan  4 10:55:09 1998
@@ -283,6 +283,7 @@
 	page20, NULL,   page22, NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -317,6 +318,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp850.c linux/fs/nls/nls_cp850.c
--- v2.1.77/linux/fs/nls/nls_cp850.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp850.c	Sun Jan  4 10:55:09 1998
@@ -247,6 +247,7 @@
 	page20, NULL,   NULL,   NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp852.c linux/fs/nls/nls_cp852.c
--- v2.1.77/linux/fs/nls/nls_cp852.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp852.c	Sun Jan  4 10:55:09 1998
@@ -247,6 +247,7 @@
 	NULL,   NULL,   NULL,   NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0x00, 0xfc, 0x00, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp855.c linux/fs/nls/nls_cp855.c
--- v2.1.77/linux/fs/nls/nls_cp855.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp855.c	Sun Jan  4 10:55:09 1998
@@ -247,6 +247,7 @@
 	NULL,   page21, NULL,   NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -281,6 +282,7 @@
 	0xf0, 0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, /* 0xf0-0xf7 */
 	0xf8, 0x00, 0xfa, 0x00, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp857.c linux/fs/nls/nls_cp857.c
--- v2.1.77/linux/fs/nls/nls_cp857.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp857.c	Sun Jan  4 10:55:09 1998
@@ -211,6 +211,7 @@
 	NULL,   NULL,   NULL,   NULL,   NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -245,6 +246,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp860.c linux/fs/nls/nls_cp860.c
--- v2.1.77/linux/fs/nls/nls_cp860.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp860.c	Sun Jan  4 10:55:09 1998
@@ -319,6 +319,7 @@
 	page20, NULL,   page22, page23, NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -353,6 +354,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/nls/nls_cp861.c linux/fs/nls/nls_cp861.c
--- v2.1.77/linux/fs/nls/nls_cp861.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/nls/nls_cp861.c	Sun Jan  4 10:55:09 1998
@@ -355,6 +355,7 @@
 	page20, NULL,   page22, page23, NULL,   page25, NULL,   NULL,   
 };
 
+#if 0
 static unsigned char charset2upper[256] = {
 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
 	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
@@ -389,6 +390,7 @@
 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
 	0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
 };
+#endif
 
 
 static void inc_use_count(void)
diff -u --recursive --new-file v2.1.77/linux/fs/open.c linux/fs/open.c
--- v2.1.77/linux/fs/open.c	Tue Sep 23 16:48:49 1997
+++ linux/fs/open.c	Sun Jan  4 00:53:41 1998
@@ -69,15 +69,16 @@
 	return error;
 }
 
-int do_truncate(struct inode *inode, unsigned long length)
+int do_truncate(struct dentry *dentry, unsigned long length)
 {
+	struct inode *inode = dentry->d_inode;
 	int error;
 	struct iattr newattrs;
 
 	down(&inode->i_sem);
 	newattrs.ia_size = length;
 	newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
-	error = notify_change(inode, &newattrs);
+	error = notify_change(dentry, &newattrs);
 	if (!error) {
 		/* truncate virtual mappings of this file */
 		vmtruncate(inode, length);
@@ -128,7 +129,7 @@
 	if (!error) {
 		if (inode->i_sb && inode->i_sb->dq_op)
 			inode->i_sb->dq_op->initialize(inode, -1);
-		error = do_truncate(inode, length);
+		error = do_truncate(dentry, length);
 	}
 	put_write_access(inode);
 dput_and_out:
@@ -161,7 +162,7 @@
 					  length<inode->i_size ? length : inode->i_size,
 					  abs(inode->i_size - length));
 		if (!error)
-			error = do_truncate(inode, length);
+			error = do_truncate(dentry, length);
 	}
 	unlock_kernel();
 	return error;
@@ -214,7 +215,7 @@
 		    (error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
-	error = notify_change(inode, &newattrs);
+	error = notify_change(dentry, &newattrs);
 dput_and_out:
 	dput(dentry);
 out:
@@ -261,7 +262,7 @@
 		if ((error = permission(inode,MAY_WRITE)) != 0)
 			goto dput_and_out;
 	}
-	error = notify_change(inode, &newattrs);
+	error = notify_change(dentry, &newattrs);
 dput_and_out:
 	dput(dentry);
 out:
@@ -441,7 +442,7 @@
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	err = notify_change(inode, &newattrs);
+	err = notify_change(dentry, &newattrs);
 out:
 	unlock_kernel();
 	return err;
@@ -474,7 +475,7 @@
 		mode = inode->i_mode;
 	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
-	error = notify_change(inode, &newattrs);
+	error = notify_change(dentry, &newattrs);
 
 dput_and_out:
 	dput(dentry);
@@ -530,11 +531,11 @@
 		error = -EDQUOT;
 		if (inode->i_sb->dq_op->transfer(inode, &newattrs, 0))
 			goto out;
-		error = notify_change(inode, &newattrs);
+		error = notify_change(dentry, &newattrs);
 		if (error)
 			inode->i_sb->dq_op->transfer(inode, &newattrs, 1);
 	} else
-		error = notify_change(inode, &newattrs);
+		error = notify_change(dentry, &newattrs);
 out:
 	return error;
 }
diff -u --recursive --new-file v2.1.77/linux/fs/proc/generic.c linux/fs/proc/generic.c
--- v2.1.77/linux/fs/proc/generic.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/proc/generic.c	Mon Jan  5 11:18:33 1998
@@ -298,8 +298,6 @@
 	}
 
 	if (de) {
-printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n",
-parent->nlink, de->nlink);
 		proc_unregister(parent, de->low_ino);
 		de->nlink = 0;
 		de->deleted = 1;
diff -u --recursive --new-file v2.1.77/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.77/linux/fs/proc/link.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/proc/link.c	Sun Jan  4 23:58:28 1998
@@ -15,8 +15,8 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
-static int proc_readlink(struct inode *, char *, int);
-static struct dentry * proc_follow_link(struct inode *, struct dentry *);
+static int proc_readlink(struct dentry *, char *, int);
+static struct dentry * proc_follow_link(struct dentry *, struct dentry *);
 
 /*
  * PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke 
@@ -60,8 +60,10 @@
 	NULL			/* permission */
 };
 
-static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * proc_follow_link(struct dentry *dentry,
+					struct dentry *base)
 {
+	struct inode *inode = dentry->d_inode;
 	struct task_struct *p;
 	struct dentry * result;
 	int ino, pid;
@@ -73,7 +75,7 @@
 	error = permission(inode, MAY_EXEC);
 	result = ERR_PTR(error);
 	if (error)
-		return result;
+		goto out;
 
 	ino = inode->i_ino;
 	pid = ino >> 16;
@@ -82,7 +84,7 @@
 	p = find_task_by_pid(pid);
 	result = ERR_PTR(-ENOENT);
 	if (!p)
-		return result;
+		goto out;
 
 	switch (ino) {
 		case PROC_PID_CWD:
@@ -126,30 +128,56 @@
 				break;
 			}
 	}
+out:
 	return result;
 }
 
-static int proc_readlink(struct inode * inode, char * buffer, int buflen)
+/*
+ * This pretty-prints the pathname of a dentry,
+ * clarifying sockets etc.
+ */
+static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen)
+{
+	struct inode * inode;
+	char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern;
+	int len;
+
+	/* Check for special dentries.. */
+	pattern = NULL;
+	inode = dentry->d_inode;
+	if (inode && dentry->d_parent == dentry) {
+		if (inode->i_sock)
+			pattern = "socket:[%lu]";
+		if (inode->i_pipe)
+			pattern = "pipe:[%lu]";
+	}
+	
+	if (pattern) {
+		len = sprintf(tmp, pattern, inode->i_ino);
+		path = tmp;
+	} else {
+		path = d_path(dentry, tmp, PAGE_SIZE);
+		len = tmp + PAGE_SIZE - path;
+	}
+
+	if (len < buflen)
+		buflen = len;
+	dput(dentry);
+	copy_to_user(buffer, path, buflen);
+	free_page((unsigned long)tmp);
+	return buflen;
+}
+
+static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
 {
 	int error;
-	struct dentry * dentry = proc_follow_link(inode, NULL);
 
+	dentry = proc_follow_link(dentry, NULL);
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
 		error = -ENOENT;
 		if (dentry) {
-			char * tmp = (char*)__get_free_page(GFP_KERNEL), *path;
-			int len;
-
-			path = d_path(dentry, tmp, PAGE_SIZE);
-			len = tmp + PAGE_SIZE - path;
-
-			if (len < buflen)
-				buflen = len;
-			dput(dentry);
-			copy_to_user(buffer, path, buflen);
-			free_page((unsigned long)tmp);
-			error = buflen;
+			error = do_proc_readlink(dentry, buffer, buflen);
 		}
 	}
 	return error;
diff -u --recursive --new-file v2.1.77/linux/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c
--- v2.1.77/linux/fs/proc/proc_devtree.c	Mon Aug 18 18:19:46 1997
+++ linux/fs/proc/proc_devtree.c	Sun Jan  4 00:53:41 1998
@@ -65,24 +65,24 @@
 	NULL			/* smap */
 };
 
-static struct dentry *devtree_follow_link(struct inode *inode,
+static struct dentry *devtree_follow_link(struct dentry *dentry,
 					  struct dentry *base)
 {
 	struct proc_dir_entry * de;
 	char *link;
 
-	de = (struct proc_dir_entry *) inode->u.generic_ip;
+	de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
 	link = (char *) de->data;
 	return lookup_dentry(link, base, 1);
 }
 
-static int devtree_readlink(struct inode *inode, char *buffer, int buflen)
+static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	struct proc_dir_entry * de;
 	char *link;
 	int linklen;
 
-	de = (struct proc_dir_entry *) inode->u.generic_ip;
+	de = (struct proc_dir_entry *) dentry->inode->u.generic_ip;
 	link = (char *) de->data;
 	linklen = strlen(link);
 	if (linklen > buflen)
diff -u --recursive --new-file v2.1.77/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.77/linux/fs/proc/root.c	Wed Oct 15 16:04:23 1997
+++ linux/fs/proc/root.c	Sun Jan  4 00:53:41 1998
@@ -368,7 +368,7 @@
 /*
  * /proc/self:
  */
-static int proc_self_readlink(struct inode * inode, char * buffer, int buflen)
+static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	int len;
 	char tmp[30];
@@ -380,12 +380,12 @@
 	return len;
 }
 
-static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry * proc_self_follow_link(struct dentry *dentry,
+						struct dentry *base)
 {
-	int len;
 	char tmp[30];
 
-	len = sprintf(tmp, "%d", current->pid);
+	sprintf(tmp, "%d", current->pid);
 	return lookup_dentry(tmp, base, 1);
 }	
 
diff -u --recursive --new-file v2.1.77/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.1.77/linux/fs/romfs/inode.c	Thu Sep 11 09:02:24 1997
+++ linux/fs/romfs/inode.c	Sun Jan  4 00:53:43 1998
@@ -391,8 +391,9 @@
  */
 
 static int
-romfs_readpage(struct inode * inode, struct page * page)
+romfs_readpage(struct dentry * dentry, struct page * page)
 {
+	struct inode *inode = dentry->d_inode;
 	unsigned long buf;
 	unsigned long offset, avail, readlen;
 	int result = -EIO;
@@ -428,8 +429,9 @@
 }
 
 static int
-romfs_readlink(struct inode *inode, char *buffer, int len)
+romfs_readlink(struct dentry *dentry, char *buffer, int len)
 {
+	struct inode *inode = dentry->d_inode;
 	int mylen;
 	char buf[ROMFS_MAXFN];		/* XXX dynamic */
 
@@ -450,11 +452,12 @@
 	return mylen;
 }
 
-static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base)
+static struct dentry *romfs_follow_link(struct dentry *dentry,
+					struct dentry *base)
 {
+	struct inode *inode = dentry->d_inode;
 	char *link;
 	int len, cnt;
-	struct dentry *dentry;
 
 	len = inode->i_size;
 
diff -u --recursive --new-file v2.1.77/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v2.1.77/linux/fs/smbfs/dir.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/smbfs/dir.c	Sun Jan  4 00:53:42 1998
@@ -89,7 +89,7 @@
 	/*
 	 * Make sure our inode is up-to-date.
 	 */
-	result = smb_revalidate_inode(dir);
+	result = smb_revalidate_inode(dentry);
 	if (result)
 		goto out;
 	/*
@@ -183,7 +183,7 @@
 	}
 
 	if (server->conn_pid)
-		error = smb_revalidate_inode(dir);
+		error = smb_revalidate_inode(dentry);
 	return error;
 }
 
@@ -235,7 +235,7 @@
 #endif
 			valid = 0;
 		} else if (!valid)
-			valid = (smb_revalidate_inode(inode) == 0);
+			valid = (smb_revalidate_inode(dentry) == 0);
 	} else
 	{
 	/*
diff -u --recursive --new-file v2.1.77/linux/fs/smbfs/file.c linux/fs/smbfs/file.c
--- v2.1.77/linux/fs/smbfs/file.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/smbfs/file.c	Sun Jan  4 00:53:42 1998
@@ -52,24 +52,17 @@
  * Read a page synchronously.
  */
 static int
-smb_readpage_sync(struct inode *inode, struct page *page)
+smb_readpage_sync(struct dentry *dentry, struct page *page)
 {
+	struct inode *inode = dentry->d_inode;
 	char *buffer = (char *) page_address(page);
 	unsigned long offset = page->offset;
-	struct dentry * dentry = inode->u.smbfs_i.dentry;
 	int rsize = smb_get_rsize(SMB_SERVER(inode));
 	int count = PAGE_SIZE;
 	int result;
 
 	clear_bit(PG_error, &page->flags);
 
-	result = -EIO;
-	if (!dentry) {
-		printk("smb_readpage_sync: no dentry for inode %ld\n",
-			inode->i_ino);
-		goto io_error;
-	}
- 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_readpage_sync: file %s/%s, count=%d@%ld, rsize=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize);
@@ -110,7 +103,7 @@
 }
 
 int
-smb_readpage(struct inode *inode, struct page *page)
+smb_readpage(struct dentry *dentry, struct page *page)
 {
 	int		error;
 
@@ -121,7 +114,7 @@
 #endif
 	set_bit(PG_locked, &page->flags);
 	atomic_inc(&page->count);
-	error = smb_readpage_sync(inode, page);
+	error = smb_readpage_sync(dentry, page);
 	free_page(page_address(page));
 	return error;
 }
@@ -131,9 +124,10 @@
  * Offset is the data offset within the page.
  */
 static int
-smb_writepage_sync(struct inode *inode, struct page *page,
+smb_writepage_sync(struct dentry *dentry, struct page *page,
 		   unsigned long offset, unsigned int count)
 {
+	struct inode *inode = dentry->d_inode;
 	u8 *buffer = (u8 *) page_address(page) + offset;
 	int wsize = smb_get_wsize(SMB_SERVER(inode));
 	int result, written = 0;
@@ -141,8 +135,7 @@
 	offset += page->offset;
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_writepage_sync: file %s/%s, count=%d@%ld, wsize=%d\n",
-((struct dentry *) inode->u.smbfs_i.dentry)->d_parent->d_name.name, 
-((struct dentry *) inode->u.smbfs_i.dentry)->d_name.name, count, offset, wsize);
+dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, wsize);
 #endif
 
 	do {
@@ -185,7 +178,7 @@
  * (for now), and we currently do this synchronously only.
  */
 static int
-smb_writepage(struct inode *inode, struct page *page)
+smb_writepage(struct dentry *dentry, struct page *page)
 {
 	int 	result;
 
@@ -195,21 +188,21 @@
 #endif
 	set_bit(PG_locked, &page->flags);
 	atomic_inc(&page->count);
-	result = smb_writepage_sync(inode, page, 0, PAGE_SIZE);
+	result = smb_writepage_sync(dentry, page, 0, PAGE_SIZE);
 	free_page(page_address(page));
 	return result;
 }
 
 static int
-smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
+smb_updatepage(struct dentry *dentry, struct page *page, const char *buffer,
 	       unsigned long offset, unsigned int count, int sync)
 {
 	unsigned long page_addr = page_address(page);
 	int result;
 
-	pr_debug("SMB:      smb_updatepage(%x/%ld %d@%ld, sync=%d)\n",
-		 inode->i_dev, inode->i_ino,
-		 count, page->offset+offset, sync);
+	pr_debug("SMBFS: smb_updatepage(%s/%s %d@%ld, sync=%d)\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name,
+	 	count, page->offset+offset, sync);
 
 #ifdef SMBFS_PARANOIA
 	if (test_bit(PG_locked, &page->flags))
@@ -220,7 +213,7 @@
 
 	if (copy_from_user((char *) page_addr + offset, buffer, count))
 		goto bad_fault;
-	result = smb_writepage_sync(inode, page, offset, count);
+	result = smb_writepage_sync(dentry, page, offset, count);
 out:
 	free_page(page_addr);
 	return result;
@@ -240,7 +233,6 @@
 smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
 {
 	struct dentry * dentry = file->f_dentry;
-	struct inode * inode = dentry->d_inode;
 	ssize_t	status;
 
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -249,7 +241,7 @@
 (unsigned long) count, (unsigned long) *ppos);
 #endif
 
-	status = smb_revalidate_inode(inode);
+	status = smb_revalidate_inode(dentry);
 	if (status)
 	{
 #ifdef SMBFS_PARANOIA
@@ -261,7 +253,8 @@
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_read: before read, size=%ld, pages=%ld, flags=%x, atime=%ld\n",
-inode->i_size, inode->i_nrpages, inode->i_flags, inode->i_atime);
+dentry->d_inode->i_size, dentry->d_inode->i_nrpages, dentry->d_inode->i_flags,
+dentry->d_inode->i_atime);
 #endif
 	status = generic_file_read(file, buf, count, ppos);
 out:
@@ -272,16 +265,14 @@
 smb_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
 	struct dentry * dentry = file->f_dentry;
-	struct inode * inode = dentry->d_inode;
 	int	status;
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_mmap: file %s/%s, address %lu - %lu\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
-vma->vm_start, vma->vm_end);
+dentry->d_parent->d_name.name, dentry->d_name.name, vma->vm_start, vma->vm_end);
 #endif
 
-	status = smb_revalidate_inode(inode);
+	status = smb_revalidate_inode(dentry);
 	if (status)
 	{
 #ifdef SMBFS_PARANOIA
@@ -302,16 +293,15 @@
 smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
 	struct dentry * dentry = file->f_dentry;
-	struct inode * inode = dentry->d_inode;
 	ssize_t	result;
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_write: file %s/%s, count=%lu@%lu, pages=%ld\n",
 dentry->d_parent->d_name.name, dentry->d_name.name,
-(unsigned long) count, (unsigned long) *ppos, inode->i_nrpages);
+(unsigned long) count, (unsigned long) *ppos, dentry->d_inode->i_nrpages);
 #endif
 
-	result = smb_revalidate_inode(inode);
+	result = smb_revalidate_inode(dentry);
 	if (result)
 	{
 #ifdef SMBFS_PARANOIA
@@ -330,7 +320,8 @@
 		result = generic_file_write(file, buf, count, ppos);
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
-(long) file->f_pos, inode->i_size, inode->i_mtime, inode->i_atime);
+(long) file->f_pos, dentry->d_inode->i_size, dentry->d_inode->i_mtime,
+dentry->d_inode->i_atime);
 #endif
 	}
 out:
diff -u --recursive --new-file v2.1.77/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.1.77/linux/fs/smbfs/inode.c	Sun Dec 21 16:17:45 1997
+++ linux/fs/smbfs/inode.c	Sun Jan  4 00:53:42 1998
@@ -186,8 +186,9 @@
  * invalidate our local caches.
  */
 int
-smb_revalidate_inode(struct inode *inode)
+smb_revalidate_inode(struct dentry *dentry)
 {
+	struct inode *inode = dentry->d_inode;
 	time_t last_time;
 	int error = 0;
 
@@ -224,8 +225,7 @@
 	{
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
-((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name,
-((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name,
+dentry->d_parent->d_name.name, dentry->d_name.name,
 (long) last_time, (long) inode->i_mtime);
 #endif
 		if (!S_ISDIR(inode->i_mode))
@@ -492,22 +492,15 @@
 }
 
 int
-smb_notify_change(struct inode *inode, struct iattr *attr)
+smb_notify_change(struct dentry *dentry, struct iattr *attr)
 {
-	struct smb_sb_info *server = SMB_SERVER(inode);
-	struct dentry *dentry = inode->u.smbfs_i.dentry;
+	struct inode *inode = dentry->d_inode;
+	struct smb_sb_info *server = server_from_dentry(dentry);
 	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
 	int error, changed, refresh = 0;
 	struct smb_fattr fattr;
 
-	error = -EIO;
-	if (!dentry)
-	{
-		printk("smb_notify_change: no dentry for inode!\n");
-		goto out;
-	}
-
-	error = smb_revalidate_inode(inode);
+	error = smb_revalidate_inode(dentry);
 	if (error)
 		goto out;
 
diff -u --recursive --new-file v2.1.77/linux/fs/stat.c linux/fs/stat.c
--- v2.1.77/linux/fs/stat.c	Thu Jul 17 10:06:07 1997
+++ linux/fs/stat.c	Sun Jan  4 00:53:41 1998
@@ -20,10 +20,11 @@
  * Revalidate the inode. This is required for proper NFS attribute caching.
  */
 static __inline__ int
-do_revalidate(struct inode *inode)
+do_revalidate(struct dentry *dentry)
 {
+	struct inode * inode = dentry->d_inode;
 	if (inode->i_op && inode->i_op->revalidate)
-		return inode->i_op->revalidate(inode);
+		return inode->i_op->revalidate(dentry);
 	return 0;
 }
 
@@ -128,10 +129,9 @@
 
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-		error = do_revalidate(inode);
+		error = do_revalidate(dentry);
 		if (!error)
-			error = cp_old_stat(inode, statbuf);
+			error = cp_old_stat(dentry->d_inode, statbuf);
 
 		dput(dentry);
 	}
@@ -150,10 +150,9 @@
 
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-		error = do_revalidate(inode);
+		error = do_revalidate(dentry);
 		if (!error)
-			error = cp_new_stat(inode,statbuf);
+			error = cp_new_stat(dentry->d_inode, statbuf);
 
 		dput(dentry);
 	}
@@ -177,10 +176,9 @@
 
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-		error = do_revalidate(inode);
+		error = do_revalidate(dentry);
 		if (!error)
-			error = cp_old_stat(inode, statbuf);
+			error = cp_old_stat(dentry->d_inode, statbuf);
 
 		dput(dentry);
 	}
@@ -200,10 +198,9 @@
 
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
-		struct inode * inode = dentry->d_inode;
-		error = do_revalidate(inode);
+		error = do_revalidate(dentry);
 		if (!error)
-			error = cp_new_stat(inode,statbuf);
+			error = cp_new_stat(dentry->d_inode, statbuf);
 
 		dput(dentry);
 	}
@@ -225,11 +222,10 @@
 	lock_kernel();
 	if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
 		struct dentry * dentry = f->f_dentry;
-		struct inode * inode = dentry->d_inode;
 
-		err = do_revalidate(inode);
+		err = do_revalidate(dentry);
 		if (!err)
-			err = cp_old_stat(inode,statbuf);
+			err = cp_old_stat(dentry->d_inode, statbuf);
 	}
 	unlock_kernel();
 	return err;
@@ -245,11 +241,10 @@
 	lock_kernel();
 	if (fd < NR_OPEN && (f = current->files->fd[fd]) != NULL) {
 		struct dentry * dentry = f->f_dentry;
-		struct inode * inode = dentry->d_inode;
 
-		err = do_revalidate(inode);
+		err = do_revalidate(dentry);
 		if (!err)
-			err = cp_new_stat(inode,statbuf);
+			err = cp_new_stat(dentry->d_inode, statbuf);
 	}
 	unlock_kernel();
 	return err;
@@ -271,9 +266,10 @@
 		struct inode * inode = dentry->d_inode;
 
 		error = -EINVAL;
-		if (inode->i_op && inode->i_op->readlink && !(error = do_revalidate(inode))) {
+		if (inode->i_op && inode->i_op->readlink &&
+		    !(error = do_revalidate(dentry))) {
 			UPDATE_ATIME(inode);
-			error = inode->i_op->readlink(inode,buf,bufsiz);
+			error = inode->i_op->readlink(dentry, buf, bufsiz);
 		}
 		dput(dentry);
 	}
diff -u --recursive --new-file v2.1.77/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
--- v2.1.77/linux/fs/sysv/inode.c	Sat Oct 25 02:44:18 1997
+++ linux/fs/sysv/inode.c	Sun Jan  4 00:53:41 1998
@@ -873,8 +873,9 @@
 }
 
 /* To avoid inconsistencies between inodes in memory and inodes on disk. */
-extern int sysv_notify_change(struct inode *inode, struct iattr *attr)
+extern int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *inode = dentry->d_inode;
 	int error;
 
 	if ((error = inode_change_ok(inode, attr)) != 0)
diff -u --recursive --new-file v2.1.77/linux/fs/sysv/symlink.c linux/fs/sysv/symlink.c
--- v2.1.77/linux/fs/sysv/symlink.c	Thu Jul 17 10:06:08 1997
+++ linux/fs/sysv/symlink.c	Sun Jan  4 00:53:41 1998
@@ -20,8 +20,8 @@
 
 #include <asm/uaccess.h>
 
-static int sysv_readlink(struct inode *, char *, int);
-static struct dentry *sysv_follow_link(struct inode *, struct dentry *);
+static int sysv_readlink(struct dentry *, char *, int);
+static struct dentry *sysv_follow_link(struct dentry *, struct dentry *);
 
 /*
  * symlinks can't do much...
@@ -46,8 +46,10 @@
 	NULL			/* permission */
 };
 
-static struct dentry *sysv_follow_link(struct inode * inode, struct dentry * base)
+static struct dentry *sysv_follow_link(struct dentry * dentry,
+					struct dentry * base)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh;
 
 	bh = sysv_file_bread(inode, 0, 0);
@@ -61,8 +63,9 @@
 	return base;
 }
 
-static int sysv_readlink(struct inode * inode, char * buffer, int buflen)
+static int sysv_readlink(struct dentry * dentry, char * buffer, int buflen)
 {
+	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh;
 	char * bh_data;
 	int i;
diff -u --recursive --new-file v2.1.77/linux/fs/ufs/ufs_symlink.c linux/fs/ufs/ufs_symlink.c
--- v2.1.77/linux/fs/ufs/ufs_symlink.c	Wed Dec 10 11:12:45 1997
+++ linux/fs/ufs/ufs_symlink.c	Sun Jan  4 00:53:41 1998
@@ -19,8 +19,9 @@
 extern int ufs_bmap (struct inode *, int);
 
 static int
-ufs_readlink(struct inode * inode, char * buffer, int buflen)
+ufs_readlink(struct dentry * dentry, char * buffer, int buflen)
 {
+	struct inode * inode = dentry->d_inode;
 	unsigned long int block;
 	struct buffer_head * bh = NULL;
 	char * link;
@@ -71,8 +72,9 @@
  * XXX - blatantly stolen from minix fs
  */
 static struct dentry *
-ufs_follow_link(struct inode * inode, struct dentry * base)
+ufs_follow_link(struct dentry * dentry, struct dentry * base)
 {
+	struct inode * inode = dentry->d_inode;
 	unsigned long int block;
 	struct buffer_head * bh = NULL;
 	char * link;
diff -u --recursive --new-file v2.1.77/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v2.1.77/linux/include/linux/cdrom.h	Fri Jan  2 14:37:02 1998
+++ linux/include/linux/cdrom.h	Mon Jan  5 00:06:26 1998
@@ -4,7 +4,7 @@
  * Copyright (C) 1992         David Giller, rafetmad@oxy.edu
  *               1994, 1995   Eberhard Moenkeberg, emoenke@gwdg.de
  *               1996         David van Leeuwen, david@tm.tno.nl
- *               1997         Erik Andersen, andersee@debian.org
+ *               1997, 1998   Erik Andersen, andersee@debian.org
  */
  
 #ifndef	_LINUX_CDROM_H
@@ -417,4 +417,4 @@
 extern int unregister_cdrom(struct cdrom_device_info *cdi);
 #endif  /* End of kernel only stuff */ 
 
-#endif  _LINUX_CDROM_H
+#endif  /* _LINUX_CDROM_H */
diff -u --recursive --new-file v2.1.77/linux/include/linux/config.h linux/include/linux/config.h
--- v2.1.77/linux/include/linux/config.h	Mon Jul  7 16:02:01 1997
+++ linux/include/linux/config.h	Mon Jan  5 01:41:01 1998
@@ -3,28 +3,4 @@
 
 #include <linux/autoconf.h>
 
-/*
- * Defines for what uname() should return 
- */
-#ifndef UTS_SYSNAME
-#define UTS_SYSNAME "Linux"
-#endif
-
-#ifndef UTS_MACHINE
-#define UTS_MACHINE "unknown"
-#endif
-
-#ifndef UTS_NODENAME
-#define UTS_NODENAME "(none)"	/* set by sethostname() */
-#endif
-
-#ifndef UTS_DOMAINNAME
-#define UTS_DOMAINNAME "(none)"	/* set by setdomainname() */
-#endif
-
-/*
- * The definitions for UTS_RELEASE and UTS_VERSION are now defined
- * in linux/version.h, and should only be used by linux/version.c
- */
-
 #endif
diff -u --recursive --new-file v2.1.77/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.77/linux/include/linux/dcache.h	Tue Dec  2 09:49:40 1997
+++ linux/include/linux/dcache.h	Sun Jan  4 00:53:40 1998
@@ -66,6 +66,7 @@
 	struct dentry_operations  *d_op;
 	struct super_block * d_sb;	/* The root of the dentry tree */
 	unsigned long d_reftime;	/* last time referenced */
+	void * d_fsdata;		/* fs-specific data */
 };
 
 struct dentry_operations {
@@ -73,6 +74,7 @@
 	int (*d_hash) (struct dentry *,struct qstr *);
 	int (*d_compare) (struct dentry *,struct qstr *, struct qstr *);
 	void (*d_delete)(struct dentry *);
+	void (*d_release)(struct dentry *);
 };
 
 /* the dentry parameter passed to d_hash and d_compare is the parent
diff -u --recursive --new-file v2.1.77/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v2.1.77/linux/include/linux/ext2_fs.h	Tue Sep 23 16:48:49 1997
+++ linux/include/linux/ext2_fs.h	Sun Jan  4 00:53:40 1998
@@ -499,9 +499,10 @@
 extern int ext2_rmdir (struct inode *,struct dentry *);
 extern int ext2_unlink (struct inode *,struct dentry *);
 extern int ext2_symlink (struct inode *,struct dentry *,const char *);
-extern int ext2_link (struct inode *, struct inode *, struct dentry *);
+extern int ext2_link (struct dentry *, struct inode *, struct dentry *);
 extern int ext2_mknod (struct inode *, struct dentry *, int, int);
-extern int ext2_rename (struct inode *, struct dentry *,struct inode *, struct dentry *);
+extern int ext2_rename (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
 
 /* super.c */
 extern void ext2_error (struct super_block *, const char *, const char *, ...)
diff -u --recursive --new-file v2.1.77/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.77/linux/include/linux/fs.h	Tue Dec 23 16:31:00 1997
+++ linux/include/linux/fs.h	Mon Jan  5 16:09:37 1998
@@ -266,6 +266,7 @@
 #include <linux/ufs_fs_i.h>
 #include <linux/romfs_fs_i.h>
 #include <linux/smb_fs_i.h>
+#include <linux/hfs_fs_i.h>
 
 /*
  * Attribute flags.  These should be or-ed together to figure out what
@@ -366,6 +367,7 @@
 		struct ufs_inode_info		ufs_i;
 		struct romfs_inode_info		romfs_i;
 		struct smb_inode_info		smbfs_i;
+		struct hfs_inode_info		hfs_i;
 		struct socket			socket_i;
 		void				*generic_ip;
 	} u;
@@ -503,6 +505,7 @@
 #include <linux/ufs_fs_sb.h>
 #include <linux/romfs_fs_sb.h>
 #include <linux/smb_fs_sb.h>
+#include <linux/hfs_fs_sb.h>
 
 struct super_block {
 	kdev_t			s_dev;
@@ -538,6 +541,7 @@
 		struct ufs_sb_info	ufs_sb;
 		struct romfs_sb_info	romfs_sb;
 		struct smb_sb_info	smbfs_sb;
+		struct hfs_sb_info	hfs_sb;
 		void			*generic_sbp;
 	} u;
 };
@@ -571,24 +575,25 @@
 	struct file_operations * default_file_ops;
 	int (*create) (struct inode *,struct dentry *,int);
 	int (*lookup) (struct inode *,struct dentry *);
-	int (*link) (struct inode *,struct inode *,struct dentry *);
+	int (*link) (struct dentry *,struct inode *,struct dentry *);
 	int (*unlink) (struct inode *,struct dentry *);
 	int (*symlink) (struct inode *,struct dentry *,const char *);
 	int (*mkdir) (struct inode *,struct dentry *,int);
 	int (*rmdir) (struct inode *,struct dentry *);
 	int (*mknod) (struct inode *,struct dentry *,int,int);
-	int (*rename) (struct inode *,struct dentry *,struct inode *,struct dentry *);
-	int (*readlink) (struct inode *,char *,int);
-	struct dentry * (*follow_link) (struct inode *, struct dentry *);
-	int (*readpage) (struct inode *, struct page *);
-	int (*writepage) (struct inode *, struct page *);
+	int (*rename) (struct inode *, struct dentry *,
+			struct inode *, struct dentry *);
+	int (*readlink) (struct dentry *, char *,int);
+	struct dentry * (*follow_link) (struct dentry *, struct dentry *);
+	int (*readpage) (struct dentry *, struct page *);
+	int (*writepage) (struct dentry *, struct page *);
 	int (*bmap) (struct inode *,int);
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int);
 	int (*smap) (struct inode *,int);
-	int (*updatepage) (struct inode *, struct page *, const char *,
+	int (*updatepage) (struct dentry *, struct page *, const char *,
 				unsigned long, unsigned int, int);
-	int (*revalidate) (struct inode *);
+	int (*revalidate) (struct dentry *);
 };
 
 struct super_operations {
@@ -596,7 +601,7 @@
 	void (*write_inode) (struct inode *);
 	void (*put_inode) (struct inode *);
 	void (*delete_inode) (struct inode *);
-	int (*notify_change) (struct inode *, struct iattr *);
+	int (*notify_change) (struct dentry *, struct iattr *);
 	void (*put_super) (struct super_block *);
 	void (*write_super) (struct super_block *);
 	int (*statfs) (struct super_block *, struct statfs *, int);
@@ -630,7 +635,7 @@
 
 extern char * getname(const char * filename);
 extern void putname(char * name);
-extern int do_truncate(struct inode *, unsigned long);
+extern int do_truncate(struct dentry *, unsigned long);
 extern int register_blkdev(unsigned int, const char *, struct file_operations *);
 extern int unregister_blkdev(unsigned int major, const char * name);
 extern int blkdev_open(struct inode * inode, struct file * filp);
@@ -710,7 +715,7 @@
 extern int fsync_dev(kdev_t dev);
 extern void sync_supers(kdev_t dev);
 extern int bmap(struct inode * inode,int block);
-extern int notify_change(struct inode *, struct iattr *);
+extern int notify_change(struct dentry *, struct iattr *);
 extern int permission(struct inode * inode,int mask);
 extern int get_write_access(struct inode *inode);
 extern void put_write_access(struct inode *inode);
@@ -800,7 +805,7 @@
 
 extern int brw_page(int, struct page *, kdev_t, int [], int, int);
 
-extern int generic_readpage(struct inode *, struct page *);
+extern int generic_readpage(struct dentry *, struct page *);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
 extern ssize_t generic_file_write(struct file *, const char*, size_t, loff_t*);
@@ -831,7 +836,6 @@
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
-extern int notify_change(struct inode * inode, struct iattr * attr);
 
 /* kludge to get SCSI modules working */
 #include <linux/minix_fs.h>
diff -u --recursive --new-file v2.1.77/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v2.1.77/linux/include/linux/genhd.h	Fri Jan  2 14:37:02 1998
+++ linux/include/linux/genhd.h	Mon Jan  5 00:22:44 1998
@@ -26,6 +26,11 @@
 #define DOS_EXTENDED_PARTITION 5
 #define LINUX_EXTENDED_PARTITION 0x85
 #define WIN98_EXTENDED_PARTITION 0x0f
+#define LINUX_SWAP_PARTITION	0x82
+
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+#define SOLARIS_X86_PARTITION	LINUX_SWAP_PARTITION
+#endif
 
 #define DM6_PARTITION		0x54	/* has DDO: use xlated geom & offset */
 #define EZD_PARTITION		0x55	/* EZ-DRIVE:  same as DM6 (we think) */
@@ -66,6 +71,34 @@
 	void *real_devices;		/* internal use */
 	struct gendisk *next;
 };
+
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+
+#define SOLARIS_X86_NUMSLICE	8
+#define SOLARIS_X86_VTOC_SANE	(0x600DDEEEUL)
+
+struct solaris_x86_slice {
+	ushort	s_tag;			/* ID tag of partition */
+	ushort	s_flag;			/* permision flags */
+	daddr_t s_start;		/* start sector no of partition */
+	long	s_size;			/* # of blocks in partition */
+};
+
+struct solaris_x86_vtoc {
+		unsigned long v_bootinfo[3];	/* info needed by mboot (unsupported) */
+	unsigned long v_sanity;		/* to verify vtoc sanity */
+	unsigned long v_version;	/* layout version */
+	char	v_volume[8];		/* volume name */
+	ushort	v_sectorsz;		/* sector size in bytes */
+	ushort	v_nparts;		/* number of partitions */
+	unsigned long v_reserved[10];	/* free space */
+	struct solaris_x86_slice
+		v_slice[SOLARIS_X86_NUMSLICE]; /* slice headers */
+	time_t	timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp (unsupported) */
+	char	v_asciilabel[128];	/* for compatibility */
+};
+
+#endif /* CONFIG_SOLARIS_X86_PARTITION */
 
 #ifdef CONFIG_BSD_DISKLABEL
 /*
diff -u --recursive --new-file v2.1.77/linux/include/linux/hfs_fs.h linux/include/linux/hfs_fs.h
--- v2.1.77/linux/include/linux/hfs_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hfs_fs.h	Mon Jan  5 16:12:20 1998
@@ -0,0 +1,362 @@
+/* 
+ * linux/include/linux/hfs_fs.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ */
+
+#ifndef _LINUX_HFS_FS_H
+#define _LINUX_HFS_FS_H
+
+#include <linux/hfs_sysdep.h>
+
+/* magic numbers for Apple Double header files */
+#define HFS_DBL_MAGIC		0x00051607
+#define HFS_SNGL_MAGIC		0x00051600
+#define HFS_HDR_VERSION_1	0x00010000
+#define HFS_HDR_VERSION_2	0x00020000
+
+/* magic numbers for various internal structures */
+#define HFS_INO_MAGIC		0x4821
+#define HFS_SB_MAGIC		0x4822
+
+/* The space used for the AppleDouble or AppleSingle headers */
+#define HFS_DBL_HDR_LEN		1024
+
+/* The space used for the Netatalk header */
+#define HFS_NAT_HDR_LEN		1024  /* 589 for an exact match */
+
+/* Macros to extract CNID and file "type" from the Linux inode number */
+#define HFS_CNID(X)	((X) & 0x3FFFFFFF)
+#define HFS_ITYPE(X)	((X) & 0xC0000000)
+
+/* Macros to enumerate types */
+#define HFS_ITYPE_TO_INT(X)	((X) >> 30)
+#define HFS_INT_TO_ITYPE(X)	((X) << 30)
+
+/* generic ITYPEs */
+#define HFS_ITYPE_0	0x00000000
+#define HFS_ITYPE_1	0x40000000
+#define HFS_ITYPE_2	0x80000000
+#define HFS_ITYPE_3	0xC0000000
+#define HFS_ITYPE_NORM	HFS_ITYPE_0	/* "normal" directory or file */
+
+/* ITYPEs for CAP */
+#define HFS_CAP_NORM	HFS_ITYPE_0	/* data fork or normal directory */
+#define HFS_CAP_DATA	HFS_ITYPE_0	/* data fork of file */
+#define HFS_CAP_NDIR	HFS_ITYPE_0	/* normal directory */
+#define HFS_CAP_FNDR	HFS_ITYPE_1	/* finder info for file or dir */
+#define HFS_CAP_RSRC	HFS_ITYPE_2	/* resource fork of file */
+#define HFS_CAP_RDIR	HFS_ITYPE_2	/* .resource directory */
+#define HFS_CAP_FDIR	HFS_ITYPE_3	/* .finderinfo directory */
+
+/* ITYPEs for Apple Double */
+#define HFS_DBL_NORM	HFS_ITYPE_0	/* data fork or directory */
+#define HFS_DBL_DATA	HFS_ITYPE_0	/* data fork of file */
+#define HFS_DBL_DIR	HFS_ITYPE_0	/* directory */
+#define HFS_DBL_HDR	HFS_ITYPE_1	/* AD header of file or dir */
+
+/* ITYPEs for netatalk */
+#define HFS_NAT_NORM	HFS_ITYPE_0	/* data fork or directory */
+#define HFS_NAT_DATA	HFS_ITYPE_0	/* data fork of file */
+#define HFS_NAT_NDIR	HFS_ITYPE_0	/* normal directory */
+#define HFS_NAT_HDR	HFS_ITYPE_1	/* AD header of file or dir */
+#define HFS_NAT_HDIR	HFS_ITYPE_2	/* directory holding AD headers */
+
+/* ITYPEs for Apple Single */
+#define HFS_SGL_NORM	HFS_ITYPE_0	/* AppleSingle file or directory */
+#define HFS_SGL_SNGL	HFS_ITYPE_0	/* AppleSingle file */
+#define HFS_SGL_DIR	HFS_ITYPE_0	/* directory */
+#define HFS_SGL_DINF	HFS_ITYPE_1	/* %DirInfo for directory */
+
+/* IDs for elements of an AppleDouble or AppleSingle header */
+#define HFS_HDR_DATA	1
+#define HFS_HDR_RSRC	2
+#define HFS_HDR_FNAME	3
+#define HFS_HDR_COMNT	4
+#define HFS_HDR_BWICN	5
+#define HFS_HDR_CICON	6
+#define HFS_HDR_OLDI	7
+#define HFS_HDR_DATES	8
+#define HFS_HDR_FINFO	9
+#define HFS_HDR_MACI	10
+#define HFS_HDR_MAX	10
+
+/*
+ * There are three time systems.  All three are based on seconds since
+ * a particular time/date.
+ *	Unix:	unsigned lil-endian since 00:00 GMT, Jan. 1, 1970
+ *	mac:	unsigned big-endian since 00:00 GMT, Jan. 1, 1904
+ *	header:	  SIGNED big-endian since 00:00 GMT, Jan. 1, 2000
+ *
+ */
+#define hfs_h_to_mtime(ARG)	htonl((hfs_s32)ntohl(ARG)+3029529600U)
+#define hfs_m_to_htime(ARG)	((hfs_s32)htonl(ntohl(ARG)-3029529600U))
+#define hfs_h_to_utime(ARG)	((hfs_s32)ntohl(ARG)+946684800U)
+#define hfs_u_to_htime(ARG)	((hfs_s32)htonl((ARG)-946684800U))
+#define hfs_u_to_mtime(ARG)	htonl((ARG)+2082844800U)
+#define hfs_m_to_utime(ARG)	(ntohl(ARG)-2082844800U)
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * A descriptor for a single entry within the header of an
+ * AppleDouble or AppleSingle header file.
+ * An array of these make up a table of contents for the file.
+ */
+struct hfs_hdr_descr {
+	hfs_u32	id;	/* The Apple assigned ID for the entry type */
+	hfs_u32	offset;	/* The offset to reach the entry */
+	hfs_u32	length;	/* The length of the entry */
+};
+
+/*
+ * The info needed to reconstruct a given header layout
+ */
+struct hfs_hdr_layout {
+	hfs_u32		magic;			/* AppleSingle or AppleDouble */
+	hfs_u32		version;		/* 0x00010000 or 0x00020000 */
+	hfs_u16		entries;		/* How many entries used */
+	struct hfs_hdr_descr	
+			descr[HFS_HDR_MAX];	/* Descriptors */
+	struct hfs_hdr_descr	
+			*order[HFS_HDR_MAX];	/* 'descr' ordered by offset */
+};
+
+/* 
+ * Default header layout for Netatalk
+ */
+struct hfs_nat_hdr {
+	hfs_lword_t	magic;
+	hfs_lword_t	version;
+	hfs_byte_t	homefs[16];
+	hfs_word_t	entries;
+	hfs_byte_t	descrs[60];
+	hfs_byte_t	real_name[255];	/* id=3 */
+	hfs_byte_t	comment[200];	/* id=4 XXX: not yet implemented */
+	hfs_lword_t	create_time;	/* \       */
+	hfs_lword_t	modify_time;	/*  |      */
+	hfs_lword_t	backup_time;	/*  | id=7 */
+	hfs_word_t	filler;		/*  |      */
+	hfs_word_t	attr;		/* /       */
+	hfs_byte_t	finderinfo[32]; /* id=9 */
+};
+
+/* 
+ * Default header layout for AppleDouble
+ */
+struct hfs_dbl_hdr {
+	hfs_lword_t	magic;
+	hfs_lword_t	version;
+	hfs_byte_t	filler[16];
+	hfs_word_t	entries;
+	hfs_byte_t	descrs[12*HFS_HDR_MAX];
+	hfs_u32		create_time;	/* \	   */
+	hfs_u32		modify_time;	/*  | id=8 */
+	hfs_u32		backup_time;	/*  |	   */
+	hfs_u32		access_time;	/* /	   */
+	hfs_u8		finderinfo[32]; /* id=9 */
+	hfs_u32		fileinfo;	/* id=10 */
+	hfs_u8		real_name[32];	/* id=3 */
+	hfs_u8		comment[200];	/* id=4 XXX: not yet implemented */
+};
+
+/* finder metadata for CAP */
+struct hfs_cap_info {
+	hfs_byte_t	fi_fndr[32];	/* Finder's info */
+	hfs_word_t	fi_attr;	/* AFP attributes */
+#define HFS_AFP_WRI		0x020	/* Write inhibit bit */
+#define HFS_AFP_RNI		0x080	/* Rename inhibit bit (AFP >= 2.0) */
+#define HFS_AFP_DEI		0x100	/* Delete inhibit bit (AFP >= 2.0) */
+#define HFS_AFP_RDONLY	(	HFS_AFP_WRI|HFS_AFP_RNI|HFS_AFP_DEI)
+	hfs_byte_t	fi_magic1;	/* Magic number: */
+#define HFS_CAP_MAGIC1		0xFF
+	hfs_byte_t	fi_version;	/* Version of this structure: */
+#define HFS_CAP_VERSION		0x10
+	hfs_byte_t	fi_magic;	/* Another magic number: */
+#define HFS_CAP_MAGIC		0xDA
+	hfs_byte_t	fi_bitmap;	/* Bitmap of which names are valid: */
+#define HFS_CAP_SHORTNAME	0x01
+#define HFS_CAP_LONGNAME	0x02
+	hfs_byte_t	fi_shortfilename[12+1];	/* "short name" (unused) */
+	hfs_byte_t	fi_macfilename[32+1];	/* Original (Macintosh) name */
+	hfs_byte_t	fi_comln;	/* Length of comment (always 0) */
+	hfs_byte_t	fi_comnt[200];	/* Finder comment (unused) */
+	/* optional: 	used by aufs only if compiled with USE_MAC_DATES */
+	hfs_byte_t	fi_datemagic;	/* Magic number for dates extension: */
+#define HFS_CAP_DMAGIC		0xDA
+	hfs_byte_t	fi_datevalid;	/* Bitmap of which dates are valid: */
+#define HFS_CAP_MDATE		0x01
+#define HFS_CAP_CDATE		0x02
+	hfs_lword_t	fi_ctime;	/* Creation date (in AFP format) */
+	hfs_lword_t	fi_mtime;	/* Modify date (in AFP format) */
+	hfs_lword_t	fi_utime;	/* Un*x time of last mtime change */
+	hfs_byte_t	pad;
+};
+
+#ifdef __KERNEL__
+
+#if defined(CONFIG_HFS_FS) || defined(CONFIG_HFS_FS_MODULE)
+
+typedef ssize_t hfs_rwret_t;
+typedef size_t hfs_rwarg_t;
+
+#include <asm/uaccess.h>
+
+/* Some forward declarations */
+struct hfs_fork;
+struct hfs_cat_key;
+struct hfs_cat_entry;
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+					 const struct hfs_cat_key *);
+extern void hfs_tolower(unsigned char *, int);
+
+/* dir.c */
+extern hfs_rwret_t hfs_dir_read(struct file *, char *, hfs_rwarg_t,
+				loff_t *);
+extern int hfs_create(struct inode *, struct dentry *, int);
+extern int hfs_mkdir(struct inode *, struct dentry *, int);
+extern int hfs_mknod(struct inode *, struct dentry *, int, int);
+extern int hfs_unlink(struct inode *, struct dentry *);
+extern int hfs_rmdir(struct inode *, struct dentry *);
+extern int hfs_rename(struct inode *, struct dentry *,
+		      struct inode *, struct dentry *);
+
+/* dir_cap.c */
+extern const struct hfs_name hfs_cap_reserved1[];
+extern const struct hfs_name hfs_cap_reserved2[];
+extern struct inode_operations hfs_cap_ndir_inode_operations;
+extern struct inode_operations hfs_cap_fdir_inode_operations;
+extern struct inode_operations hfs_cap_rdir_inode_operations;
+extern void hfs_cap_drop_dentry(const ino_t, struct dentry *);
+
+/* dir_dbl.c */
+extern const struct hfs_name hfs_dbl_reserved1[];
+extern const struct hfs_name hfs_dbl_reserved2[];
+extern struct inode_operations hfs_dbl_dir_inode_operations;
+extern void hfs_dbl_drop_dentry(const ino_t, struct dentry *);
+
+/* dir_nat.c */
+extern const struct hfs_name hfs_nat_reserved1[];
+extern const struct hfs_name hfs_nat_reserved2[];
+extern struct inode_operations hfs_nat_ndir_inode_operations;
+extern struct inode_operations hfs_nat_hdir_inode_operations;
+extern void hfs_nat_drop_dentry(const ino_t, struct dentry *);
+
+/* dir_sngl.c */
+extern const struct hfs_name hfs_sngl_reserved1[];
+extern const struct hfs_name hfs_sngl_reserved2[];
+extern struct inode_operations hfs_sngl_dir_inode_operations;
+
+/* file.c */
+extern hfs_s32 hfs_do_read(struct inode *, struct hfs_fork *, hfs_u32,
+			   char *, hfs_u32, int);
+extern hfs_s32 hfs_do_write(struct inode *, struct hfs_fork *, hfs_u32,
+			    const char *, hfs_u32);
+extern void hfs_file_fix_mode(struct hfs_cat_entry *entry);
+extern struct inode_operations hfs_file_inode_operations;
+
+/* file_cap.c */
+extern struct inode_operations hfs_cap_info_inode_operations;
+
+/* file_hdr.c */
+extern struct inode_operations hfs_hdr_inode_operations;
+extern const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout;
+extern const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout;
+extern const struct hfs_hdr_layout hfs_nat_hdr_layout;
+extern const struct hfs_hdr_layout hfs_sngl_hdr_layout;
+
+/* inode.c */
+extern void hfs_put_inode(struct inode *);
+extern int hfs_notify_change(struct inode *, struct iattr *);
+extern struct inode *hfs_iget(struct hfs_cat_entry *, ino_t, struct dentry *);
+
+extern void hfs_cap_ifill(struct inode *, ino_t);
+extern void hfs_dbl_ifill(struct inode *, ino_t);
+extern void hfs_nat_ifill(struct inode *, ino_t);
+extern void hfs_sngl_ifill(struct inode *, ino_t);
+
+/* super.c */
+extern struct super_block *hfs_read_super(struct super_block *,void *,int);
+extern int init_hfs_fs(void);
+
+/* trans.c */
+extern void hfs_colon2mac(struct hfs_name *, const char *, int);
+extern void hfs_prcnt2mac(struct hfs_name *, const char *, int);
+extern void hfs_triv2mac(struct hfs_name *, const char *, int);
+extern void hfs_latin2mac(struct hfs_name *, const char *, int);
+extern int hfs_mac2cap(char *, const struct hfs_name *);
+extern int hfs_mac2nat(char *, const struct hfs_name *);
+extern int hfs_mac2latin(char *, const struct hfs_name *);
+extern int hfs_mac2seven(char *, const struct hfs_name *);
+extern int hfs_mac2eight(char *, const struct hfs_name *);
+extern int hfs_mac2alpha(char *, const struct hfs_name *);
+extern int hfs_mac2triv(char *, const struct hfs_name *);
+
+#define	HFS_I(X)	(&((X)->u.hfs_i))
+#define	HFS_SB(X)	(&((X)->u.hfs_sb))
+
+extern __inline__ void hfs_nameout(struct inode *dir, struct hfs_name *out,
+				   const char *in, int len) {
+	HFS_SB(dir->i_sb)->s_nameout(out, in, len);
+}
+
+extern __inline__ int hfs_namein(struct inode *dir, char *out,
+				 const struct hfs_name *in) {
+	int len = HFS_SB(dir->i_sb)->s_namein(out, in);
+	if (HFS_SB(dir->i_sb)->s_lowercase) {
+		hfs_tolower(out, len);
+	}
+	return len;
+}
+
+static __inline__ struct dentry 
+*hfs_lookup_dentry(const char *name, const int len, 
+		   struct dentry *base)
+{
+  struct qstr this;
+
+  this.name = name;
+  this.len = len;
+  this.hash = full_name_hash(name, len);
+
+  return d_lookup(base, &this);
+}
+
+/* drop a dentry for one of the special subdirectories */
+static __inline__ void hfs_drop_special(const struct hfs_name *name,
+					struct dentry *base,
+					struct dentry *dentry)
+{
+  struct dentry *dparent, *de;
+  
+  dparent = hfs_lookup_dentry(name->Name, name->Len, base);
+  if (dparent) {
+    de = hfs_lookup_dentry(dentry->d_name.name, dentry->d_name.len,
+			   dparent);
+    dput(dparent);
+
+    if (de) {
+      if (!de->d_inode)
+	d_drop(de);
+      dput(de);
+    }
+  }
+}
+
+#endif
+#endif /* __KERNEL__ */
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/include/linux/hfs_fs_i.h linux/include/linux/hfs_fs_i.h
--- v2.1.77/linux/include/linux/hfs_fs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hfs_fs_i.h	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,40 @@
+/* 
+ * linux/include/linux/hfs_fs_i.h
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_inode_info) and the two
+ * subordinate types hfs_extent and hfs_file.
+ */
+
+#ifndef _LINUX_HFS_FS_I_H
+#define _LINUX_HFS_FS_I_H
+
+/*
+ * struct hfs_inode_info
+ *
+ * The HFS-specific part of a Linux (struct inode)
+ */
+struct hfs_inode_info {
+	int				magic;     /* A magic number */
+
+	struct hfs_cat_entry		*entry;
+
+	/* For a regular or header file */
+	struct hfs_fork 		*fork;
+	int				convert;
+
+	/* For a directory */
+	ino_t				file_type;
+	char				dir_size;
+
+	/* For header files */
+	const struct hfs_hdr_layout	*default_layout;
+	struct hfs_hdr_layout		*layout;
+
+        /* for dentry cleanup */
+        void (*d_drop_op)(const ino_t, struct dentry *);
+};
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/include/linux/hfs_fs_sb.h linux/include/linux/hfs_fs_sb.h
--- v2.1.77/linux/include/linux/hfs_fs_sb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hfs_fs_sb.h	Sun Jan  4 10:40:17 1998
@@ -0,0 +1,52 @@
+/* 
+ * linux/include/linux/hfs_fs_sb.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_sb_info) which contains the
+ * HFS-specific information in the in-core superblock.
+ */
+
+#ifndef _LINUX_HFS_FS_SB_H
+#define _LINUX_HFS_FS_SB_H
+
+/* forward declaration: */
+struct hfs_name;
+
+typedef int (*hfs_namein_fn) (char *, const struct hfs_name *);
+typedef void (*hfs_nameout_fn) (struct hfs_name *, const char *, int);
+typedef void (*hfs_ifill_fn) (struct inode *, ino_t);
+
+/*
+ * struct hfs_sb_info
+ *
+ * The HFS-specific part of a Linux (struct super_block)
+ */
+struct hfs_sb_info {
+	int			magic;		/* A magic number */
+	struct hfs_mdb		*s_mdb;		/* The HFS MDB */
+	int			s_quiet;	/* Silent failure when 
+						   changing owner or mode? */
+	int			s_lowercase;	/* Map names to lowercase? */
+	int			s_afpd;		/* AFPD compatible mode? */
+	hfs_namein_fn		s_namein;	/* The function used to
+						   map Mac filenames to
+						   Linux filenames */
+	hfs_nameout_fn		s_nameout;	/* The function used to
+						    map Linux filenames
+						    to Mac filenames */
+	hfs_ifill_fn		s_ifill;	/* The function used
+						   to fill in inode fields */
+	const struct hfs_name	*s_reserved1;	/* Reserved names */
+	const struct hfs_name	*s_reserved2;	/* Reserved names */
+	__u32			s_type;		/* Type for new files */
+	__u32			s_creator;	/* Creator for new files */
+	umode_t			s_umask;	/* The umask applied to the
+						   permissions on all files */
+	uid_t			s_uid;		/* The uid of all files */
+	gid_t			s_gid;		/* The gid of all files */
+	char			s_conv;		/* Type of text conversion */
+};
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/include/linux/hfs_sysdep.h linux/include/linux/hfs_sysdep.h
--- v2.1.77/linux/include/linux/hfs_sysdep.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/hfs_sysdep.h	Mon Jan  5 16:12:14 1998
@@ -0,0 +1,224 @@
+/*
+ * linux/include/linux/hfs_sysdep.h
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains constants, types and inline
+ * functions for various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#ifndef _HFS_SYSDEP_H
+#define _HFS_SYSDEP_H
+
+#include <linux/malloc.h>
+#include <linux/types.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+
+#undef offsetof
+#define offsetof(TYPE, MEMB) ((size_t) &((TYPE *)0)->MEMB)
+
+/* Typedefs for integer types by size and signedness */
+typedef __u8            hfs_u8;
+typedef __u16           hfs_u16;
+typedef __u32           hfs_u32;
+typedef __s8            hfs_s8;
+typedef __s16           hfs_s16;
+typedef __s32           hfs_s32;
+
+/* Typedefs for unaligned integer types */
+typedef unsigned char hfs_byte_t;
+typedef unsigned char hfs_word_t[2];
+typedef unsigned char hfs_lword_t[4];
+
+/* these funny looking things are GCC variable argument macros */
+#define hfs_warn(format, args...) printk(KERN_WARNING format , ## args)
+#define hfs_error(format, args...) printk(KERN_ERR format , ## args)
+
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+extern long int hfs_alloc;
+#endif
+
+extern inline void *hfs_malloc(unsigned int size) {
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+	hfs_warn("%ld bytes allocation at %s:%u\n",
+		 (hfs_alloc += size), __FILE__, __LINE__);
+#endif
+	return kmalloc(size, GFP_KERNEL);
+}
+
+extern inline void hfs_free(void *ptr, unsigned int size) {
+	kfree_s(ptr, size);
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+	hfs_warn("%ld bytes allocation at %s:%u\n",
+		  (hfs_alloc -= ptr ? size : 0), __FILE__, __LINE__);
+#endif
+}
+
+
+extern inline hfs_u32 hfs_time(void) {
+	return htonl(CURRENT_TIME+2082844800U);
+}
+
+
+/*
+ * hfs_wait_queue 
+ */
+typedef struct wait_queue *hfs_wait_queue;
+
+extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
+	sleep_on(queue);
+}
+
+extern inline void hfs_wake_up(hfs_wait_queue *queue) {
+	wake_up(queue);
+}
+
+extern inline void hfs_relinquish(void) {
+	schedule();
+}
+
+
+/*
+ * hfs_sysmdb 
+ */
+typedef struct super_block *hfs_sysmdb;
+
+extern inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
+	sys_mdb->s_dirt = 1;
+}
+
+extern inline char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+	return kdevname(sys_mdb->s_dev);
+}
+
+
+/*
+ * hfs_sysentry
+ */
+typedef struct dentry *hfs_sysentry[4];
+
+/*
+ * hfs_buffer
+ */
+typedef struct buffer_head *hfs_buffer;
+
+#define HFS_BAD_BUFFER NULL
+
+/* In sysdep.c, since it needs HFS_SECTOR_SIZE */
+extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int);
+
+extern inline int hfs_buffer_ok(hfs_buffer buffer) {
+	return (buffer != NULL);
+}
+
+extern inline void hfs_buffer_put(hfs_buffer buffer) {
+	brelse(buffer);
+}
+
+extern inline void hfs_buffer_dirty(hfs_buffer buffer) {
+	mark_buffer_dirty(buffer, 1);
+}
+
+extern inline void hfs_buffer_sync(hfs_buffer buffer) {
+	while (buffer_locked(buffer)) {
+		wait_on_buffer(buffer);
+	}
+	if (buffer_dirty(buffer)) {
+		ll_rw_block(WRITE, 1, &buffer);
+		wait_on_buffer(buffer);
+	}
+}
+
+extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
+	return buffer->b_data;
+}
+
+
+/*
+ * bit operations
+ */
+
+#undef BITNR
+#if defined(__BIG_ENDIAN)
+#	define BITNR(X)	((X)^31)
+#	if !defined(__constant_htonl)
+#		define __constant_htonl(x) (x)
+#	endif
+#	if !defined(__constant_htons)
+#		define __constant_htons(x) (x)
+#	endif
+#elif defined(__LITTLE_ENDIAN)
+#	define BITNR(X)	((X)^7)
+#	if !defined(__constant_htonl)
+#		define __constant_htonl(x) \
+        ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+                             (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
+                             (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
+                             (((unsigned long int)(x) & 0xff000000U) >> 24)))
+#	endif
+#	if !defined(__constant_htons)
+#		define __constant_htons(x) \
+        ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+                              (((unsigned short int)(x) & 0xff00) >> 8)))
+#	endif
+#else
+#	error "Don't know if bytes are big- or little-endian!"
+#endif
+
+extern inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
+	return test_and_clear_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
+	return test_and_set_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
+	/* the kernel should declare the second arg of test_bit as const */
+	return test_bit(BITNR(bitnr), (void *)lword);
+}
+
+#undef BITNR
+
+/*
+ * HFS structures have fields aligned to 16-bit boundaries.
+ * So, 16-bit get/put are easy while 32-bit get/put need
+ * some care on architectures like the DEC Alpha.
+ *
+ * In what follows:
+ *	ns  = 16-bit integer in network byte-order w/ 16-bit alignment
+ *	hs  = 16-bit integer in host byte-order w/ 16-bit alignment
+ *	nl  = 32-bit integer in network byte-order w/ unknown alignment
+ *	hl  = 32-bit integer in host byte-order w/ unknown alignment
+ *	anl = 32-bit integer in network byte-order w/ 32-bit alignment
+ *	ahl = 32-bit integer in host byte-order w/ 32-bit alignment
+ * Example: hfs_get_hl() gets an unaligned 32-bit integer converting
+ *	it to host byte-order.
+ */
+#define hfs_get_hs(addr)	ntohs(*((hfs_u16 *)(addr)))
+#define hfs_get_ns(addr)	(*((hfs_u16 *)(addr)))
+#define hfs_get_hl(addr)	ntohl(get_unaligned((hfs_u32 *)(addr)))
+#define hfs_get_nl(addr)	get_unaligned((hfs_u32 *)(addr))
+#define hfs_get_ahl(addr)	ntohl(*((hfs_u32 *)(addr)))
+#define hfs_get_anl(addr)	(*((hfs_u32 *)(addr)))
+#define hfs_put_hs(val, addr) 	((void)(*((hfs_u16 *)(addr)) = ntohs(val)))
+#define hfs_put_ns(val, addr) 	((void)(*((hfs_u16 *)(addr)) = (val)))
+#define hfs_put_hl(val, addr) 	put_unaligned(htonl(val), (hfs_u32 *)(addr))
+#define hfs_put_nl(val, addr) 	put_unaligned((val), (hfs_u32 *)(addr))
+#define hfs_put_ahl(val, addr) 	((void)(*((hfs_u32 *)(addr)) = ntohl(val)))
+#define hfs_put_anl(val, addr) 	((void)(*((hfs_u32 *)(addr)) = (val)))
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v2.1.77/linux/include/linux/msdos_fs.h	Sat Oct 25 02:44:18 1997
+++ linux/include/linux/msdos_fs.h	Mon Jan  5 16:12:14 1998
@@ -225,7 +225,7 @@
 
 /* inode.c */
 extern int fat_bmap(struct inode *inode,int block);
-extern int fat_notify_change(struct inode *,struct iattr *);
+extern int fat_notify_change(struct dentry *, struct iattr *);
 extern void fat_put_inode(struct inode *inode);
 extern void fat_delete_inode(struct inode *inode);
 extern void fat_put_super(struct super_block *sb);
diff -u --recursive --new-file v2.1.77/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.77/linux/include/linux/nfs_fs.h	Sun Dec 21 22:36:17 1997
+++ linux/include/linux/nfs_fs.h	Mon Jan  5 16:09:38 1998
@@ -11,9 +11,10 @@
 
 #include <linux/signal.h>
 #include <linux/sched.h>
-#include <linux/nfs.h>
 #include <linux/in.h>
+
 #include <linux/sunrpc/debug.h>
+#include <linux/nfs.h>
 #include <linux/nfs_mount.h>
 
 /*
@@ -53,11 +54,12 @@
  */
 #define NFS_SUPER_MAGIC			0x6969
 
+#define NFS_FH(dentry)			((struct nfs_fh *) ((dentry)->d_fsdata))
+#define NFS_DSERVER(dentry)		(&(dentry)->d_sb->u.nfs_sb.s_server)
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_CLIENT(inode)		(NFS_SERVER(inode)->client)
 #define NFS_ADDR(inode)			(RPC_PEERADDR(NFS_CLIENT(inode)))
 #define NFS_CONGESTED(inode)		(RPC_CONGESTED(NFS_CLIENT(inode)))
-#define NFS_FH(inode)			(&(inode)->u.nfs_i.fhandle)
 
 #define NFS_READTIME(inode)		((inode)->u.nfs_i.read_cache_jiffies)
 #define NFS_OLDMTIME(inode)		((inode)->u.nfs_i.read_cache_mtime)
@@ -138,8 +140,8 @@
 extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
 			       struct nfs_fattr *);
 extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
-extern int nfs_revalidate(struct inode *);
-extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *);
+extern int nfs_revalidate(struct dentry *);
+extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *);
 
 /*
  * linux/fs/nfs/file.c
@@ -150,6 +152,7 @@
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
+extern struct dentry_operations nfs_dentry_operations;
 extern void nfs_free_dircache(void);
 extern void nfs_invalidate_dircache(struct inode *);
 extern void nfs_invalidate_dircache_sb(struct super_block *);
@@ -162,25 +165,24 @@
 /*
  * linux/fs/nfs/locks.c
  */
-extern int nfs_lock(struct file *file, int cmd, struct file_lock *fl);
+extern int nfs_lock(struct file *, int, struct file_lock *);
 
 /*
  * linux/fs/nfs/write.c
  */
-extern int  nfs_writepage(struct inode *, struct page *);
+extern int  nfs_writepage(struct dentry *, struct page *);
 extern int  nfs_check_failed_request(struct inode *);
 extern int  nfs_check_error(struct inode *);
 extern int  nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
 extern int  nfs_truncate_dirty_pages(struct inode *, unsigned long);
 extern void nfs_invalidate_pages(struct inode *);
-extern int  nfs_updatepage(struct inode *, struct page *, const char *,
+extern int  nfs_updatepage(struct dentry *, struct page *, const char *,
 			unsigned long, unsigned int, int);
 
 /*
  * linux/fs/nfs/read.c
  */
-extern int  nfs_readpage(struct inode *, struct page *);
-extern int  nfs_readpage_sync(struct inode *, struct page *);
+extern int  nfs_readpage(struct dentry *, struct page *);
 
 /*
  * linux/fs/mount_clnt.c
@@ -192,11 +194,12 @@
  * inline functions
  */
 static inline int
-nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+nfs_revalidate_inode(struct nfs_server *server, struct dentry *dentry)
 {
+	struct inode *inode = dentry->d_inode;
 	if (jiffies - NFS_READTIME(inode) < NFS_ATTRTIMEO(inode))
 		return 0;
-	return _nfs_revalidate_inode(server, inode);
+	return _nfs_revalidate_inode(server, dentry);
 }
 
 extern struct nfs_wreq *	nfs_failed_requests;
diff -u --recursive --new-file v2.1.77/linux/include/linux/nfs_fs_i.h linux/include/linux/nfs_fs_i.h
--- v2.1.77/linux/include/linux/nfs_fs_i.h	Wed Sep  3 20:52:44 1997
+++ linux/include/linux/nfs_fs_i.h	Sun Jan  4 00:53:40 1998
@@ -16,11 +16,6 @@
 	struct pipe_inode_info	pipeinfo;
 
 	/*
-	 * The file handle
-	 */
-	struct nfs_fh		fhandle;
-
-	/*
 	 * Various flags
 	 */
 	unsigned short		flags;
diff -u --recursive --new-file v2.1.77/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.77/linux/include/linux/sched.h	Fri Jan  2 14:37:02 1998
+++ linux/include/linux/sched.h	Mon Jan  5 16:09:38 1998
@@ -182,14 +182,14 @@
 	long counter;
 	long priority;
 	unsigned long flags;	/* per process flags, defined below */
-	mm_segment_t addr_limit;	/* thread address space:
-					 	0-0xBFFFFFFF for user-thead
-						0-0xFFFFFFFF for kernel-thread
-					 */
 	int sigpending;
 	long debugreg[8];  /* Hardware debugging registers */
 	struct exec_domain *exec_domain;
 /* various fields */
+	mm_segment_t addr_limit;	/* thread address space:
+					 	0-0xBFFFFFFF for user-thead
+						0-0xFFFFFFFF for kernel-thread
+					 */
 	struct linux_binfmt *binfmt;
 	struct task_struct *next_task, *prev_task;
 	struct task_struct *next_run,  *prev_run;
@@ -311,9 +311,10 @@
  * your own risk!. Base=0, limit=0x1fffff (=2MB)
  */
 #define INIT_TASK \
-/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,KERNEL_DS,0, \
+/* state etc */	{ 0,DEF_PRIORITY,DEF_PRIORITY,0,0, \
 /* debugregs */ { 0, },            \
 /* exec domain */&default_exec_domain, \
+/* mm_seg */	KERNEL_DS, \
 /* binfmt */	NULL, \
 /* schedlink */	&init_task,&init_task, &init_task, &init_task, \
 /* ec,brk... */	0,0,0,0,0,0, \
diff -u --recursive --new-file v2.1.77/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h
--- v2.1.77/linux/include/linux/smb_fs.h	Fri Dec 19 15:53:05 1997
+++ linux/include/linux/smb_fs.h	Mon Jan  5 16:12:14 1998
@@ -94,9 +94,9 @@
 struct super_block *smb_read_super(struct super_block *, void *, int);
 void smb_get_inode_attr(struct inode *, struct smb_fattr *);
 void smb_invalidate_inodes(struct smb_sb_info *);
-int  smb_revalidate_inode(struct inode *);
+int  smb_revalidate_inode(struct dentry *);
 int  smb_refresh_inode(struct inode *);
-int  smb_notify_change(struct inode *, struct iattr *);
+int  smb_notify_change(struct dentry *, struct iattr *);
 unsigned long smb_invent_inos(unsigned long);
 struct inode *smb_iget(struct super_block *, struct smb_fattr *);
 
diff -u --recursive --new-file v2.1.77/linux/include/linux/uts.h linux/include/linux/uts.h
--- v2.1.77/linux/include/linux/uts.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/uts.h	Mon Jan  5 01:41:01 1998
@@ -0,0 +1,23 @@
+#ifndef _LINUX_UTS_H
+#define _LINUX_UTS_H
+
+/*
+ * Defines for what uname() should return 
+ */
+#ifndef UTS_SYSNAME
+#define UTS_SYSNAME "Linux"
+#endif
+
+#ifndef UTS_MACHINE
+#define UTS_MACHINE "unknown"
+#endif
+
+#ifndef UTS_NODENAME
+#define UTS_NODENAME "(none)"	/* set by sethostname() */
+#endif
+
+#ifndef UTS_DOMAINNAME
+#define UTS_DOMAINNAME "(none)"	/* set by setdomainname() */
+#endif
+
+#endif
diff -u --recursive --new-file v2.1.77/linux/init/main.c linux/init/main.c
--- v2.1.77/linux/init/main.c	Fri Jan  2 14:37:03 1998
+++ linux/init/main.c	Sun Jan  4 10:40:17 1998
@@ -105,7 +105,10 @@
 #ifdef CONFIG_DECNET
 extern void decnet_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_BLK_DEV_XD
 extern void xd_setup(char *str, int *ints);
+extern void xd_manual_geo_init(char *str, int *ints);
+#endif
 #ifdef CONFIG_BLK_DEV_IDE
 extern void ide_setup(char *);
 #endif
@@ -583,6 +586,7 @@
 #endif
 #ifdef CONFIG_BLK_DEV_XD
 	{ "xd=", xd_setup },
+	{ "xd_geo=", xd_manual_geo_init },
 #endif
 #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_AMIGA_FLOPPY) || defined(CONFIG_ATARI_FLOPPY)
 	{ "floppy=", floppy_setup },
diff -u --recursive --new-file v2.1.77/linux/init/version.c linux/init/version.c
--- v2.1.77/linux/init/version.c	Sun Nov 19 07:45:02 1995
+++ linux/init/version.c	Mon Jan  5 01:41:01 1998
@@ -6,14 +6,10 @@
  *  May be freely distributed as part of Linux.
  */
 
-#include <linux/config.h>
+#include <linux/uts.h>
 #include <linux/utsname.h>
 #include <linux/version.h>
 #include <linux/compile.h>
-
-/* make the "checkconfig" script happy: we really need to include config.h */
-#ifdef CONFIG_BOGUS
-#endif
 
 #define version(a) Version_ ## a
 #define version_string(a) version(a)
diff -u --recursive --new-file v2.1.77/linux/kernel/Makefile linux/kernel/Makefile
--- v2.1.77/linux/kernel/Makefile	Mon Dec  1 12:04:15 1997
+++ linux/kernel/Makefile	Mon Jan  5 01:41:01 1998
@@ -21,7 +21,6 @@
 OX_OBJS  += ksyms.o
 endif
 
-include $(TOPDIR)/Rules.make
+CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer
 
-sched.o: sched.c
-	$(CC) $(CFLAGS) $(PROFILING) -fno-omit-frame-pointer -c $<
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.77/linux/kernel/module.c linux/kernel/module.c
--- v2.1.77/linux/kernel/module.c	Tue Dec  2 09:49:40 1997
+++ linux/kernel/module.c	Sun Jan  4 10:40:17 1998
@@ -772,8 +772,10 @@
 	/* Let the module clean up.  */
 
 	mod->flags |= MOD_DELETED;
-	if (mod->flags & MOD_RUNNING) {
-		mod->cleanup();
+	if (mod->flags & MOD_RUNNING) 
+	{
+		if(mod->cleanup)
+			mod->cleanup();
 		mod->flags &= ~MOD_RUNNING;
 	}
 
diff -u --recursive --new-file v2.1.77/linux/mm/filemap.c linux/mm/filemap.c
--- v2.1.77/linux/mm/filemap.c	Tue Dec 23 16:31:00 1997
+++ linux/mm/filemap.c	Sun Jan  4 00:53:41 1998
@@ -260,8 +260,10 @@
  * that we could use for the cache (if it is 0 we can try to create one,
  * this is all overlapped with the IO on the previous page finishing anyway)
  */
-static unsigned long try_to_read_ahead(struct inode * inode, unsigned long offset, unsigned long page_cache)
+static unsigned long try_to_read_ahead(struct dentry * dentry,
+				unsigned long offset, unsigned long page_cache)
 {
+	struct inode *inode = dentry->d_inode;
 	struct page * page;
 	struct page ** hash;
 
@@ -282,7 +284,7 @@
 			 */
 			page = mem_map + MAP_NR(page_cache);
 			add_to_page_cache(page, inode, offset, hash);
-			inode->i_op->readpage(inode, page);
+			inode->i_op->readpage(dentry, page);
 			page_cache = 0;
 		}
 		release_page(page);
@@ -445,9 +447,9 @@
 	return max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)];
 }
 
-static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode,
-	unsigned long ppos, struct page * page,
-	unsigned long page_cache)
+static inline unsigned long generic_file_readahead(int reada_ok,
+	struct file * filp, struct inode * inode,
+	unsigned long ppos, struct page * page, unsigned long page_cache)
 {
 	unsigned long max_ahead, ahead;
 	unsigned long raend;
@@ -511,7 +513,8 @@
 	ahead = 0;
 	while (ahead < max_ahead) {
 		ahead += PAGE_SIZE;
-		page_cache = try_to_read_ahead(inode, raend + ahead, page_cache);
+		page_cache = try_to_read_ahead(filp->f_dentry, raend + ahead,
+						page_cache);
 	}
 /*
  * If we tried to read ahead some pages,
@@ -559,7 +562,8 @@
 ssize_t generic_file_read(struct file * filp, char * buf,
 			  size_t count, loff_t *ppos)
 {
-	struct inode *inode = filp->f_dentry->d_inode;
+	struct dentry *dentry = filp->f_dentry;
+	struct inode *inode = dentry->d_inode;
 	ssize_t error, read;
 	size_t pos, pgpos, page_cache;
 	int reada_ok;
@@ -716,7 +720,7 @@
 		if (reada_ok && filp->f_ramax > MIN_READAHEAD)
 			filp->f_ramax = MIN_READAHEAD;
 
-		error = inode->i_op->readpage(inode, page);
+		error = inode->i_op->readpage(dentry, page);
 		if (!error)
 			goto found_page;
 		release_page(page);
@@ -728,7 +732,7 @@
 		 * Try to re-read it _once_. We do this synchronously,
 		 * because this happens only if there were errors.
 		 */
-		error = inode->i_op->readpage(inode, page);
+		error = inode->i_op->readpage(dentry, page);
 		if (!error) {
 			wait_on_page(page);
 			if (PageUptodate(page) && !PageError(page))
@@ -763,9 +767,10 @@
  */
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
 {
+	struct dentry * dentry = area->vm_dentry;
+	struct inode * inode = dentry->d_inode;
 	unsigned long offset;
 	struct page * page, **hash;
-	struct inode * inode = area->vm_dentry->d_inode;
 	unsigned long old_page, new_page;
 
 	new_page = 0;
@@ -846,14 +851,14 @@
 	new_page = 0;
 	add_to_page_cache(page, inode, offset, hash);
 
-	if (inode->i_op->readpage(inode, page) != 0)
+	if (inode->i_op->readpage(dentry, page) != 0)
 		goto failure;
 
 	/*
 	 * Do a very limited read-ahead if appropriate
 	 */
 	if (PageLocked(page))
-		new_page = try_to_read_ahead(inode, offset + PAGE_SIZE, 0);
+		new_page = try_to_read_ahead(dentry, offset + PAGE_SIZE, 0);
 	goto found_page;
 
 page_locked_wait:
@@ -868,7 +873,7 @@
 	 * because there really aren't any performance issues here
 	 * and we need to check for errors.
 	 */
-	if (inode->i_op->readpage(inode, page) != 0)
+	if (inode->i_op->readpage(dentry, page) != 0)
 		goto failure;
 	wait_on_page(page);
 	if (PageError(page))
@@ -1305,7 +1310,8 @@
 generic_file_write(struct file *file, const char *buf,
 		   size_t count, loff_t *ppos)
 {
-	struct inode	*inode = file->f_dentry->d_inode; 
+	struct dentry	*dentry = file->f_dentry; 
+	struct inode	*inode = dentry->d_inode; 
 	struct page	*page, **hash;
 	unsigned long	page_cache = 0;
 	unsigned long	pgpos, offset;
@@ -1367,10 +1373,10 @@
 		 */
 		if (!PageUptodate(page)) {
 			if (bytes < PAGE_SIZE && pgpos < inode->i_size) {
+				status = -EIO; /* two tries ... error out */
 				if (didread < 2)
-				    status = inode->i_op->readpage(inode, page);
-				else 
-				    status = -EIO; /* two tries ... error out */
+					status = inode->i_op->readpage(dentry,
+									page);
 				if (status < 0)
 					goto done_with_page;
 				didread++;
@@ -1380,7 +1386,7 @@
 		}
 
 		/* Alright, the page is there.  Now update it. */
-		status = inode->i_op->updatepage(inode, page, buf,
+		status = inode->i_op->updatepage(dentry, page, buf,
 							offset, bytes, sync);
 done_with_page:
 		__free_page(page);
diff -u --recursive --new-file v2.1.77/linux/net/core/filter.c linux/net/core/filter.c
--- v2.1.77/linux/net/core/filter.c	Fri Jan  2 14:37:03 1998
+++ linux/net/core/filter.c	Sun Jan  4 10:40:17 1998
@@ -322,7 +322,7 @@
 	 *	end they _will_ hit this.
 	 */
 	 
-        return (BPF_CLASS(filter[flen - 1].code) == BPF_RET);
+        return (BPF_CLASS(filter[flen - 1].code) == BPF_RET)?0:-EINVAL;
 }
 
 /*
@@ -340,7 +340,7 @@
         if(fprog->filter == NULL || fprog->len == 0 || fsize > BPF_MAXINSNS)
                 return (-EINVAL);
 
-	if((err = sk_chk_filter(fprog->filter, fprog->len)))
+	if((err = sk_chk_filter(fprog->filter, fprog->len))==0)
 	{
 		/* If existing filter, remove it first */
 		if(sk->filter)
diff -u --recursive --new-file v2.1.77/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.77/linux/net/ipv4/af_inet.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/af_inet.c	Sun Jan  4 10:40:17 1998
@@ -326,7 +326,15 @@
 	if (sock->type == SOCK_PACKET) {
 		static int warned; 
 		if (net_families[AF_PACKET]==NULL)
+		{
+#if defined(CONFIG_KERNELD) && defined(CONFIG_PACKET_MODULE)
+			char module_name[30];
+			sprintf(module_name,"net-pf-%d", AF_PACKET);
+			request_module(module_name);
+			if (net_families[AF_PACKET] == NULL)
+#endif
 			return -ESOCKTNOSUPPORT;
+		}
 		if (!warned++)
 			printk(KERN_INFO "%s uses obsolete (AF_INET,SOCK_PACKET)\n", current->comm);
 		return net_families[AF_PACKET]->create(sock, protocol);
diff -u --recursive --new-file v2.1.77/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.77/linux/net/netsyms.c	Sun Dec 21 22:36:18 1997
+++ linux/net/netsyms.c	Sun Jan  4 10:40:17 1998
@@ -191,6 +191,9 @@
 EXPORT_SYMBOL(in_ntoa);
 EXPORT_SYMBOL(net_ratelimit);
 
+/* needed for ip_gre -cw */
+EXPORT_SYMBOL(ip_statistics);
+
 #ifdef CONFIG_IPV6_MODULE
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_stream_ops);
diff -u --recursive --new-file v2.1.77/linux/scripts/Makefile linux/scripts/Makefile
--- v2.1.77/linux/scripts/Makefile	Tue Dec 23 16:31:00 1997
+++ linux/scripts/Makefile	Mon Jan  5 01:41:01 1998
@@ -25,15 +25,15 @@
 	${HOSTCC} -o tkparse tkparse.o tkcond.o tkgen.o
 
 tkparse.o: tkparse.c tkparse.h
-	$(HOSTCC) $(HOSTCFLAGS) -c -o tkparse.o tkparse.c
 
 tkcond.o: tkcond.c tkparse.h
-	$(HOSTCC) $(HOSTCFLAGS) -c -o tkcond.o tkcond.c
 
 tkgen.o: tkgen.c tkparse.h
-	$(HOSTCC) $(HOSTCFLAGS) -c -o tkgen.o tkgen.c
+
+tkparse.o tkcond.o tkgen.o:
+	$(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c)
 
 clean:
-	rm -f *~ kconfig.tk *.o tkparse
+	rm -f *~ kconfig.tk *.o tkparse mkdep split-include
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.77/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.1.77/linux/scripts/Menuconfig	Fri Jan  2 14:37:03 1998
+++ linux/scripts/Menuconfig	Mon Jan  5 01:41:01 1998
@@ -31,6 +31,11 @@
 # 081297 Pavel Machek (pavel@atrey.karlin.mff.cuni.cz) - better error 
 # handling
 #
+# 131197 Michael Chastain (mec@shout.net) - output all lines for a
+# choice list, not just the selected one.  This makes the output
+# the same as Configure output, which is important for smart config
+# dependencies.
+#
 # 101297 Michael Chastain (mec@shout.net) - remove sound driver cruft.
 #
 # 221297 Michael Chastain (mec@shout.net) - make define_bool actually
@@ -1051,11 +1056,19 @@
 		: ${current:=$default}
 
 		#
-		# Then extract the actual option from the list of choices.
+		# Output all choices (to be compatible with other configs).
 		#
-		current=${choices#*$current} ; set $current
-
-		define_bool "$1" "y"
+		set -- $choices
+		while [ -n "$2" ]
+		do
+			if eval [ "$1" = "$current" ]
+			then
+			    define_bool "$2" "y"
+			else
+			    define_bool "$2" "n"
+			fi
+			shift ; shift
+		done
 	}
 
 	function mainmenu_name () {
diff -u --recursive --new-file v2.1.77/linux/scripts/checkconfig.pl linux/scripts/checkconfig.pl
--- v2.1.77/linux/scripts/checkconfig.pl	Wed Dec 31 16:00:00 1969
+++ linux/scripts/checkconfig.pl	Mon Jan  5 01:41:01 1998
@@ -0,0 +1,50 @@
+#! /usr/bin/perl
+#
+# checkconfig: find uses of CONFIG_* names without matching definitions.
+
+use integer;
+
+foreach $file (@ARGV)
+{
+    # Open this file.
+    open(FILE, $file) || die "Can't open $file: $!\n";
+
+    # Initialize variables.
+    my $fInComment   = 0;
+    my $fUseConfig   = 0;
+    my $iLinuxConfig = 0;
+    my %configList   = ();
+
+    LINE: while ( <FILE> )
+    {
+	# Strip comments.
+	$fInComment && (s+^.*?\*/+ +o ? ($fInComment = 0) : next);
+	m+/\*+o && (s+/\*.*?\*/+ +go, (s+/\*.*$+ +o && ($fInComment = 1)));
+
+	# Pick up definitions.
+	if ( m/^#/o )
+	{
+	    $iLinuxConfig      = $. if m/^#\s*include\s+<linux\/config\.h>/o;
+	    $configList{uc $1} = 1  if m/^#\s*include\s+<config\/(\S*)\.h>/o;
+	    $configList{$1}    = 1  if m/^#\s*define\s+CONFIG_(\w*)/o;
+	    $configList{$1}    = 1  if m/^#\s*undef\s+CONFIG_(\w*)/o;
+	}
+
+	# Look for usages.
+	next unless m/CONFIG_/o;
+	WORD: while ( m/\bCONFIG_(\w*)/og )
+	{
+	    $fUseConfig = 1;
+	    last LINE if $iLinuxConfig;
+	    next WORD if exists $configList{$1};
+	    print "$file: $.: need CONFIG_$1.\n";
+	    $configList{$1} = 0;
+	}
+    }
+
+    # Report superfluous includes.
+    if ( $iLinuxConfig && ! $fUseConfig )
+	{ print "$file: $iLinuxConfig: <linux/config.h> not needed.\n"; }
+
+    close(FILE);
+}
diff -u --recursive --new-file v2.1.77/linux/scripts/depend.awk linux/scripts/depend.awk
--- v2.1.77/linux/scripts/depend.awk	Sun May 19 22:37:40 1996
+++ linux/scripts/depend.awk	Mon Jan  5 01:41:01 1998
@@ -1,154 +0,0 @@
-# This is an awk script which does dependencies. We do NOT want it to
-# recursively follow #include directives.
-#
-# The HPATH environment variable should be set to indicate where to look
-# for include files.  The -I in front of the path is optional.
-
-#
-# Surely there is a more elegant way to see if a file exists.  Anyone know
-# what it is?
-#
-function fileExists(f,    TMP, dummy, result) {
-	if(result=FILEHASH[f]) {
-		if(result=="Yes") {
-			return "Yes"
-		} else {return ""}
-	}
-	ERRNO = getline dummy < f
-	if(ERRNO >= 0) {
-		close(f)
-		return FILEHASH[f]="Yes"
-	} else {
-		FILEHASH[f]="No"
-		return ""
-	}
-}
-
-function endfile(f) {
-	if (hasconfig && !needsconfig) {
-		printf "%s doesn't need config\n",f > "/dev/stderr"
-	}
-	if (hasdep) {
-		print cmd
-	}
-}
-
-BEGIN{
-	hasdep=0
-	hasconfig=0
-	needsconfig=0
-	incomment=0
-	if(!(TOPDIR=ENVIRON["TOPDIR"])) {
-		print "Environment variable TOPDIR is not set"
-		exit 1
-	}
-	split(ENVIRON["HPATH"],parray," ")
-	for(path in parray) {
-	    sub("^-I","",parray[path])
-	    sub("[/ ]*$","",parray[path])
-	}
-}
-
-# eliminate comments
-{
-    # remove all comments fully contained on a single line
-	gsub("\\/\\*.*\\*\\/", "")
-	if (incomment) {
-		if ($0 ~ /\*\//) {
-			incomment = 0;
-			gsub(".*\\*\\/", "")
-		} else {
-			next
-		}
-	} else {
-		# start of multi-line comment
-		if ($0 ~ /\/\*/)
-		{
-			incomment = 1;
-			sub("\\/\\*.*", "")
-		} else if ($0 ~ /\*\//) {
-			incomment = 0;
-			sub(".*\\*\\/", "")
-		}
-	}
-}
-
-/^[ 	]*#[ 	]*if.*[^A-Za-z_]CONFIG_/ {
-	needsconfig=1
-	if (!hasconfig) {
-		printf "%s needs config but has not included config file\n",FILENAME > "/dev/stderr"
-		# only say it once per file..
-		hasconfig = 1
-	}
-}
-
-/^[ 	]*#[ 	]*include[ 	]*[<"][^ 	]*[>"]/{
-	found=0
-	if(LASTFILE!=FILENAME) {
-		endfile(LASTFILE)
-		hasdep=0
-		hasconfig=0
-		needsconfig=0
-		incomment=0
-		cmd=""
-		LASTFILE=FILENAME
-		depname=FILENAME
-		relpath=FILENAME
-		sub("\\.c$",".o: ",depname)
-		sub("\\.S$",".o: ",depname)
-		if (depname==FILENAME) {
-			cmd="\n\t@touch "depname
-		}
-		sub("\\.h$",".h: ",depname)
-		if(relpath ~ "^\\." ) {
-			sub("[^/]*$","",  relpath)
-			relpath=relpath"/"
-			sub("//","/",  relpath)
-		} else {
-			relpath=""
-		}
-	}
-	fname=$0
-	sub("^#[ 	]*include[ 	]*[<\"]","",fname)
-	sub("[>\"].*","",fname)
-	if (fname=="linux/config.h") {
-		hasconfig=1
-	}
-	rfname=relpath""fname
-	if(fileExists(rfname)) {
-		found=1
-		if (!hasdep) {
-			printf "%s", depname
-		}
-		hasdep=1
-		printf " \\\n   %s", rfname
-		if(fname ~ "^\\." ) {
-			fnd=0;
-			for(i in ARGV) {
-				if(ARGV[i]==rfname) {
-					fnd=1
-				}
-			}
-			if(fnd==0) {
-				ARGV[ARGC]=rfname
-				++ARGC
-			}
-		}
-	} else {
-		for(path in parray) {
-			if(fileExists(parray[path]"/"fname)) {
-				shortp=parray[path]
-				found=1
-				if (!hasdep) {
-					printf "%s", depname
-				}
-				hasdep=1
-				printf " \\\n   %s", parray[path]"/"fname
-			}
-		}
-	}
-}
-
-END{
-	endfile(FILENAME)
-}
diff -u --recursive --new-file v2.1.77/linux/scripts/header.tk linux/scripts/header.tk
--- v2.1.77/linux/scripts/header.tk	Tue Dec 23 16:31:00 1997
+++ linux/scripts/header.tk	Mon Jan  5 01:41:01 1998
@@ -228,10 +228,10 @@
 proc write_tristate { file1 file2 varname variable dep } {
 	if { $variable == 0 } \
 		then { puts $file1 "# $varname is not set"; \
-		       puts $file2 "#undef $varname"} \
+		       puts $file2 "#undef  $varname"} \
 	elseif { $variable == 2 || ($dep == 2 && $variable == 1) } \
 		then { puts $file1 "$varname=m"; \
-		       puts $file2 "#undef $varname"; \
+		       puts $file2 "#undef  $varname"; \
 		       puts $file2 "#define ${varname}_MODULE 1" } \
 	elseif { $variable == 1 && $dep != 2 } \
 		then { puts $file1 "$varname=y"; \
@@ -244,7 +244,7 @@
 proc write_int { file1 file2 varname variable dep } {
 	if { $dep == 0 } \
 		then { puts $file1 "# $varname is not set"; \
-		       puts $file2 "#undef $varname"} \
+		       puts $file2 "#undef  $varname"} \
 	else {
 		puts $file1 "$varname=$variable"; \
 		puts $file2 "#define $varname $variable"; \
@@ -254,7 +254,7 @@
 proc write_hex { file1 file2 varname variable dep } {
 	if { $dep == 0 } \
 		then { puts $file1 "# $varname is not set"; \
-		       puts $file2 "#undef $varname"} \
+		       puts $file2 "#undef  $varname"} \
 	else {
 		puts $file1 "$varname=$variable"; \
 		puts $file2 "#define $varname 0x$variable"; \
@@ -264,7 +264,7 @@
 proc write_string { file1 file2 varname variable dep } {
 	if { $dep == 0 } \
 		then { puts $file1 "# $varname is not set"; \
-		       puts $file2 "#undef $varname"} \
+		       puts $file2 "#undef  $varname"} \
 	else {
 		puts $file1 "$varname=\"$variable\""; \
 		puts $file2 "#define $varname \"$variable\""; \
diff -u --recursive --new-file v2.1.77/linux/scripts/hfiles.sh linux/scripts/hfiles.sh
--- v2.1.77/linux/scripts/hfiles.sh	Tue Aug 15 05:07:03 1995
+++ linux/scripts/hfiles.sh	Mon Jan  5 01:41:01 1998
@@ -1,11 +0,0 @@
-#!/bin/sh
-#
-# This script looks to see if a directory contains .h files
-#
-for dir in $@; do
-	for hfile in $dir/*.h; do
-		if [ -f $hfile ]; then echo $dir; fi
-		break
-	done
-done
-exit 0
diff -u --recursive --new-file v2.1.77/linux/scripts/mkdep.c linux/scripts/mkdep.c
--- v2.1.77/linux/scripts/mkdep.c	Mon Jun 16 16:36:02 1997
+++ linux/scripts/mkdep.c	Mon Jan  5 01:41:01 1998
@@ -1,18 +1,31 @@
+/*
+ * Originally by Linus Torvalds.
+ * Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
+ *
+ * Usage: mkdep file ...
+ * 
+ * Read source files and output makefile dependency lines for them.
+ * I make simple dependency lines for #include <*.h> and #include "*.h".
+ * I also find instances of CONFIG_FOO and generate dependencies
+ *    like include/config/foo.h.
+ */
+
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
-
-#include <errno.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
+
 #include <sys/fcntl.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
-char *filename, *command, __depname[256] = "\n\t@touch ";
-int needsconfig, hasconfig, hasmodules, hasdep;
 
+
+char __depname[512] = "\n\t@touch ";
 #define depname (__depname+9)
+int hasdep;
 
 struct path_struct {
 	int len;
@@ -22,22 +35,113 @@
 	{  0, "" }
 };
 
-static void handle_include(int type, char *name, int len)
+
+
+/*
+ * This records all the configuration options seen.
+ * In perl this would be a hash, but here it's a long string
+ * of values separated by newlines.  This is simple and
+ * extremely fast.
+ */
+char * str_config  = NULL;
+int    size_config = 0;
+int    len_config  = 0;
+
+
+
+/*
+ * Grow the configuration string to a desired length.
+ * Usually the first growth is plenty.
+ */
+void grow_config(int len)
+{
+	if (str_config == NULL) {
+		len_config  = 0;
+		size_config = 4096;
+		str_config  = malloc(4096);
+		if (str_config == NULL)
+			{ perror("malloc"); exit(1); }
+	}
+
+	while (len_config + len > size_config) {
+		str_config = realloc(str_config, size_config *= 2);
+		if (str_config == NULL)
+			{ perror("malloc"); exit(1); }
+	}
+}
+
+
+
+/*
+ * Lookup a value in the configuration string.
+ */
+int is_defined_config(const char * name, int len)
+{
+	const char * pconfig;
+	const char * plast = str_config + len_config - len;
+	for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
+		if (pconfig[ -1] == '\n'
+		&&  pconfig[len] == '\n'
+		&&  !memcmp(pconfig, name, len))
+			return 1;
+	}
+	return 0;
+}
+
+
+
+/*
+ * Add a new value to the configuration string.
+ */
+void define_config(int convert, const char * name, int len)
+{
+	grow_config(len + 1);
+
+	memcpy(str_config+len_config, name, len);
+
+	if (convert) {
+		int i;
+		for (i = 0; i < len; i++) {
+			char c = str_config[len_config+i];
+			if (isupper(c)) c = tolower(c);
+			if (c == '_')   c = '/';
+			str_config[len_config+i] = c;
+		}
+	}
+
+	len_config += len;
+	str_config[len_config++] = '\n';
+}
+
+
+
+/*
+ * Clear the set of configuration strings.
+ */
+void clear_config( )
+{
+	len_config = 0;
+	define_config(0, "", 0);
+}
+
+
+
+/*
+ * Handle an #include line.
+ */
+void handle_include(int type, const char * name, int len)
 {
-	int plen;
 	struct path_struct *path = path_array+type;
 
-	if (len == 14)
-		if (!memcmp(name, "linux/config.h", len))
-			hasconfig = 1;
-		else if (!memcmp(name, "linux/module.h", len))
-			hasmodules = 1;
-
-	plen = path->len;
-	memcpy(path->buffer+plen, name, len);
-	len += plen;
-	path->buffer[len] = '\0';
-	if (access(path->buffer, F_OK))
+	if (len == 14 && !memcmp(name, "linux/config.h", len))
+		return;
+
+	if (len >= 7 && !memcmp(name, "config/", 7))
+		define_config(0, name+7, len-7-2);
+
+	memcpy(path->buffer+path->len, name, len);
+	path->buffer[path->len+len] = '\0';
+	if (access(path->buffer, F_OK) != 0)
 		return;
 
 	if (!hasdep) {
@@ -47,15 +151,48 @@
 	printf(" \\\n   %s", path->buffer);
 }
 
-static void handle_config(void)
+
+
+/*
+ * Record the use of a CONFIG_* word.
+ */
+void use_config(const char * name, int len)
 {
-	needsconfig = 1;
-	if (!hasconfig)
-		fprintf(stderr,
-			"%s needs config but has not included config file\n",
-			filename);
+	char *pc;
+	int i;
+
+	pc = path_array[0].buffer + path_array[0].len;
+	memcpy(pc, "config/", 7);
+	pc += 7;
+
+	for (i = 0; i < len; i++) {
+	    char c = name[i];
+	    if (isupper(c)) c = tolower(c);
+	    if (c == '_')   c = '/';
+	    pc[i] = c;
+	}
+	pc[len] = '\0';
+
+	if (is_defined_config(pc, len))
+	    return;
+
+	define_config(0, pc, len);
+
+	if (!hasdep) {
+		hasdep = 1;
+		printf("%s: ", depname);
+	}
+	printf(" \\\n   $(wildcard %s.h)", path_array[0].buffer);
 }
 
+
+
+/*
+ * Macros for stunningly fast map-based character access.
+ * __buf is a register which holds the current word of the input.
+ * Thus, there is one memory access per sizeof(unsigned long) characters.
+ */
+
 #if defined(__alpha__) || defined(__i386__)
 #define LE_MACHINE
 #endif
@@ -69,243 +206,283 @@
 #endif
 
 #define GETNEXT { \
-next_byte(__buf); \
-if (!__nrbuf) { \
-	__buf = *(unsigned long *) next; \
-	__nrbuf = sizeof(unsigned long); \
-	if (!__buf) \
-		break; \
-} next++; __nrbuf--; }
+	next_byte(__buf); \
+	if ((unsigned long) next % sizeof(unsigned long) == 0) { \
+		__buf = * (unsigned long *) next; \
+		if (!__buf) \
+			break; \
+	} \
+	next++; \
+}
+
+/*
+ * State machine macros.
+ */
 #define CASE(c,label) if (current == c) goto label
 #define NOTCASE(c,label) if (current != c) goto label
 
-static void state_machine(register char *next)
-{
-	for(;;) {
-	register unsigned long __buf = 0;
-	register unsigned long __nrbuf = 0;
-
-normal:
-	GETNEXT
-__normal:
-	CASE('/',slash);
-	CASE('"',string);
-	CASE('\'',char_const);
-	CASE('#',preproc);
-	goto normal;
-
-slash:
-	GETNEXT
-	CASE('*',comment);
-	goto __normal;
+/*
+ * Yet another state machine speedup.
+ */
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MIN2(a,b) ((a)<(b)?(a):(b))
+#define MAX5(a,b,c,d,e) (MAX2(a,MAX2(b,MAX2(c,MAX2(d,e)))))
+#define MIN5(a,b,c,d,e) (MIN2(a,MIN2(b,MIN2(c,MIN2(d,e)))))
 
-string:
-	GETNEXT
-	CASE('"',normal);
-	NOTCASE('\\',string);
-	GETNEXT
-	goto string;
 
-char_const:
-	GETNEXT
-	CASE('\'',normal);
-	NOTCASE('\\',char_const);
-	GETNEXT
-	goto char_const;
 
-comment:
-	GETNEXT
-__comment:
-	NOTCASE('*',comment);
-	GETNEXT
-	CASE('/',normal);
-	goto __comment;
-
-preproc:
-	GETNEXT
-	CASE('\n',normal);
-	CASE(' ',preproc);
-	CASE('\t',preproc);
-	CASE('i',i_preproc);
-	CASE('e',e_preproc);
-	GETNEXT
-
-skippreproc:
-	CASE('\n',normal);
-	CASE('\\',skippreprocslash);
-	GETNEXT
-	goto skippreproc;
-
-skippreprocslash:
-	GETNEXT;
-	GETNEXT;
-	goto skippreproc;
-
-e_preproc:
-	GETNEXT
-	NOTCASE('l',skippreproc);
-	GETNEXT
-	NOTCASE('i',skippreproc);
-	GETNEXT
-	CASE('f',if_line);
-	goto skippreproc;
+/*
+ * The state machine looks for (approximately) these Perl regular expressions:
+ *
+ *    m|\/\*.*?\*\/|
+ *    m|'.*?'|
+ *    m|".*?"|
+ *    m|#\s*include\s*"(.*?)"|
+ *    m|#\s*include\s*<(.*?>"|
+ *    m|#\s*(?define|undef)\s*CONFIG_(\w*)|
+ *    m|(?!\w)CONFIG_|
+ *
+ * About 98% of the CPU time is spent here, and most of that is in
+ * the 'start' paragraph.  Because the current characters are
+ * in a register, the start loop usually eats 4 or 8 characters
+ * per memory read.  The MAX5 and MIN5 tests dispose of most
+ * input characters with 1 or 2 comparisons.
+ */
+void state_machine(const char * map)
+{
+	const char * next = map;
+	const char * map_dot;
+	unsigned long __buf = 0;
+
+	for (;;) {
+start:
+	GETNEXT
+__start:
+	if (current > MAX5('/','\'','"','#','C')) goto start;
+	if (current < MIN5('/','\'','"','#','C')) goto start;
+	CASE('/',  slash);
+	CASE('\'', squote);
+	CASE('"',  dquote);
+	CASE('#',  pound);
+	CASE('C',  cee);
+	goto start;
 
-i_preproc:
-	GETNEXT
-	CASE('f',if_line);
-	NOTCASE('n',skippreproc);
-	GETNEXT
-	NOTCASE('c',skippreproc);
-	GETNEXT
-	NOTCASE('l',skippreproc);
-	GETNEXT
-	NOTCASE('u',skippreproc);
+/* / */
+slash:
 	GETNEXT
-	NOTCASE('d',skippreproc);
+	NOTCASE('*', __start);
+slash_star_dot_star:
 	GETNEXT
-	NOTCASE('e',skippreproc);
-
-/* "# include" found */
-include_line:
+__slash_star_dot_star:
+	NOTCASE('*', slash_star_dot_star);
 	GETNEXT
-	CASE('\n',normal);
-	CASE('<', std_include_file);
-	NOTCASE('"', include_line);
+	NOTCASE('/', __slash_star_dot_star);
+	goto start;
 
-/* "local" include file */
-{
-	char *incname = next;
-local_include_name:
-	GETNEXT
-	CASE('\n',normal);
-	NOTCASE('"', local_include_name);
-	handle_include(1, incname, next-incname-1);
-	goto skippreproc;
+/* '.*?' */
+squote:
+	GETNEXT
+	CASE('\'', start);
+	NOTCASE('\\', squote);
+	GETNEXT
+	goto squote;
+
+/* ".*?" */
+dquote:
+	GETNEXT
+	CASE('"', start);
+	NOTCASE('\\', dquote);
+	GETNEXT
+	goto dquote;
+
+/* #\s* */
+pound:
+	GETNEXT
+	CASE(' ',  pound);
+	CASE('\t', pound);
+	CASE('i',  pound_i);
+	CASE('d',  pound_d);
+	CASE('u',  pound_u);
+	goto __start;
+
+/* #\s*i */
+pound_i:
+	GETNEXT NOTCASE('n', __start);
+	GETNEXT NOTCASE('c', __start);
+	GETNEXT NOTCASE('l', __start);
+	GETNEXT NOTCASE('u', __start);
+	GETNEXT NOTCASE('d', __start);
+	GETNEXT NOTCASE('e', __start);
+	goto pound_include;
+
+/* #\s*include\s* */
+pound_include:
+	GETNEXT
+	CASE(' ',  pound_include);
+	CASE('\t', pound_include);
+	map_dot = next;
+	CASE('"',  pound_include_dquote);
+	CASE('<',  pound_include_langle);
+	goto __start;
+
+/* #\s*include\s*"(.*)" */
+pound_include_dquote:
+	GETNEXT
+	CASE('\n', start);
+	NOTCASE('"', pound_include_dquote);
+	handle_include(1, map_dot, next - map_dot - 1);
+	goto start;
+
+/* #\s*include\s*<(.*)> */
+pound_include_langle:
+	GETNEXT
+	CASE('\n', start);
+	NOTCASE('>', pound_include_langle);
+	handle_include(0, map_dot, next - map_dot - 1);
+	goto start;
+
+/* #\s*d */
+pound_d:
+	GETNEXT NOTCASE('e', __start);
+	GETNEXT NOTCASE('f', __start);
+	GETNEXT NOTCASE('i', __start);
+	GETNEXT NOTCASE('n', __start);
+	GETNEXT NOTCASE('e', __start);
+	goto pound_define_undef;
+
+/* #\s*u */
+pound_u:
+	GETNEXT NOTCASE('n', __start);
+	GETNEXT NOTCASE('d', __start);
+	GETNEXT NOTCASE('e', __start);
+	GETNEXT NOTCASE('f', __start);
+	goto pound_define_undef;
+
+/* #\s*(define|undef)\s*CONFIG_(\w*) */
+pound_define_undef:
+	GETNEXT
+	CASE(' ',  pound_define_undef);
+	CASE('\t', pound_define_undef);
+
+	        NOTCASE('C', __start);
+	GETNEXT NOTCASE('O', __start);
+	GETNEXT NOTCASE('N', __start);
+	GETNEXT NOTCASE('F', __start);
+	GETNEXT NOTCASE('I', __start);
+	GETNEXT NOTCASE('G', __start);
+	GETNEXT NOTCASE('_', __start);
+
+	map_dot = next;
+pound_define_undef_CONFIG_word:
+	GETNEXT
+	if (isalnum(current) || current == '_')
+		goto pound_define_undef_CONFIG_word;
+	define_config(1, map_dot, next - map_dot - 1);
+	goto __start;
+
+/* \<CONFIG_(\w*) */
+cee:
+	if (next >= map+2 && (isalnum(next[-2]) || next[-2] == '_'))
+		goto start;
+	GETNEXT NOTCASE('O', __start);
+	GETNEXT NOTCASE('N', __start);
+	GETNEXT NOTCASE('F', __start);
+	GETNEXT NOTCASE('I', __start);
+	GETNEXT NOTCASE('G', __start);
+	GETNEXT NOTCASE('_', __start);
+
+	map_dot = next;
+cee_CONFIG_word:
+	GETNEXT
+	if (isalnum(current) || current == '_')
+		goto cee_CONFIG_word;
+	use_config(map_dot, next - map_dot - 1);
+	goto __start;
+    }
 }
 
-/* <std> include file */
-std_include_file:
-{
-	char *incname = next;
-std_include_name:
-	GETNEXT
-	CASE('\n',normal);
-	NOTCASE('>', std_include_name);
-	handle_include(0, incname, next-incname-1);
-	goto skippreproc;
-}
 
-if_line:
-	if (needsconfig)
-		goto skippreproc;
-if_start:
-	GETNEXT
-	CASE('C', config);
-	CASE('\n', normal);
-	CASE('_', if_middle);
-	if (current >= 'a' && current <= 'z')
-		goto if_middle;
-	if (current < 'A' || current > 'Z')
-		goto if_start;
-config:
-	GETNEXT
-	NOTCASE('O', __if_middle);
-	GETNEXT
-	NOTCASE('N', __if_middle);
-	GETNEXT
-	NOTCASE('F', __if_middle);
-	GETNEXT
-	NOTCASE('I', __if_middle);
-	GETNEXT
-	NOTCASE('G', __if_middle);
-	GETNEXT
-	NOTCASE('_', __if_middle);
-	handle_config();
-	goto skippreproc;
-
-if_middle:
-	GETNEXT
-__if_middle:
-	CASE('\n', normal);
-	CASE('_', if_middle);
-	if (current >= 'a' && current <= 'z')
-		goto if_middle;
-	if (current < 'A' || current > 'Z')
-		goto if_start;
-	goto if_middle;
-	}
-}
 
-static void do_depend(void)
+/*
+ * Generate dependencies for one file.
+ */
+void do_depend(const char * filename, const char * command)
 {
-	char *map;
 	int mapsize;
 	int pagesizem1 = getpagesize()-1;
-	int fd = open(filename, O_RDONLY);
+	int fd;
 	struct stat st;
+	char * map;
 
+	fd = open(filename, O_RDONLY);
 	if (fd < 0) {
-		if (errno != ENOENT)
-			perror(filename);
+		perror(filename);
 		return;
 	}
+
 	fstat(fd, &st);
 	if (st.st_size == 0) {
 		fprintf(stderr,"%s is empty\n",filename);
+		close(fd);
 		return;
 	}
+
 	mapsize = st.st_size + 2*sizeof(unsigned long);
 	mapsize = (mapsize+pagesizem1) & ~pagesizem1;
 	map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (-1 == (long)map) {
+	if ((long) map == -1) {
 		perror("mkdep: mmap");
 		close(fd);
 		return;
 	}
-	close(fd);
+	if ((unsigned long) map % sizeof(unsigned long) != 0)
+	{
+		fprintf(stderr, "do_depend: map not aligned\n");
+		exit(1);
+	}
+
+	hasdep = 0;
+	clear_config();
 	state_machine(map);
-	munmap(map, mapsize);
 	if (hasdep)
 		puts(command);
+
+	munmap(map, mapsize);
+	close(fd);
 }
 
+
+
+/*
+ * Generate dependencies for all files.
+ */
 int main(int argc, char **argv)
 {
 	int len;
-	char * hpath;
+	char *hpath;
 
 	hpath = getenv("HPATH");
 	if (!hpath)
 		hpath = "/usr/src/linux/include";
 	len = strlen(hpath);
 	memcpy(path_array[0].buffer, hpath, len);
-	if (len && hpath[len-1] != '/') {
-		path_array[0].buffer[len] = '/';
-		len++;
-	}
+	if (len && hpath[len-1] != '/')
+		path_array[0].buffer[len++] = '/';
 	path_array[0].buffer[len] = '\0';
 	path_array[0].len = len;
 
 	while (--argc > 0) {
-		int len;
-		char *name = *++argv;
-
-		filename = name;
-		len = strlen(name);
-		memcpy(depname, name, len+1);
-		command = __depname;
-		if (len > 2 && name[len-2] == '.') {
-			switch (name[len-1]) {
-				case 'c':
-				case 'S':
-					depname[len-1] = 'o';
-					command = "";
+		const char * filename = *++argv;
+		const char * command  = __depname;
+		len = strlen(filename);
+		memcpy(depname, filename, len+1);
+		if (len > 2 && filename[len-2] == '.') {
+			if (filename[len-1] == 'c' || filename[len-1] == 'S') {
+			    depname[len-1] = 'o';
+			    command = "";
 			}
 		}
-		needsconfig = hasconfig = hasmodules = hasdep = 0;
-		do_depend();
-		if (hasconfig && !hasmodules && !needsconfig)
-			fprintf(stderr, "%s doesn't need config\n", filename);
+		do_depend(filename, command);
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.1.77/linux/scripts/split-include.c linux/scripts/split-include.c
--- v2.1.77/linux/scripts/split-include.c	Wed Dec 31 16:00:00 1969
+++ linux/scripts/split-include.c	Mon Jan  5 01:41:01 1998
@@ -0,0 +1,226 @@
+/*
+ * split-include.c
+ *
+ * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ * This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ * This program takes autoconf.h as input and outputs a directory full
+ * of one-line include files, merging onto the old values.
+ *
+ * Think of the configuration options as key-value pairs.  Then there
+ * are five cases:
+ *
+ *    key      old value   new value   action
+ *
+ *    KEY-1    VALUE-1     VALUE-1     leave file alone
+ *    KEY-2    VALUE-2A    VALUE-2B    write VALUE-2B into file
+ *    KEY-3    -           VALUE-3     write VALUE-3  into file
+ *    KEY-4    VALUE-4     -           write an empty file
+ *    KEY-5    (empty)     -           leave old empty file alone
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERROR_EXIT(strExit)						\
+    {									\
+	const int errnoSave = errno;					\
+	fprintf(stderr, "%s: ", str_my_name);				\
+	errno = errnoSave;						\
+	perror((strExit));						\
+	exit(1);							\
+    }
+
+
+
+int main(int argc, const char * argv [])
+{
+    const char * str_my_name;
+    const char * str_file_autoconf;
+    const char * str_dir_config;
+
+    FILE * fp_config;
+    FILE * fp_target;
+    FILE * fp_find;
+
+    int buffer_size;
+
+    char * line;
+    char * old_line;
+    char * list_target;
+    char * ptarget;
+
+    struct stat stat_buf;
+
+    /* Check arg count. */
+    if (argc != 3)
+    {
+	fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]);
+	exit(1);
+    }
+
+    str_my_name       = argv[0];
+    str_file_autoconf = argv[1];
+    str_dir_config    = argv[2];
+
+    /* Find a buffer size. */
+    if (stat(str_file_autoconf, &stat_buf) != 0)
+	ERROR_EXIT(str_file_autoconf);
+    buffer_size = 2 * stat_buf.st_size + 4096;
+
+    /* Allocate buffers. */
+    if ( (line        = malloc(buffer_size)) == NULL
+    ||   (old_line    = malloc(buffer_size)) == NULL
+    ||   (list_target = malloc(buffer_size)) == NULL )
+	ERROR_EXIT(str_file_autoconf);
+
+    /* Open autoconfig file. */
+    if ((fp_config = fopen(str_file_autoconf, "r")) == NULL)
+	ERROR_EXIT(str_file_autoconf);
+
+    /* Make output directory if needed. */
+    if (stat(str_dir_config, &stat_buf) != 0)
+    {
+	if (mkdir(str_dir_config, 0755) != 0)
+	    ERROR_EXIT(str_dir_config);
+    }
+
+    /* Change to output directory. */
+    if (chdir(str_dir_config) != 0)
+	ERROR_EXIT(str_dir_config);
+	
+    /* Put initial separator into target list. */
+    ptarget = list_target;
+    *ptarget++ = '\n';
+
+    /* Read config lines. */
+    while (fgets(line, buffer_size, fp_config))
+    {
+	const char * str_config;
+	int is_same;
+	int itarget;
+
+	if (line[0] != '#')
+	    continue;
+	if ((str_config = strstr(line, "CONFIG_")) == NULL)
+	    continue;
+
+	/* Make the output file name. */
+	str_config += sizeof("CONFIG_") - 1;
+	for (itarget = 0; !isspace(str_config[itarget]); itarget++)
+	{
+	    char c = str_config[itarget];
+	    if (isupper(c)) c = tolower(c);
+	    if (c == '_')   c = '/';
+	    ptarget[itarget] = c;
+	}
+	ptarget[itarget++] = '.';
+	ptarget[itarget++] = 'h';
+	ptarget[itarget++] = '\0';
+
+	/* Check for existing file. */
+	is_same = 0;
+	if ((fp_target = fopen(ptarget, "r")) != NULL)
+	{
+	    fgets(old_line, buffer_size, fp_target);
+	    if (fclose(fp_target) != 0)
+		ERROR_EXIT(ptarget);
+	    if (!strcmp(line, old_line))
+		is_same = 1;
+	}
+
+	if (!is_same)
+	{
+	    /* Auto-create directories. */
+	    int islash;
+	    for (islash = 0; islash < itarget; islash++)
+	    {
+		if (ptarget[islash] == '/')
+		{
+		    ptarget[islash] = '\0';
+		    if (stat(ptarget, &stat_buf) != 0
+		    &&  mkdir(ptarget, 0755)     != 0)
+			ERROR_EXIT( ptarget );
+		    ptarget[islash] = '/';
+		}
+	    }
+
+	    /* Write the file. */
+	    if ((fp_target = fopen(ptarget, "w" )) == NULL)
+		ERROR_EXIT(ptarget);
+	    fputs(line, fp_target);
+	    if (ferror(fp_target) || fclose(fp_target) != 0)
+		ERROR_EXIT(ptarget);
+	}
+
+	/* Update target list */
+	ptarget += itarget;
+	*(ptarget-1) = '\n';
+    }
+
+    /*
+     * Close autoconfig file.
+     * Terminate the target list.
+     */
+    if (fclose(fp_config) != 0)
+	ERROR_EXIT(str_file_autoconf);
+    *ptarget = '\0';
+
+    /*
+     * Fix up existing files which have no new value.
+     * This is Case 4 and Case 5.
+     *
+     * I re-read the tree and filter it against list_target.
+     * This is crude.  But it avoids data copies.  Also, list_target
+     * is compact and contiguous, so it easily fits into cache.
+     *
+     * Notice that list_target contains strings separated by \n,
+     * with a \n before the first string and after the last.
+     * fgets gives the incoming names a terminating \n.
+     * So by having an initial \n, strstr will find exact matches.
+     */
+
+    fp_find = popen("find * -type f -print", "r");
+    if (fp_find == 0)
+	ERROR_EXIT( "find" );
+
+    line[0] = '\n';
+    while (fgets(line+1, buffer_size, fp_find))
+    {
+	if (strstr(list_target, line) == NULL)
+	{
+	    /*
+	     * This is an old file with no CONFIG_* flag in autoconf.h.
+	     */
+
+	    /* First strip the \n. */
+	    line[strlen(line)-1] = '\0';
+
+	    /* Grab size. */
+	    if (stat(line+1, &stat_buf) != 0)
+		ERROR_EXIT(line);
+
+	    /* If file is not empty, make it empty and give it a fresh date. */
+	    if (stat_buf.st_size != 0)
+	    {
+		if ((fp_target = fopen(line+1, "w")) == NULL)
+		    ERROR_EXIT(line);
+		if (fclose(fp_target) != 0)
+		    ERROR_EXIT(line);
+	    }
+	}
+    }
+
+    if (pclose(fp_find) != 0)
+	ERROR_EXIT("find");
+
+    return 0;
+}
diff -u --recursive --new-file v2.1.77/linux/scripts/tkgen.c linux/scripts/tkgen.c
--- v2.1.77/linux/scripts/tkgen.c	Tue Dec 23 16:31:00 1997
+++ linux/scripts/tkgen.c	Mon Jan  5 01:41:02 1998
@@ -53,6 +53,14 @@
  * 1997 12 08
  * Michael Chastain - Remove sound driver special cases.
  *
+ * 1997 11 15
+ * Michael Chastain - For choice buttons, write values for all options,
+ *                    not just the single chosen one.  This is compatible
+ *                    with 'make config' and 'make oldconfig', and is
+ *                    needed so smart-config dependencies work if the
+ *                    user switches from one configuration method to
+ *                    another.
+ *
  * TO DO:
  *   - clean up - there are useless ifdef's everywhere.
  *   - better comments throughout - C code generating tcl is really cryptic.
@@ -1034,9 +1042,10 @@
 		      cfg1 != NULL && cfg1->tok == tok_choice;
 		      cfg1 = cfg1->next)
 		    {
-		      printf("\tif { $%s == \"%s\" } then { write_tristate $cfg $autocfg %s 1 $notmod }\n",
+		      printf("\tif { $%s == \"%s\" } then { write_tristate $cfg $autocfg %s 1 $notmod } else { write_tristate $cfg $autocfg %s 0 $notmod }\n",
 			     cfg->optionname,
 			     cfg1->label,
+			     cfg1->optionname,
 			     cfg1->optionname);
 		    }
 		}
