Date: Mon, 20 Apr 1998 06:58:08 +0200
From: Hans-Joachim Widmaier <hjwidmai@foxboro.com>
To: linux-m68k@lists.linux-m68k.org
Cc: Jes.Sorensen@cern.ch
Subject: L68K: affs patch (long)
Mail-Followup-To: linux-m68k@lists.linux-m68k.org, Jes.Sorensen@cern.ch
Sender: owner-linux-m68k@phil.uni-sb.de

Hello all,

last weekend I finally found the time (and, more important,
the motivation) to backport the current affs to 2.0.33.

The patch below applies to the newest m68k tree that I managed
to find (2.0.33pl1-pre3).

I hope to find some volunteers who will throw some tests on it
(knowing there won't be any; you can always hope, can't you?).

As always, I just did some simple tests: create and delete some
files, dirs and links, a move, untarring a 400MB file,
compile a kernel on it, loopback-mount an ext2 image on it
(which was the trigger for the whole thing). I did not test
different blocksizes, different filesystem types, mount
options (other than ro), compiling into the kernel (modules
are _so_ handy while fixing bugs unless the box freezes).

At any rate, this version should already be much better than
the one that's currently in 2.0.33pl1-pre3.
If this proves to be so, I intend to create a patch against
the "official" 2.0.33 and send that to Alan Cox.

Thanks for your patience,
jbhr

--- linux-2.0.33pl1-pre3/Documentation/filesystems/affs.txt	Sat Oct  5 15:23:36 1996
+++ linux/Documentation/filesystems/affs.txt	Sun Apr 19 12:41:32 1998
@@ -26,7 +26,7 @@
 
 All of the above filesystems allow block sizes from 512 to 32K bytes.
 Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks
-speed up almost everything with the expense of wasted disk space. The speed
+speed up almost everything at the expense of wasted disk space. The speed
 gain above 4K seems not really worth the price, so you don't lose too
 much here, either.
 
@@ -38,28 +38,20 @@
 
 protect		If this option is set, the protection bits cannot be altered.
 
-uid[=uid]	This sets the uid of the root directory (i. e. the mount point
-		to uid or to the uid of the current user, if the =uid is
-		omitted.
-
-gid[=gid]	Same as above, but for gid.
-
 setuid[=uid]	This sets the owner of all files and directories in the file
 		system to uid or the uid of the current user, respectively.
 
 setgid[=gid]	Same as above, but for gid.
 
-use_mp		The uid and gid are taken from the now covered mount point
-		instead of the current user or value defined.
-
 mode=mode	Sets the mode flags to the given (octal) value, regardless
 		of the original permissions. Directories will get an x
-		permission, if the corresponding r bit is set.
+		permission if the corresponding r bit is set.
 		This is useful since most of the plain AmigaOS files
 		will map to 600.
 
 reserved=num	Sets the number of reserved blocks at the start of the
-		partition to num. Default is 2.
+		partition to num. You should never need this option.
+		Default is 2.
 
 root=block	Sets the block number of the root block. This should never
 		be necessary.
@@ -72,7 +64,21 @@
 		mode changes.
 
 verbose		The volume name, file system type and block size will
-		be written to the syslog.
+		be written to the syslog when the filesystem is mounted.
+
+mufs		The filesystem is really a muFS, also it doesn't
+		identify itself as one. This option is neccessary if
+		the filesystem wasn't formatted as muFS, but is used
+		as one.
+
+prefix=path	Path will be prefixed to every absolute path name of
+		symbolic links on an AFFS partition. Default = "/".
+		(See below.)
+
+volume=name	When symbolic links with an absolute path are created
+		on an AFFS partition, name will be prepended as the
+		volume name. Default = "" (empty string).
+		(See below.)
 
 Handling of the Users/Groups and protection flags
 =================================================
@@ -85,15 +91,17 @@
 
   - If both W and D are allowed, w will be set.
 
-  - If both R and S are set, x will be set.
+  - E maps to x.
 
-  - H, P and E are always retained and ignored under Linux.
+  - H and P are always retained and ignored under Linux.
 
-  - A is always reset when written.
+  - A is always reset when a file is written to.
 
 User id and group id will be used unless set[gu]id are given as mount
 options. Since most of the Amiga file systems are single user systems
-they will be owned by root.
+they will be owned by root. The root directory of the Amiga filesystem
+(i. e. the mount point) will be owned by the user who actually mounts
+the filesystem (the root directory doesn't have uid/gid fields).
 
 Linux -> Amiga:
 
@@ -103,7 +111,7 @@
 
   - w permission will set W and D for user, group and others.
 
-  - x permission of the user will set S for plain files.
+  - x permission of the user will set E for plain files.
 
   - All other flags (suid, sgid, ...) are ignored and will
     not be retained.
@@ -111,31 +119,55 @@
 Newly created files and directories will get the user and group id
 of the current user and a mode according to the umask.
 
-Command line example
-====================
-    mount  Archive/Amiga/Workbench3.1.adf /mnt -t affs -o loop,size=1760
+Symbolic links
+==============
+
+Although the Amiga and Linux file systems resemble each other, there
+are some, not always subtle, differences. One of them becomes apparent
+with symbolic links. While Linux has a file system with exactly one
+root directory, the Amiga has a seperate root directory for each
+file system (i. e. partition, floppy disk, ...). With the Amiga,
+these entities are called "volumes". They have symbolic names which
+can be used to access them. Thus, symbolic links can point to a
+different volume. AFFS turns the volume name into a directory name
+and prepends the prefix path (see prefix option) to it.
+
+Example:
+You mount all your Amiga partitions under /amiga/<volume> (where
+<volume> is the name of the volume), and you give the option
+"prefix=/amiga/" when mounting all your AFFS partitions. (They
+might be "User", "WB" and "Graphics", the mount points /amiga/User,
+/amiga/WB and /amiga/Graphics). A symbolic link referring to
+"User:sc/include/dos/dos.h" will be followed to
+"/amiga/User/sc/include/dos/dos.h".
+
+Examples
+========
+
+Command line:
+    mount  Archive/Amiga/Workbench3.1.adf /mnt -t affs -o loop,verbose
     mount  /dev/sda3 /Amiga -t affs
 
-/etc/fstab example
-    /dev/sdb5	/d/f    affs    ro
+/etc/fstab entry:
+    /dev/sdb5	/amiga/Workbench    affs    noauto,user,exec,verbose 0 0
 
 Bugs, Restrictions, Caveats
 ===========================
 
 Quite a few things may not work as advertised. Not everything is
 tested, though several hundred MB have been read and written using
-this fs.
-
-Filenames are truncated to 30 characters without warning.
+this fs. For a most up-to-date list of bugs please consult
+fs/affs/Changes.
 
-Currently there are no checks against invalid characters (':')
-in filenames.
+Filenames are truncated to 30 characters without warning (this
+can be changed by setting the compile-time option AFFS_NO_TRUNCATE
+ina include/linux/amigaffs.h).
 
 Case is ignored by the affs in filename matching, but Linux shells
-do care about the case. Example (with /mnt being an affs mounted fs):
-    rm /mnt/WRONGCASE
+do care about the case. Example (with /wb being an affs mounted fs):
+    rm /wb/WRONGCASE
 will remove /mnt/wrongcase, but
-    rm /mnt/WR*
+    rm /wb/WR*
 will not since the names are matched by the shell.
 
 The block allocation is designed for hard disk partitions. If more
@@ -145,12 +177,13 @@
 
 The bitmap valid flag in the root block may not be accurate when the
 system crashes while an affs partition is mounted. There's currently
-no way to fix this without an Amiga (disk validator) or manually
-(who would do this?). Maybe later.
+no way to fix a garbled filesystem without an Amiga (disk validator)
+or manually (who would do this?). Maybe later.
 
 A fsck.affs and mkfs.affs will probably be available in the future.
-Until then, you should do
-    ln -s /bin/true /etc/fs/mkfs.affs
+If you mount them on system startup, you may want to tell fsck
+that the fs should not be checked (place a '0' in the sixth field
+of /etc/fstab).
 
 It's not possible to read floppy disks with a normal PC or workstation
 due to an incompatibility with the Amiga floppy controller.
--- linux-2.0.33pl1-pre3/fs/affs/Changes	Mon Jul 14 19:40:05 1997
+++ linux/fs/affs/Changes	Sun Apr 19 12:58:46 1998
@@ -12,48 +12,163 @@
   apparently not causing the failure, as directory
   reads basically work (but all files are of size 0).
   Alas, I've got no alpha to debug. :-(
-- If an affs mounted filesystem is exported via
-  nfs, it cannot be written to. 
-  As soon as I have my network up and running, I'll
-  try to fix this.
+
 - The partition checker (drivers/block/genhd.c)
   doesn't work with devices which have 256 byte
   blocks (some very old SCSI drives). 
 
+- The feature to automatically make the fs clean
+  might leave a trashed file system with the
+  bitmap flag set valid.
+
+- When a file is truncated to a size that is not
+  a multiple of the blocksize, the rest of the
+  last allocated block is not cleared. Well,
+  this fs never claimed to be Posix conformant.
+
 Please direct bug reports to: hjw@zvw.de
 
-Version 3.5
------------
+Version 3.8o
+------------
+This is a backport of affs 3.8 from the development
+kernel (2.1). Fixes marked with (BH) were done by
+Bill Hawes, who was kind enough to review the fs.
+Thanks, Bill!
+
+- Moved freeing of preallocated blocks to
+  put_inode(), so the special release_file()
+  function isn't needed anymore.
+  (This is not in the original 3.8.)
+
+- Cleanup of error handling in read_super().
+  Didn't release all ressources in case of an
+  error. (BH)
+
+- put_inode() releases the ext cache only if it's
+  no longer needed. (BH)
+
+- Cleanup of error handling in file.c. (BH)
+
+- The original blocksize of the device is
+  restored when the fs is unmounted. (BH)
+
+- getblock() did not invalidate the key cache
+  when it allocated a new block.
+
+- Removed some unneccessary locks as Bill
+  suggested.
+
+- Simplified match().
+
+- Added mount option 'mufs' to force muFS
+  uid/gid interpretation.
+
+- File mode changes were not updated on disk.
+  This was fixed before, but somehow got lost.
+
+- getblock() didn't update the lastblock field in the
+  inode if the fs was not an OFS. This bug only shows
+  up if a file was enlarged via truncate() and there
+  was not enough space.
+
+- Fixed bug in balloc(): Superblock was not set dirty after
+  the bitmap was changed, so the bitmap wasn't sync'd.
+
+- Fixed nasty bug in find_new_zone(): If the current
+  zone number was zero, the loop didn't terminate,
+  causing a solid lock-up.
+
+- Removed support for old-style directory reads.
+
+- Fixed bug in add_entry(): When doing a sorted insert,
+  the pointer to the next entry in the hash chain wasn't
+  correctly byte-swapped. Since most of the users of the
+  affs use it on a 68k, they didn't notice. But why did
+  I not find this during my tests?
+
+- Fixed some oversights (version wasn't updated on some
+  directory changes).
+
+- Handling of hard links rewritten. To the VFS
+  they appear now as normal unix links. They are
+  now resolved only once in lookup(). The backside
+  is that unlink(), rename() and rmdir() have to
+  be smart about them, but the result is worth the
+  effort. This also led to some code cleanup.
+
+- Changed name type to unsigned char; the test for
+  invalid filenames didn't work correctly.
+  (Thanks to Michael Krause for pointing this out.)
+
+- Changed mapping of executable flag.
+
+- Added a remount function, so attempts to remount
+  a dircache filesystem or one with errors read/write
+  can be trapped. Previously, ro remounts didn't
+  flush the super block, and rw remounts didn't
+  create allocation zones ...
+
+- Permission checks in unlink().
+
+- Allow mounting of volumes with superfluous
+  bitmap pointers read only, also allows them
+  to be remounted read/write.
+
+- Owner/Group defaults now to the fs user (i.e.
+  the one that mounted it) instead of root. This
+  obsoletes the mount options uid and gid.
+
+- Argument to volume option could overflow the
+  name buffer. It is now silently truncated to
+  30 characters. (Dammit! This kind of bug
+  is too embarrassing.)
+
+- Split inode.c into 2 files, the superblock
+  routines desperately wanted their own file.
+
+- truncate() didn't allocate an extension block
+  cache. If a file was extended by means of
+  truncate(), this led to an Oops.
+
+- fsuser is now checked last.
+
+- rename() will not ignore changes in filename
+  casing any more (though mv(1) still won't allow
+  you to do "mv oldname OldName").
 
 - Extension block caches are now allocated on
   demand instead of when a file is opened, as
   files can be read and written without opening
   them (e. g. the loopback device does this).
 
-- Removed an unused function.
-
 Version 3.4
 -----------
 
 - Hash chains are now sorted by block numbers.
   (Thanks to Kars de Jong for finding this.)
+
 - Removed all unnecessary external symbols.
 
 Version 3.3
 -----------
 
 - Tried to make all types 'correct' and consistent.
+
 - Errors and warnings are now reported via a
   function. They are all prefixed by a severity
   and have the same appearance:
     "AFFS: <function>: <error message>"
   (There's one exception to this, as in that function
   is no pointer to the super block available.)
+
 - The filesystem is remounted read-only after an
   error.
+
 - The names of newly created filesystem objects are
   now checked for validity.
+
 - Minor cleanups in comments.
+
 - Added this Changes file. At last!
 
 Version 3.2
@@ -63,17 +178,23 @@
   (several MB) is much faster (of course the added
   overhead slows down opening, but this is hardly
   noticeable).
+
 - The same get_block()-routine can now be used for
   both OFS and FFS.
+
 - The super block is now searched in the block that
   was calculated and in the one following. This
   should remedy the round-off error introduced by
   the 1-k blocks that Linux uses.
+
 - Minor changes to adhere to the new VFS interface.
+
 - The number of used blocks is now also calculated
   if the filesystem is mounted read-only.
+
 - Prefixed some constants with AFFS_ to avoid name
   clashes.
+
 - Removed 'EXPERIMENTAL' status.
 
 Version 3.1
@@ -81,9 +202,12 @@
 
 - Fixed a nasty bug which didn't allow read-only
   mounts.
+
 - Allow dir-cache filesystems to be mounted
   read only.
+
 - OFS support.
+
 - Several other changes I just cannot remember
   any more.
 
@@ -92,15 +216,20 @@
 
 - Almost complete rewrite for the new VFS
   interface in Linux 1.3.
+
 - Write support.
+
 - Support for hard and symbolic links.
+
 - Lots of things I remember even less ...
 
 Version 2.0
 -----------
 
 - Fixed a few things to get it compiled.
+
 - Automatic root block calculation.
+
 - Partition checker for genhd.c
 
 ========================================
--- linux-2.0.33pl1-pre3/fs/affs/Makefile	Sat Oct  5 15:25:54 1996
+++ linux/fs/affs/Makefile	Sat Apr 18 14:25:20 1998
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := affs.o
-O_OBJS   := namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
+O_OBJS   := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
--- linux-2.0.33pl1-pre3/fs/affs/amigaffs.c	Thu Sep  4 14:12:50 1997
+++ linux/fs/affs/amigaffs.c	Sun Apr 19 11:38:28 1998
@@ -5,8 +5,11 @@
  *
  *  (C) 1993  Ray Burr - Amiga FFS filesystem.
  *
+ *  Please send bug reports to: hjw@zvw.de
  */
 
+#define DEBUG 0
+#include <stdarg.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
@@ -17,143 +20,165 @@
 
 extern struct timezone sys_tz;
 
+static char ErrorBuffer[256];
+
 /*
  * Functions for accessing Amiga-FFS structures.
  *
  */
 
-/* Find the next used hash entry at or after *HASH_POS in a directory's hash
-   table.  *HASH_POS is assigned that entry's number.  DIR_DATA points to
-   the directory header block in memory.  If there are no more entries,
-   0 is returned.  Otherwise, the key number in the next used hash slot
-   is returned. */
-
-int
-affs_find_next_hash_entry(int hsize, void *dir_data, int *hash_pos)
-{
-	struct dir_front *dir_front = dir_data;
-	int i;
-
-	for (i = *hash_pos; i < hsize; i++)
-		if (dir_front->hashtable[i] != 0)
-			break;
-	if (i >= hsize)
-		return 0;
-	*hash_pos = i;
-	return htonl(dir_front->hashtable[i]);
-}
-
 /* Set *NAME to point to the file name in a file header block in memory
    pointed to by FH_DATA.  The length of the name is returned. */
 
 int
-affs_get_file_name(int bsize, void *fh_data, char **name)
+affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
 {
 	struct file_end *file_end;
 
 	file_end = GET_END_PTR(struct file_end, fh_data, bsize);
 	if (file_end->file_name[0] == 0
 	    || file_end->file_name[0] > 30) {
-		printk ("affs_get_file_name: OOPS! bad filename\n");
-		printk ("  file_end->file_name[0] = %d\n",
+		printk(KERN_WARNING "AFFS: bad filename (length=%d chars)\n",
 			file_end->file_name[0]);
 		*name = "***BAD_FILE***";
 		return 14;
         }
-	*name = (char *) &file_end->file_name[1];
+	*name = (unsigned char *)&file_end->file_name[1];
         return file_end->file_name[0];
 }
 
-/* Find the predecessor in the hash chain */
+/* Insert a header block (file) into the directory (next).
+ * This routine assumes that the caller has the superblock locked.
+ */
 
 int
-affs_fix_hash_pred(struct inode *startino, int startoffset, int key, int newkey)
+affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
 {
-	struct buffer_head	*bh = NULL;
-	int			 nextkey;
-	int			 ptype, stype;
-	int			 retval;
+	struct buffer_head	*bh;
+	s32			 ino;
+	int			 offset;
+
+	offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
+				FILE_END(file->b_data,inode)->file_name[0],
+				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+	ino    = htonl(((struct dir_front *)file->b_data)->own_key);
+
+	pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
+
+	FILE_END(file->b_data,inode)->parent = ntohl(next);
 
-	nextkey = startino->i_ino;
-	retval  = -ENOENT;
-	lock_super(startino->i_sb);
 	while (1) {
-		pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
-		if (nextkey == 0)
+		if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
+			return -EIO;
+		next = htonl(((s32 *)bh->b_data)[offset]);
+		if (!next || next > ino)
 			break;
-		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+		offset = AFFS_I2BSIZE(inode) / 4 - 4;
+		affs_brelse(bh);
+	}
+
+	DIR_END(file->b_data,inode)->hash_chain = ntohl(next);
+	((s32 *)bh->b_data)[offset]             = ntohl(ino);
+	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+
+	return 0;
+}
+/* Remove a header block from it's hash table (directory).
+ * 'inode' may be any inode on the partition, it's only
+ * used for calculating the block size and superblock
+ * reference.
+ */
+
+int
+affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
+{
+	s32			 ownkey;
+	s32			 key;
+	s32			 ptype;
+	s32			 stype;
+	int			 offset;
+	int			 retval;
+	struct buffer_head	*bh;
+
+	ownkey = htonl(((struct dir_front *)dbh->b_data)->own_key);
+	key    = htonl(FILE_END(dbh->b_data,inode)->parent);
+	offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
+				FILE_END(dbh->b_data,inode)->file_name[0],
+				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+	pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
+	retval = -ENOENT;
+
+	lock_super(inode->i_sb);
+	while (key) {
+		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+			retval = -EIO;
 			break;
-		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+		}
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
 		    || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
 					    stype != ST_LINKFILE && stype != ST_LINKDIR &&
 					    stype != ST_ROOT && stype != ST_SOFTLINK)) {
-			printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n",
-			       ptype,stype);
+			affs_error(inode->i_sb,"affs_remove_hash",
+				"Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
+				key,ptype,stype,ownkey);
 			affs_brelse(bh);
+			retval = -EINVAL;
 			break;
 		}
-		nextkey = htonl(((__u32 *)bh->b_data)[startoffset]);
-		if (nextkey == key) {
-			((__u32 *)bh->b_data)[startoffset] = newkey;
-			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+		key = htonl(((s32 *)bh->b_data)[offset]);
+		if (ownkey == key) {
+			((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			retval = 0;
 			break;
 		}
 		affs_brelse(bh);
-		startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
+		offset = AFFS_I2BSIZE(inode) / 4 - 4;
 	}
-	unlock_super(startino->i_sb);
+	unlock_super(inode->i_sb);
 
 	return retval;
 }
 
-/* Remove inode from link chain */
+/* Remove header from link chain */
 
 int
-affs_fix_link_pred(struct inode *startino, int key, int newkey)
+affs_remove_link(struct buffer_head *dbh, struct inode *inode)
 {
-	struct buffer_head	*bh = NULL;
-	int			 nextkey;
-	int			 offset;
-	int			 etype = 0;
-	int			 ptype, stype;
 	int			 retval;
-
-	offset  = AFFS_I2BSIZE(startino) / 4 - 10;
-	nextkey = startino->i_ino;
-	retval  = -ENOENT;
-	lock_super(startino->i_sb);
-	while (1) {
-		if (nextkey == 0)
-			break;
-		pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
-		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
-			break;
-		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
-		    || ptype != T_SHORT) {
-			affs_brelse(bh);
+	s32			 key;
+	s32			 ownkey;
+	s32			 ptype;
+	s32			 stype;
+	struct buffer_head	*bh;
+
+	ownkey = htonl((DIR_FRONT(dbh)->own_key));
+	key    = htonl(FILE_END(dbh->b_data,inode)->original);
+	retval = -ENOENT;
+
+	pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
+
+	lock_super(inode->i_sb);
+	while (key) {
+		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+			retval = -EIO;
 			break;
 		}
-		if (!etype) {
-			if (stype != ST_FILE && stype != ST_USERDIR) {
-				affs_brelse(bh);
-				break;
-			}
-			if (stype == ST_FILE)
-				etype = ST_LINKFILE;
-			else
-				etype = ST_LINKDIR;
-		} else if (stype != etype) {
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
+			affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
 			affs_brelse(bh);
-			retval = -EPERM;
+			retval = -EINVAL;
 			break;
 		}
-		nextkey = htonl(((__u32 *)bh->b_data)[offset]);
-		if (nextkey == key) {
-			FILE_END(bh->b_data,startino)->link_chain = newkey;
-			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+		key = htonl(FILE_END(bh->b_data,inode)->link_chain);
+		if (ownkey == key) {
+			FILE_END(bh->b_data,inode)->link_chain =
+						FILE_END(dbh->b_data,inode)->link_chain;
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			retval = 0;
@@ -161,10 +186,102 @@
 		}
 		affs_brelse(bh);
 	}
-	unlock_super(startino->i_sb);
+	unlock_super(inode->i_sb);
+
 	return retval;
 }
 
+/* Remove a filesystem object. If the object to be removed has
+ * links to it, one of the links must be changed to inherit
+ * the file or directory. As above, any inode will do.
+ * The buffer will not be freed. If the header is a link, the
+ * block will be marked as free.
+ * This function returns a negative error number in case of
+ * an error, else 0 if the inode is to be deleted or 1 if not.
+ */
+
+int
+affs_remove_header(struct buffer_head *bh, struct inode *inode)
+{
+	struct buffer_head	*link_bh;
+	struct inode		*dir;
+	unsigned long		 link_ino;
+	unsigned long		 orig_ino;
+	unsigned int		 dir_ino;
+	int			 error;
+
+	pr_debug("AFFS: remove_header(key=%ld)\n",htonl(DIR_FRONT(bh)->own_key));
+
+	/* Mark directory as changed.  We do this before anything else,
+	 * as it must be done anyway and doesn't hurt even if an
+	 * error occures later.
+	 */
+	dir = iget(inode->i_sb,htonl(FILE_END(bh->b_data,inode)->parent));
+	if (!dir)
+		return -EIO;
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version++;
+	dir->i_dirt = 1;
+	iput(dir);
+
+	orig_ino = htonl(FILE_END(bh->b_data,inode)->original);
+	if (orig_ino) {		/* This is just a link. Nothing much to do. */
+		pr_debug("      Removing link.\n");
+		if ((error = affs_remove_link(bh,inode)))
+			return error;
+		if ((error = affs_remove_hash(bh,inode)))
+			return error;
+		affs_free_block(inode->i_sb,htonl(DIR_FRONT(bh)->own_key));
+		return 1;
+	}
+	
+	link_ino = htonl(FILE_END(bh->b_data,inode)->link_chain);
+	if (link_ino) {		/* This is the complicated case. Yuck. */
+		pr_debug("      Removing original with links to it.\n");
+		/* Unlink the object and its first link from their directories. */
+		if ((error = affs_remove_hash(bh,inode)))
+			return error;
+		if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
+			return -EIO;
+		if ((error = affs_remove_hash(link_bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		/* Fix link chain. */
+		if ((error = affs_remove_link(link_bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		/* Rename link to object. */
+		memcpy(FILE_END(bh->b_data,inode)->file_name,
+			FILE_END(link_bh->b_data,inode)->file_name,32);
+		/* Insert object into dir the link was in. */
+		dir_ino = htonl(FILE_END(link_bh->b_data,inode)->parent);
+		if ((error = affs_insert_hash(dir_ino,bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+		mark_buffer_dirty(bh,1);
+		affs_brelse(link_bh);
+		affs_free_block(inode->i_sb,link_ino);
+		/* Mark the link's parent dir as changed, too. */
+		if (!(dir = iget(inode->i_sb,dir_ino)))
+			return -EIO;
+		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		dir->i_version++;
+		dir->i_dirt = 1;
+		iput(dir);
+		return 1;
+	}
+	/* Plain file/dir. This is the simplest case. */
+	pr_debug("      Removing plain file/dir.\n");
+	if ((error = affs_remove_hash(bh,inode)))
+		return error;
+	return 0;
+}
+
+
 /* Checksum a block, do various consistency checks and optionally return
    the blocks type number.  DATA points to the block.  If their pointers
    are non-null, *PTYPE and *STYPE are set to the primary and secondary
@@ -172,17 +289,17 @@
    (which lets us calculate the block size).
    Returns non-zero if the block is not consistent. */
 
-__u32
-affs_checksum_block(int bsize, void *data, int *ptype, int *stype)
+u32
+affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype)
 {
-	__u32	 sum;
-	__u32	*p;
+	u32	 sum;
+	u32	*p;
 
 	bsize /= 4;
 	if (ptype)
-		*ptype = htonl(((__s32 *)data)[0]);
+		*ptype = htonl(((s32 *)data)[0]);
 	if (stype)
-		*stype = htonl(((__s32 *)data)[bsize - 1]);
+		*stype = htonl(((s32 *)data)[bsize - 1]);
 
 	sum    = 0;
 	p      = data;
@@ -194,22 +311,22 @@
 void
 affs_fix_checksum(int bsize, void *data, int cspos)
 {
-	__u32	 ocs;
-	__u32	 cs;
+	u32	 ocs;
+	u32	 cs;
 
 	cs   = affs_checksum_block(bsize,data,NULL,NULL);
-	ocs  = htonl (((__u32 *)data)[cspos]);
+	ocs  = htonl(((u32 *)data)[cspos]);
 	ocs -= cs;
-	((__u32 *)data)[cspos] = htonl(ocs);
+	((u32 *)data)[cspos] = htonl(ocs);
 }
 
 void
-secs_to_datestamp(int secs, struct DateStamp *ds)
+secs_to_datestamp(time_t secs, struct DateStamp *ds)
 {
-	__u32	 days;
-	__u32	 minute;
+	u32	 days;
+	u32	 minute;
 
-	secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
+	secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
 	if (secs < 0)
 		secs = 0;
 	days    = secs / 86400;
@@ -223,7 +340,7 @@
 }
 
 int
-prot_to_mode(__u32 prot)
+prot_to_mode(u32 prot)
 {
 	int	 mode = 0;
 
@@ -249,10 +366,10 @@
 	return mode;
 }
 
-unsigned int
+u32
 mode_to_prot(int mode)
 {
-	unsigned int	 prot = 0;
+	u32	 prot = 0;
 
 	if (mode & S_IXUSR)
 		prot |= FIBF_SCRIPT;
@@ -270,4 +387,78 @@
 		prot |= FIBF_OTR_WRITE;
 	
 	return prot;
+}
+
+void
+affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
+{
+	va_list	 args;
+
+	va_start(args,fmt);
+	vsprintf(ErrorBuffer,fmt,args);
+	va_end(args);
+
+	printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
+		function,ErrorBuffer);
+	if (!(sb->s_flags & MS_RDONLY))
+		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
+	sb->s_flags |= MS_RDONLY;
+	sb->u.affs_sb.s_flags |= SF_READONLY;	/* Don't allow to remount rw */
+}
+
+void
+affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
+{
+	va_list	 args;
+
+	va_start(args,fmt);
+	vsprintf(ErrorBuffer,fmt,args);
+	va_end(args);
+
+	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
+		function,ErrorBuffer);
+}
+
+/* Check if the name is valid for a affs object. */
+
+int
+affs_check_name(const unsigned char *name, int len)
+{
+	int	 i;
+
+	if (len > 30)
+#ifdef AFFS_NO_TRUNCATE
+		return -ENAMETOOLONG;
+#else
+		len = 30;
+#endif
+
+	for (i = 0; i < len; i++) {
+		if (name[i] < ' ' || name[i] == ':'
+		    || (name[i] > 0x7e && name[i] < 0xa0))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* This function copies name to bstr, with at most 30
+ * characters length. The bstr will be prepended by
+ * a length byte.
+ * NOTE: The name will must be already checked by
+ *       affs_check_name()!
+ */
+
+int
+affs_copy_name(unsigned char *bstr, const unsigned char *name)
+{
+	int	 len;
+
+	for (len = 0; len < 30; len++) {
+		bstr[len + 1] = name[len];
+		if (name[len] == '\0')
+			break;
+	}
+	bstr[0] = len;
+	return len;
 }
--- linux-2.0.33pl1-pre3/fs/affs/bitmap.c	Thu Sep  4 14:12:50 1997
+++ linux/fs/affs/bitmap.c	Sun Apr 19 11:38:32 1998
@@ -3,11 +3,11 @@
  *
  *  (c) 1996 Hans-Joachim Widmaier
  *
- *
  *  bitmap.c contains the code that handles all bitmap related stuff -
  *  block allocation, deallocation, calculation of free space.
  */
 
+#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/stat.h>
@@ -54,7 +54,7 @@
 }
 
 void
-affs_free_block(struct super_block *sb, int block)
+affs_free_block(struct super_block *sb, s32 block)
 {
 	int			 bmap;
 	int			 bit;
@@ -70,7 +70,7 @@
 	zone_no = (bmap << (sb->s_blocksize_bits - 7)) + bit / 1024;
 	bm      = &sb->u.affs_sb.s_bitmap[bmap];
 	if (bmap >= sb->u.affs_sb.s_bm_count) {
-		printk("AFFS: free_block(): block %d outside partition.\n",block);
+		affs_error(sb,"affs_free_block","Block %d outside partition",block);
 		return;
 	}
 	blk = 0;
@@ -83,15 +83,16 @@
 		if (!bm->bm_bh) {
 			bm->bm_count--;
 			unlock_super(sb);
-			printk("AFFS: free_block(): Cannot read bitmap block %d\n",bm->bm_key);
+			affs_error(sb,"affs_free_block","Cannot read bitmap block %d",bm->bm_key);
 			return;
 		}
 	}
 	if (set_bit(bit ^ BO_EXBITS,bm->bm_bh->b_data + 4))
-		printk("AFFS: free_block(): block %d is already free.\n",block);
+		affs_warning(sb,"affs_free_block","Trying to free block %d which is already free",
+				block);
 	else {
 		sb->u.affs_sb.s_alloc[zone_no].az_free++;
-		((__u32 *)bm->bm_bh->b_data)[0] = ntohl(htonl(((__u32 *)bm->bm_bh->b_data)[0]) - blk);
+		((u32 *)bm->bm_bh->b_data)[0] = ntohl(htonl(((u32 *)bm->bm_bh->b_data)[0]) - blk);
 		mark_buffer_dirty(bm->bm_bh,1);
 		sb->s_dirt = 1;
 	}
@@ -102,15 +103,15 @@
 	unlock_super(sb);
 }
 
-static int
+static s32
 affs_balloc(struct inode *inode, int zone_no)
 {
-	__u32			 w;
-	__u32			*bm;
+	u32			 w;
+	u32			*bm;
 	int			 fb;
 	int			 i;
 	int			 fwb;
-	int			 block;
+	s32			 block;
 	struct affs_zone	*zone;
 	struct affs_alloc_zone	*az;
 	struct super_block	*sb;
@@ -124,7 +125,7 @@
 	pr_debug("AFFS: balloc(inode=%lu,zone=%d)\n",inode->i_ino,zone_no);
 
 	az = &sb->u.affs_sb.s_alloc[zone->z_az_no];
-	bm = (__u32 *)zone->z_bm->bm_bh->b_data;
+	bm = (u32 *)zone->z_bm->bm_bh->b_data;
 repeat:
 	for (i = zone->z_start; i < zone->z_end; i++) {
 		if (bm[i])
@@ -140,7 +141,7 @@
 	fb  = find_first_zero_bit(&w,32);
 	if (fb > 31 || !clear_bit(fb ^ BO_EXBITS,&bm[i])) {
 		unlock_super(sb);
-		printk("AFFS: balloc(): empty block disappeared somehow\n");
+		affs_warning(sb,"balloc","Empty block disappeared somehow");
 		goto repeat;
 	}
 	block = fwb + fb;
@@ -154,7 +155,7 @@
 			if (fb > 31)
 				break;
 			if (!clear_bit(fb ^ BO_EXBITS,&bm[i])) {
-				printk("AFFS: balloc(): empty block disappeared\n");
+				affs_warning(sb,"balloc","Empty block disappeared somehow");
 				break;
 			}
 			inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
@@ -167,6 +168,7 @@
 	bm[0] = ntohl(htonl(bm[0]) + w);
 	unlock_super(sb);
 	mark_buffer_dirty(zone->z_bm->bm_bh,1);
+	sb->s_dirt = 1;
 	zone->z_lru_time = jiffies;
 
 	return block;
@@ -204,12 +206,10 @@
 		if (az->az_count)
 			az->az_count--;
 		else
-			printk("AFFS: find_new_zone(): az_count=0, but bm used\n");
+			affs_error(sb,"find_new_zone","az_count=0, but bm used");
 
 	}
 	while (1) {
-		if (i >= sb->u.affs_sb.s_num_az)
-			i = 0;
 		az = &sb->u.affs_sb.s_alloc[i];
 		if (!az->az_count) {
 			if (az->az_free > min) {
@@ -223,7 +223,9 @@
 			lusers   = az->az_count;
 			bestused = i;
 		}
-		if (++i == zone->z_az_no) {		/* Seen all */
+		if (++i >= sb->u.affs_sb.s_num_az)
+			i = 0;
+		if (i == zone->z_az_no) {		/* Seen all */
 			if (bestno >= 0) {
 				i = bestno;
 			} else {
@@ -247,7 +249,7 @@
 		bm->bm_count--;
 		az->az_count--;
 		unlock_super(sb);
-		printk("AFFS: find_new_zone(): Cannot read bitmap\n");
+		affs_error(sb,"find_new_zone","Cannot read bitmap");
 		return 0;
 	}
 	zone->z_bm    = bm;
@@ -261,16 +263,16 @@
 	return az->az_free;
 }
 
-int
+s32
 affs_new_header(struct inode *inode)
 {
-	int			 block;
+	s32			 block;
 	struct buffer_head	*bh;
 
 	pr_debug("AFFS: new_header(ino=%lu)\n",inode->i_ino);
 
 	if (!(block = affs_balloc(inode,0))) {
-		while(affs_find_new_zone(inode->i_sb,0)) {
+		while (affs_find_new_zone(inode->i_sb,0)) {
 			if ((block = affs_balloc(inode,0)))
 				goto init_block;
 			schedule();
@@ -279,7 +281,7 @@
 	}
 init_block:
 	if (!(bh = getblk(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
-		printk("AFFS: balloc(): cannot read block %d\n",block);
+		affs_error(inode->i_sb,"new_header","Cannot get block %d",block);
 		return 0;
 	}
 	memset(bh->b_data,0,AFFS_I2BSIZE(inode));
@@ -290,7 +292,7 @@
 	return block;
 }
 
-int
+s32
 affs_new_data(struct inode *inode)
 {
 	int			 empty, old;
@@ -299,7 +301,7 @@
 	struct super_block	*sb;
 	struct buffer_head	*bh;
 	int			 i = 0;
-	int			 block;
+	s32			 block;
 
 	pr_debug("AFFS: new_data(ino=%lu)\n",inode->i_ino);
 
@@ -345,7 +347,7 @@
 found:
 	zone = &sb->u.affs_sb.s_zones[i];
 	if (!(block = affs_balloc(inode,i))) {		/* No data zones left */
-		while(affs_find_new_zone(sb,i)) {
+		while (affs_find_new_zone(sb,i)) {
 			if ((block = affs_balloc(inode,i)))
 				goto init_block;
 			schedule();
@@ -357,7 +359,7 @@
 
 init_block:
 	if (!(bh = getblk(inode->i_dev,block,sb->s_blocksize))) {
-		printk("AFFS: balloc(): cannot read block %u\n",block);
+		affs_error(inode->i_sb,"new_data","Cannot get block %d",block);
 		return 0;
 	}
 	memset(bh->b_data,0,sb->s_blocksize);
@@ -376,9 +378,7 @@
 	pr_debug("AFFS: make_zones(): num_zones=%d\n",sb->u.affs_sb.s_num_az);
 
 	mid = (sb->u.affs_sb.s_num_az + 1) / 2;
-	sb->u.affs_sb.s_zones[0].z_az_no = mid;
-	affs_find_new_zone(sb,0);
-	for (i = 1; i < MAX_ZONES; i++) {
+	for (i = 0; i < MAX_ZONES; i++) {
 		sb->u.affs_sb.s_zones[i].z_az_no = mid;
 		affs_find_new_zone(sb,i);
 	}
--- linux-2.0.33pl1-pre3/fs/affs/dir.c	Thu Sep  4 14:12:51 1997
+++ linux/fs/affs/dir.c	Sun Apr 19 11:38:37 1998
@@ -13,6 +13,7 @@
  *
  */
 
+#define DEBUG	0
 #include <asm/segment.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -42,24 +43,35 @@
 /*
  * directories can handle most operations...
  */
+
+/* This is unbelievably ugly, but I do want the names to consist of unsigned chars */
+
+#define S_CREATE_T	int (*)(struct inode *,const char *,int,int,struct inode **)
+#define S_LOOKUP_T	int (*)(struct inode *,const char *,int,struct inode **)
+#define S_UNLINK_T	int (*)(struct inode *,const char *,int)
+#define S_MKDIR_T	int (*)(struct inode *,const char *,int,int)
+#define S_LINK_T	int (*)(struct inode *,struct inode *,const char *,int)
+#define S_SYLINK_T	int (*)(struct inode *,const char *,int,const char *)
+#define S_RENAME_T	int (*)(struct inode *,const char *,int,struct inode *,const char *,int,int)
+
 struct inode_operations affs_dir_inode_operations = {
-	&affs_dir_operations,	/* default directory file-ops */
-	affs_create,		/* create */
-	affs_lookup,		/* lookup */
-	affs_link,		/* link */
-	affs_unlink,		/* unlink */
-	affs_symlink,		/* symlink */
-	affs_mkdir,		/* mkdir */
-	affs_rmdir,		/* rmdir */
-	NULL,			/* mknod */
-	affs_rename,		/* rename */
-	NULL,			/* readlink */
-	NULL,			/* follow_link */
-	NULL,			/* readpage */
-	NULL,			/* writepage */
-	NULL,			/* bmap */
-	NULL,			/* truncate */
-	NULL			/* permissions */
+	&affs_dir_operations,		/* default directory file-ops */
+	(S_CREATE_T)affs_create,	/* create */
+	(S_LOOKUP_T)affs_lookup,	/* lookup */
+	(S_LINK_T)  affs_link,		/* link */
+	(S_UNLINK_T)affs_unlink,	/* unlink */
+	(S_SYLINK_T)affs_symlink,	/* symlink */
+	(S_MKDIR_T) affs_mkdir,		/* mkdir */
+	(S_UNLINK_T)affs_rmdir,		/* rmdir */
+	NULL,				/* mknod */
+	(S_RENAME_T)affs_rename,	/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* readpage */
+	NULL,				/* writepage */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL				/* permissions */
 };
 
 static int
@@ -75,16 +87,13 @@
 	int			 i;
 	int			 hash_pos;
 	int			 chain_pos;
-	unsigned long		 ino;
-	unsigned long		 old;
-	int stored;
-	char *name;
-	struct buffer_head *dir_bh;
-	struct buffer_head *fh_bh;
-	struct inode	   *dir;
+	unsigned long		 ino = inode->i_ino;
+	int			 stored;
+	unsigned char		*name;
+	struct buffer_head	*dir_bh;
+	struct buffer_head	*fh_bh;
 
-	pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos);
-	
+	pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",ino,(unsigned long)filp->f_pos);
 
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
@@ -92,9 +101,6 @@
 	stored = 0;
 	dir_bh = NULL;
 	fh_bh  = NULL;
-	dir    = NULL;
-	old    = filp->f_pos & 0x80000000;
-	filp->f_pos &= 0x7FFFFFFF;
 
 	if (filp->f_pos == 0) {
 		filp->private_data = (void *)0;
@@ -106,18 +112,12 @@
 	}
 	if (filp->f_pos == 1) {
 		if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) {
-			filp->f_pos |= 0x80000000;
 			return stored;
 		}
 		filp->f_pos = 2;
 		stored++;
 	}
 
-	/* Read original if this is a link */
-	ino = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-	if (!(dir = iget(inode->i_sb,ino)))
-		return stored;
-	
 	chain_pos = (filp->f_pos - 2) & 0xffff;
 	hash_pos  = (filp->f_pos - 2) >> 16;
 	if (chain_pos == 0xffff) {
@@ -129,25 +129,26 @@
 	if (!(dir_bh = affs_bread(inode->i_dev,ino,AFFS_I2BSIZE(inode))))
 		goto readdir_done;
 
-	while (!stored || !old) {
+	while (1) {
 		while (hash_pos < AFFS_I2HSIZE(inode) &&
 		     !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
 			hash_pos++;
 		if (hash_pos >= AFFS_I2HSIZE(inode))
-			goto readdir_done;
+			break;
 		
 		i = htonl(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
 		j = chain_pos;
+
 		/* If the directory hasn't changed since the last call to readdir(),
 		 * we can jump directly to where we left off.
 		 */
-		if (filp->private_data && filp->f_version == dir->i_version) {
+		if (filp->private_data && filp->f_version == inode->i_version) {
 			i = (int)filp->private_data;
 			j = 0;
 			pr_debug("AFFS: readdir() left off=%d\n",i);
 		}
-		filp->f_version = dir->i_version;
-		pr_debug("AFFS: hash_pos=%lu chain_pos=%lu\n", hash_pos, chain_pos);
+		filp->f_version = inode->i_version;
+		pr_debug("AFFS: hash_pos=%u chain_pos=%u\n", hash_pos, chain_pos);
 		while (i) {
 			if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
 				printk("AFFS: readdir: Can't get block %d\n",i);
@@ -163,7 +164,7 @@
 		}
 		if (fh_bh) {
 			namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
-			pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%lu\n",
+			pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%u\n",
 				 namelen,name,ino,i);
 			filp->private_data = (void *)ino;
 			if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0)
@@ -182,10 +183,8 @@
 	}
 
 readdir_done:
-	filp->f_pos |= old;
 	affs_brelse(dir_bh);
 	affs_brelse(fh_bh);
-	iput(dir);
 	pr_debug("AFFS: readdir()=%d\n",stored);
 	return stored;
 }
--- linux-2.0.33pl1-pre3/fs/affs/file.c	Thu Sep  4 14:12:52 1997
+++ linux/fs/affs/file.c	Sun Apr 19 11:38:41 1998
@@ -12,7 +12,7 @@
  *  affs regular file handling primitives
  */
 
-#include <asm/segment.h>
+#define DEBUG 0
 #include <asm/system.h>
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
@@ -35,11 +35,12 @@
 #error PAGE_SIZE must be at least 4096
 #endif
 
+static int affs_bmap(struct inode *inode, int block);
+static struct buffer_head *affs_getblock(struct inode *inode, s32 block);
 static int affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count);
 static int affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count);
-static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count);
-static int affs_open_file(struct inode *inode, struct file *filp);
-static void affs_release_file(struct inode *inode, struct file *filp);
+static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int cnt);
+static int alloc_ext_cache(struct inode *inode);
 
 static struct file_operations affs_file_operations = {
 	NULL,			/* lseek - default */
@@ -49,8 +50,8 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	generic_file_mmap,	/* mmap */
-	affs_open_file,		/* special open is needed */
-	affs_release_file,	/* release */
+	NULL,			/* no special open */
+	NULL,			/* release */
 	file_fsync		/* brute force, but works */
 };
 
@@ -83,8 +84,8 @@
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	NULL,			/* mmap */
-	affs_open_file,		/* special open is needed */
-	affs_release_file,	/* release */
+	NULL,			/* no special open */
+	NULL,			/* release */
 	file_fsync		/* brute force, but works */
 };
 
@@ -197,7 +198,7 @@
 	return 128 + 192 * 2 + 128 * 4 + 64 * 16 + 32 * 64 + 64 * 256 + (index << 9);
 }
 
-static int __inline__
+static s32 __inline__
 calc_key(struct inode *inode, int *ext)
 {
 	int		  index;
@@ -223,34 +224,33 @@
 	return inode->u.affs_i.i_ec->ec[index];
 }
 
-int
+static int
 affs_bmap(struct inode *inode, int block)
 {
 	struct buffer_head	*bh;
-	int			 ext, key, nkey;
-	int			 ptype, stype;
+	s32			 key, nkey;
+	s32			 ptype, stype;
+	int			 ext;
 	int			 index;
 	int			 keycount;
 	struct key_cache	*kc;
 	struct key_cache	*tkc;
 	struct timeval		 tv;
-	__s32			*keyp;
+	s32			*keyp;
 	int			 i;
 
 	pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0) {
-		printk("affs_bmap: block < 0\n");
+		affs_error(inode->i_sb,"bmap","Block < 0");
 		return 0;
 	}
-	if (!inode->u.affs_i.i_ec) {
-		printk("affs_bmap(): No ext_cache!?\n");
+	if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode))
 		return 0;
-	}
 
 	/* Try to find the requested key in the cache.
 	 * In order to speed this up as much as possible,
-	 * the cache line lookup is done in a seperate
+	 * the cache line lookup is done in a separate
 	 * step.
 	 */
 
@@ -292,7 +292,7 @@
 
 	for (;;) {
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-		if (!bh)
+		if (!bh) 
 			return 0;
 		index = seqnum_to_index(ext);
 		if (index > inode->u.affs_i.i_ec->max_ext &&
@@ -331,106 +331,115 @@
 	return key;
 }
 
-struct buffer_head *
-affs_getblock(struct inode *inode, int block)
+/* With the affs, getting a random block from a file is not
+ * a simple business. Since this fs does not allow holes,
+ * it may be neccessary to allocate all the missing blocks
+ * inbetween, as well as some new extension blocks. The OFS
+ * is even worse: All data blocks contain pointers to the
+ * next ones, so you have to fix [n-1] after allocating [n].
+ * What a mess.
+ */
+
+static struct buffer_head *
+affs_getblock(struct inode *inode, s32 block)
 {
-	struct buffer_head	*bh;
-	struct buffer_head	*ebh;
-	struct buffer_head	*pbh;
+	struct super_block	*sb = inode->i_sb;
+	int			 ofs = sb->u.affs_sb.s_flags & SF_OFS;
+	int			 ext = block / AFFS_I2HSIZE(inode);
+	struct buffer_head	*bh, *ebh, *pbh = NULL;
 	struct key_cache	*kc;
-	int			 key, nkey;
-	int			 ext;
+	s32			 key, nkey;
 	int			 cf, j, pt;
 	int			 index;
-	int			 ofs;
+	int			 err;
 
 	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0)
-		return NULL;
+		goto out_fail;
 
-	/* Writers always use cache line 3. In almost all cases, files
-	 * will be written by only one process at the same time, and
-	 * they also will be written in strict sequential order. Thus
-	 * there is not much sense in looking whether the key of the
-	 * requested block is available - it won't be there.
-	 */
-	kc     = &inode->u.affs_i.i_ec->kc[3];
-	ofs    = inode->i_sb->u.affs_sb.s_flags & SF_OFS;
-	ext    = block / AFFS_I2HSIZE(inode);
 	key    = calc_key(inode,&ext);
 	block -= ext * AFFS_I2HSIZE(inode);
 	pt     = ext ? T_LIST : T_SHORT;
-	pbh    = NULL;
 
-	for (;;) {
+	/* Key refers now to the last known extension block,
+	 * ext is its sequence number (if 0, key refers to the
+	 * header block), and block is the block number relative
+	 * to the first block stored in that extension block.
+	 */
+	for (;;) {	/* Loop over header block and extension blocks */
+		struct file_front *fdp;
+
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
-			return NULL;
-		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j) ||
-		    cf != pt || j != ST_FILE) {
-		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
-			       pt == T_SHORT ? "file header" : "extension block");
-			affs_brelse(bh);
-			return NULL;
+			goto out_fail;
+		fdp = (struct file_front *) bh->b_data;
+		err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j);
+		if (err || cf != pt || j != ST_FILE) {
+		    	affs_error(sb, "getblock",
+				"Block %d is not a valid %s", key,
+				pt == T_SHORT ? "file header" : "ext block");
+			goto out_free_bh;
 		}
 		j  = htonl(((struct file_front *)bh->b_data)->block_count);
-		cf = 0;
-		while (j < AFFS_I2HSIZE(inode) && j <= block) {
+		for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) {
 			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
-				if (j > 0)
-					pbh = affs_bread(inode->i_dev,ntohl(AFFS_BLOCK(bh->b_data,inode,j - 1)),
-							 AFFS_I2BSIZE(inode));
-				else
+				if (j > 0) {
+					s32 k = AFFS_BLOCK(bh->b_data, inode,
+								j - 1);
+					pbh = affs_bread(inode->i_dev,
+							htonl(k),
+							AFFS_I2BSIZE(inode));
+				} else
 					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
 				if (!pbh) {
-					printk("AFFS: getblock(): cannot get last block in file\n");
+					affs_error(sb,"getblock",
+						"Cannot get last block in file");
 					break;
 				}
 			}
 			nkey = affs_new_data(inode);
 			if (!nkey)
 				break;
-			lock_super(inode->i_sb);
+			inode->u.affs_i.i_lastblock++;
 			if (AFFS_BLOCK(bh->b_data,inode,j)) {
-				unlock_super(inode->i_sb);
-				printk("AFFS: getblock(): block already allocated\n");
-				affs_free_block(inode->i_sb,nkey);
-				j++;
+				affs_warning(sb,"getblock","Block already allocated");
+				affs_free_block(sb,nkey);
 				continue;
 			}
-			unlock_super(inode->i_sb);
 			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(nkey);
 			if (ofs) {
 				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
 				if (!ebh) {
-					printk("AFFS: getblock(): cannot get block %d\n",nkey);
-					affs_free_block(inode->i_sb,nkey);
+					affs_error(sb,"getblock",
+						   "Cannot get block %d",nkey);
+					affs_free_block(sb,nkey);
 					AFFS_BLOCK(bh->b_data,inode,j) = 0;
 					break;
 				}
-				inode->u.affs_i.i_lastblock++;
 				DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
 				DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
 				DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+				affs_fix_checksum(AFFS_I2BSIZE(inode),
+							ebh->b_data, 5);
+				mark_buffer_dirty(ebh, 0);
 				if (pbh) {
 					DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
 					DATA_FRONT(pbh)->next_data = ntohl(nkey);
 					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
 					mark_buffer_dirty(pbh,0);
-					mark_buffer_dirty(ebh,0);
 					affs_brelse(pbh);
 				}
 				pbh = ebh;
 			}
-			j++;
 			cf = 1;
 		}
+		/* N.B. May need to release pbh after here */
+
 		if (cf) {
 			if (pt == T_SHORT)
-				((struct file_front *)bh->b_data)->first_data =
-								AFFS_BLOCK(bh->b_data,inode,0);
-			((struct file_front *)bh->b_data)->block_count = ntohl(j);
+				fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0);
+			fdp->block_count = ntohl(j);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 		}
@@ -441,76 +450,84 @@
 			break;
 		}
 		if (j < AFFS_I2HSIZE(inode)) {
-			affs_brelse(bh);
-			return NULL;
+			/* N.B. What about pbh here? */
+			goto out_free_bh;
 		}
 
 		block -= AFFS_I2HSIZE(inode);
 		key    = htonl(FILE_END(bh->b_data,inode)->extension);
 		if (!key) {
 			key = affs_new_header(inode);
-			if (!key) {
-				affs_brelse(bh);
-				return NULL;
-			}
+			if (!key)
+				goto out_free_bh;
 			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 			if (!ebh) {
-				affs_free_block(inode->i_sb,key);
-				return NULL;
+				/* N.B. must free bh here */
+				goto out_free_block;
 			}
 			((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
 			((struct file_front *)ebh->b_data)->own_key      = ntohl(key);
 			FILE_END(ebh->b_data,inode)->secondary_type      = ntohl(ST_FILE);
 			FILE_END(ebh->b_data,inode)->parent              = ntohl(inode->i_ino);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+			mark_buffer_dirty(ebh, 1);
 			FILE_END(bh->b_data,inode)->extension = ntohl(key);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			bh = ebh;
 		}
-		affs_brelse(bh);
 		pt = T_LIST;
 		ext++;
-		if ((index = seqnum_to_index(ext)) > inode->u.affs_i.i_ec->max_ext &&
-		    AFFS_ISINDEX(ext) && inode->u.affs_i.i_ec) {
+		index = seqnum_to_index(ext);
+		if (index > inode->u.affs_i.i_ec->max_ext &&
+		    AFFS_ISINDEX(ext)) {
 			inode->u.affs_i.i_ec->ec[index] = key;
 			inode->u.affs_i.i_ec->max_ext   = index;
 		}
+		affs_brelse(bh);
+	}
+
+	/* Invalidate key cache */
+	for (j = 0; j < 4; j++) {
+		kc = &inode->u.affs_i.i_ec->kc[j];
+		kc->kc_last = -1;
 	}
-	kc->kc_this_key = key;
-	kc->kc_this_seq = ext;
-	kc->kc_next_key = htonl(FILE_END(bh->b_data,inode)->extension);
 	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	if (!key)
-		return NULL;
-	
-	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
-}
+		goto out_fail;
 
-/* This could be made static, regardless of what the former comment said.
- * You cannot directly read affs directories.
- */
+	bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode));
+	return bh;
+
+out_free_block:
+	affs_free_block(sb, key);
+out_free_bh:
+	affs_brelse(bh);
+out_fail:
+	return NULL;
+}
 
 static int
 affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count)
 {
-	char *start;
-	int left, offset, size, sector;
-	int blocksize;
-	struct buffer_head *bh;
-	void *data;
+	char			*start;
+	int			 left, offset, size, sector;
+	int			 blocksize;
+	struct buffer_head	*bh;
+	void			*data;
 
-	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,(long)filp->f_pos,count);
+	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,
+		 (unsigned long)filp->f_pos,count);
 
 	if (!inode) {
-		printk("affs_file_read: inode = NULL\n");
+		affs_error(inode->i_sb,"file_read_ofs","Inode = NULL");
 		return -EINVAL;
 	}
 	blocksize = AFFS_I2BSIZE(inode) - 24;
 	if (!(S_ISREG(inode->i_mode))) {
-		pr_debug("affs_file_read: mode = %07o\n",inode->i_mode);
+		pr_debug("affs_file_read: mode = %07o",inode->i_mode);
 		return -EINVAL;
 	}
 	if (filp->f_pos >= inode->i_size || count <= 0)
@@ -521,10 +538,10 @@
 		left = MIN (inode->i_size - filp->f_pos,count - (buf - start));
 		if (!left)
 			break;
-		sector = affs_bmap(inode,(__u32)filp->f_pos / blocksize);
+		sector = affs_bmap(inode,(u32)filp->f_pos / blocksize);
 		if (!sector)
 			break;
-		offset = (__u32)filp->f_pos % blocksize;
+		offset = (u32)filp->f_pos % blocksize;
 		bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
 		if (!bh)
 			break;
@@ -548,34 +565,28 @@
 	int			 c;
 	int			 blocksize;
 	struct buffer_head	*bh;
-	struct inode		*ino;
 	char			*p;
 
+	if (!count)
+		return 0;
 	pr_debug("AFFS: file_write(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
-		(unsigned long)filp->f_pos,count);
+		 (unsigned long)filp->f_pos,count);
 
-	ino = NULL;
 	if (!inode) {
-		printk("AFFS: file_write(): inode=NULL\n");
+		affs_error(inode->i_sb,"file_write","Inode = NULL");
 		return -EINVAL;
 	}
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			printk("AFFS: could not follow link from inode %lu to %d\n",
-			       inode->i_ino,inode->u.affs_i.i_original);
-			return -EINVAL;
-		}
-		inode = ino;
-	}
 	if (!S_ISREG(inode->i_mode)) {
-		printk("AFFS: file_write(): mode=%07o\n",inode->i_mode);
-		iput(inode);
+		affs_error(inode->i_sb,"file_write",
+			   "Trying to write to non-regular file (mode=%07o)",
+			   inode->i_mode);
 		return -EINVAL;
 	}
-	if (filp->f_flags & O_APPEND) {
+	if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode))
+		return -ENOMEM;
+	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
-	} else
+	else
 		pos = filp->f_pos;
 	written   = 0;
 	blocksize = AFFS_I2BSIZE(inode);
@@ -600,7 +611,7 @@
 				break;
 			}
 		}
-		p = (pos % blocksize) + bh->b_data;
+		p  = (pos % blocksize) + bh->b_data;
 		memcpy_fromfs(p,buf,c);
 		update_vm_cache(inode,pos,p,c);
 		mark_buffer_uptodate(bh,1);
@@ -615,7 +626,6 @@
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	filp->f_pos    = pos;
 	inode->i_dirt  = 1;
-	iput(ino);
 	return written;
 }
 
@@ -627,31 +637,28 @@
 	int			 c;
 	int			 blocksize;
 	struct buffer_head	*bh;
-	struct inode		*ino;
 	char			*p;
 
 	pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
 		(unsigned long)filp->f_pos,count);
 
+	if (!count)
+		return 0;
 	if (!inode) {
-		printk("AFFS: file_write_ofs(): inode=NULL\n");
+		affs_error(inode->i_sb,"file_write_ofs","Inode = NULL");
 		return -EINVAL;
 	}
-	ino = NULL;
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			printk("AFFS: could not follow link from inode %lu to %d\n",
-			       inode->i_ino,inode->u.affs_i.i_original);
-			return -EINVAL;
-		}
-		inode = ino;
-	}
 	if (!S_ISREG(inode->i_mode)) {
-		printk("AFFS: file_write_ofs(): mode=%07o\n",inode->i_mode);
-		iput(inode);
+		affs_error(inode->i_sb,"file_write_ofs",
+			   "Trying to write to non-regular file (mode=%07o)",
+			   inode->i_mode);
 		return -EINVAL;
 	}
+	if (!inode->u.affs_i.i_ec) {
+		if (alloc_ext_cache(inode)) {
+			return -ENOMEM;
+		}
+	}
 	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
 	else
@@ -680,7 +687,7 @@
 				break;
 			}
 		}
-		p = (pos % blocksize) + bh->b_data + 24;
+		p  = (pos % blocksize) + bh->b_data + 24;
 		memcpy_fromfs(p,buf,c);
 		update_vm_cache(inode,pos,p,c);
 
@@ -695,63 +702,72 @@
 	}
 	if (pos > inode->i_size)
 		inode->i_size = pos;
-	filp->f_pos = pos;
+	filp->f_pos    = pos;
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	inode->i_dirt  = 1;
-	iput(ino);
 	return written;
 }
 
+/* Free any preallocated blocks and set the current user
+ * of the last used allocation zone (if any) to 0.
+ */
+
+void
+affs_free_prealloc(struct inode *inode)
+{
+	struct super_block	*sb = inode->i_sb;
+	struct affs_zone	*zone;
+	s32			 block;
+
+	pr_debug("AFFS: free_prealloc(ino=%lu,cnt=%d,next=%d,last=%d)\n",
+		inode->i_ino, inode->u.affs_i.i_pa_cnt,
+		inode->u.affs_i.i_pa_next,inode->u.affs_i.i_pa_last);
+
+	while (inode->u.affs_i.i_pa_cnt) {	
+		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
+		inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
+		inode->u.affs_i.i_pa_cnt--;
+		affs_free_block(sb,block);
+	}
+	if (inode->u.affs_i.i_zone) {
+		zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+		if (zone->z_ino == inode->i_ino)
+			zone->z_ino = 0;
+	}
+}
+
 void
 affs_truncate(struct inode *inode)
 {
 	struct buffer_head	*bh;
 	struct buffer_head	*ebh;
-	struct inode		*ino;
-	struct affs_zone	*zone;
 	int	 first;
 	int	 block;
-	int	 key;
-	int	*keyp;
-	int	 ekey;
-	int	 ptype, stype;
+	s32	 key;
+	s32	*keyp;
+	s32	 ekey;
+	s32	 ptype, stype;
 	int	 freethis;
 	int	 blocksize;
 	int	 rem;
 	int	 ext;
 
-	pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+	pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
 
-	ino = NULL;
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
-			       inode->i_ino,inode->u.affs_i.i_original);
-			return;
-		}
-		inode = ino;
-	}
 	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
 	if (inode->u.affs_i.i_lastblock < first - 1) {
-			bh = affs_getblock(inode,first - 1);
-
-		while (inode->u.affs_i.i_pa_cnt) {		/* Free any preallocated blocks */
-			affs_free_block(inode->i_sb,
-					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-			inode->u.affs_i.i_pa_cnt--;
-		}
-		if (inode->u.affs_i.i_zone) {
-			lock_super(inode->i_sb);
-			zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
-			if (zone->z_ino == inode->i_ino)
-				zone->z_ino = 0;
-			unlock_super(inode->i_sb);
+		if (!inode->u.affs_i.i_ec) {
+			if (alloc_ext_cache(inode)) {
+				/* Fine! No way to indicate an error. What can we do? */
+				return;
+			}
 		}
+		bh = affs_getblock(inode,first - 1);
+
+		affs_free_prealloc(inode);
 		if (!bh) {
-			printk("AFFS: truncate(): Cannot extend file\n");
+			affs_warning(inode->i_sb,"truncate","Cannot extend file");
 			inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
 		} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
 			rem = inode->i_size % blocksize;
@@ -760,7 +776,6 @@
 			mark_buffer_dirty(bh,0);
 		}
 		affs_brelse(bh);
-		iput(ino);
 		return;
 	}
 	ekey  = inode->i_ino;
@@ -768,26 +783,20 @@
 
 	while (ekey) {
 		if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
-			printk("AFFS: truncate(): Can't read block %d\n",ekey);
+			affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey);
 			break;
 		}
 		ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
 		stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
-		if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
-		    LINK_END(bh->b_data,inode)->original == 0) {
-			pr_debug("AFFS: truncate(): dumping link\n");
-			affs_brelse(bh);
-			break;
-		}
 		if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
-			printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
-			        ptype,stype);
+			affs_error(inode->i_sb,"truncate","Bad block (ptype=%d, stype=%d)",
+			           ptype,stype);
 			affs_brelse(bh);
 			break;
 		}
 		/* Do not throw away file header */
 		freethis = first == 0 && ekey != inode->i_ino;
-		for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+		for (block = first; block < AFFS_I2HSIZE(inode); block++) {
 			keyp = &AFFS_BLOCK(bh->b_data,inode,block);
 			key  = htonl(*keyp);
 			if (key) {
@@ -843,81 +852,52 @@
 		}
 	}
 
-	iput(ino);
 }
 
+/*
+ * Called only when we need to allocate the extension cache.
+ */
 static int
-affs_open_file(struct inode *inode, struct file *filp)
+alloc_ext_cache(struct inode *inode)
 {
-	int	 error;
-	int	 key;
+	s32	 key;
 	int	 i;
+	unsigned long cache_page;
+	int      error = 0;
 
-	pr_debug("AFFS: open_file(ino=%lu)\n",inode->i_ino);
+	pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
 
-	error = 0;
-	inode->u.affs_i.i_cache_users++;
-	lock_super(inode->i_sb);
-	if (!inode->u.affs_i.i_ec) {
-		inode->u.affs_i.i_ec = (struct ext_cache *)get_free_page(GFP_KERNEL);
-		if (!inode->u.affs_i.i_ec) {
-			printk("AFFS: cache allocation failed\n");
-			error = ENOMEM;
-		} else {
-			/* We only have to initialize non-zero values.
-			 * get_free_page() zeroed the page already.
-			 */
-			key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-			inode->u.affs_i.i_ec->ec[0] = key;
-			for (i = 0; i < 4; i++) {
-				inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
-				inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
-			}
-		}
+	cache_page = get_free_page(GFP_KERNEL);
+	/*
+	 * Check whether somebody else allocated it for us ...
+	 */
+	if (inode->u.affs_i.i_ec)
+		goto out_free;
+	if (!cache_page)
+		goto out_error;
+
+	inode->u.affs_i.i_ec = (struct ext_cache *) cache_page;
+	/* We only have to initialize non-zero values.
+	 * get_free_page() zeroed the page already.
+	 */
+	key = inode->u.affs_i.i_original;
+	if (!inode->u.affs_i.i_original)
+		key = inode->i_ino;
+	inode->u.affs_i.i_ec->ec[0] = key;
+	for (i = 0; i < 4; i++) {
+		inode->u.affs_i.i_ec->kc[i].kc_this_key = key;
+		inode->u.affs_i.i_ec->kc[i].kc_last     = -1;
 	}
-	unlock_super(inode->i_sb);
-
+out:
 	return error;
-}
-
-static void
-affs_release_file(struct inode *inode, struct file *filp)
-{
-	struct affs_zone	*zone;
-
-	pr_debug("AFFS: release_file(ino=%lu)\n",inode->i_ino);
 
-	if (filp->f_mode & 2) {		/* Free preallocated blocks */
-		while (inode->u.affs_i.i_pa_cnt) {
-			affs_free_block(inode->i_sb,
-					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
-			inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1;
-			inode->u.affs_i.i_pa_cnt--;
-		}
-		if (inode->u.affs_i.i_zone) {
-			lock_super(inode->i_sb);
-			zone = &inode->i_sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
-			if (zone->z_ino == inode->i_ino)
-				zone->z_ino = 0;
-			unlock_super(inode->i_sb);
-		}
-	}
-	lock_super(inode->i_sb);
-	if (--inode->u.affs_i.i_cache_users == 0) {
-		if (inode->u.affs_i.i_ec) {
-#if 0
-			{
-				int i, *p = inode->u.affs_i.i_ec->ec;
-				for (i = 0; i < 640; i += 16) {
-					printk("# %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d %6d\n",
-					  p[0+i],p[1+i],p[2+i],p[3+i],p[4+i],p[5+i],p[6+i],p[7+i],
-					  p[8+i],p[9+i],p[10+i],p[11+i],p[12+i],p[13+i],p[14+i],p[15+i]);
-				}
-			}
-#endif
-			free_page((unsigned long)inode->u.affs_i.i_ec);
-			inode->u.affs_i.i_ec = NULL;
-		}
-	}
-	unlock_super(inode->i_sb);
+out_free:
+	if (cache_page)
+		free_page(cache_page);
+	goto out;
+
+out_error:
+	affs_error(inode->i_sb,"alloc_ext_cache","Cache allocation failed");
+	error = -ENOMEM;
+	goto out;
 }
--- linux-2.0.33pl1-pre3/fs/affs/inode.c	Thu Sep  4 14:12:53 1997
+++ linux/fs/affs/inode.c	Sun Apr 19 13:35:20 1998
@@ -4,13 +4,13 @@
  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
  *
  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
- * 
+ *
  *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
  *
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#include <linux/module.h>
+#define DEBUG 0
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/malloc.h>
@@ -27,95 +27,18 @@
 #include <linux/major.h>
 #include <linux/blkdev.h>
 #include <asm/system.h>
-#include <asm/segment.h>
 
-extern int *blk_size[];
 extern struct timezone sys_tz;
 
 #define MIN(a,b) (((a)<(b))?(a):(b))
 
-void
-affs_put_super(struct super_block *sb)
-{
-	int	 i;
-
-	pr_debug("affs_put_super()\n");
-
-	lock_super(sb);
-	for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
-		affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
-	if (!(sb->s_flags & MS_RDONLY)) {
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(1);
-		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
-	}
-
-	if (sb->u.affs_sb.s_flags & SF_PREFIX)
-		kfree(sb->u.affs_sb.s_prefix);
-	kfree(sb->u.affs_sb.s_bitmap);
-	affs_brelse(sb->u.affs_sb.s_root_bh);
-
-	/* I'm not happy with this. It would be better to save the previous
-	 * value of this devices blksize_size[][] in the super block and
-	 * restore it here, but with the affs superblock being quite large
-	 * already ...
-	 */
-	set_blocksize(sb->s_dev,BLOCK_SIZE);
-
-	sb->s_dev = 0;
-	unlock_super(sb);
-	MOD_DEC_USE_COUNT;
-	return;
-}
-
-static void
-affs_write_super(struct super_block *sb)
-{
-	int	 i, clean = 2;
-
-	if (!(sb->s_flags & MS_RDONLY)) {
-		lock_super(sb);
-		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
-			if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
-				if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
-					clean = 0;
-					break;
-				}
-			}
-		}
-		unlock_super(sb);
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(clean);
-		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
-		sb->s_dirt = !clean;	/* redo until bitmap synced */
-	} else
-		sb->s_dirt = 0;
-
-	pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
-}
-
-static struct super_operations affs_sops = { 
-	affs_read_inode,
-	affs_notify_change,
-	affs_write_inode,
-	affs_put_inode,	
-	affs_put_super,
-	affs_write_super,
-	affs_statfs,
-	NULL			/* remount */
-};
-
-int
+unsigned long
 affs_parent_ino(struct inode *dir)
 {
 	int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
 
-	if (!S_ISDIR (dir->i_mode)) {
-		printk ("affs_parent_ino: argument is not a directory\n");
+	if (!S_ISDIR(dir->i_mode)) {
+		affs_error(dir->i_sb,"parent_ino","Trying to get parent of non-directory");
 		return root_ino;
 	}
 	if (dir->i_ino == root_ino)
@@ -123,529 +46,15 @@
 	return dir->u.affs_i.i_parent;
 }
 
-static int
-parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, int *root,
-		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
-{
-	char	*this_char, *value;
-	int	 f;
-
-	/* Fill in defaults */
-
-	*uid        = 0;
-	*gid        = 0;
-	*reserved   = 2;
-	*root       = -1;
-	*blocksize  = -1;
-	*prefix     = "/";
-	volume[0]   = ':';
-	volume[1]   = 0;
-	*mount_opts = 0;
-	if (!options)
-		return 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
-		f = 0;
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
-		if (!strcmp(this_char,"protect")) {
-			if (value) {
-				printk("AFFS: option protect does not take an argument\n");
-				return 0;
-			}
-			*mount_opts |= SF_IMMUTABLE;
-		}
-		if (!strcmp(this_char,"verbose")) {
-			if (value) {
-				printk("AFFS: option verbose does not take an argument\n");
-				return 0;
-			}
-			*mount_opts |= SF_VERBOSE;
-		}
-		else if ((f = !strcmp(this_char,"uid")) || !strcmp(this_char,"setuid")) {
-			if (!value)
-				*uid = current->uid;
-			else if (!*value) {
-				printk("AFFS: argument for uid option missing\n");
-				return 0;
-			} else {
-				*uid = simple_strtoul(value,&value,0);
-				if (*value)
-					return 0;
-				if (!f)
-					*mount_opts |= SF_SETUID;
-			}
-		}
-		else if ((f = !strcmp(this_char,"gid")) || !strcmp(this_char,"setgid")) {
-			if (!value)
-				*gid = current->gid;
-			else if (!*value) {
-				printk("AFFS: argument for gid option missing\n");
-				return 0;
-			} else {
-				*gid = simple_strtoul(value,&value,0);
-				if (*value)
-					return 0;
-				if (!f)
-					*mount_opts |= SF_SETGID;
-			}
-		}
-		else if (!strcmp(this_char,"prefix")) {
-			if (!value) {
-				printk("AFFS: The prefix option requires an argument\n");
-				return 0;
-			}
-			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
-			if (!*prefix)
-				return 0;
-			strcpy(*prefix,value);
-			*mount_opts |= SF_PREFIX;
-		}
-		else if (!strcmp(this_char,"volume")) {
-			if (!value) {
-				printk("AFFS: The volume option requires an argument\n");
-				return 0;
-			}
-			if (strlen(value) > 30)
-				value[30] = 0;
-			strcpy(volume,value);
-		}
-		else if (!strcmp(this_char,"mode")) {
-			if (!value || !*value) {
-				printk("AFFS: The mode option requires an argument\n");
-				return 0;
-			}
-			*mode = simple_strtoul(value,&value,8) & 0777;
-			if (*value)
-				return 0;
-			*mount_opts |= SF_SETMODE;
-		}
-		else if (!strcmp(this_char,"reserved")) {
-			if (!value || !*value) {
-				printk("AFFS: The reserved option requires an argument\n");
-				return 0;
-			}
-			*reserved = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"root")) {
-			if (!value || !*value) {
-				printk("AFFS: The root option requires an argument\n");
-				return 0;
-			}
-			*root = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"bs")) {
-			if (!value || !*value) {
-				printk("AFFS: The bs option requires an argument\n");
-				return 0;
-			}
-			*blocksize = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-			if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
-			    && *blocksize != 4096) {
-				printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed).\n");
-				return 0;
-			}
-		}
-		/* Silently ignore the quota options */
-		else if (!strcmp (this_char, "grpquota")
-			 || !strcmp (this_char, "noquota")
-			 || !strcmp (this_char, "quota")
-			 || !strcmp (this_char, "usrquota"))
-			;
-		else {
-			printk("AFFS: Unrecognized mount option %s\n", this_char);
-			return 0;
-		}
-	}
-	return 1;
-}
-
-/* This function definitely needs to be split up. Some fine day I'll
- * hopefully have the guts to do so. Until then: sorry for the mess.
- */
-
-struct super_block *
-affs_read_super(struct super_block *s,void *data, int silent)
-{
-	struct buffer_head	*bh = NULL;
-	struct buffer_head	*bb;
-	kdev_t			 dev = s->s_dev;
-	int			 root_block;
-	int			 size;
-	__u32			 chksum;
-	__u32			*bm;
-	int			 ptype, stype;
-	int			 mapidx;
-	int			 num_bm;
-	int			 i, j;
-	int			 key;
-	int			 blocksize;
-	uid_t			 uid;
-	gid_t			 gid;
-	int			 reserved;
-	int			 az_no;
-	unsigned long		 mount_flags;
-	unsigned long		 offset;
-
-	pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
-
-	MOD_INC_USE_COUNT;
-
-	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
-	    &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
-		s->s_dev = 0;
-		printk("AFFS: error parsing options.\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	lock_super(s);
-
-	/* Get the size of the device in 512-byte blocks.
-	 * If we later see that the partition uses bigger
-	 * blocks, we will have to change it.
-	 */
-
-	size = blksize_size[MAJOR(dev)][MINOR(dev)];
-	size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
-
-	s->u.affs_sb.s_bitmap  = NULL;
-	s->u.affs_sb.s_root_bh = NULL;
-	s->u.affs_sb.s_flags   = mount_flags;
-	s->u.affs_sb.s_mode    = i;
-	s->u.affs_sb.s_uid     = uid;
-	s->u.affs_sb.s_gid     = gid;
-
-	if (size == 0) {
-		s->s_dev = 0;
-		unlock_super(s);
-		printk("affs_read_super: could not determine device size\n");
-		goto out;
-	}
-	s->u.affs_sb.s_partition_size = size;
-	s->u.affs_sb.s_reserved       = reserved;
-
-	/* Try to find root block. Its location may depend on the block size. */
-
-	s->u.affs_sb.s_hashsize = 0;
-	if (blocksize > 0) {
-		i = blocksize;
-		j = blocksize;
-	} else {
-		i = 512;
-		j = 4096;
-	}
-	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
-		if (root_block < 0)
-			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
-		else
-			s->u.affs_sb.s_root_block = root_block;
-		set_blocksize(dev,blocksize);
-
-		/* The root block location that was calculated above is not
-		 * correct if the partition size is an odd number of 512-
-		 * byte blocks, which will be rounded down to a number of
-		 * 1024-byte blocks, and if there were an even number of
-		 * reserved blocks. Ideally, all partition checkers should
-		 * report the real number of blocks of the real blocksize,
-		 * but since this just cannot be done, we have to try to
-		 * find the root block anyways. In the above case, it is one
-		 * block behind the calculated one. So we check this one, too.
-		 */
-		for (num_bm = 0; num_bm < 2; num_bm++) {
-			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %d, "
-				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
-				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
-			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
-			if (!bh) {
-				printk("AFFS: unable to read root block\n");
-				goto out;
-			}
-			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
-			    ptype == T_SHORT && stype == ST_ROOT) {
-				s->s_blocksize             = blocksize;
-				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
-				s->u.affs_sb.s_root_block += num_bm;
-				key                        = 1;
-				break;
-			}
-		}
-		if (key)
-			break;
-		affs_brelse(bh);
-		bh = NULL;
-	}
-	if (!key) {
-		affs_brelse(bh);
-		if (!silent)
-			printk("AFFS: Can't find a valid root block on device %s\n",kdevname(dev));
-		goto out;
-	}
-	root_block = s->u.affs_sb.s_root_block;
-
-	s->u.affs_sb.s_partition_size   = size;
-	s->s_blocksize_bits             = blocksize == 512 ? 9 :
-					  blocksize == 1024 ? 10 :
-					  blocksize == 2048 ? 11 : 12;
-
-	/* Find out which kind of FS we have */
-	bb = affs_bread(dev,0,s->s_blocksize);
-	if (bb) {
-		chksum = htonl(*(__u32 *)bb->b_data);
-		switch (chksum) {
-			case MUFS_FS:
-			case MUFS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_INTL;
-				break;
-			case MUFS_DCFFS:
-			case MUFS_FFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				break;
-			case FS_DCFFS:
-			case FS_FFS:
-				break;
-			case MUFS_OFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_OFS:
-				s->u.affs_sb.s_flags |= SF_OFS;
-				break;
-			case MUFS_DCOFS:
-				if (!(s->s_flags & MS_RDONLY)) {
-					printk("AFFS: Dircache FS - mounting %s read only.\n",
-					       kdevname(dev));
-				}
-				/* fall thru */
-			case MUFS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				if (0)
-				/* fall thru */
-			case FS_DCOFS:
-				if (!(s->s_flags & MS_RDONLY)) {
-					printk("AFFS: Dircache FS - mounting %s read only.\n",
-					       kdevname(dev));
-				}
-				/* fall thru */
-			case FS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
-				break;
-			default:
-				printk("AFFS: Unknown filesystem on device %s: %08X\n",
-				       kdevname(dev),chksum);
-				affs_brelse(bb);
-				goto out;
-		}
-		affs_brelse(bb);
-	} else {
-		printk("AFFS: Can't get boot block.\n");
-		goto out;
-	}
-	if (mount_flags & SF_VERBOSE) {
-		chksum = ntohl(chksum);
-		printk("AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
-		       GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
-		       &GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
-		       (char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
-	}
-
-	s->s_magic  = AFFS_SUPER_MAGIC;
-	s->s_flags |= MS_NODEV | MS_NOSUID;
-
-	/* Keep super block in cache */
-	if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
-		printk("AFFS: Can't read root block a second time\n");
-		goto out;
-	}
-
-	/* Allocate space for bitmaps, zones and others */
-
-	size   = s->u.affs_sb.s_partition_size - reserved;
-	num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
-	az_no  = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
-	ptype  = num_bm * sizeof(struct affs_bm_info) + 
-		 az_no * sizeof(struct affs_alloc_zone) +
-		 MAX_ZONES * sizeof(struct affs_zone);
-	pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
-	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
-		printk("AFFS: Not enough memory.\n");
-		goto out;
-	}
-	memset(s->u.affs_sb.s_bitmap,0,ptype);
-
-	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
-	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
-	s->u.affs_sb.s_num_az  = az_no;
-
-	mapidx = 0;
-
-	if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
-		if (!(s->s_flags & MS_RDONLY)) {
-			printk("AFFS: Bitmap invalid - mounting %s read only.\n",kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-		}
-		affs_brelse(bh);
-		bh = NULL;
-		goto nobitmap;
-	}
-
-	/* The following section is ugly, I know. Especially because of the
-	 * reuse of some variables that are not named properly.
-	 */
-
-	key    = root_block;
-	ptype  = s->s_blocksize / 4 - 49;
-	stype  = ptype + 25;
-	offset = s->u.affs_sb.s_reserved;
-	az_no  = 0;
-	while (bh) {
-		bm = (__u32 *)bh->b_data;
-		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
-			if (mapidx >= num_bm) {
-				printk("AFFS: Not enough bitmap space!?\n");
-				goto out;
-			}
-			bb = affs_bread(s->s_dev,htonl(bm[i]),s->s_blocksize);
-			if (bb) {
-				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
-				    !(s->s_flags & MS_RDONLY)) {
-					printk("AFFS: Bitmap (%d,key=%lu) invalid - "
-					       "mounting %s read only.\n",mapidx,htonl(bm[i]),
-						kdevname(dev));
-					s->s_flags |= MS_RDONLY;
-				}
-				/* Mark unused bits in the last word as allocated */
-				if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
-					ptype = size / 32 + 1;		/* word number */
-					key   = size & 0x1F;		/* used bits */
-					if (key) {
-						chksum = ntohl(0x7FFFFFFF >> (31 - key));
-						((__u32 *)bb->b_data)[ptype] &= chksum;
-						affs_fix_checksum(s->s_blocksize,bb->b_data,0);
-						mark_buffer_dirty(bb,1);
-					}
-					ptype = (size + 31) & ~0x1F;
-					size  = 0;
-					if (!(s->s_flags & MS_RDONLY))
-						s->u.affs_sb.s_flags |= SF_BM_VALID;
-				} else {
-					ptype = s->s_blocksize * 8 - 32;
-					size -= ptype;
-				}
-				s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
-				s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
-				s->u.affs_sb.s_bitmap[mapidx].bm_key      = htonl(bm[i]);
-				s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
-				offset += ptype;
-
-				for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
-					key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
-					s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
-					s->u.affs_sb.s_alloc[az_no].az_free =
-						affs_count_free_bits(key / 8,bb->b_data +
-								     j * (AFFS_ZONE_SIZE / 8) + 4);
-				}
-				affs_brelse(bb);
-			} else {
-				printk("AFFS: Can't read bitmap.\n");
-				goto out;
-			}
-		}
-		key   = htonl(bm[stype]);		/* Next block of bitmap pointers	*/
-		ptype = 0;
-		stype = s->s_blocksize / 4 - 1;
-		affs_brelse(bh);
-		if (key) {
-			if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
-				printk("AFFS: Can't read bitmap extension.\n");
-				goto out;
-			}
-		} else
-			bh = NULL;
-	}
-	if (mapidx != num_bm) {
-		printk("AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
-		goto out;
-	}
-nobitmap:
-	s->u.affs_sb.s_bm_count  = mapidx;
-
-	/* set up enough so that it can read an inode */
-
-	s->s_dev       = dev;
-	s->s_op        = &affs_sops;
-	s->s_mounted   = iget(s,root_block);
-	s->s_dirt      = 1;
-	unlock_super(s);
-
-	if (!(s->s_mounted)) {
-		s->s_dev = 0;
-		printk("AFFS: get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-
-	/* create data zones if the fs is mounted r/w */
-
-	if (!(s->s_flags & MS_RDONLY)) {
-		ROOT_END(s->u.affs_sb.s_root_bh->b_data,s->s_mounted)->bm_flag = 0;
-		secs_to_datestamp(CURRENT_TIME,&ROOT_END(s->u.affs_sb.s_root_bh->b_data,
-				  s->s_mounted)->disk_altered);
-		affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(s->u.affs_sb.s_root_bh,1);
-		affs_make_zones(s);
-	}
-
-	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
-	return s;
-
- out: /* Kick out for various error conditions */
-	affs_brelse (bh);
-	affs_brelse(s->u.affs_sb.s_root_bh);
-	if (s->u.affs_sb.s_bitmap)
-		kfree(s->u.affs_sb.s_bitmap);
-	set_blocksize(dev,BLOCK_SIZE);
-	s->s_dev = 0;
-	unlock_super(s);
-	MOD_DEC_USE_COUNT;
-	return NULL;
-}
-
-void
-affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
-{
-	int		 free;
-	struct statfs	 tmp;
-
-	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
-	     sb->u.affs_sb.s_reserved);
-
-	free          = affs_count_free_blocks(sb);
-	tmp.f_type    = AFFS_SUPER_MAGIC;
-	tmp.f_bsize   = sb->s_blocksize;
-	tmp.f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
-	tmp.f_bfree   = free;
-	tmp.f_bavail  = free;
-	tmp.f_files   = 0;
-	tmp.f_ffree   = 0;
-	memcpy_tofs(buf,&tmp,bufsiz);
-}
-
 void
 affs_read_inode(struct inode *inode)
 {
 	struct buffer_head	*bh, *lbh;
 	struct file_front	*file_front;
 	struct file_end		*file_end;
-	int			 block;
+	s32			 block;
 	unsigned long		 prot;
-	int			 ptype, stype;
+	s32			 ptype, stype;
 	unsigned short		 id;
 
 	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
@@ -653,12 +62,12 @@
 	lbh   = NULL;
 	block = inode->i_ino;
 	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
-		printk("AFFS: unable to read i-node block %d\n",block);
+		affs_error(inode->i_sb,"read_inode","Cannot read block %d",block);
 		return;
 	}
 	if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) {
-		printk("AFFS: read_inode(): checksum or type (ptype=%d) error on inode %d\n",
-		       ptype,block);
+		affs_error(inode->i_sb,"read_inode",
+			   "Checksum or type (ptype=%d) error on inode %d",ptype,block);
 		affs_brelse(bh);
 		return;
 	}
@@ -669,14 +78,13 @@
 
 	inode->u.affs_i.i_protect      = prot;
 	inode->u.affs_i.i_parent       = htonl(file_end->parent);
-	inode->u.affs_i.i_original     = 0;	
+	inode->u.affs_i.i_original     = 0;
 	inode->u.affs_i.i_zone         = 0;
 	inode->u.affs_i.i_hlink        = 0;
 	inode->u.affs_i.i_pa_cnt       = 0;
 	inode->u.affs_i.i_pa_next      = 0;
 	inode->u.affs_i.i_pa_last      = 0;
 	inode->u.affs_i.i_ec           = NULL;
-	inode->u.affs_i.i_cache_users  = 0;
 	inode->u.affs_i.i_lastblock    = -1;
 	inode->i_nlink                 = 1;
 	inode->i_mode                  = 0;
@@ -685,32 +93,27 @@
 		inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
 	else
 		inode->i_mode = prot_to_mode(prot);
-	
-	if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
+
+	id = htons(file_end->owner_uid);
+	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
 		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
-	else {
-		id = htons(file_end->owner_uid);
-		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
-			if (id == 0 || id == 0xFFFF)
-				id ^= ~0;
-		}
+	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+		inode->i_uid = 0;
+	else 
 		inode->i_uid = id;
-	}
-	if (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
+
+	id = htons(file_end->owner_gid);
+	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
 		inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
-	else {
-		id = htons(file_end->owner_gid);
-		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
-			if (id == 0 || id == 0xFFFF)
-				id ^= ~0;
-		}
+	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+		inode->i_gid = 0;
+	else
 		inode->i_gid = id;
-	}
 
 	switch (htonl(file_end->secondary_type)) {
 		case ST_ROOT:
-			inode->i_uid   = inode->i_sb->u.affs_sb.s_uid;
-			inode->i_gid   = inode->i_sb->u.affs_sb.s_gid;
+			inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
+			inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
 		case ST_USERDIR:
 			if (htonl(file_end->secondary_type) == ST_USERDIR ||
 			    inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
@@ -726,21 +129,32 @@
 			inode->i_size  = 0;
 			break;
 		case ST_LINKDIR:
+#if 0
 			inode->u.affs_i.i_original = htonl(file_end->original);
 			inode->u.affs_i.i_hlink    = 1;
 			inode->i_mode             |= S_IFDIR;
 			inode->i_size              = 0;
-			break;
+#endif
+			affs_error(inode->i_sb,"read_inode","inode is LINKDIR");
+			affs_brelse(bh);
+			return;
 		case ST_LINKFILE:
+#if 0
 			inode->u.affs_i.i_original = htonl(file_end->original);
 			inode->u.affs_i.i_hlink    = 1;
 			if (!(lbh = affs_bread(inode->i_dev,inode->u.affs_i.i_original,
-			                  AFFS_I2BSIZE(inode)))) {
+					AFFS_I2BSIZE(inode)))) {
 				affs_brelse(bh);
-				printk("AFFS: unable to read i-node block %ld\n",inode->i_ino);
+				affs_error(i_sb,"read_inode","Cannot read block %d\n",
+						inode->u.affs_i.i_original);
 				return;
 			}
 			file_end = GET_END_PTR(struct file_end,lbh->b_data,AFFS_I2BSIZE(inode));
+			/* Fall thru */
+#endif
+			affs_error(inode->i_sb,"read_inode","inode is LINKFILE");
+			affs_brelse(bh);
+			return;
 		case ST_FILE:
 			inode->i_mode |= S_IFREG;
 			inode->i_size  = htonl(file_end->byte_size);
@@ -764,7 +178,7 @@
 			 sys_tz.tz_minuteswest * 60;
 	affs_brelse(bh);
 	affs_brelse(lbh);
-	
+
 	inode->i_op = NULL;
 	if (S_ISREG(inode->i_mode)) {
 		if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
@@ -781,9 +195,10 @@
 void
 affs_write_inode(struct inode *inode)
 {
-	struct buffer_head *bh;
-	struct file_end	   *file_end;
-	short		    uid, gid;
+	struct buffer_head	*bh;
+	struct file_end		*file_end;
+	uid_t			 uid;
+	gid_t			 gid;
 
 	pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
 
@@ -791,8 +206,7 @@
 	if (!inode->i_nlink)
 		return;
 	if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) {
-		printk("AFFS: Unable to read block of inode %ld on %s\n",
-		       inode->i_ino,kdevname(inode->i_dev));
+		affs_error(inode->i_sb,"write_inode","Cannot read block %lu",inode->i_ino);
 		return;
 	}
 	file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
@@ -812,7 +226,7 @@
 					gid = inode->i_gid ^ ~0;
 			}
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
-				file_end->owner_gid = ntohs(uid);
+				file_end->owner_uid = ntohs(uid);
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
 				file_end->owner_gid = ntohs(gid);
 		}
@@ -831,35 +245,48 @@
 
 	error = inode_change_ok(inode,attr);
 	if (error)
-		return error;
-	
+		goto out;
+
 	if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
 	    ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
 	    ((attr->ia_valid & ATTR_MODE) &&
-	     (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE))))
-		error = -EPERM;
-	
-	if (error)
-		return (inode->i_sb->u.affs_sb.s_flags & SF_QUIET) ? 0 : error;
-	
+	     (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
+		if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET))
+			error = -EPERM;
+		goto out;
+	}
+
 	if (attr->ia_valid & ATTR_MODE)
 		inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
 
-	inode_setattr(inode,attr);
-
-	return 0;
+	inode_setattr(inode, attr);
+	inode->i_dirt = 1;
+	error         = 0;
+out:
+	return error;
 }
 
 void
 affs_put_inode(struct inode *inode)
 {
-	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
-	if (inode->i_nlink) {
-		return;
+	unsigned long	 cache_page = (unsigned long)inode->u.affs_i.i_ec;
+
+	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u, count=%d, mmap=%p)\n",
+		inode->i_ino,inode->i_nlink,inode->i_count,inode->i_mmap);
+
+	affs_free_prealloc(inode);
+	if (cache_page) {
+		pr_debug("AFFS: freeing ext cache\n");
+		inode->u.affs_i.i_ec = NULL;
+		free_page(cache_page);
 	}
+	if (inode->i_nlink) 
+		return;
+
 	inode->i_size = 0;
 	if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
 		affs_truncate(inode);
+
 	affs_free_block(inode->i_sb,inode->i_ino);
 	clear_inode(inode);
 }
@@ -869,11 +296,11 @@
 {
 	struct inode		*inode;
 	struct super_block	*sb;
-	int			 block;
+	s32			 block;
 
 	if (!dir || !(inode = get_empty_inode()))
 		return NULL;
-	
+
 	sb = dir->i_sb;
 	inode->i_sb    = sb;
 	inode->i_flags = sb->s_flags;
@@ -888,7 +315,6 @@
 	inode->i_dev     = sb->s_dev;
 	inode->i_uid     = current->fsuid;
 	inode->i_gid     = current->fsgid;
-	inode->i_dirt    = 1;
 	inode->i_ino     = block;
 	inode->i_op      = NULL;
 	inode->i_blocks  = 0;
@@ -897,73 +323,68 @@
 	inode->i_blksize = 0;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 
-	inode->u.affs_i.i_original     = 0;
-	inode->u.affs_i.i_parent       = dir->i_ino;
-	inode->u.affs_i.i_zone         = 0;
-	inode->u.affs_i.i_hlink        = 0;
-	inode->u.affs_i.i_pa_cnt       = 0;
-	inode->u.affs_i.i_pa_next      = 0;
-	inode->u.affs_i.i_pa_last      = 0;
-	inode->u.affs_i.i_ec           = NULL;
-	inode->u.affs_i.i_cache_users  = 0;
-	inode->u.affs_i.i_lastblock    = -1;
+	inode->u.affs_i.i_original  = 0;
+	inode->u.affs_i.i_parent    = dir->i_ino;
+	inode->u.affs_i.i_zone      = 0;
+	inode->u.affs_i.i_hlink     = 0;
+	inode->u.affs_i.i_pa_cnt    = 0;
+	inode->u.affs_i.i_pa_next   = 0;
+	inode->u.affs_i.i_pa_last   = 0;
+	inode->u.affs_i.i_ec        = NULL;
+	inode->u.affs_i.i_lastblock = -1;
 
 	insert_inode_hash(inode);
+	inode->i_dirt = 1;
 
 	return inode;
 }
 
 int
 affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
-	       const char *name, int len, int type)
+	       const unsigned char *name, int len, int type)
 {
 	struct buffer_head	*dir_bh;
 	struct buffer_head	*inode_bh;
 	struct buffer_head	*link_bh;
-	int			 hash;
+	int			 retval;
 
-	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d\n",dir->i_ino,inode->i_ino,
+	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino,
 		 len,name,type);
 
-	dir_bh      = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
-	inode_bh    = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	link_bh     = NULL;
-	if (!dir_bh || !inode_bh) {
-		affs_brelse(dir_bh);
-		affs_brelse(inode_bh);
-		return -ENOSPC;
-	}
+	if ((retval = affs_check_name(name,len)))
+		return retval;
+	if (len > 30)
+		len = 30;
+
+	dir_bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	link_bh  = NULL;
+	retval   = -EIO;
+	if (!dir_bh || !inode_bh)
+		goto addentry_done;
 	if (link) {
 		link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link));
-		if (!link_bh) {
-			affs_brelse(dir_bh);
-			affs_brelse(inode_bh);
-			return -EINVAL;
-		}
+		if (!link_bh)
+			goto addentry_done;
 	}
 	((struct dir_front *)inode_bh->b_data)->primary_type = ntohl(T_SHORT);
 	((struct dir_front *)inode_bh->b_data)->own_key      = ntohl(inode->i_ino);
-
-	if (len > 30)		/* truncate name quietly */
-		len = 30;
-	DIR_END(inode_bh->b_data,inode)->dir_name[0] = len;
+	DIR_END(inode_bh->b_data,inode)->dir_name[0]         = len;
 	strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
 	DIR_END(inode_bh->b_data,inode)->secondary_type = ntohl(type);
 	DIR_END(inode_bh->b_data,inode)->parent         = ntohl(dir->i_ino);
-	hash = affs_hash_name(name,len,AFFS_I2FSTYPE(dir),AFFS_I2HSIZE(dir));
 
 	lock_super(inode->i_sb);
-	DIR_END(inode_bh->b_data,inode)->hash_chain = 
-				((struct dir_front *)dir_bh->b_data)->hashtable[hash];
-	((struct dir_front *)dir_bh->b_data)->hashtable[hash] = ntohl(inode->i_ino);
+	retval = affs_insert_hash(dir->i_ino,inode_bh,dir);
+
 	if (link_bh) {
 		LINK_END(inode_bh->b_data,inode)->original   = ntohl(link->i_ino);
-		LINK_END(inode_bh->b_data,inode)->link_chain = 
+		LINK_END(inode_bh->b_data,inode)->link_chain =
 						FILE_END(link_bh->b_data,link)->link_chain;
 		FILE_END(link_bh->b_data,link)->link_chain   = ntohl(inode->i_ino);
 		affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
 		link->i_version = ++event;
-		link->i_dirt    = 1;
+		link->i_dirt = 1;
 		mark_buffer_dirty(link_bh,1);
 	}
 	affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
@@ -972,45 +393,15 @@
 	dir->i_mtime   = dir->i_atime = dir->i_ctime = CURRENT_TIME;
 	unlock_super(inode->i_sb);
 
-	dir->i_dirt    = 1;
-	inode->i_dirt  = 1;
+	dir->i_dirt = 1;
+	inode->i_dirt = 1;
 	mark_buffer_dirty(dir_bh,1);
 	mark_buffer_dirty(inode_bh,1);
+
+addentry_done:
 	affs_brelse(dir_bh);
 	affs_brelse(inode_bh);
 	affs_brelse(link_bh);
 
-	return 0;
+	return retval;
 }
-
-static struct file_system_type affs_fs_type = {
-	affs_read_super,
-	"affs",
-	1,
-	NULL
-};
-
-int
-init_affs_fs(void)
-{
-	return register_filesystem(&affs_fs_type);
-}
-
-#ifdef MODULE
-
-int
-init_module(void)
-{
-	int	 status;
-	if ((status = init_affs_fs()) == 0)
-		register_symtab(0);
-	return status;
-}
-
-void
-cleanup_module(void)
-{
-	unregister_filesystem(&affs_fs_type);
-}
-
-#endif
--- linux-2.0.33pl1-pre3/fs/affs/namei.c	Thu Sep  4 14:12:54 1997
+++ linux/fs/affs/namei.c	Sun Apr 19 13:05:04 1998
@@ -8,6 +8,7 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
+#define DEBUG	0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/kernel.h>
@@ -22,7 +23,7 @@
 
 /* Simple toupper() for DOS\1 */
 
-static inline unsigned int
+static unsigned int
 affs_toupper(unsigned int ch)
 {
 	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
@@ -30,7 +31,7 @@
 
 /* International toupper() for DOS\3 */
 
-static inline unsigned int
+static unsigned int
 affs_intl_toupper(unsigned int ch)
 {
 	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
@@ -43,8 +44,11 @@
  */
 
 static int
-affs_match(const char *name, int len, const char *compare, int dlen, int intl)
+affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
 {
+	unsigned int    (*toupper)(unsigned int) = intl ? affs_intl_toupper : affs_toupper;
+	int		  i;
+
 	if (!compare)
 		return 0;
 
@@ -58,26 +62,14 @@
 		return 1;
 	if (dlen != len)
 		return 0;
-	if (intl) {
-		while (dlen--) {
-			if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
-				return 0;
-			name++;
-			compare++;
-		}
-	} else {
-		while (dlen--) {
-			if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
-				return 0;
-			name++;
-			compare++;
-		}
-	}
+	for (i = 0; i < len; i++)
+		if (toupper(name[i]) != toupper(compare[i]))
+			return 0;
 	return 1;
 }
 
 int
-affs_hash_name(const char *name, int len, int intl, int hashsize)
+affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
 {
 	unsigned int i, x;
 
@@ -87,41 +79,40 @@
 	x = len;
 	for (i = 0; i < len; i++)
 		if (intl)
-			x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
+			x = (x * 13 + affs_intl_toupper(name[i])) & 0x7ff;
 		else
-			x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+			x = (x * 13 + affs_toupper(name[i])) & 0x7ff;
 
 	return x % hashsize;
 }
 
 static struct buffer_head *
-affs_find_entry(struct inode *dir, const char *name, int namelen,
-		unsigned long *ino)
+affs_find_entry(struct inode *dir, const unsigned char *name, int namelen, unsigned long *ino)
 {
-	struct buffer_head *bh;
-	int	 intl;
-	int	 key;
+	struct buffer_head	*bh;
+	int			 intl;
+	int			 key = 0;
 
-	pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
+	pr_debug("AFFS: find_entry(%.*s)\n",namelen,name);
 
 	intl = AFFS_I2FSTYPE(dir);
 	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
 	if (!bh)
-		return NULL;
+		goto findentry_out;
 
 	if (affs_match(name,namelen,".",1,intl)) {
 		*ino = dir->i_ino;
-		return bh;
+		goto findentry_out;
 	}
 	if (affs_match(name,namelen,"..",2,intl)) {
 		*ino = affs_parent_ino(dir);
-		return bh;
+		goto findentry_out;
 	}
 
 	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
 
 	for (;;) {
-		char *cname;
+		unsigned char *cname;
 		int cnamelen;
 
 		affs_brelse(bh);
@@ -138,15 +129,18 @@
 		key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
 	}
 	*ino = key;
+findentry_out:
+	pr_debug("      key=%d, bh=%p\n",key,bh);
 	return bh;
 }
 
 int
-affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+affs_lookup(struct inode *dir, const unsigned char *name, int len, struct inode **result)
 {
-	int res;
-	unsigned long ino;
-	struct buffer_head *bh;
+	int			 res;
+	unsigned long		 ino;
+	struct buffer_head	*bh;
+	struct inode		*inode;
 
 	pr_debug("AFFS: lookup(%.*s)\n",len,name);
 
@@ -157,21 +151,26 @@
 	res = -ENOENT;
 	if (S_ISDIR(dir->i_mode)) {
 		if ((bh = affs_find_entry(dir,name,len,&ino))) {
-			if (FILE_END(bh->b_data,dir)->original)
+			if (FILE_END(bh->b_data,dir)->original) {
 				ino = htonl(FILE_END(bh->b_data,dir)->original);
+				pr_debug("AFFS:   following link to ino %lu\n",ino);
+			}
+			inode = iget(dir->i_sb,ino);
 			affs_brelse(bh);
-			if ((*result = iget(dir->i_sb,ino))) 
-				res = 0;
-			else
+			if (inode) {
+				res     = 0;
+				*result = inode;
+			} else
 				res = -EACCES;
 		}
 	}
 	iput(dir);
+	pr_debug("      inode=%p\n",*result);
 	return res;
 }
 
 int
-affs_unlink(struct inode *dir, const char *name, int len)
+affs_unlink(struct inode *dir, const unsigned char *name, int len)
 {
 	int			 retval;
 	struct buffer_head	*bh;
@@ -186,27 +185,23 @@
 	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
 		goto unlink_done;
 	}
-	if (!(inode = iget(dir->i_sb,ino))) {
+	if (!(inode = iget(dir->i_sb,ino)))
 		goto unlink_done;
-	}
+
 	if (S_ISDIR(inode->i_mode)) {
 		retval = -EPERM;
 		goto unlink_done;
 	}
 
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
-		goto unlink_done;
-
-	if ((retval = affs_fixup(bh,inode)))
+	if ((retval = affs_remove_header(bh,inode)) < 0)
 		goto unlink_done;
 
-	inode->i_nlink=0;
-	inode->i_dirt=1;
+	inode->i_nlink = retval;
+	inode->i_dirt  = 1;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_version = ++event;
-	dir->i_dirt=1;
+	dir->i_dirt    = 1;
+	retval         = 0;
 unlink_done:
 	affs_brelse(bh);
 	iput(inode);
@@ -215,7 +210,7 @@
 }
 
 int
-affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+affs_create(struct inode *dir, const unsigned char *name, int len, int mode, struct inode **result)
 {
 	struct inode	*inode;
 	int		 error;
@@ -257,7 +252,7 @@
 }
 
 int
-affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+affs_mkdir(struct inode *dir, const unsigned char *name, int len, int mode)
 {
 	struct inode		*inode;
 	struct buffer_head	*bh;
@@ -310,7 +305,7 @@
 }
 
 int
-affs_rmdir(struct inode *dir, const char *name, int len)
+affs_rmdir(struct inode *dir, const unsigned char *name, int len)
 {
 	int			 retval;
 	unsigned long		 ino;
@@ -347,19 +342,14 @@
 		retval = -EBUSY;
 		goto rmdir_done;
 	}
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
-		goto rmdir_done;
-
-	if ((retval = affs_fixup(bh,inode)))
+	if ((retval = affs_remove_header(bh,inode)) < 0)
 		goto rmdir_done;
 
-	inode->i_nlink=0;
-	inode->i_dirt=1;
+	inode->i_nlink = 0;
+	inode->i_dirt  = 1;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	dir->i_version = ++event;
-	dir->i_dirt=1;
+	dir->i_dirt    = 1;
 rmdir_done:
 	iput(dir);
 	iput(inode);
@@ -368,7 +358,7 @@
 }
 
 int
-affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+affs_symlink(struct inode *dir, const unsigned char *name, int len, const unsigned char *symname)
 {
 	struct buffer_head	*bh;
 	struct inode		*inode;
@@ -451,7 +441,7 @@
 }
 
 int
-affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+affs_link(struct inode *oldinode, struct inode *dir, const unsigned char *name, int len)
 {
 	struct inode		*inode;
 	struct buffer_head	*bh;
@@ -468,6 +458,9 @@
 		return -EEXIST;
 	}
 	if (oldinode->u.affs_i.i_hlink) {
+		affs_warning(dir->i_sb,"link","Impossible link to link");
+		return -EINVAL;
+#if 0
 		i = oldinode->u.affs_i.i_original;
 		iput(oldinode);
 		oldinode = iget(dir->i_sb,i);
@@ -476,6 +469,7 @@
 			iput(dir);
 			return -ENOENT;
 		}
+#endif
 	}
 	inode = affs_new_inode(dir);
 	if (!inode) {
@@ -534,8 +528,8 @@
 /* I'm afraid this might not be race proof. Maybe next time. */
 
 int
-affs_rename(struct inode *old_dir, const char *old_name, int old_len,
-	    struct inode *new_dir, const char *new_name, int new_len,
+affs_rename(struct inode *old_dir, const unsigned char *old_name, int old_len,
+	    struct inode *new_dir, const unsigned char *new_name, int new_len,
 	    int must_be_dir)
 {
 	struct inode		*old_inode;
@@ -549,6 +543,8 @@
 	pr_debug("AFFS: rename(old=%lu,\"%*s\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
 		 new_dir->i_ino,new_len,new_name);
 	
+	if ((retval = affs_check_name(new_name,new_len)))
+		goto out_rename;
 	if (new_len > 30)
 		new_len = 30;
 	goto start_up;
@@ -580,7 +576,14 @@
 			new_bh = NULL;
 		}
 	}
-	if (new_inode == old_inode) {		/* Won't happen */
+	if (new_inode == old_inode) {
+		if (old_ino == new_ino) {	/* Filename might have changed case	*/
+			retval = new_len < 31 ? new_len : 30;
+			strncpy(DIR_END(old_bh->b_data,old_inode)->dir_name + 1,
+				new_name,retval);
+			DIR_END(old_bh->b_data,old_inode)->dir_name[0] = retval;
+			goto new_checksum;
+		}
 		retval = 0;
 		goto end_rename;
 	}
@@ -608,14 +611,9 @@
 		if (affs_parent_ino(old_inode) != old_dir->i_ino)
 			goto end_rename;
 	}
-	/* Unlink destination if existent */
+	/* Unlink destination if it already exists */
 	if (new_inode) {
-		if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
-		                                 AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
-						 new_ino,
-						 FILE_END(new_bh->b_data,new_dir)->hash_chain)))
-			goto retry;
-		if ((retval = affs_fixup(new_bh,new_inode)))
+		if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
 			goto retry;
 		mark_buffer_dirty(new_bh,1);
 		new_dir->i_version = ++event;
@@ -623,16 +621,18 @@
 		new_inode->i_nlink = 0;
 		new_inode->i_dirt  = 1;
 	}
-	retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
-				    AFFS_I2HSIZE(old_dir)) + 6,old_ino,
-				    FILE_END(old_bh->b_data,old_dir)->hash_chain);
-	if (retval)
-		goto retry;
-
-	retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
-				htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+	/* Remove header from its parent directory. */
+	if ((retval = affs_remove_hash(old_bh,old_dir)))
+		goto end_rename;
+	/* And insert it into the new directory with the new name. */
+	affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_name);
+	if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
+		goto end_rename;
 
-	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+new_checksum:
+	affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
+	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime
+			   = old_dir->i_mtime = CURRENT_TIME;
 	new_dir->i_version = ++event;
 	old_dir->i_version = ++event;
 	new_dir->i_dirt    = 1;
@@ -644,12 +644,14 @@
 	affs_brelse(new_bh);
 	iput(new_inode);
 	iput(old_inode);
+out_rename:
 	iput(old_dir);
 	iput(new_dir);
 
 	return retval;
 }
 
+#if 0
 int
 affs_fixup(struct buffer_head *bh, struct inode *inode)
 {
@@ -739,3 +741,4 @@
 		return -EBADF;
 	}
 }
+#endif
--- linux-2.0.33pl1-pre3/fs/affs/super.c	Sun Apr 19 13:21:13 1998
+++ linux/fs/affs/super.c	Sun Apr 19 11:39:48 1998
@@ -0,0 +1,706 @@
+/*
+ *  linux/fs/affs/inode.c
+ *
+ *  (c) 1996  Hans-Joachim Widmaier - Rewritten
+ *
+ *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ *  (C) 1991  Linus Torvalds - minix filesystem
+ */
+
+#define DEBUG 0
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/amigaffs.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <asm/system.h>
+
+extern int *blk_size[];
+extern struct timezone sys_tz;
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+static void affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int affs_remount(struct super_block *sb, int *flags, char *data);
+
+static void
+affs_put_super(struct super_block *sb)
+{
+	int	 i;
+
+	pr_debug("AFFS: put_super()\n");
+
+	lock_super(sb);
+	for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
+		affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(1);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+	}
+
+	if (sb->u.affs_sb.s_flags & SF_PREFIX)
+		kfree(sb->u.affs_sb.s_prefix);
+	kfree(sb->u.affs_sb.s_bitmap);
+	affs_brelse(sb->u.affs_sb.s_root_bh);
+
+	/* Restore the previous value of this device's blksize_size[][] */
+	set_blocksize(sb->s_dev,sb->u.affs_sb.s_blksize);
+
+	sb->s_dev = 0;
+	unlock_super(sb);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static void
+affs_write_super(struct super_block *sb)
+{
+	int	 i, clean = 2;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		lock_super(sb);
+		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
+			if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
+				if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
+					clean = 0;
+					break;
+				}
+			}
+		}
+		unlock_super(sb);
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(clean);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+		sb->s_dirt = !clean;	/* redo until bitmap synced */
+	} else
+		sb->s_dirt = 0;
+
+	pr_debug("AFFS: write_super() at %u, clean=%d\n", CURRENT_TIME, clean);
+}
+
+static struct super_operations affs_sops = {
+	affs_read_inode,
+	affs_notify_change,
+	affs_write_inode,
+	affs_put_inode,
+	affs_put_super,
+	affs_write_super,
+	affs_statfs,
+	affs_remount
+};
+
+static int
+parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
+		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
+{
+	char	*this_char, *value, *optn;
+	int	 f;
+
+	/* Fill in defaults */
+
+	*uid        = current->uid;
+	*gid        = current->gid;
+	*reserved   = 2;
+	*root       = -1;
+	*blocksize  = -1;
+	volume[0]   = ':';
+	volume[1]   = 0;
+	*mount_opts = 0;
+	if (!options)
+		return 1;
+	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+		f = 0;
+		if ((value = strchr(this_char,'=')) != NULL)
+			*value++ = 0;
+		if ((optn = "protect") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
+			*mount_opts |= SF_IMMUTABLE;
+		} else if ((optn = "verbose") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
+			*mount_opts |= SF_VERBOSE;
+		} else if ((optn = "mufs") && !strcmp(this_char, optn)) {
+			if (value)
+				goto out_inv_arg;
+			*mount_opts |= SF_MUFS;
+		} else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
+			if (value) {
+				if (!*value) {
+					printk("AFFS: Argument for set[ug]id option missing\n");
+					return 0;
+				} else {
+					(f ? *uid : *gid) = simple_strtoul(value,&value,0);
+					if (*value) {
+						printk("AFFS: Bad set[ug]id argument\n");
+						return 0;
+					}
+					*mount_opts |= f ? SF_SETUID : SF_SETGID;
+				}
+			}
+		} else if (!strcmp(this_char,"prefix")) {
+			optn = "prefix";
+			if (!value || !*value)
+				goto out_no_arg;
+			if (*prefix) {		/* Free any previous prefix */
+				kfree(*prefix);
+				*prefix = NULL;
+			}
+			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
+			if (!*prefix)
+				return 0;
+			strcpy(*prefix,value);
+			*mount_opts |= SF_PREFIX;
+		} else if (!strcmp(this_char,"volume")) {
+			optn = "volume";
+			if (!value || !*value)
+				goto out_no_arg;
+			if (strlen(value) > 30)
+				value[30] = 0;
+			strncpy(volume,value,30);
+		} else if (!strcmp(this_char,"mode")) {
+			optn = "mode";
+			if (!value || !*value)
+				goto out_no_arg;
+			*mode = simple_strtoul(value,&value,8) & 0777;
+			if (*value)
+				return 0;
+			*mount_opts |= SF_SETMODE;
+		} else if (!strcmp(this_char,"reserved")) {
+			optn = "reserved";
+			if (!value || !*value)
+				goto out_no_arg;
+			*reserved = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		} else if (!strcmp(this_char,"root")) {
+			optn = "root";
+			if (!value || !*value)
+				goto out_no_arg;
+			*root = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		} else if (!strcmp(this_char,"bs")) {
+			optn = "bs";
+			if (!value || !*value)
+				goto out_no_arg;
+			*blocksize = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+			if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
+			    && *blocksize != 4096) {
+				printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
+				return 0;
+			}
+		} else if (!strcmp (this_char, "grpquota")
+			 || !strcmp (this_char, "noquota")
+			 || !strcmp (this_char, "quota")
+			 || !strcmp (this_char, "usrquota"))
+			 /* Silently ignore the quota options */
+			;
+		else {
+			printk("AFFS: Unrecognized mount option %s\n", this_char);
+			return 0;
+		}
+	}
+	return 1;
+
+out_no_arg:
+	printk("AFFS: The %s option requires an argument\n", optn);
+	return 0;
+out_inv_arg:
+	printk("AFFS: Option %s does not take an argument\n", optn);
+	return 0;
+}
+
+/* This function definitely needs to be split up. Some fine day I'll
+ * hopefully have the guts to do so. Until then: sorry for the mess.
+ */
+
+static struct super_block *
+affs_read_super(struct super_block *s, void *data, int silent)
+{
+	struct buffer_head	*bh = NULL;
+	struct buffer_head	*bb;
+	struct inode		*root_inode;
+	kdev_t			 dev = s->s_dev;
+	s32			 root_block;
+	int			 blocks, size, blocksize;
+	u32			 chksum;
+	u32			*bm;
+	s32			 ptype, stype;
+	int			 mapidx;
+	int			 num_bm;
+	int			 i, j;
+	s32			 key;
+	uid_t			 uid;
+	gid_t			 gid;
+	int			 reserved;
+	int			 az_no;
+	int			 bmalt = 0;
+	unsigned long		 mount_flags;
+	unsigned long		 offset;
+
+	pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
+
+	MOD_INC_USE_COUNT;
+	lock_super(s);
+	s->s_magic             = AFFS_SUPER_MAGIC;
+	s->s_op                = &affs_sops;
+	s->u.affs_sb.s_bitmap  = NULL;
+	s->u.affs_sb.s_root_bh = NULL;
+	s->u.affs_sb.s_prefix  = NULL;
+	s->u.affs_sb.s_hashsize= 0;
+
+	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+				&blocksize,&s->u.affs_sb.s_prefix,
+				s->u.affs_sb.s_volume, &mount_flags))
+		goto out_bad_opts;
+	/* N.B. after this point s_prefix must be released */
+
+	s->u.affs_sb.s_flags   = mount_flags;
+	s->u.affs_sb.s_mode    = i;
+	s->u.affs_sb.s_uid     = uid;
+	s->u.affs_sb.s_gid     = gid;
+	s->u.affs_sb.s_reserved= reserved;
+
+	/* Get the size of the device in 512-byte blocks.
+	 * If we later see that the partition uses bigger
+	 * blocks, we will have to change it.
+	 */
+
+	blocks = blk_size[MAJOR(dev)][MINOR(dev)];
+	if (blocks == 0)
+		goto out_bad_size;
+	s->u.affs_sb.s_blksize = blksize_size[MAJOR(dev)][MINOR(dev)];
+	if (!s->u.affs_sb.s_blksize)
+		s->u.affs_sb.s_blksize = BLOCK_SIZE;
+	size = (s->u.affs_sb.s_blksize / 512) * blocks;
+	pr_debug("AFFS: initial blksize=%d, blocks=%d\n",
+		s->u.affs_sb.s_blksize, blocks);
+
+	/* Try to find root block. Its location depends on the block size. */
+
+	i = 512;
+	j = 4096;
+	if (blocksize > 0) {
+		i = j = blocksize;
+		size = size / (blocksize / 512);
+	}
+	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+		s->u.affs_sb.s_root_block = root_block;
+		if (root_block < 0)
+			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+		pr_debug("AFFS: setting blocksize to %d\n", blocksize);
+		set_blocksize(dev, blocksize);
+
+		/* The root block location that was calculated above is not
+		 * correct if the partition size is an odd number of 512-
+		 * byte blocks, which will be rounded down to a number of
+		 * 1024-byte blocks, and if there were an even number of
+		 * reserved blocks. Ideally, all partition checkers should
+		 * report the real number of blocks of the real blocksize,
+		 * but since this just cannot be done, we have to try to
+		 * find the root block anyways. In the above case, it is one
+		 * block behind the calculated one. So we check this one, too.
+		 */
+		for (num_bm = 0; num_bm < 2; num_bm++) {
+			pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
+				"size=%d, reserved=%d\n",
+				kdevname(dev),
+				s->u.affs_sb.s_root_block + num_bm,
+				blocksize, size, reserved);
+			bh = affs_bread(dev, s->u.affs_sb.s_root_block + num_bm,
+					blocksize);
+			if (!bh)
+				continue;
+			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+			    ptype == T_SHORT && stype == ST_ROOT) {
+				s->s_blocksize             = blocksize;
+				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
+				s->u.affs_sb.s_root_block += num_bm;
+				key                        = 1;
+				goto got_root;
+			}
+			affs_brelse(bh);
+			bh = NULL;
+		}
+	}
+	goto out_no_valid_block;
+
+	/* N.B. after this point bh must be released */
+got_root:
+	root_block = s->u.affs_sb.s_root_block;
+
+	s->u.affs_sb.s_partition_size   = size;
+	s->s_blocksize_bits             = blocksize == 512 ? 9 :
+					  blocksize == 1024 ? 10 :
+					  blocksize == 2048 ? 11 : 12;
+
+	/* Find out which kind of FS we have */
+	bb = affs_bread(dev,0,s->s_blocksize);
+	if (!bb)
+		goto out_no_root_block;
+	chksum = htonl(*(u32 *)bb->b_data);
+	affs_brelse(bb);
+
+	/* Dircache filesystems are compatible with non-dircache ones
+	 * when reading. As long as they aren't supported, writing is
+	 * not recommended.
+	 */
+	if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+	     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+		printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+			kdevname(dev));
+		s->s_flags |= MS_RDONLY;
+		s->u.affs_sb.s_flags |= SF_READONLY;
+	}
+	switch (chksum) {
+		case MUFS_FS:
+		case MUFS_INTLFFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			/* fall thru */
+		case FS_INTLFFS:
+			s->u.affs_sb.s_flags |= SF_INTL;
+			break;
+		case MUFS_DCFFS:
+		case MUFS_FFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			break;
+		case FS_DCFFS:
+		case FS_FFS:
+			break;
+		case MUFS_OFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+			/* fall thru */
+		case FS_OFS:
+			s->u.affs_sb.s_flags |= SF_OFS;
+			break;
+		case MUFS_DCOFS:
+		case MUFS_INTLOFS:
+			s->u.affs_sb.s_flags |= SF_MUFS;
+		case FS_DCOFS:
+		case FS_INTLOFS:
+			s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+			break;
+		default:
+			goto out_unknown_fs;
+	}
+
+	if (mount_flags & SF_VERBOSE) {
+		chksum = ntohl(chksum);
+		printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
+			GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
+			&GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+			(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
+	}
+
+	s->s_flags |= MS_NODEV | MS_NOSUID;
+
+	/* Keep super block in cache */
+	bb = affs_bread(dev,root_block,s->s_blocksize);
+	if (!bb)
+		goto out_no_root_block;
+	s->u.affs_sb.s_root_bh = bb;
+	/* N.B. after this point s_root_bh must be released */
+
+	/* Allocate space for bitmaps, zones and others */
+
+	size   = s->u.affs_sb.s_partition_size - reserved;
+	num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
+	az_no  = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
+	ptype  = num_bm * sizeof(struct affs_bm_info) +
+		 az_no * sizeof(struct affs_alloc_zone) +
+		 MAX_ZONES * sizeof(struct affs_zone);
+	pr_debug("AFFS: num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
+	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype, GFP_KERNEL)))
+		goto out_no_bitmap;
+	memset(s->u.affs_sb.s_bitmap,0,ptype);
+	/* N.B. after the point s_bitmap must be released */
+
+	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
+	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
+	s->u.affs_sb.s_num_az  = az_no;
+
+	mapidx = 0;
+
+	if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
+		if (!(s->s_flags & MS_RDONLY)) {
+			printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
+				kdevname(dev));
+			s->s_flags |= MS_RDONLY;
+		}
+		affs_brelse(bh);
+		bh = NULL;
+		goto nobitmap;
+	}
+
+	/* The following section is ugly, I know. Especially because of the
+	 * reuse of some variables that are not named properly.
+	 */
+
+	key    = root_block;
+	ptype  = s->s_blocksize / 4 - 49;
+	stype  = ptype + 25;
+	offset = s->u.affs_sb.s_reserved;
+	az_no  = 0;
+	while (bh) {
+		bm = (u32 *)bh->b_data;
+		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
+			if (mapidx >= num_bm) {
+				printk(KERN_ERR "AFFS: Extraneous bitmap pointer - "
+					       "mounting %s read only.\n",kdevname(dev));
+				s->s_flags |= MS_RDONLY;
+				s->u.affs_sb.s_flags |= SF_READONLY;
+				continue;
+			}
+			bb = affs_bread(dev,htonl(bm[i]),s->s_blocksize);
+			if (!bb)
+				goto out_no_read_bm;
+			if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+			    !(s->s_flags & MS_RDONLY)) {
+				printk(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
+				       "mounting %s read only.\n",mapidx,htonl(bm[i]),
+					kdevname(dev));
+				s->s_flags |= MS_RDONLY;
+				s->u.affs_sb.s_flags |= SF_READONLY;
+			}
+			/* Mark unused bits in the last word as allocated */
+			if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
+				ptype = size / 32 + 1;		/* word number */
+				key   = size & 0x1F;		/* used bits */
+				if (key && !(s->s_flags & MS_RDONLY)) {
+					chksum = ntohl(0x7FFFFFFF >> (31 - key));
+					((u32 *)bb->b_data)[ptype] &= chksum;
+					affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+					mark_buffer_dirty(bb,1);
+					bmalt = 1;
+				}
+				ptype = (size + 31) & ~0x1F;
+				size  = 0;
+				s->u.affs_sb.s_flags |= SF_BM_VALID;
+			} else {
+				ptype = s->s_blocksize * 8 - 32;
+				size -= ptype;
+			}
+			s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+			s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
+			s->u.affs_sb.s_bitmap[mapidx].bm_key      = htonl(bm[i]);
+			s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
+			offset += ptype;
+
+			for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
+				key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
+				s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
+				s->u.affs_sb.s_alloc[az_no].az_free =
+					affs_count_free_bits(key / 8,bb->b_data +
+					     j * (AFFS_ZONE_SIZE / 8) + 4);
+			}
+			affs_brelse(bb);
+		}
+		key   = htonl(bm[stype]);		/* Next block of bitmap pointers	*/
+		ptype = 0;
+		stype = s->s_blocksize / 4 - 1;
+		affs_brelse(bh);
+		bh = NULL;
+		if (key) {
+			bh = affs_bread(dev,key,s->s_blocksize);
+			if (!bh)
+				goto out_no_bm_ext;
+		}
+	}
+	if (mapidx < num_bm)
+		goto out_bad_num;
+
+nobitmap:
+	s->u.affs_sb.s_bm_count = num_bm;
+
+	/* set up enough so that it can read an inode */
+
+	s->s_dirt  = 1;
+	root_inode = iget(s,root_block);
+	if (!root_inode)
+		goto out_no_root;
+	s->s_mounted = root_inode;
+	s->s_op      = &affs_sops;
+
+	unlock_super(s);
+	/* Record date of last change if the bitmap was truncated and
+	 * create data zones if the volume is writable.
+	 */
+
+	if (!(s->s_flags & MS_RDONLY)) {
+		if (bmalt) {
+			secs_to_datestamp(CURRENT_TIME,&ROOT_END(
+				s->u.affs_sb.s_root_bh->b_data,root_inode)->disk_altered);
+			affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
+			mark_buffer_dirty(s->u.affs_sb.s_root_bh,1);
+		}
+		affs_make_zones(s);
+	}
+
+	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
+	return s;
+
+out_bad_opts:
+	printk(KERN_ERR "AFFS: Error parsing options\n");
+	goto out_fail;
+out_bad_size:
+	printk(KERN_ERR "AFFS: Could not determine device size\n");
+	goto out_free_prefix;
+out_no_valid_block:
+	if (!silent)
+		printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+			kdevname(dev));
+	goto out_restore;
+out_unknown_fs:
+	printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+		kdevname(dev), chksum);
+	goto out_free_bh;
+out_no_root_block:
+	printk(KERN_ERR "AFFS: Cannot read root block\n");
+	goto out_free_bh;
+out_no_bitmap:
+	printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+	goto out_free_root_block;
+out_no_read_bm:
+	printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+	goto out_free_bitmap;
+out_no_bm_ext:
+	printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+	goto out_free_bitmap;
+out_bad_num:
+	printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",
+		mapidx, num_bm);
+	goto out_free_bitmap;
+out_no_root:
+	printk(KERN_ERR "AFFS: get root inode failed\n");
+
+	/*
+	 * Begin the cascaded cleanup ...
+	 */
+	iput(root_inode);
+out_free_bitmap:
+	kfree(s->u.affs_sb.s_bitmap);
+out_free_root_block:
+	affs_brelse(s->u.affs_sb.s_root_bh);
+out_free_bh:
+	affs_brelse(bh);
+out_restore:
+	set_blocksize(dev, s->u.affs_sb.s_blksize);
+out_free_prefix:
+	if (s->u.affs_sb.s_prefix)
+		kfree(s->u.affs_sb.s_prefix);
+out_fail:
+	s->s_dev = 0;
+	unlock_super(s);
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+static int
+affs_remount(struct super_block *sb, int *flags, char *data)
+{
+	int			 blocksize;
+	uid_t			 uid;
+	gid_t			 gid;
+	int			 mode;
+	int			 reserved;
+	int			 root_block;
+	unsigned long		 mount_flags;
+	unsigned long		 read_only = sb->u.affs_sb.s_flags & SF_READONLY;
+
+	pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
+
+	if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
+	    &blocksize,&sb->u.affs_sb.s_prefix,sb->u.affs_sb.s_volume,&mount_flags))
+		return -EINVAL;
+	sb->u.affs_sb.s_flags = mount_flags | read_only;
+	sb->u.affs_sb.s_mode  = mode;
+	sb->u.affs_sb.s_uid   = uid;
+	sb->u.affs_sb.s_gid   = gid;
+
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+	if (*flags & MS_RDONLY) {
+		sb->s_dirt = 1;
+		while (sb->s_dirt)
+			affs_write_super(sb);
+		sb->s_flags |= MS_RDONLY;
+	} else if (!(sb->u.affs_sb.s_flags & SF_READONLY)) {
+		sb->s_flags &= ~MS_RDONLY;
+		affs_make_zones(sb);
+	} else {
+		affs_warning(sb,"remount","Cannot remount fs read/write because of errors");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void
+affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	int		 free;
+	struct statfs	 tmp;
+
+	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
+	     sb->u.affs_sb.s_reserved);
+
+	free          = affs_count_free_blocks(sb);
+	tmp.f_type    = AFFS_SUPER_MAGIC;
+	tmp.f_bsize   = sb->s_blocksize;
+	tmp.f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+	tmp.f_bfree   = free;
+	tmp.f_bavail  = free;
+	tmp.f_files   = 0;
+	tmp.f_ffree   = 0;
+	memcpy_tofs(buf,&tmp,bufsiz);
+}
+
+static struct file_system_type affs_fs_type = {
+	affs_read_super,
+	"affs",
+	1,
+	NULL
+};
+
+int
+init_affs_fs(void)
+{
+	return register_filesystem(&affs_fs_type);
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	return register_filesystem(&affs_fs_type);
+}
+
+void
+cleanup_module(void)
+{
+	unregister_filesystem(&affs_fs_type);
+}
+
+#endif
--- linux-2.0.33pl1-pre3/fs/affs/symlink.c	Thu Sep  4 14:12:55 1997
+++ linux/fs/affs/symlink.c	Sun Apr 19 11:40:00 1998
@@ -8,6 +8,7 @@
  *  affs symlink handling code
  */
 
+#define DEBUG	0
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
--- linux-2.0.33pl1-pre3/include/linux/affs_fs.h	Thu Sep  4 14:13:19 1997
+++ linux/include/linux/affs_fs.h	Sat Apr 18 17:23:09 1998
@@ -22,9 +22,9 @@
 
 struct DateStamp
 {
-  __u32 ds_Days;
-  __u32 ds_Minute;
-  __u32 ds_Tick;
+  u32 ds_Days;
+  u32 ds_Minute;
+  u32 ds_Tick;
 };
 
 
@@ -32,18 +32,22 @@
 
 /* amigaffs.c */
 
-extern int		   affs_get_key_entry(int bsize, void *data, int entry_pos);
-extern int		   affs_find_next_hash_entry(int bsize, void *dir_data, int *hash_pos);
-extern int		   affs_get_file_name(int bsize, void *fh_data, char **name);
-extern unsigned int	   affs_checksum_block(int bsize, void *data, int *ptype, int *stype);
-extern void		   affs_fix_checksum(int bsize, void *data, int cspos);
-extern void		   secs_to_datestamp(int secs, struct DateStamp *ds);
-extern int		   prot_to_mode(unsigned int prot);
-extern unsigned int	   mode_to_prot(int mode);
-extern int		   affs_fix_hash_pred(struct inode *startino, int startoffset,
-		 			      int key, int newkey);
-extern int		   affs_fix_link_pred(struct inode *startino, int key, int newkey);
-
+extern int	affs_get_key_entry(int bsize, void *data, int entry_pos);
+extern int	affs_get_file_name(int bsize, void *fh_data, unsigned char **name);
+extern u32	affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype);
+extern void	affs_fix_checksum(int bsize, void *data, int cspos);
+extern void	secs_to_datestamp(time_t secs, struct DateStamp *ds);
+extern int	prot_to_mode(unsigned int prot);
+extern u32	mode_to_prot(int mode);
+extern int	affs_insert_hash(unsigned long dir_ino, struct buffer_head *header,
+			struct inode *inode);
+extern int	affs_remove_hash(struct buffer_head *bh, struct inode *inode);
+extern int	affs_remove_link(struct buffer_head *bh, struct inode *inode);
+extern int	affs_remove_header(struct buffer_head *bh, struct inode *inode);
+extern void	affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
+extern void	affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
+extern int	affs_check_name(const unsigned char *name, int len);
+extern int	affs_copy_name(unsigned char *bstr, const unsigned char *name);
 /* bitmap. c */
 
 extern int		   affs_count_free_blocks(struct super_block *s);
@@ -55,45 +59,40 @@
 
 /* namei.c */
 
-extern int		   affs_hash_name(const char *name, int len, int intl, int hashsize);
-extern int		   affs_lookup(struct inode *dir,const char *name, int len,
+extern int		   affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
+extern int		   affs_lookup(struct inode *dir,const unsigned char *name, int len,
 				       struct inode **result);
-extern int		   affs_unlink(struct inode *dir, const char *name, int len);
-extern int		   affs_create(struct inode *dir, const char *name, int len, int mode,
+extern int		   affs_unlink(struct inode *dir, const unsigned char *name, int len);
+extern int		   affs_create(struct inode *dir, const unsigned char *name, int len, int mode,
 				       struct inode **result);
-extern int		   affs_mkdir(struct inode *dir, const char *name, int len, int mode);
-extern int		   affs_rmdir(struct inode *dir, const char *name, int len);
+extern int		   affs_mkdir(struct inode *dir, const unsigned char *name, int len, int mode);
+extern int		   affs_rmdir(struct inode *dir, const unsigned char *name, int len);
 extern int		   affs_link(struct inode *oldinode, struct inode *dir,
-				     const char *name, int len);
-extern int		   affs_symlink(struct inode *dir, const char *name, int len,
-				        const char *symname);
+				     const unsigned char *name, int len);
+extern int		   affs_symlink(struct inode *dir, const unsigned char *name, int len,
+				        const unsigned char *symname);
 extern int		   affs_fixup(struct buffer_head *bh, struct inode *inode);
-extern int		   affs_rename(struct inode *old_dir, const char *old_name, int old_len,
-				       struct inode *new_dir, const char *new_name, int new_len,
+extern int		   affs_rename(struct inode *old_dir, const unsigned char *old_name, int old_len,
+				       struct inode *new_dir, const unsigned char *new_name, int new_len,
 				       int must_be_dir);
 
 /* inode.c */
 
 extern struct buffer_head *affs_bread(kdev_t dev, int block, int size);
 extern void		   affs_brelse(struct buffer_head *buf);
-extern void		   affs_put_super(struct super_block *);
-extern int		   affs_parent_ino(struct inode *dir);
-extern struct super_block *affs_read_super(struct super_block *,void *, int);
-extern void		   affs_statfs(struct super_block *, struct statfs *, int bufsiz);
+extern unsigned long	   affs_parent_ino(struct inode *dir);
 extern void		   affs_read_inode(struct inode *);
 extern void		   affs_write_inode(struct inode *);
 extern int		   affs_notify_change(struct inode *inode, struct iattr *attr);
 extern void		   affs_put_inode(struct inode *);
 extern struct inode	  *affs_new_inode(const struct inode *dir);
 extern int		   affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
-					  const char *name, int len, int type);
+					  const unsigned char *name, int len, int type);
 
 /* file.c */
 
-extern int		   affs_bmap(struct inode *inode, int block);
-extern struct buffer_head *affs_getblock(struct inode *inode, int block);
 extern void		   affs_truncate(struct inode *);
-extern void		   affs_truncate_ofs(struct inode *);
+extern void		   affs_free_prealloc(struct inode *inode);
 
 /* dir.c */
 
--- linux-2.0.33pl1-pre3/include/linux/affs_fs_i.h	Thu Sep  4 14:13:20 1997
+++ linux/include/linux/affs_fs_i.h	Sat Apr 18 11:53:11 1998
@@ -9,19 +9,19 @@
 
 struct key_cache {
 	struct timeval	 kc_lru_time;	/* Last time this cache was used */
-	int	 kc_first;		/* First cached key */
-	int	 kc_last;		/* Last cached key */
-	int	 kc_this_key;		/* Key of extension block this data block keys are from */
+	s32	 kc_first;		/* First cached key */
+	s32	 kc_last;		/* Last cached key */
+	s32	 kc_this_key;		/* Key of extension block this data block keys are from */
 	int	 kc_this_seq;		/* Sequence number of this extension block */
-	int	 kc_next_key;		/* Key of next extension block */
-	int	 kc_keys[AFFS_KCSIZE];	/* Key cache */
+	s32	 kc_next_key;		/* Key of next extension block */
+	s32	 kc_keys[AFFS_KCSIZE];	/* Key cache */
 };
 
 #define EC_SIZE	(PAGE_SIZE - 4 * sizeof(struct key_cache) - 4) / 4
 
 struct ext_cache {
 	struct key_cache  kc[4];	/* The 4 key caches */
-	__s32	 ec[EC_SIZE];		/* Keys of assorted extension blocks */
+	s32	 ec[EC_SIZE];		/* Keys of assorted extension blocks */
 	int	 max_ext;		/* Index of last known extension block */
 };
 
@@ -29,18 +29,18 @@
  * affs fs inode data in memory
  */
 struct affs_inode_info {
-	__u32	 i_protect;		/* unused attribute bits */
-	__s32	 i_parent;		/* parent ino */
-	__s32	 i_original;		/* if != 0, this is the key of the original */
-	__s32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
-	struct ext_cache *i_ec;		/* Cache gets allocated dynamically */
-	int	 i_cache_users;		/* Cache cannot be freed while > 0 */
-	int	 i_lastblock;		/* last allocated block */
-	short	 i_pa_cnt;		/* number of preallocated blocks */
-	short	 i_pa_next;		/* Index of next block in i_data[] */
-	short	 i_pa_last;		/* Index of next free slot in i_data[] */
-	short	 i_zone;		/* write zone */
-	unsigned char i_hlink;		/* This is a fake */
+	u32	 i_protect;			/* unused attribute bits */
+	s32	 i_parent;			/* parent ino */
+	s32	 i_original;			/* if != 0, this is the key of the original */
+	s32	 i_data[AFFS_MAX_PREALLOC];	/* preallocated blocks */
+	struct ext_cache *i_ec;			/* Cache gets allocated dynamically */
+	int	 i_cache_users;			/* Cache cannot be freed while > 0 */
+	int	 i_lastblock;			/* last allocated block */
+	short	 i_pa_cnt;			/* number of preallocated blocks */
+	short	 i_pa_next;			/* Index of next block in i_data[] */
+	short	 i_pa_last;			/* Index of next free slot in i_data[] */
+	short	 i_zone;			/* write zone */
+	unsigned char i_hlink;			/* This is a fake */
 	unsigned char i_pad;
 };
 
--- linux-2.0.33pl1-pre3/include/linux/affs_fs_sb.h	Thu Sep  4 14:13:21 1997
+++ linux/include/linux/affs_fs_sb.h	Sat Apr 18 11:52:09 1998
@@ -15,8 +15,8 @@
 
 struct affs_bm_info {
 	struct buffer_head *bm_bh;	/* Buffer head if loaded (bm_count > 0) */
-	int bm_firstblk;		/* Block number of first bit in this map */
-	int bm_key;			/* Disk block number */
+	s32 bm_firstblk;		/* Block number of first bit in this map */
+	s32 bm_key;			/* Disk block number */
 	int bm_count;			/* Usage counter */
 };
 
@@ -37,11 +37,12 @@
 
 struct affs_sb_info {
 	int s_partition_size;		/* Partition size in blocks. */
-	int s_root_block;		/* FFS root block number. */
+	int s_blksize;			/* Initial device blksize */
+	s32 s_root_block;		/* FFS root block number. */
 	int s_hashsize;			/* Size of hash table. */
 	unsigned long s_flags;		/* See below. */
-	short s_uid;			/* uid to override */
-	short s_gid;			/* gid to override */
+	s16 s_uid;			/* uid to override */
+	s16 s_gid;			/* gid to override */
 	umode_t s_mode;			/* mode to override */
 	int s_reserved;			/* Number of reserved blocks. */
 	struct buffer_head *s_root_bh;	/* Cached root block. */
@@ -68,5 +69,6 @@
 #define SF_OFS		0x0200		/* Old filesystem */
 #define SF_PREFIX	0x0400		/* Buffer for prefix is allocated */
 #define SF_VERBOSE	0x0800		/* Talk about fs when mounting */
+#define SF_READONLY	0x1000		/* Don't allow to remount rw */
 
 #endif
--- linux-2.0.33pl1-pre3/include/linux/affs_hardblocks.h	Thu Sep  4 14:13:21 1997
+++ linux/include/linux/affs_hardblocks.h	Sat Apr 18 11:53:41 1998
@@ -4,59 +4,59 @@
 /* Just the needed definitions for the RDB of an Amiga HD. */
 
 struct RigidDiskBlock {
-	__u32	rdb_ID;
-	__u32	rdb_SummedLongs;
-	__s32	rdb_ChkSum;
-	__u32	rdb_HostID;
-	__u32	rdb_BlockBytes;
-	__u32	rdb_Flags;
-	__u32	rdb_BadBlockList;
-	__u32	rdb_PartitionList;
-	__u32	rdb_FileSysHeaderList;
-	__u32	rdb_DriveInit;
-	__u32	rdb_Reserved1[6];
-	__u32	rdb_Cylinders;
-	__u32	rdb_Sectors;
-	__u32	rdb_Heads;
-	__u32	rdb_Interleave;
-	__u32	rdb_Park;
-	__u32	rdb_Reserved2[3];
-	__u32	rdb_WritePreComp;
-	__u32	rdb_ReducedWrite;
-	__u32	rdb_StepRate;
-	__u32	rdb_Reserved3[5];
-	__u32	rdb_RDBBlocksLo;
-	__u32	rdb_RDBBlocksHi;
-	__u32	rdb_LoCylinder;
-	__u32	rdb_HiCylinder;
-	__u32	rdb_CylBlocks;
-	__u32	rdb_AutoParkSeconds;
-	__u32	rdb_HighRDSKBlock;
-	__u32	rdb_Reserved4;
+	u32	rdb_ID;
+	u32	rdb_SummedLongs;
+	s32	rdb_ChkSum;
+	u32	rdb_HostID;
+	u32	rdb_BlockBytes;
+	u32	rdb_Flags;
+	u32	rdb_BadBlockList;
+	u32	rdb_PartitionList;
+	u32	rdb_FileSysHeaderList;
+	u32	rdb_DriveInit;
+	u32	rdb_Reserved1[6];
+	u32	rdb_Cylinders;
+	u32	rdb_Sectors;
+	u32	rdb_Heads;
+	u32	rdb_Interleave;
+	u32	rdb_Park;
+	u32	rdb_Reserved2[3];
+	u32	rdb_WritePreComp;
+	u32	rdb_ReducedWrite;
+	u32	rdb_StepRate;
+	u32	rdb_Reserved3[5];
+	u32	rdb_RDBBlocksLo;
+	u32	rdb_RDBBlocksHi;
+	u32	rdb_LoCylinder;
+	u32	rdb_HiCylinder;
+	u32	rdb_CylBlocks;
+	u32	rdb_AutoParkSeconds;
+	u32	rdb_HighRDSKBlock;
+	u32	rdb_Reserved4;
 	char	rdb_DiskVendor[8];
 	char	rdb_DiskProduct[16];
 	char	rdb_DiskRevision[4];
 	char	rdb_ControllerVendor[8];
 	char	rdb_ControllerProduct[16];
 	char	rdb_ControllerRevision[4];
-	__u32	rdb_Reserved5[10];
+	u32	rdb_Reserved5[10];
 };
 
 #define	IDNAME_RIGIDDISK	0x5244534B	/* "RDSK" */
 
 struct PartitionBlock {
-	__u32	pb_ID;
-	__u32	pb_SummedLongs;
-	__s32	pb_ChkSum;
-	__u32	pb_HostID;
-	__u32	pb_Next;
-	__u32	pb_Flags;
-	__u32	pb_Reserved1[2];
-	__u32	pb_DevFlags;
-	__u8	pb_DriveName[32];
-	__u32	pb_Reserved2[15];
-	__u32	pb_Environment[17];
-	__u32	pb_EReserved[15];
+	u32	pb_ID;
+	u32	pb_SummedLongs;
+	s32	pb_ChkSum;
+	u32	pb_HostID;
+	u32	pb_Next;
+	u32	pb_Flags;
+	u32	pb_Reserved1[2];
+	u32	pb_DevFlags;
+	u8	pb_DriveName[32];
+	u32	pb_Reserved2[15];
+	u32	pb_Environment[17];
+	u32	pb_EReserved[15];
 };
 
 #define	IDNAME_PARTITION	0x50415254	/* "PART" */
--- linux-2.0.33pl1-pre3/include/linux/amigaffs.h	Thu Sep  4 14:13:22 1997
+++ linux/include/linux/amigaffs.h	Sun Apr 19 11:26:51 1998
@@ -4,6 +4,14 @@
 #include <asm/byteorder.h>
 #include <linux/types.h>
 
+/* AmigaOS allows file names with up to 30 characters length.
+ * Names longer than that will be silently truncated. If you
+ * want to disallow this, comment out the following #define.
+ * Creating filesystem objects with longer names will then
+ * result in an error (ENAMETOOLONG).
+ */
+/*#define AFFS_NO_TRUNCATE */
+
 /* Ugly macros make the code more pretty. */
 
 #define GET_END_PTR(st,p,sz)		 ((st *)((char *)(p)+((sz)-sizeof(st))))
@@ -16,6 +24,7 @@
 #define LINK_END(p,i)	GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
 #define ROOT_END_S(p,s)	GET_END_PTR(struct root_end,p,(s)->s_blocksize)
 #define DATA_FRONT(bh)	((struct data_front *)(bh)->b_data)
+#define DIR_FRONT(bh)	((struct dir_front *)(bh)->b_data)
 
 /* Only for easier debugging if need be */
 #define affs_bread	bread
@@ -56,131 +65,131 @@
 
 struct root_front
 {
-  __s32 primary_type;
-  __s32 spare1[2];
-  __s32 hash_size;
-  __s32 spare2;
-  __u32 checksum;
-  __s32 hashtable[0];
+  s32 primary_type;
+  s32 spare1[2];
+  s32 hash_size;
+  s32 spare2;
+  u32 checksum;
+  s32 hashtable[0];
 };
 
 struct root_end
 {
-  __s32 bm_flag;
-  __s32 bm_keys[25];
-  __s32 bm_extend;
+  s32 bm_flag;
+  s32 bm_keys[25];
+  s32 bm_extend;
   struct DateStamp dir_altered;
-  __u8 disk_name[40];
+  u8 disk_name[40];
   struct DateStamp disk_altered;
   struct DateStamp disk_made;
-  __s32 spare1[3];
-  __s32 secondary_type;
+  s32 spare1[3];
+  s32 secondary_type;
 };
 
 struct dir_front
 {
-  __s32 primary_type;
-  __s32 own_key;
-  __s32 spare1[3];
-  __u32 checksum;
-  __s32 hashtable[0];
+  s32 primary_type;
+  s32 own_key;
+  s32 spare1[3];
+  u32 checksum;
+  s32 hashtable[0];
 };
 
 struct dir_end
 {
-  __s32 spare1;
-  __s16 owner_uid;
-  __s16 owner_gid;
-  __u32 protect;
-  __s32 spare2;
-  __u8 comment[92];
+  s32 spare1;
+  s16 owner_uid;
+  s16 owner_gid;
+  u32 protect;
+  s32 spare2;
+  u8 comment[92];
   struct DateStamp created;
-  __u8 dir_name[32];
-  __s32 spare3[2];
-  __s32 link_chain;
-  __s32 spare4[5];
-  __s32 hash_chain;
-  __s32 parent;
-  __s32 spare5;
-  __s32 secondary_type;
+  u8 dir_name[32];
+  s32 spare3[2];
+  s32 link_chain;
+  s32 spare4[5];
+  s32 hash_chain;
+  s32 parent;
+  s32 spare5;
+  s32 secondary_type;
 };
 
 struct file_front
 {
-  __s32 primary_type;
-  __s32 own_key;
-  __s32 block_count;
-  __s32 unknown1;
-  __s32 first_data;
-  __u32 checksum;
-  __s32 blocks[0];
+  s32 primary_type;
+  s32 own_key;
+  s32 block_count;
+  s32 unknown1;
+  s32 first_data;
+  u32 checksum;
+  s32 blocks[0];
 };
 
 struct file_end
 {
-  __s32 spare1;
-  __s16 owner_uid;
-  __s16 owner_gid;
-  __u32 protect;
-  __s32 byte_size;
-  __u8 comment[92];
+  s32 spare1;
+  s16 owner_uid;
+  s16 owner_gid;
+  u32 protect;
+  s32 byte_size;
+  u8 comment[92];
   struct DateStamp created;
-  __u8 file_name[32];
-  __s32 spare2;
-  __s32 original;	/* not really in file_end */
-  __s32 link_chain;
-  __s32 spare3[5];
-  __s32 hash_chain;
-  __s32 parent;
-  __s32 extension;
-  __s32 secondary_type;
+  u8 file_name[32];
+  s32 spare2;
+  s32 original;	/* not really in file_end */
+  s32 link_chain;
+  s32 spare3[5];
+  s32 hash_chain;
+  s32 parent;
+  s32 extension;
+  s32 secondary_type;
 };
 
 struct hlink_front
 {
-  __s32 primary_type;
-  __s32 own_key;
-  __s32 spare1[3];
-  __u32 checksum;
+  s32 primary_type;
+  s32 own_key;
+  s32 spare1[3];
+  u32 checksum;
 };
 
 struct hlink_end
 {
-  __s32 spare1;
-  __s16 owner_uid;
-  __s16 owner_gid;
-  __u32 protect;
-  __u8 comment[92];
+  s32 spare1;
+  s16 owner_uid;
+  s16 owner_gid;
+  u32 protect;
+  u8 comment[92];
   struct DateStamp created;
-  __u8 link_name[32];
-  __s32 spare2;
-  __s32 original;
-  __s32 link_chain;
-  __s32 spare3[5];
-  __s32 hash_chain;
-  __s32 parent;
-  __s32 spare4;
-  __s32 secondary_type;
+  u8 link_name[32];
+  s32 spare2;
+  s32 original;
+  s32 link_chain;
+  s32 spare3[5];
+  s32 hash_chain;
+  s32 parent;
+  s32 spare4;
+  s32 secondary_type;
 };
 
 struct slink_front
 {
-  __s32 primary_type;
-  __s32 own_key;
-  __s32 spare1[3];
-  __s32 checksum;
-  __u8	symname[288];	/* depends on block size */
+  s32 primary_type;
+  s32 own_key;
+  s32 spare1[3];
+  s32 checksum;
+  u8	symname[288];	/* depends on block size */
 };
 
 struct data_front
 {
-  __s32 primary_type;
-  __s32 header_key;
-  __s32 sequence_number;
-  __s32 data_size;
-  __s32 next_data;
-  __s32 checksum;
-  __u8 data[488];	/* depends on block size */
+  s32 primary_type;
+  s32 header_key;
+  s32 sequence_number;
+  s32 data_size;
+  s32 next_data;
+  s32 checksum;
+  u8 data[488];	/* depends on block size */
 };
 
 /* Permission bits */
@@ -206,14 +215,14 @@
 
 #define AFFS_UMAYWRITE(prot)	(((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
 #define AFFS_UMAYREAD(prot)	((prot) & FIBF_READ)
-#define AFFS_UMAYEXECUTE(prot)	(((prot) & (FIBF_SCRIPT|FIBF_READ)) == (FIBF_SCRIPT|FIBF_READ))
+#define AFFS_UMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 #define AFFS_GMAYWRITE(prot)	(((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
 							(FIBF_GRP_WRITE|FIBF_GRP_DELETE))
 #define AFFS_GMAYREAD(prot)	((prot) & FIBF_GRP_READ)
-#define AFFS_GMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_GRP_READ))==(FIBF_SCRIPT|FIBF_GRP_READ))
+#define AFFS_GMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 #define AFFS_OMAYWRITE(prot)	(((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
 							(FIBF_OTR_WRITE|FIBF_OTR_DELETE))
 #define AFFS_OMAYREAD(prot)	((prot) & FIBF_OTR_READ)
-#define AFFS_OMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_OTR_READ))==(FIBF_SCRIPT|FIBF_OTR_READ))
+#define AFFS_OMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 
 #endif
