<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">
From: Marek Szyprowski &lt;march@staszic.waw.pl&gt;

Amiga SFS is native filesystem on MorphOS, OS that can be installed on Pegasos
/ Open Desktop Workstation (http://www.pegasosppc.com/).  This fs is also
widely used on all Amiga systems.  It becomes even more popular than the old
AFFS.

This fs has been included in debian kernel tree for over a year.

Signed-off-by: Andrew Morton &lt;akpm@osdl.org&gt;
---

 Documentation/filesystems/00-INDEX |    2 
 Documentation/filesystems/asfs.txt |  161 +++++++
 fs/Kconfig                         |   47 ++
 fs/Makefile                        |    1 
 fs/asfs/Changes                    |  112 +++++
 fs/asfs/Makefile                   |    8 
 fs/asfs/adminspace.c               |  446 +++++++++++++++++++++
 fs/asfs/asfs_fs.h                  |  234 +++++++++++
 fs/asfs/bitfuncs.c                 |  171 ++++++++
 fs/asfs/bitfuncs.h                 |   59 ++
 fs/asfs/dir.c                      |  240 +++++++++++
 fs/asfs/extents.c                  |  586 ++++++++++++++++++++++++++++
 fs/asfs/file.c                     |  251 ++++++++++++
 fs/asfs/inode.c                    |  426 ++++++++++++++++++++
 fs/asfs/namei.c                    |  197 +++++++++
 fs/asfs/nodes.c                    |  455 ++++++++++++++++++++++
 fs/asfs/objects.c                  |  765 +++++++++++++++++++++++++++++++++++++
 fs/asfs/super.c                    |  488 +++++++++++++++++++++++
 fs/asfs/symlink.c                  |  235 +++++++++++
 include/linux/amigasfs.h           |  276 +++++++++++++
 20 files changed, 5160 insertions(+)

diff -puN Documentation/filesystems/00-INDEX~asfs-filesystem-driver Documentation/filesystems/00-INDEX
--- devel/Documentation/filesystems/00-INDEX~asfs-filesystem-driver	2005-09-12 03:34:53.000000000 -0700
+++ devel-akpm/Documentation/filesystems/00-INDEX	2005-09-12 03:34:53.000000000 -0700
@@ -6,6 +6,8 @@ adfs.txt
 	- info and mount options for the Acorn Advanced Disc Filing System.
 affs.txt
 	- info and mount options for the Amiga Fast File System.
+asfs.txt
+	- info and mount options for the Amiga Smart File System.
 bfs.txt
 	- info for the SCO UnixWare Boot Filesystem (BFS).
 cifs.txt
diff -puN /dev/null Documentation/filesystems/asfs.txt
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/Documentation/filesystems/asfs.txt	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,161 @@
+
+Amiga SmartFileSystem, Linux implementation
+===========================================
+
+ASFS is a Amiga Smart FileSystem driver for Linux. It supports reading
+files and directories. From version 1.0 there is also an experimental
+(almost full) write support. Experimental means that it hasn't been
+tested enough yet, so use it with care. Symbolic links (in AmigaOS
+called soft links) are also supported read/write. Read notes below
+about symlinks support.
+
+
+Unsupported features of Amiga SFS
+================================
+
+ASFS currently does not support safe-delete feature of Amiga SFS
+filesystem. It simply deletes files instead of moving them to
+".recycled" directory. It also doesn't remove files from ".recycled"
+directory, when there is no space left on drive.
+
+If there is no space left, you need to manually remove files from
+".recycled" directory. Also if you want to delete a file in a safe
+way, you need to move it to ".recycled" directory by hand.
+
+Because of all of above, the amount of free space on disk does not
+include space used by all files from ".recycled" directory.
+
+
+Limitations
+===========
+
+There is no Amiga protection bits into Linux permission bits tranlation
+and vice versa. If you need this feature, mail me.
+
+ASFS will always keep some amount of blocks free. This means that you
+cannot fill the drive completely. It is because Amiga SFS uses some
+special methods of writing data (called safe write), which needs some
+additional free space.
+
+File systems with unfinished transactions (this happens when system crashed
+during writing data to disk on AmigaOS/MorphOS) will be mounted read-only
+to protect data. The only way to fix such filesystem is to mount it under
+AmigaOS or MorphOS.
+
+Do not try to mount and write to filesystem with errors. Bad things will
+happen.
+
+
+Mount options for the ASFS
+==========================
+
+setuid=uid
+		This sets the owner of all files and directories in the file
+		system to uid.
+
+setgid=gid
+		Same as above, but for gid.
+
+mode=mode
+		Sets the mode flags to the given (octal) value. Directories
+		will get an x permission if the corresponding r bit is set.
+		The default mode is 0644, which means that everybody are allowed
+		to read files, but only root can write to them.
+		(for directories this means also that search bits are set).
+
+prefix=path
+		Path will be prefixed to every absolute path name of symbolic
+		links on an ASFS/AFFS partition. Default = "/". (See below.)
+
+volume=name
+		When symbolic links with an absolute path are created
+		on an ASFS/AFFS partition, name will be prepended as the
+		volume name. Default = "" (empty string). (See below.)
+
+lowercasevol
+		Translate all volume names in symlinks to lower case.
+		Disabled by default. (See below.)
+
+iocharset=name
+		Character set to use for converting file names. Specifies
+		character set used by your Linux system.
+codepage=name
+		Set the codepage number for converting file names. Specifies
+		character set used by your Amiga. Use full name (for example
+		'cp1251' instead of '1251') here, this allows to specify any
+		character set, not only numbered one (like 'iso8859-2').
+		Use special name 'none' to disable the NLS file name
+		translation.
+
+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 separate root directory for each
+file system (for example, 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. ASFS turns the volume name into a directory name
+and prepends the prefix path (see prefix option) to it. When option
+"lowercasevol" is set, it also translates volume names to lower case.
+If the volume name is the same as a name given in "volume" option,
+it will be ignored and an absolute path will be created.
+
+Example:
+You mount all your Amiga partitions under /amiga/&lt;volume&gt; (where
+&lt;volume&gt; is the name of the volume), and you give options
+`prefix="/amiga/",volume="Linux",lowercasevol' when mounting all your
+ASFS 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
+translated to "/amiga/user/sc/include/dos/dos.h".
+A symbolic link referring to "Linux:etc/fstab" will be translated to
+"/etc/fstab".
+If you create a symlink referring to "/amiga/graphics/data/pict.jpg",
+it will be saved as "graphics:data/pict.jpg".
+If you create a symlink referring to "/boot/System.map", it will be
+saved as "Linux:boot/System.map".
+
+
+Other information
+=================
+
+Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks
+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.
+
+This file system has been tested on Motorola PPC and 68k, as well as
+Intel x86 systems. I don't know, if it works on other Linux systems.
+
+This filesystem is in BETA STAGE. This means that driver MIGHT corrupt
+or damage data on your disk. Remember! YOU USE IT ON YOUR OWN RISK!
+
+I made almost all I could to minimalize this risk. On my systems several
+gigabytes has been succesfully copied from and to SFS disks. I would also
+appreciate any infomation if this filesystem works on your system or not.
+See next paragraph for my email.
+
+Some parts of this documentation has been adapted from AFFS driver docs.
+
+
+Author, contact and copyright infos
+===================================
+
+ASFS has been written by Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;.
+Mail me if you have any suggestions or found a bug.
+
+Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+
+Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts
+of original amiga version of SmartFilesystem source code.
+
+SmartFilesystem is copyrighted (C) 2003,2004 by: John Hendrikx,
+Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+
+The ASFS driver is realased under the terms of of the GNU General
+Public License. See source code for more details.
+
diff -puN /dev/null fs/asfs/adminspace.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/adminspace.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,446 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+#include "bitfuncs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+#ifdef CONFIG_ASFS_RW
+
+static int setfreeblocks(struct super_block *sb, u32 freeblocks)
+{
+	struct buffer_head *bh;
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh-&gt;b_data + sb-&gt;s_blocksize - sizeof(struct fsRootInfo));
+		ASFS_SB(sb)-&gt;freeblocks = freeblocks;
+		ri-&gt;freeblocks = cpu_to_be32(freeblocks);
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+		return 0;
+	}
+	return -EIO;
+}
+
+static inline int enoughspace(struct super_block *sb, u32 blocks)
+{
+	if (ASFS_SB(sb)-&gt;freeblocks - ASFS_ALWAYSFREE &lt; blocks)
+		return FALSE;
+
+	return TRUE;
+}
+
+	/* Determines the amount of free blocks starting from block /block/.
+	   If there are no blocks found or if there was an error -1 is returned,
+	   otherwise this function will count the number of free blocks until
+	   an allocated block is encountered or until maxneeded has been
+	   exceeded. */
+
+static int availablespace(struct super_block *sb, u32 block, u32 maxneeded)
+{
+	struct buffer_head *bh = NULL;
+	struct fsBitmap *b;
+	u32 longs = ASFS_SB(sb)-&gt;blocks_inbitmap &gt;&gt; 5;
+	u32 maxbitmapblock = ASFS_SB(sb)-&gt;bitmapbase + ASFS_SB(sb)-&gt;blocks_bitmap;
+	int blocksfound = 0;
+	u32 bitstart;
+	int bitend;
+	u32 nextblock = ASFS_SB(sb)-&gt;bitmapbase + block / ASFS_SB(sb)-&gt;blocks_inbitmap;
+
+	bitstart = block % ASFS_SB(sb)-&gt;blocks_inbitmap;
+
+	while (nextblock &lt; maxbitmapblock &amp;&amp; (bh = asfs_breadcheck(sb, nextblock++, ASFS_BITMAP_ID))) {
+		b = (void *) bh-&gt;b_data;
+
+		if ((bitend = bmffz(b-&gt;bitmap, longs, bitstart)) &gt;= 0) {
+			blocksfound += bitend - bitstart;
+			asfs_brelse(bh);
+			return blocksfound;
+		}
+		blocksfound += ASFS_SB(sb)-&gt;blocks_inbitmap - bitstart;
+		if (blocksfound &gt;= maxneeded) {
+			asfs_brelse(bh);
+			return blocksfound;
+		}
+		bitstart = 0;
+		asfs_brelse(bh);
+	}
+
+	if (bh == NULL)
+		return (-1);
+
+	return (blocksfound);
+}
+
+int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end, u32 * returned_block, u32 * returned_blocks)
+{
+	struct buffer_head *bh;
+	u32 longs = ASFS_SB(sb)-&gt;blocks_inbitmap &gt;&gt; 5;
+	u32 space = 0;
+	u32 block;
+	u32 bitmapblock = ASFS_SB(sb)-&gt;bitmapbase + start / ASFS_SB(sb)-&gt;blocks_inbitmap;
+	u32 break_point;
+	int bitstart, bitend;
+	int reads;
+
+	if (enoughspace(sb, maxneeded) == FALSE) {
+		*returned_block = 0;
+		*returned_blocks = 0;
+		return -ENOSPC;
+	}
+
+	if (start &gt;= ASFS_SB(sb)-&gt;totalblocks)
+		start -= ASFS_SB(sb)-&gt;totalblocks;
+
+	if (end == 0)
+		end = ASFS_SB(sb)-&gt;totalblocks;
+
+	reads = ((end - 1) / ASFS_SB(sb)-&gt;blocks_inbitmap) + 1 - start / ASFS_SB(sb)-&gt;blocks_inbitmap;
+
+	if (start &gt;= end)
+		reads += (ASFS_SB(sb)-&gt;totalblocks - 1) / ASFS_SB(sb)-&gt;blocks_inbitmap + 1;
+
+	break_point = (start &lt; end ? end : ASFS_SB(sb)-&gt;totalblocks);
+
+	*returned_block = 0;
+	*returned_blocks = 0;
+
+	bitend = start % ASFS_SB(sb)-&gt;blocks_inbitmap;
+	block = start - bitend;
+
+	while ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+		struct fsBitmap *b = (void *) bh-&gt;b_data;
+		u32 localbreak_point = break_point - block;
+
+		if (localbreak_point &gt; ASFS_SB(sb)-&gt;blocks_inbitmap)
+			localbreak_point = ASFS_SB(sb)-&gt;blocks_inbitmap;
+
+		/* At this point space contains the amount of free blocks at
+		   the end of the previous bitmap block.  If there are no
+		   free blocks at the start of this bitmap block, space will
+		   be set to zero, since in that case the space isn't adjacent. */
+
+		while ((bitstart = bmffo(b-&gt;bitmap, longs, bitend)) &lt; ASFS_SB(sb)-&gt;blocks_inbitmap) {
+			/* found the start of an empty space, now find out how large it is */
+
+			if (bitstart &gt;= localbreak_point)
+				break;
+
+			if (bitstart != 0)
+				space = 0;
+
+			bitend = bmffz(b-&gt;bitmap, longs, bitstart);
+
+			if (bitend &gt; localbreak_point)
+				bitend = localbreak_point;
+
+			space += bitend - bitstart;
+
+			if (*returned_blocks &lt; space) {
+				*returned_block = block + bitend - space;
+				if (space &gt;= maxneeded) {
+					*returned_blocks = maxneeded;
+					asfs_brelse(bh);
+					return 0;
+				}
+				*returned_blocks = space;
+			}
+
+			if (bitend &gt;= localbreak_point)
+				break;
+		}
+
+		if (--reads == 0)
+			break;
+
+		/* no (more) empty spaces found in this block */
+
+		if (bitend != ASFS_SB(sb)-&gt;blocks_inbitmap)
+			space = 0;
+
+		bitend = 0;
+		block += ASFS_SB(sb)-&gt;blocks_inbitmap;
+
+		if (block &gt;= ASFS_SB(sb)-&gt;totalblocks) {
+			block = 0;
+			space = 0;
+			break_point = end;
+			bitmapblock = ASFS_SB(sb)-&gt;bitmapbase;
+		}
+		asfs_brelse(bh);
+	}
+
+	if (bh == NULL)
+		return -EIO;
+
+	asfs_brelse(bh);
+
+	if (*returned_blocks == 0)
+		return -ENOSPC;
+	else
+		return 0;
+}
+
+int asfs_markspace(struct super_block *sb, u32 block, u32 blocks)
+{
+	int errorcode;
+
+	asfs_debug("markspace: Marking %d blocks from block %d\n", blocks, block);
+
+	if ((availablespace(sb, block, blocks)) &lt; blocks) {
+		printk("ASFS: Attempted to mark %d blocks from block %d, but some of them were already full!\n", blocks, block);
+		return -EIO;
+	}
+
+	if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)-&gt;freeblocks - blocks)) == 0) {
+		struct buffer_head *bh;
+		u32 skipblocks = block / ASFS_SB(sb)-&gt;blocks_inbitmap;
+		u32 longs = (sb-&gt;s_blocksize - sizeof(struct fsBitmap)) &gt;&gt; 2;
+		u32 bitmapblock;
+
+		block -= skipblocks * ASFS_SB(sb)-&gt;blocks_inbitmap;
+		bitmapblock = ASFS_SB(sb)-&gt;bitmapbase + skipblocks;
+
+		while (blocks &gt; 0) {
+			if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+				struct fsBitmap *b = (void *) bh-&gt;b_data;
+
+				blocks -= bmclr(b-&gt;bitmap, longs, block, blocks);
+				block = 0;
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else
+				return -EIO;
+		}
+	}
+
+	return (errorcode);
+}
+
+	/* This function checks the bitmap and tries to locate at least /blocksneeded/
+	   adjacent unused blocks.  If found it sets returned_block to the start block
+	   and returns no error.  If not found, ERROR_DISK_IS_FULL is returned and
+	   returned_block is set to zero.  Any other errors are returned as well. */
+
+static inline int internalfindspace(struct super_block *sb, u32 blocksneeded, u32 startblock, u32 endblock, u32 * returned_block)
+{
+	u32 blocks;
+	int errorcode;
+
+	if ((errorcode = asfs_findspace(sb, blocksneeded, startblock, endblock, returned_block, &amp;blocks)) == 0)
+		if (blocks != blocksneeded)
+			return -ENOSPC;
+
+	return errorcode;
+}
+
+static int findandmarkspace(struct super_block *sb, u32 blocksneeded, u32 * returned_block)
+{
+	int errorcode;
+
+	if (enoughspace(sb, blocksneeded) != FALSE) {
+		if ((errorcode = internalfindspace(sb, blocksneeded, 0, ASFS_SB(sb)-&gt;totalblocks, returned_block)) == 0)
+			errorcode = asfs_markspace(sb, *returned_block, blocksneeded);
+	} else
+		errorcode = -ENOSPC;
+
+	return (errorcode);
+}
+
+/* ************************** */
+
+int asfs_freespace(struct super_block *sb, u32 block, u32 blocks)
+{
+	int errorcode;
+
+	asfs_debug("freespace: Freeing %d blocks from block %d\n", blocks, block);
+
+	if ((errorcode = setfreeblocks(sb, ASFS_SB(sb)-&gt;freeblocks + blocks)) == 0) {
+		struct buffer_head *bh;
+		u32 skipblocks = block / ASFS_SB(sb)-&gt;blocks_inbitmap;
+		u32 longs = (sb-&gt;s_blocksize - sizeof(struct fsBitmap)) &gt;&gt; 2;
+		u32 bitmapblock;
+
+		block -= skipblocks * ASFS_SB(sb)-&gt;blocks_inbitmap;
+		bitmapblock = ASFS_SB(sb)-&gt;bitmapbase + skipblocks;
+
+		while (blocks &gt; 0) {
+			if ((bh = asfs_breadcheck(sb, bitmapblock++, ASFS_BITMAP_ID))) {
+				struct fsBitmap *b = (void *) bh-&gt;b_data;
+
+				blocks -= bmset(b-&gt;bitmap, longs, block, blocks);
+				block = 0;
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else
+				return -EIO;
+		}
+	}
+
+	return (errorcode);
+}
+
+/*************** admin space containers ****************/
+
+int asfs_allocadminspace(struct super_block *sb, u32 *returned_block)
+{
+	struct buffer_head *bh;
+	u32 adminspaceblock = ASFS_SB(sb)-&gt;adminspacecontainer;
+	int errorcode = -EIO;
+
+	asfs_debug("allocadminspace: allocating new block\n");
+
+	while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+		struct fsAdminSpaceContainer *asc1 = (void *) bh-&gt;b_data;
+		struct fsAdminSpace *as1 = asc1-&gt;adminspace;
+		int adminspaces1 = (sb-&gt;s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+		while (adminspaces1-- &gt; 0) {
+			s16 bitoffset;
+
+			if (as1-&gt;space != 0 &amp;&amp; (bitoffset = bfffz(be32_to_cpu(as1-&gt;bits), 0)) &gt;= 0) {
+				u32 emptyadminblock = be32_to_cpu(as1-&gt;space) + bitoffset;
+				as1-&gt;bits |= cpu_to_be32(1 &lt;&lt; (31 - bitoffset));
+				asfs_bstore(sb, bh);
+				*returned_block = emptyadminblock;
+				asfs_brelse(bh);
+				asfs_debug("allocadminspace: found block %d\n", *returned_block);
+				return 0;
+			}
+			as1++;
+		}
+
+		adminspaceblock = be32_to_cpu(asc1-&gt;next);
+		asfs_brelse(bh);
+
+		if (adminspaceblock == 0) {
+			u32 startblock;
+
+			asfs_debug("allocadminspace: allocating new adminspace area\n");
+
+			/* If we get here it means current adminspace areas are all filled.
+			   We would now need to find a new area and create a fsAdminSpace
+			   structure in one of the AdminSpaceContainer blocks.  If these
+			   don't have any room left for new adminspace areas a new
+			   AdminSpaceContainer would have to be created first which is
+			   placed as the first block in the newly found admin area. */
+
+			adminspaceblock = ASFS_SB(sb)-&gt;adminspacecontainer;
+
+			if ((errorcode = findandmarkspace(sb, 32, &amp;startblock)))
+				return errorcode;
+
+			while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+				struct fsAdminSpaceContainer *asc2 = (void *) bh-&gt;b_data;
+				struct fsAdminSpace *as2 = asc2-&gt;adminspace;
+				int adminspaces2 = (sb-&gt;s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+				while (adminspaces2-- &gt; 0 &amp;&amp; as2-&gt;space != 0)
+					as2++;
+
+				if (adminspaces2 &gt;= 0) {	/* Found a unused AdminSpace in this AdminSpaceContainer! */
+					as2-&gt;space = cpu_to_be32(startblock);
+					as2-&gt;bits = 0;
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+					break;
+				}
+
+				if (asc2-&gt;next == 0) {
+					/* Oh-oh... we marked our new adminspace area in use, but we couldn't
+					   find space to store a fsAdminSpace structure in the existing
+					   fsAdminSpaceContainer blocks.  This means we need to create and
+					   link a new fsAdminSpaceContainer as the first block in our newly
+					   marked adminspace. */
+
+					asc2-&gt;next = cpu_to_be32(startblock);
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					/* Now preparing new AdminSpaceContainer */
+
+					if ((bh = asfs_getzeroblk(sb, startblock)) == NULL)
+						return -EIO;
+
+					asc2 = (void *) bh-&gt;b_data;
+					asc2-&gt;bheader.id = cpu_to_be32(ASFS_ADMINSPACECONTAINER_ID);
+					asc2-&gt;bheader.ownblock = cpu_to_be32(startblock);
+					asc2-&gt;previous = cpu_to_be32(adminspaceblock);
+					asc2-&gt;adminspace[0].space = cpu_to_be32(startblock);
+					asc2-&gt;adminspace[0].bits = cpu_to_be32(0x80000000);
+					asc2-&gt;bits = 32;
+
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					adminspaceblock = startblock;
+					break;	/* Breaks through to outer loop! */
+				}
+				adminspaceblock = be32_to_cpu(asc2-&gt;next);
+				asfs_brelse(bh);
+			}
+		}
+	}
+	return errorcode;
+}
+
+int asfs_freeadminspace(struct super_block *sb, u32 block)
+{
+	struct buffer_head *bh;
+	u32 adminspaceblock = ASFS_SB(sb)-&gt;adminspacecontainer;
+
+	asfs_debug("freeadminspace: Entry -- freeing block %d\n", block);
+
+	while ((bh = asfs_breadcheck(sb, adminspaceblock, ASFS_ADMINSPACECONTAINER_ID))) {
+		struct fsAdminSpaceContainer *asc = (void *) bh-&gt;b_data;
+		struct fsAdminSpace *as = asc-&gt;adminspace;
+		int adminspaces = (sb-&gt;s_blocksize - sizeof(struct fsAdminSpaceContainer)) / sizeof(struct fsAdminSpace);
+
+		while (adminspaces-- &gt; 0) {
+			if (block &gt;= be32_to_cpu(as-&gt;space) &amp;&amp; block &lt; be32_to_cpu(as-&gt;space) + 32) {
+				s16 bitoffset = block - be32_to_cpu(as-&gt;space);
+				asfs_debug("freeadminspace: Block to be freed is located in AdminSpaceContainer block at %d\n", adminspaceblock);
+				as-&gt;bits &amp;= cpu_to_be32(~(1 &lt;&lt; (31 - bitoffset)));
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+				return 0;
+			}
+			as++;
+		}
+
+		if ((adminspaceblock = be32_to_cpu(asc-&gt;next)) == 0)
+			break;
+
+		asfs_brelse(bh);
+	}
+
+	if (bh != NULL) {
+		asfs_brelse(bh);
+		printk("ASFS: Unable to free an administration block. The block cannot be found.");
+		return -ENOENT;
+	}
+
+	return -EIO;
+}
+
+#endif
diff -puN /dev/null fs/asfs/asfs_fs.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/asfs_fs.h	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,234 @@
+#ifndef __LINUX_ASFS_FS_H
+#define __LINUX_ASFS_FS_H
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;asm/byteorder.h&gt;
+#include &lt;linux/amigasfs.h&gt;
+
+#define asfs_debug(fmt,arg...) /* no debug at all */
+//#define asfs_debug(fmt,arg...) printk(fmt,##arg)  /* general debug infos */
+
+#if !defined (__BIG_ENDIAN) &amp;&amp; !defined (__LITTLE_ENDIAN)
+#error Endianes must be known for ASFS to work. Sorry.
+#endif
+
+#define ASFS_MAXFN_BUF (ASFS_MAXFN + 4)
+#define ASFS_DEFAULT_UID 0
+#define ASFS_DEFAULT_GID 0
+#define ASFS_DEFAULT_MODE 0644	/* default permission bits for files, dirs have same permission, but with "x" set */
+
+/* Extent structure located in RAM (e.g. inside inode structure),
+   currently used to store last used extent */
+
+struct inramExtent {
+	u32 startblock;	/* Block from begginig of the file */
+	u32 key;
+	u32 next;
+	u16 blocks;
+};
+
+/* inode in-kernel data */
+
+struct asfs_inode_info {
+	u32 firstblock;
+	u32 hashtable;
+	int modified;
+	loff_t mmu_private;
+	struct inramExtent ext_cache;
+	struct inode vfs_inode;
+};
+
+/* short cut to get to the asfs specific inode data */
+static inline struct asfs_inode_info *ASFS_I(struct inode *inode)
+{
+   return list_entry(inode, struct asfs_inode_info, vfs_inode);
+}
+
+/* Amiga SFS superblock in-core data */
+
+struct asfs_sb_info {
+	u32 totalblocks;
+	u32 rootobjectcontainer;
+	u32 extentbnoderoot;
+	u32 objectnoderoot;
+
+	u32 adminspacecontainer;
+	u32 bitmapbase;
+	u32 freeblocks;
+	u32 blocks_inbitmap;
+	u32 blocks_bitmap;
+	u32 block_rovingblockptr;
+
+	uid_t uid;
+	gid_t gid;
+	umode_t mode;
+	u16 flags;
+	char *prefix;
+	char *root_volume;		/* Volume prefix for absolute symlinks. */
+	char *iocharset;
+	char *codepage;
+	struct nls_table *nls_io;
+	struct nls_table *nls_disk;
+};
+
+/* short cut to get to the asfs specific sb data */
+static inline struct asfs_sb_info *ASFS_SB(struct super_block *sb)
+{
+	return sb-&gt;s_fs_info;
+}
+
+/* io inline code */
+
+u32 asfs_calcchecksum(void *block, u32 blocksize);
+
+static inline int
+asfs_check_block(struct fsBlockHeader *block, u32 blocksize, u32 n, u32 id)
+{
+	if (asfs_calcchecksum(block, blocksize) ==
+	    be32_to_cpu(((struct fsBlockHeader *) block)-&gt;checksum) &amp;&amp;
+	    n == be32_to_cpu(((struct fsBlockHeader *) block)-&gt;ownblock) &amp;&amp;
+	    id == be32_to_cpu(((struct fsBlockHeader *) block)-&gt;id))
+		return TRUE;
+	return FALSE;
+}
+
+/* get fs structure from block and do some checks... */
+static inline struct buffer_head *
+asfs_breadcheck(struct super_block *sb, u32 n, u32 type)
+{
+	struct buffer_head *bh;
+	if ((bh = sb_bread(sb, n))) {
+		if (asfs_check_block ((void *)bh-&gt;b_data, sb-&gt;s_blocksize, n, type)) {
+			return bh;	/* all okay */
+		}
+		brelse(bh);
+	}
+	return NULL;		/* error */
+}
+
+static inline struct buffer_head *
+asfs_getzeroblk(struct super_block *sb, int block)
+{
+	struct buffer_head *bh;
+	bh = sb_getblk(sb, block);
+	lock_buffer(bh);
+	memset(bh-&gt;b_data, 0, sb-&gt;s_blocksize);
+	set_buffer_uptodate(bh);
+	unlock_buffer(bh);
+	return bh;
+}
+
+static inline void
+asfs_bstore(struct super_block *sb, struct buffer_head *bh)
+{
+	((struct fsBlockHeader *) (bh-&gt;b_data))-&gt;checksum =
+	    cpu_to_be32(asfs_calcchecksum(bh-&gt;b_data, sb-&gt;s_blocksize));
+	mark_buffer_dirty(bh);
+}
+
+static inline void asfs_brelse(struct buffer_head *bh)
+{
+	brelse(bh);
+}
+
+static inline void dec_count(struct inode *inode)
+{
+	inode-&gt;i_nlink--;
+	mark_inode_dirty(inode);
+}
+
+/* all prototypes */
+
+/* adminspace.c */
+int asfs_allocadminspace(struct super_block *sb, u32 * block);
+int asfs_freeadminspace(struct super_block *sb, u32 block);
+int asfs_markspace(struct super_block *sb, u32 block, u32 blocks);
+int asfs_freespace(struct super_block *sb, u32 block, u32 blocks);
+int asfs_findspace(struct super_block *sb, u32 maxneeded, u32 start, u32 end,
+	      u32 * returned_block, u32 * returned_blocks);
+
+/* dir.c */
+int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir);
+struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd);
+
+/* extents.c */
+int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh,
+	      struct fsExtentBNode **ret_ebn);
+int asfs_deletebnode(struct super_block *sb, struct buffer_head *cb, u32 key);
+int asfs_deleteextents(struct super_block *sb, u32 key);
+int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace,
+	      u32 objectnode, u32 * io_lastextentbnode);
+
+/* file.c */
+int asfs_readpage(struct file *file, struct page *page);
+sector_t asfs_bmap(struct address_space *mapping, sector_t block);
+int asfs_writepage(struct page *page, struct writeback_control *wbc);
+int asfs_prepare_write(struct file *file, struct page *page, unsigned from,
+		       unsigned to);
+void asfs_truncate(struct inode *inode);
+int asfs_file_open(struct inode *inode, struct file *filp);
+int asfs_file_release(struct inode *inode, struct file *filp);
+
+/* inode.c */
+struct inode *asfs_get_root_inode(struct super_block *sb);
+void asfs_read_locked_inode(struct inode *inode, void *arg);
+int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd);
+int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
+int asfs_rmdir(struct inode *dir, struct dentry *dentry);
+int asfs_unlink(struct inode *dir, struct dentry *dentry);
+int asfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+		struct inode *new_dir, struct dentry *new_dentry);
+int asfs_notify_change(struct dentry *dentry, struct iattr *attr);
+
+/* namei */
+u8 asfs_lowerchar(u8 c);
+int asfs_check_name(const u8 *name, int len);
+int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t);
+u16 asfs_hash(u8 *name, int casesensitive);
+void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit);
+
+/* nodes */
+int asfs_getnode(struct super_block *sb, u32 nodeno,
+	    struct buffer_head **ret_bh, struct fsObjectNode **ret_node);
+int asfs_createnode(struct super_block *sb, struct buffer_head **returned_cb,
+	       struct fsNode **returned_node, u32 * returned_nodeno);
+int asfs_deletenode(struct super_block *sb, u32 objectnode);
+
+/* objects */
+struct fsObject *asfs_nextobject(struct fsObject *obj);
+struct fsObject *asfs_find_obj_by_name(struct super_block *sb,
+		struct fsObjectContainer *objcont, u8 * name);
+int asfs_readobject(struct super_block *sb, u32 objectnode,
+	       struct buffer_head **cb, struct fsObject **returned_object);
+int asfs_createobject(struct super_block *sb, struct buffer_head **io_cb,
+		 struct fsObject **io_o, struct fsObject *src_o,
+		 u8 * objname, int force);
+int asfs_deleteobject(struct super_block *sb, struct buffer_head *cb,
+		 struct fsObject *o);
+int asfs_renameobject(struct super_block *sb, struct buffer_head *cb1,
+		 struct fsObject *o1, struct buffer_head *cbparent,
+		 struct fsObject *oparent, u8 * newname);
+
+int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objcb,
+		    struct fsObject *o, u32 blocks, u32 * newspace,
+		    u32 * addedblocks);
+int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh,
+			 struct fsObject *o, u32 newsize);
+
+/* super.c */
+struct super_block *asfs_read_super(struct super_block *sb, void *data,
+				    int silent);
+void asfs_put_super(struct super_block *sb);
+int asfs_statfs(struct super_block *sb, struct kstatfs *buf);
+int asfs_remount(struct super_block *sb, int *flags, char *data);
+struct inode *asfs_alloc_inode(struct super_block *sb);
+void asfs_destroy_inode(struct inode *inode);
+
+/* symlink.c */
+int asfs_symlink_readpage(struct file *file, struct page *page);
+int asfs_write_symlink(struct inode *symfile, const char *symname);
+
+#endif
diff -puN /dev/null fs/asfs/bitfuncs.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/bitfuncs.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,171 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;asm/byteorder.h&gt;
+#include "bitfuncs.h"
+
+/* Bitmap (bm) functions:
+   These functions perform bit-operations on regions of memory which
+   are a multiple of 4 bytes in length. Bitmap is in bigendian byte order.
+*/
+
+/* This function finds the first set bit in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  It
+   returns the bitoffset of the first set bit it finds. */
+
+int bmffo(u32 *bitmap, int longs, int bitoffset)
+{
+	u32 *scan = bitmap;
+	int longoffset, bit;
+
+	longoffset = bitoffset &gt;&gt; 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset &amp; 0x1F;
+
+	if (bitoffset != 0) {
+		if ((bit = bfffo(be32_to_cpu(*scan), bitoffset)) &gt;= 0) {
+			return (bit + ((scan - bitmap) &lt;&lt; 5));
+		}
+		scan++;
+		longs--;
+	}
+
+	while (longs-- &gt; 0) {
+		if (*scan++ != 0) {
+			return (bfffo(be32_to_cpu(*--scan), 0) + ((scan - bitmap) &lt;&lt; 5));
+		}
+	}
+
+	return (-1);
+}
+
+/* This function finds the first unset bit in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  It
+   returns the bitoffset of the first unset bit it finds. */
+
+int bmffz(u32 *bitmap, int longs, int bitoffset)
+{
+	u32 *scan = bitmap;
+	int longoffset, bit;
+
+	longoffset = bitoffset &gt;&gt; 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset &amp; 0x1F;
+
+	if (bitoffset != 0) {
+		if ((bit = bfffz(be32_to_cpu(*scan), bitoffset)) &gt;= 0) {
+			return (bit + ((scan - bitmap) &lt;&lt; 5));
+		}
+		scan++;
+		longs--;
+	}
+
+	while (longs-- &gt; 0) {
+		if (*scan++ != 0xFFFFFFFF) {
+			return (bfffz(be32_to_cpu(*--scan), 0) + ((scan - bitmap) &lt;&lt; 5));
+		}
+	}
+
+	return (-1);
+}
+
+/* This function clears /bits/ bits in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  If
+   the region of memory is too small to clear /bits/ bits then this
+   function exits after having cleared all bits till the end of the
+   memory region.  In any case it returns the number of bits which
+   were actually cleared. */
+
+int bmclr(u32 *bitmap, int longs, int bitoffset, int bits)
+{
+	u32 *scan = bitmap;
+	int longoffset;
+	int orgbits = bits;
+
+	longoffset = bitoffset &gt;&gt; 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset &amp; 0x1F;
+
+	if (bitoffset != 0) {
+		if (bits &lt; 32) {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, bits));
+		} else {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), bitoffset, 32));
+		}
+		scan++;
+		longs--;
+		bits -= 32 - bitoffset;
+	}
+
+	while (bits &gt; 0 &amp;&amp; longs-- &gt; 0) {
+		if (bits &gt; 31) {
+			*scan++ = 0;
+		} else {
+			*scan = cpu_to_be32(bfclr(be32_to_cpu(*scan), 0, bits));
+		}
+		bits -= 32;
+	}
+
+	if (bits &lt;= 0) {
+		return (orgbits);
+	}
+	return (orgbits - bits);
+}
+
+/* This function sets /bits/ bits in a region of memory starting
+   with /bitoffset/.  The region of memory is /longs/ longs long.  If
+   the region of memory is too small to set /bits/ bits then this
+   function exits after having set all bits till the end of the
+   memory region.  In any case it returns the number of bits which
+   were actually set. */
+
+int bmset(u32 *bitmap, int longs, int bitoffset, int bits)
+{
+	u32 *scan = bitmap;
+	int longoffset;
+	int orgbits = bits;
+
+	longoffset = bitoffset &gt;&gt; 5;
+	longs -= longoffset;
+	scan += longoffset;
+
+	bitoffset = bitoffset &amp; 0x1F;
+
+	if (bitoffset != 0) {
+		if (bits &lt; 32) {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, bits));
+		} else {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), bitoffset, 32));
+		}
+		scan++;
+		longs--;
+		bits -= 32 - bitoffset;
+	}
+
+	while (bits &gt; 0 &amp;&amp; longs-- &gt; 0) {
+		if (bits &gt; 31) {
+			*scan++ = 0xFFFFFFFF;
+		} else {
+			*scan = cpu_to_be32(bfset(be32_to_cpu(*scan), 0, bits));
+		}
+		bits -= 32;
+	}
+
+	if (bits &lt;= 0) {
+		return (orgbits);
+	}
+	return (orgbits - bits);
+}
diff -puN /dev/null fs/asfs/bitfuncs.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/bitfuncs.h	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,59 @@
+/*
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef __BITFUNCS_H
+#define __BITFUNCS_H
+
+#include &lt;linux/types.h&gt;
+#include &lt;asm/byteorder.h&gt;
+
+#include &lt;asm/bitops.h&gt;
+#include &lt;linux/bitops.h&gt;
+
+/* Finds first set bit in /data/ starting at /bitoffset/.  This function
+   considers the MSB to be the first bit. */
+static inline int bfffo(u32 data, int bitoffset)
+{
+	u32 mask = 0xffffffff &gt;&gt; bitoffset;
+	data &amp;= mask;
+	return data == 0 ? -1 : 32-fls(data);
+}
+
+/* Finds first zero bit in /data/ starting at /bitoffset/.  This function
+   considers the MSB to be the first bit. */
+static inline int bfffz(u32 data, int bitoffset)
+{
+	return bfffo(~data, bitoffset);
+}
+
+/* Sets /bits/ bits starting from /bitoffset/ in /data/.
+   /bits/ must be between 1 and 32. */
+static inline u32 bfset(u32 data, int bitoffset, int bits)
+{
+	u32 mask = ~((1 &lt;&lt; (32 - bits)) - 1);
+	mask &gt;&gt;= bitoffset;
+	return data | mask;
+}
+
+/* Clears /bits/ bits starting from /bitoffset/ in /data/.
+   /bits/ must be between 1 and 32. */
+static inline u32 bfclr(u32 data, int bitoffset, int bits)
+{
+	u32 mask = ~((1 &lt;&lt; (32 - bits)) - 1);
+	mask &gt;&gt;= bitoffset;
+	return data &amp; ~mask;
+}
+
+/* bm??? functions assumes that in-memory bitmap is in bigendian byte order */
+int bmffo(u32 *, int, int);
+int bmffz(u32 *, int, int);
+int bmclr(u32 *, int, int, int);
+int bmset(u32 *, int, int, int);
+
+#endif
diff -puN /dev/null fs/asfs/Changes
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/Changes	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,112 @@
+
+Amiga Smart File System, Linux implementation
+
+Please direct bug reports to: marek@amiga.pl
+
+History:
+
+v1.0beta10 (13.06.2005)
+- fixed ugly bug introduced in beta9 that caused kernel crash on x86
+  (thanks to Emiliano for reporting it!)
+
+v1.0beta9 (17.03.2005)
+- added NLS support (thanks to Pavel Fedin!)
+
+v1.0beta8 (07.01.2005)
+- adapted to 2.6.10 kernel VFS changes
+- added workaround for buggy Mandrake kernel headers
+
+v1.0beta7 (25.06.2004)
+- small changes in documentation
+- code clean up: bitfuncs.c, super.c, inode.c, *.h, Makefile, added
+  asfs_ prefix to function names, made some functions static
+  (big thanks to Christoph Hellwig for advice!)
+- fixed minor bugs (inode leak in super.c, not-realesed buffer during
+  object renaming in inode.c)
+- now files/dirs are created with global ownership/permission bits
+
+v1.0beta6 (04.06.2004)
+- fixed: ASFS_SB(sb)-&gt;flags was always zero in 2.6.x code
+
+v1.0beta5 (07.05.2004)
+- finally fixed a problem with file size attrib. not being written
+  to disk
+- fixed some problems with GCC 3.x and debug enabled
+
+v1.0beta4 (12.04.2004)
+- removed dummy asfs_notify_change (this fixes major bug introduced
+  in 1.0beta3 - file size wasn't written to disk) until it will
+  be implemented completely
+
+v1.0beta3 (22.03.2004) - still beta
+- updated for 2.6.x kernels VFS changes
+- code clean-up
+- added dummy asfs_notify_change (chmod now returns no errors)
+- added symlinks write support
+- fixed: ASFS_SB(sb)-&gt;flags was always zero
+
+v1.0beta2 (11.01.2004) - special version for Pegasos][ kernel
+- separated read and write functions, can be compiled also
+  as read-only fs
+
+v1.0beta1 (02.12.2003) - first public beta with write support
+- added dentry hashing/comparing routines
+- code clean-up
+
+v1.0aplha4 (30.11.2003) - preparing for first public beta
+- fixed some problems with renaming/moving files
+- fixed two major bugs, which didn't occur when fs was mounted
+  on loopback device (newly allocated blocks were not written to
+  disk and state bits were not set correctly on newly mapped file
+  blocks)
+- fixed many small bugs in io code (some buffers were not freed)
+- added/modified sb locks in asfs_lookup and asfs_getblock
+- fixed serious bug in file block allocation routines
+
+v1.0aplha3 (23.11.2003)
+- added (hopefully) all byteswap code, should now work again on
+  little-endian systems (also with write support!)
+- updated documentation
+
+v1.0alpha2 (13.11.2003)
+- now alocates file blocks in chunks during one request
+- fixed some dead-locks, other fixes
+
+v1.0alpha (02.11.2003) - first working version with full write support
+- too much to list it here ;)
+
+... (working on write support)
+
+v0.7 (12.10.2003) - internal realase
+- added asfs_breadcheck, modified asfs_get_node, asfs_search_BTree,
+  no more from_be32/16 macros, other...
+- code splitted into several files
+
+v0.6 (04.09.2003) - final read-only version
+- added support for HashTables, directory scaning should be
+  MUCH faster now
+- added checking of block IDs before reading any data from block
+
+v0.5 (19.07.2003)
+- added simple but effective extent cache - real speed-up
+  in reading large files
+- added read support for symlinks - based on AFFS symlinks
+
+v0.4 (10.07.2003)
+- third code clean-up (thanks to Roman Zippel for advice)
+- now uses generic readpage and readinode routines
+
+v0.3beta (17.06.2003)
+- second code clean-up
+
+v0.2beta2 (15.06.2003)
+- fixed yet another stupid bug - driver can't read root block on little-endian systems
+v0.2beta (15.06.2003)
+- fixed stupid bug - now files have 'file' flag (S_IFREG) set...
+- added mount options to set uid, gid and mode of files and dirs
+- made hidden files &amp; dirs really hidden (= not listed in directories)
+- code clean-up
+
+v0.1beta (11.06.2003)
+- after many kernel crashes, finally got it!
+- first working read-only filesystem driver
diff -puN /dev/null fs/asfs/dir.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/dir.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,240 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * Copyright (C) 2003,2004  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+extern struct dentry_operations asfs_dentry_operations;
+
+int asfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *dir = filp-&gt;f_dentry-&gt;d_inode;
+	struct super_block *sb = dir-&gt;i_sb;
+	struct nls_table *nls_io = ASFS_SB(sb)-&gt;nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)-&gt;nls_disk;
+	u8 buf[512];
+	unsigned long f_pos;
+	int stored = 0;
+
+	struct buffer_head *bh;
+	struct fsObjectContainer *objcont;
+	struct fsObject *obj;
+	u32 block;
+	int startnode;
+	int add;
+
+	asfs_debug("asfs_readdir:\n");
+
+	if (filp-&gt;f_pos == ASFS_SB(sb)-&gt;totalblocks)
+		return stored;
+
+	f_pos = filp-&gt;f_pos;
+
+	if (f_pos == 0) {
+		filp-&gt;private_data = (void *)0;
+		if (filldir(dirent, ".", 1, f_pos, dir-&gt;i_ino, DT_DIR) &lt; 0)
+			return 0;
+		filp-&gt;f_pos = f_pos = 1;
+		stored++;
+	}
+	if (f_pos == 1) {
+		if (filldir(dirent, "..", 2, f_pos, parent_ino(filp-&gt;f_dentry), DT_DIR) &lt; 0)
+			return stored;
+		filp-&gt;f_pos = f_pos = 2;
+		stored++;
+	}
+
+	if (ASFS_I(dir)-&gt;firstblock == 0) {	/* empty directory */
+		filp-&gt;f_pos = ASFS_SB(sb)-&gt;totalblocks;
+		ASFS_I(dir)-&gt;modified = 0;
+		return stored;
+	}
+
+	if (f_pos == 2) {	/* reading directory from its beginning */
+		block = ASFS_I(dir)-&gt;firstblock;
+		add = 1;
+		startnode = 0;
+	} else {
+		startnode = (int)filp-&gt;private_data;
+		add = 0;
+		if (ASFS_I(dir)-&gt;modified == 0)
+			block = f_pos;
+		else
+			block = ASFS_I(dir)-&gt;firstblock;
+	}
+
+	do {
+		if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID)))
+			return stored;
+		objcont = (struct fsObjectContainer *) bh-&gt;b_data;
+		obj = &amp;(objcont-&gt;object[0]);
+
+		while (be32_to_cpu(obj-&gt;objectnode) &gt; 0 &amp;&amp;
+		      ((char *)obj - (char *)objcont) + sizeof(struct fsObject) + 2 &lt; sb-&gt;s_blocksize) {
+
+			if (!add &amp;&amp; be32_to_cpu(obj-&gt;objectnode) == startnode)
+				add++;
+
+			if (add &amp;&amp; !(obj-&gt;bits &amp; OTYPE_HIDDEN)) {
+				unsigned int type;
+				asfs_translate(buf, obj-&gt;name, nls_io, nls_disk, 512);
+				asfs_debug("ASFS: DirFilling: entry #%d \"%s\" (node %u offset %u), type %x\n", \
+				           stored, buf, be32_to_cpu(obj-&gt;objectnode), block, obj-&gt;bits);
+				filp-&gt;f_pos = block;
+
+				if (obj-&gt;bits &amp; OTYPE_DIR)
+					type = DT_DIR;
+				else if (obj-&gt;bits &amp; OTYPE_LINK &amp;&amp; !(obj-&gt;bits &amp; OTYPE_HARDLINK))
+					type = DT_LNK;
+				else
+					type = DT_REG;
+
+				if (filldir(dirent, buf, strlen(buf), block, be32_to_cpu(obj-&gt;objectnode), type) &lt; 0) {
+					filp-&gt;private_data = (void *)be32_to_cpu(obj-&gt;objectnode);
+					ASFS_I(dir)-&gt;modified = 0;
+					asfs_debug("ASFS: DirFilling: to be continued...\n");
+					asfs_brelse(bh);
+					return stored;
+				}
+				stored++;
+			}
+			obj = asfs_nextobject(obj);
+		}
+		block = be32_to_cpu(objcont-&gt;next);
+		asfs_brelse(bh);
+
+	} while (block != 0);
+
+	filp-&gt;f_pos = ASFS_SB(sb)-&gt;totalblocks;
+	ASFS_I(dir)-&gt;modified = 0;
+
+	return stored;
+}
+
+static struct fsObject *asfs_find_obj_by_name_nls(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name)
+{
+	struct fsObject *obj;
+	u8 buf[512];
+
+	obj = &amp;(objcont-&gt;object[0]);
+	while (be32_to_cpu(obj-&gt;objectnode) &gt; 0 &amp;&amp; ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 &lt; sb-&gt;s_blocksize) {
+		asfs_translate(buf, obj-&gt;name, ASFS_SB(sb)-&gt;nls_io, ASFS_SB(sb)-&gt;nls_disk, 512);
+		if (asfs_namecmp(buf, name, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE, ASFS_SB(sb)-&gt;nls_io) == 0) {
+			asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj-&gt;objectnode), obj-&gt;name, obj-&gt;bits, be32_to_cpu(objcont-&gt;bheader.ownblock));
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+struct dentry *asfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	int res = -EACCES;       /* placeholder for "no data here" */
+	struct inode *inode;
+	struct super_block *sb = dir-&gt;i_sb;
+	u8 *name = (u8 *) dentry-&gt;d_name.name;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_translate(bufname, name, ASFS_SB(sb)-&gt;nls_disk, ASFS_SB(sb)-&gt;nls_io, ASFS_MAXFN_BUF);
+
+	asfs_debug("asfs_lookup: (searching \"%s\"...) ", name);
+
+	lock_super(sb);
+
+	if ((!strchr(name, '?')) &amp;&amp; (ASFS_I(dir)-&gt;hashtable != 0)) {	/* hashtable block is available and name can be reverse translated, quick search */
+		struct fsObjectNode *node_p;
+		struct buffer_head *node_bh;
+		u32 node;
+		u16 hash16;
+
+		asfs_debug("(quick search) ");
+
+		if (!(bh = asfs_breadcheck(sb, ASFS_I(dir)-&gt;hashtable, ASFS_HASHTABLE_ID))) {
+			unlock_super(sb);
+			return ERR_PTR(res);
+		}
+		hash16 = asfs_hash(bufname, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE);
+		node = be32_to_cpu(((struct fsHashTable *) bh-&gt;b_data)-&gt;hashentry[HASHCHAIN(hash16)]);
+		asfs_brelse(bh);
+
+		while (node != 0) {
+			if (asfs_getnode(sb, node, &amp;node_bh, &amp;node_p) != 0)
+				goto not_found;
+			if (be16_to_cpu(node_p-&gt;hash16) == hash16) {
+				if (!(bh = asfs_breadcheck(sb, be32_to_cpu(node_p-&gt;node.data), ASFS_OBJECTCONTAINER_ID))) {
+					asfs_brelse(node_bh);
+					unlock_super(sb);
+					return ERR_PTR(res);
+				}
+				if ((obj = asfs_find_obj_by_name(sb, (struct fsObjectContainer *) bh-&gt;b_data, bufname)) != NULL) {
+					asfs_brelse(node_bh);
+					goto found_inode;
+				}
+				asfs_brelse(bh);
+			}
+			node = be32_to_cpu(node_p-&gt;next);
+			asfs_brelse(node_bh);
+		}
+	} else { /* hashtable not available or name can't be reverse-translated, long search */
+		struct fsObjectContainer *objcont;
+		u32 block;
+
+		asfs_debug("(long search) ");
+		block = ASFS_I(dir)-&gt;firstblock;
+		while (block != 0) {
+			if (!(bh = asfs_breadcheck(sb, block, ASFS_OBJECTCONTAINER_ID))) {
+				unlock_super(sb);
+				return ERR_PTR(res);
+			}
+			objcont = (struct fsObjectContainer *) bh-&gt;b_data;
+			if ((obj = asfs_find_obj_by_name_nls(sb, objcont, name)) != NULL)
+				goto found_inode;
+			block = be32_to_cpu(objcont-&gt;next);
+			asfs_brelse(bh);
+		}
+	}
+
+not_found:
+	unlock_super(sb);
+	inode = NULL;
+	asfs_debug("object not found.\n");
+	if (0) {
+found_inode:
+		unlock_super(sb);
+		if (!(inode = iget_locked(sb, be32_to_cpu(obj-&gt;objectnode)))) {
+			asfs_debug("ASFS: Strange - no inode allocated.\n");
+			return ERR_PTR(res);
+		}
+		if (inode-&gt;i_state &amp; I_NEW) {
+			asfs_read_locked_inode(inode, obj);
+			unlock_new_inode(inode);
+		}
+		asfs_brelse(bh);
+	}
+	res = 0;
+	dentry-&gt;d_op = &amp;asfs_dentry_operations;
+	d_add(dentry, inode);
+	return ERR_PTR(res);
+}
diff -puN /dev/null fs/asfs/extents.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/extents.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,586 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+	/* This function looks for the BNode equal to the key.  If no
+	   exact match is available then the BNode which is slightly
+	   lower than key will be returned.  If no such BNode exists
+	   either, then the first BNode in this block is returned.
+
+	   This function will return the first BNode even if there
+	   are no BNode's at all in this block (this can only happen
+	   for the Root of the tree).  Be sure to check if the Root
+	   is not empty before calling this function. */
+
+static struct BNode *searchforbnode(u32 key, struct BTreeContainer *tc)
+{
+	struct BNode *tn;
+	s16 n = be16_to_cpu(tc-&gt;nodecount) - 1;
+
+	tn = (struct BNode *) ((u8 *) tc-&gt;bnode + n * tc-&gt;nodesize);
+	for (;;) {
+		if (n &lt;= 0 || key &gt;= be32_to_cpu(tn-&gt;key))
+			return tn;
+
+		tn = (struct BNode *) ((u8 *) tn - tc-&gt;nodesize);
+		n--;
+	}
+}
+
+/* This function finds the BNode with the given key.  If no exact match can be
+   found then this function will return either the next or previous closest
+   match (don't rely on this).
+
+   If there were no BNode's at all, then *returned_bh will be NULL. */
+
+static int findbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode)
+{
+	u32 rootblock = ASFS_SB(sb)-&gt;extentbnoderoot;
+
+	asfs_debug("findbnode: Looking for BNode with key %d\n", key);
+
+	while ((*returned_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) {
+		struct fsBNodeContainer *bnc = (void *) (*returned_bh)-&gt;b_data;
+		struct BTreeContainer *btc = &amp;bnc-&gt;btc;
+
+		if (btc-&gt;nodecount == 0) {
+			*returned_bnode = NULL;
+			break;
+		}
+
+		*returned_bnode = searchforbnode(key, btc);
+		if (btc-&gt;isleaf == TRUE)
+			break;
+
+		rootblock = be32_to_cpu((*returned_bnode)-&gt;data);
+		asfs_brelse(*returned_bh);
+	}
+
+	if (*returned_bh == NULL)
+		return -EIO;
+
+	return 0;
+}
+
+int asfs_getextent(struct super_block *sb, u32 key, struct buffer_head **ret_bh, struct fsExtentBNode **ret_ebn)
+{
+	int result;
+	if ((result = findbnode(sb, key, ret_bh, (struct BNode **)ret_ebn)) == 0)
+		if (be32_to_cpu((*ret_ebn)-&gt;key) != key) {
+			brelse(*ret_bh);
+			*ret_bh = NULL;
+			return -ENOENT;
+		}
+
+	return result;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+	/* This routine inserts a node sorted into a BTreeContainer.  It does
+	   this by starting at the end, and moving the nodes one by one to
+	   a higher slot until the empty slot has the correct position for
+	   this key.  Donot use this function on completely filled
+	   BTreeContainers! */
+
+static struct BNode *insertbnode(u32 key, struct BTreeContainer *btc)
+{
+	struct BNode *bn;
+	bn = (struct BNode *) ((u8 *) btc-&gt;bnode + btc-&gt;nodesize * (be16_to_cpu(btc-&gt;nodecount) - 1));
+
+	for (;;) {
+		if (bn &lt; btc-&gt;bnode || key &gt; be32_to_cpu(bn-&gt;key)) {
+			bn = (struct BNode *) ((u8 *) bn + btc-&gt;nodesize);
+			bn-&gt;key = cpu_to_be32(key);
+			btc-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc-&gt;nodecount) + 1);
+			break;
+		} else
+			memmove((u8 *)bn + btc-&gt;nodesize, bn, btc-&gt;nodesize);
+
+		bn = (struct BNode *) ((u8 *) bn - btc-&gt;nodesize);
+	}
+
+	return bn;
+}
+
+static int getparentbtreecontainer(struct super_block *sb, struct buffer_head *bh, struct buffer_head **parent_bh)
+{
+	u32 rootblock = ASFS_SB(sb)-&gt;extentbnoderoot;
+	u32 childkey = be32_to_cpu(((struct fsBNodeContainer *) bh-&gt;b_data)-&gt;btc.bnode[0].key);
+	u32 childblock = be32_to_cpu(((struct fsBNodeContainer *) bh-&gt;b_data)-&gt;bheader.ownblock);
+
+	asfs_debug("getparentbtreecontainer: Getting parent of block %d\n", childblock);
+
+	/* This function gets the BTreeContainer parent of the passed in buffer_head. If
+	   there is no parent this function sets dest_cont io_bh to NULL */
+
+	if (rootblock != childblock) {
+		while ((*parent_bh = asfs_breadcheck(sb, rootblock, ASFS_BNODECONTAINER_ID))) {
+			struct fsBNodeContainer *bnc = (void *) (*parent_bh)-&gt;b_data;
+			struct BTreeContainer *btc = &amp;bnc-&gt;btc;
+			struct BNode *bn;
+			s16 n = be16_to_cpu(btc-&gt;nodecount);
+
+			if (btc-&gt;isleaf == TRUE) {
+				asfs_brelse(*parent_bh);
+				break;
+			}
+
+			while (n-- &gt; 0)
+				if (be32_to_cpu(btc-&gt;bnode[n].data) == childblock)
+					return 0;	/* Found parent!! */
+
+			bn = searchforbnode(childkey, btc);	/* This searchforbnode() doesn't have to get EXACT key matches. */
+			rootblock = be32_to_cpu(bn-&gt;data);
+			asfs_brelse(*parent_bh);
+		}
+		if (*parent_bh == NULL)
+			return -EIO;
+	}
+
+	*parent_bh = NULL;
+	return 0;
+}
+
+/* Spits a btreecontainer. It realses passed in bh! */
+
+static int splitbtreecontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	struct buffer_head *bhparent;
+	struct BNode *bn;
+	int errorcode;
+
+	asfs_debug("splitbtreecontainer: splitting block %u\n", be32_to_cpu(((struct fsBlockHeader *) bh-&gt;b_data)-&gt;ownblock));
+
+	if ((errorcode = getparentbtreecontainer(sb, bh, &amp;bhparent)) == 0) {
+		if (bhparent == NULL) {
+			u32 newbcontblock;
+			u32 bcontblock;
+			/* We need to create Root tree-container - adding new level to extent tree */
+
+			asfs_debug("splitbtreecontainer: creating root tree-container.\n");
+
+			bhparent = bh;
+			if ((errorcode = asfs_allocadminspace(sb, &amp;newbcontblock)) == 0 &amp;&amp; (bh = asfs_getzeroblk(sb, newbcontblock))) {
+				struct fsBNodeContainer *bnc = (void *) bh-&gt;b_data;
+				struct fsBNodeContainer *bncparent = (void *) bhparent-&gt;b_data;
+				struct BTreeContainer *btcparent = &amp;bncparent-&gt;btc;
+
+				bcontblock = be32_to_cpu(bncparent-&gt;bheader.ownblock);
+				memcpy(bh-&gt;b_data, bhparent-&gt;b_data, sb-&gt;s_blocksize);
+				bnc-&gt;bheader.ownblock = cpu_to_be32(newbcontblock);
+				asfs_bstore(sb, bh);
+
+				memset(bhparent-&gt;b_data, '\0', sb-&gt;s_blocksize);	/* Not strictly needed, but makes things more clear. */
+				bncparent-&gt;bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID);
+				bncparent-&gt;bheader.ownblock = cpu_to_be32(bcontblock);
+				btcparent-&gt;isleaf = FALSE;
+				btcparent-&gt;nodesize = sizeof(struct BNode);
+				btcparent-&gt;nodecount = 0;
+
+				bn = insertbnode(0, btcparent);
+				bn-&gt;data = cpu_to_be32(newbcontblock);
+
+				asfs_bstore(sb, bhparent);
+			}
+			if (bh == NULL)
+				errorcode = -EIO;
+		}
+
+		if (errorcode == 0) {
+			struct fsBNodeContainer *bncparent = (void *) bhparent-&gt;b_data;
+			struct BTreeContainer *btcparent = &amp;bncparent-&gt;btc;
+			int branches1 = (sb-&gt;s_blocksize - sizeof(struct fsBNodeContainer)) / btcparent-&gt;nodesize;
+
+			if (be16_to_cpu(btcparent-&gt;nodecount) == branches1) {
+				/* We need to split the parent tree-container first! */
+				if ((errorcode = splitbtreecontainer(sb, bhparent)) == 0) {
+					/* bhparent might have changed after the split and has been released */
+					if ((errorcode = getparentbtreecontainer(sb, bh, &amp;bhparent)) == 0) {
+						bncparent = (void *) bhparent-&gt;b_data;
+						btcparent = &amp;bncparent-&gt;btc;
+					}
+				}
+			}
+
+			if (errorcode == 0) {
+				u32 newbcontblock;
+				struct buffer_head *bhnew;
+
+				/* We can split this container and add it to the parent
+				   because the parent has enough room. */
+
+				if ((errorcode = asfs_allocadminspace(sb, &amp;newbcontblock)) == 0 &amp;&amp; (bhnew = asfs_getzeroblk(sb, newbcontblock))) {
+					struct fsBNodeContainer *bncnew = (void *) bhnew-&gt;b_data;
+					struct BTreeContainer *btcnew = &amp;bncnew-&gt;btc;
+					struct fsBNodeContainer *bnc = (void *) bh-&gt;b_data;
+					struct BTreeContainer *btc = &amp;bnc-&gt;btc;
+					int branches2 = (sb-&gt;s_blocksize - sizeof(struct fsBNodeContainer)) / btc-&gt;nodesize;
+					u32 newkey;
+
+					bncnew-&gt;bheader.id = cpu_to_be32(ASFS_BNODECONTAINER_ID);
+					bncnew-&gt;bheader.ownblock = cpu_to_be32(newbcontblock);
+
+					btcnew-&gt;isleaf = btc-&gt;isleaf;
+					btcnew-&gt;nodesize = btc-&gt;nodesize;
+
+					btcnew-&gt;nodecount = cpu_to_be16(branches2 - branches2 / 2);
+
+					memcpy(btcnew-&gt;bnode, (u8 *) btc-&gt;bnode + branches2 / 2 * btc-&gt;nodesize, (branches2 - branches2 / 2) * btc-&gt;nodesize);
+					newkey = be32_to_cpu(btcnew-&gt;bnode[0].key);
+
+					asfs_bstore(sb, bhnew);
+					asfs_brelse(bhnew);
+
+					btc-&gt;nodecount = cpu_to_be16(branches2 / 2);
+					asfs_bstore(sb, bh);
+
+					bn = insertbnode(newkey, btcparent);
+					bn-&gt;data = cpu_to_be32(newbcontblock);
+					asfs_bstore(sb, bhparent);
+				}
+			}
+		}
+		asfs_brelse(bhparent);
+	}
+	asfs_brelse(bh);
+
+	return errorcode;
+}
+
+/* Returns created extentbnode - returned_bh need to saved and realesed in caller funkction! */
+
+int createextentbnode(struct super_block *sb, u32 key, struct buffer_head **returned_bh, struct BNode **returned_bnode)
+{
+	int errorcode;
+
+	asfs_debug("createbnode: Creating BNode with key %d\n", key);
+
+	while ((errorcode = findbnode(sb, key, returned_bh, returned_bnode)) == 0) {
+		struct fsBNodeContainer *bnc = (void *) (*returned_bh)-&gt;b_data;
+		struct BTreeContainer *btc = &amp;bnc-&gt;btc;
+		int extbranches = (sb-&gt;s_blocksize - sizeof(struct fsBNodeContainer)) / btc-&gt;nodesize;
+
+		asfs_debug("createbnode: findbnode found block %d\n", be32_to_cpu(((struct fsBlockHeader *) (*returned_bh)-&gt;b_data)-&gt;ownblock));
+
+		if (be16_to_cpu(btc-&gt;nodecount) &lt; extbranches) {
+			/* Simply insert new node in this BTreeContainer */
+			asfs_debug("createbnode: Simple insert\n");
+			*returned_bnode = insertbnode(key, btc);
+			break;
+		} else if ((errorcode = splitbtreecontainer(sb, *returned_bh)) != 0)
+			break;
+
+		/* Loop and try insert it the normal way again :-) */
+	}
+
+	return (errorcode);
+}
+
+
+/* This routine removes a node from a BTreeContainer indentified
+   by its key.  If no such key exists this routine does nothing.
+   It correctly handles empty BTreeContainers. */
+
+static void removebnode(u32 key, struct BTreeContainer *btc)
+{
+	struct BNode *bn = btc-&gt;bnode;
+	int n = 0;
+
+	asfs_debug("removebnode: key %d\n", key);
+
+	while (n &lt; be16_to_cpu(btc-&gt;nodecount)) {
+		if (be32_to_cpu(bn-&gt;key) == key) {
+			btc-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc-&gt;nodecount) - 1);
+			memmove(bn, (u8 *) bn + btc-&gt;nodesize, (be16_to_cpu(btc-&gt;nodecount) - n) * btc-&gt;nodesize);
+			break;
+		}
+		bn = (struct BNode *) ((u8 *) bn + btc-&gt;nodesize);
+		n++;
+	}
+}
+
+int asfs_deletebnode(struct super_block *sb, struct buffer_head *bh, u32 key)
+{
+	struct fsBNodeContainer *bnc1 = (void *) bh-&gt;b_data;
+	struct BTreeContainer *btc = &amp;bnc1-&gt;btc;
+	u16 branches = (sb-&gt;s_blocksize - sizeof(struct fsBNodeContainer)) / btc-&gt;nodesize;
+	int errorcode = 0;
+
+	/* Deletes specified internal node. */
+
+	removebnode(key, btc);
+	asfs_bstore(sb, bh);
+
+	/* Now checks if the container still contains enough nodes,
+	   and takes action accordingly. */
+
+	asfs_debug("deletebnode: branches = %d, btc-&gt;nodecount = %d\n", branches, be16_to_cpu(btc-&gt;nodecount));
+
+	if (be16_to_cpu(btc-&gt;nodecount) &lt; (branches + 1) / 2) {
+		struct buffer_head *bhparent;
+		struct buffer_head *bhsec;
+
+		/* nodecount has become to low.  We need to merge this Container
+		   with a neighbouring Container, or we need to steal a few nodes
+		   from a neighbouring Container. */
+
+		/* We get the parent of the container here, so we can find out what
+		   containers neighbour the container which currently hasn't got enough nodes. */
+
+		if ((errorcode = getparentbtreecontainer(sb, bh, &amp;bhparent)) == 0) {
+			if (bhparent != NULL) {
+				struct fsBNodeContainer *bncparent = (void *) bhparent-&gt;b_data;
+				struct BTreeContainer *btcparent = &amp;bncparent-&gt;btc;
+				s16 n;
+
+				asfs_debug("deletebnode: get parent returned block %d.\n", be32_to_cpu(((struct fsBlockHeader *) bhparent-&gt;b_data)-&gt;ownblock));
+
+				for (n = 0; n &lt; be16_to_cpu(btcparent-&gt;nodecount); n++)
+					if (btcparent-&gt;bnode[n].data == bnc1-&gt;bheader.ownblock)
+						break;
+				/* n is now the offset of our own bnode. */
+
+				if (n &lt; be16_to_cpu(btcparent-&gt;nodecount) - 1) {	/* Check if we have a next neighbour. */
+					asfs_debug("deletebnode: using next container - merging blocks %d and %d\n", be32_to_cpu(bnc1-&gt;bheader.ownblock), be32_to_cpu(btcparent-&gt;bnode[n+1].data));
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent-&gt;bnode[n + 1].data), ASFS_BNODECONTAINER_ID))) {
+						struct fsBNodeContainer *bnc_next = (void *) bhsec-&gt;b_data;
+						struct BTreeContainer *btc_next = &amp;bnc_next-&gt;btc;
+
+						if (be16_to_cpu(btc_next-&gt;nodecount) + be16_to_cpu(btc-&gt;nodecount) &gt; branches) {	/* Check if we need to steal nodes. */
+							s16 nodestosteal = (be16_to_cpu(btc_next-&gt;nodecount) + be16_to_cpu(btc-&gt;nodecount)) / 2 - be16_to_cpu(btc-&gt;nodecount);
+
+							/* Merging them is not possible.  Steal a few nodes then. */
+							memcpy((u8 *) btc-&gt;bnode + be16_to_cpu(btc-&gt;nodecount) * btc-&gt;nodesize, btc_next-&gt;bnode, nodestosteal * btc-&gt;nodesize);
+							btc-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc-&gt;nodecount) + nodestosteal);
+							asfs_bstore(sb, bh);
+
+							memcpy(btc_next-&gt;bnode, (u8 *) btc_next-&gt;bnode + btc_next-&gt;nodesize * nodestosteal,
+							       btc-&gt;nodesize * (be16_to_cpu(btc_next-&gt;nodecount) - nodestosteal));
+							btc_next-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc_next-&gt;nodecount) - nodestosteal);
+							asfs_bstore(sb, bhsec);
+
+							btcparent-&gt;bnode[n + 1].key = btc_next-&gt;bnode[0].key;
+							asfs_bstore(sb, bhparent);
+						} else {	/* Merging is possible. */
+							memcpy((u8 *) btc-&gt;bnode + btc-&gt;nodesize * be16_to_cpu(btc-&gt;nodecount), btc_next-&gt;bnode, btc-&gt;nodesize * be16_to_cpu(btc_next-&gt;nodecount));
+							btc-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc-&gt;nodecount) + be16_to_cpu(btc_next-&gt;nodecount));
+							asfs_bstore(sb, bh);
+
+							if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec-&gt;b_data)-&gt;ownblock))) == 0)
+								errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent-&gt;bnode[n + 1].key));
+						}
+						asfs_brelse(bhsec);
+					}
+				} else if (n &gt; 0) {	/* Check if we have a previous neighbour. */
+					asfs_debug("deletebnode: using prev container.\n");
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btcparent-&gt;bnode[n - 1].data), ASFS_BNODECONTAINER_ID)) == 0) {
+						struct fsBNodeContainer *bnc2 = (void *) bhsec-&gt;b_data;
+						struct BTreeContainer *btc2 = &amp;bnc2-&gt;btc;
+
+						if (be16_to_cpu(btc2-&gt;nodecount) + be16_to_cpu(btc-&gt;nodecount) &gt; branches) {
+							/* Merging them is not possible.  Steal a few nodes then. */
+							s16 nodestosteal = (be16_to_cpu(btc2-&gt;nodecount) + be16_to_cpu(btc-&gt;nodecount)) / 2 - be16_to_cpu(btc-&gt;nodecount);
+
+							memmove((u8 *) btc-&gt;bnode + nodestosteal * btc-&gt;nodesize, btc-&gt;bnode, be16_to_cpu(btc-&gt;nodecount) * btc-&gt;nodesize);
+							btc-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc-&gt;nodecount) + nodestosteal);
+							memcpy(btc-&gt;bnode, (u8 *) btc2-&gt;bnode + (be16_to_cpu(btc2-&gt;nodecount) - nodestosteal) * btc2-&gt;nodesize, nodestosteal * btc-&gt;nodesize);
+
+							asfs_bstore(sb, bh);
+
+							btc2-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc2-&gt;nodecount) - nodestosteal);
+							asfs_bstore(sb, bhsec);
+
+							btcparent-&gt;bnode[n].key = btc-&gt;bnode[0].key;
+							asfs_bstore(sb, bhparent);
+						} else {	/* Merging is possible. */
+							memcpy((u8 *) btc2-&gt;bnode + be16_to_cpu(btc2-&gt;nodecount) * btc2-&gt;nodesize, btc-&gt;bnode, be16_to_cpu(btc-&gt;nodecount) * btc-&gt;nodesize);
+							btc2-&gt;nodecount = cpu_to_be16(be16_to_cpu(btc2-&gt;nodecount) + be16_to_cpu(btc-&gt;nodecount));
+							asfs_bstore(sb, bhsec);
+
+							if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec-&gt;b_data)-&gt;ownblock))) == 0)
+								errorcode = asfs_deletebnode(sb, bhparent, be32_to_cpu(btcparent-&gt;bnode[n].key));
+						}
+						asfs_brelse(bhsec);
+					}
+				}
+				/*      else
+				   {
+				   // Never happens, except for root and then we don't care.
+				   } */
+			} else if (btc-&gt;nodecount == 1) {
+				/* No parent, so must be root. */
+
+				asfs_debug("deletebnode: no parent so must be root\n");
+
+				if (btc-&gt;isleaf == FALSE) {
+					struct fsBNodeContainer *bnc3 = (void *) bh-&gt;b_data;
+
+					/* The current root has only 1 node.  We now copy the data of this node into the
+					   root and promote that data to be the new root.  The rootblock number stays the
+					   same that way. */
+
+					if ((bhsec = asfs_breadcheck(sb, be32_to_cpu(btc-&gt;bnode[0].data), ASFS_BNODECONTAINER_ID))) {
+						u32 blockno = be32_to_cpu(((struct fsBlockHeader *) bh-&gt;b_data)-&gt;ownblock);
+						memcpy(bh-&gt;b_data, bhsec-&gt;b_data, sb-&gt;s_blocksize);
+						bnc3-&gt;bheader.ownblock = cpu_to_be32(blockno);
+
+						asfs_bstore(sb, bh);
+						errorcode = asfs_freeadminspace(sb, be32_to_cpu(((struct fsBlockHeader *) bhsec-&gt;b_data)-&gt;ownblock));
+						asfs_brelse(bhsec);
+					} else
+						errorcode = -EIO;
+				}
+				/* If not, then root contains leafs. */
+			}
+
+			asfs_debug("deletebnode: almost done\n");
+			/* otherwise, it must be the root, and the root is allowed
+			   to contain less than the minimum amount of nodes. */
+
+		}
+		if (bhparent != NULL)
+			asfs_brelse(bhparent);
+	}
+
+	return errorcode;
+}
+
+   /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it.
+      This function DOES NOT fix the next pointer in a possible fsExtentBNode which
+      might have been pointing to the first BNode we are deleting.  Make sure you check
+      this yourself, if needed.
+
+      If key is zero, than this function does nothing. */
+
+int asfs_deleteextents(struct super_block *sb, u32 key)
+{
+	struct buffer_head *bh;
+	struct fsExtentBNode *ebn;
+	int errorcode = 0;
+
+	asfs_debug("deleteextents: Entry -- deleting extents from key %d\n", key);
+
+	while (key != 0 &amp;&amp; (errorcode = findbnode(sb, key, &amp;bh, (struct BNode **) &amp;ebn)) == 0) {
+		/* node to be deleted located. */
+		key = be32_to_cpu(ebn-&gt;next);
+		if ((errorcode = asfs_freespace(sb, be32_to_cpu(ebn-&gt;key), be16_to_cpu(ebn-&gt;blocks))) != 0)
+			break;
+
+		if ((errorcode = asfs_deletebnode(sb, bh, be32_to_cpu(ebn-&gt;key))) != 0)
+			break;
+
+		asfs_brelse(bh);
+	}
+
+	return (errorcode);
+}
+
+   /* This function adds /blocks/ blocks starting at block /newspace/ to a file
+      identified by /objectnode/ and /lastextentbnode/.  /io_lastextentbnode/ can
+      be zero if there is no ExtentBNode chain attached to this file yet.
+      /blocks/ ranges from 1 to 8192.  To be able to extend Extents which are
+      almost full, it is wise to make this value no higher than 8192 blocks.
+      /io_lastextentbnode/ will contain the new lastextentbnode value when this
+      function completes.
+      If there was no chain yet, then this function will create a new one.  */
+
+int asfs_addblocks(struct super_block *sb, u16 blocks, u32 newspace, u32 objectnode, u32 *io_lastextentbnode)
+{
+	struct buffer_head *bh;
+	struct fsExtentBNode *ebn;
+	int errorcode = 0;
+
+	if (*io_lastextentbnode != 0) {
+		/* There was already a ExtentBNode chain for this file.  Extending it. */
+
+		asfs_debug("  addblocks: Extending existing ExtentBNode chain.\n");
+
+		if ((errorcode = asfs_getextent(sb, *io_lastextentbnode, &amp;bh, &amp;ebn)) == 0) {
+			if (be32_to_cpu(ebn-&gt;key) + be16_to_cpu(ebn-&gt;blocks) == newspace &amp;&amp; be16_to_cpu(ebn-&gt;blocks) + blocks &lt; 65536) {
+				/* It is possible to extent the last ExtentBNode! */
+				asfs_debug("  addblocks: Extending last ExtentBNode.\n");
+
+				ebn-&gt;blocks = cpu_to_be16(be16_to_cpu(ebn-&gt;blocks) + blocks);
+
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+			} else {
+				/* It isn't possible to extent the last ExtentBNode so we create
+				   a new one and link it to the last ExtentBNode. */
+
+				ebn-&gt;next = cpu_to_be32(newspace);
+				asfs_bstore(sb, bh);
+				asfs_brelse(bh);
+
+				if ((errorcode = createextentbnode(sb, newspace, &amp;bh, (struct BNode **) &amp;ebn)) == 0) {
+					asfs_debug("  addblocks: Created new ExtentBNode.\n");
+
+					ebn-&gt;key = cpu_to_be32(newspace);
+					ebn-&gt;prev = cpu_to_be32(*io_lastextentbnode);
+					ebn-&gt;next = 0;
+					ebn-&gt;blocks = cpu_to_be16(blocks);
+
+					*io_lastextentbnode = newspace;
+
+					asfs_bstore(sb, bh);
+					asfs_brelse(bh);
+
+					ASFS_SB(sb)-&gt;block_rovingblockptr = newspace + blocks;
+
+	/* to be changed in the future */
+/*					if (ASFS_SB(sb)-&gt;block_rovingblockptr &gt;= ASFS_SB(sb)-&gt;totalblocks)
+						ASFS_SB(sb)-&gt;block_rovingblockptr = 0;*/
+				}
+			}
+		}
+	} else {
+		/* There is no ExtentBNode chain yet for this file.  Attaching one! */
+		if ((errorcode = createextentbnode(sb, newspace, &amp;bh, (struct BNode **) &amp;ebn)) == 0) {
+			asfs_debug("  addblocks: Created new ExtentBNode chain.\n");
+
+			ebn-&gt;key = cpu_to_be32(newspace);
+			ebn-&gt;prev = cpu_to_be32(objectnode + 0x80000000);
+			ebn-&gt;next = 0;
+			ebn-&gt;blocks = cpu_to_be16(blocks);
+
+			*io_lastextentbnode = newspace;
+
+			asfs_bstore(sb, bh);
+			asfs_brelse(bh);
+
+			ASFS_SB(sb)-&gt;block_rovingblockptr = newspace + blocks;
+
+/*			if (ASFS_SB(sb)-&gt;block_rovingblockptr &gt;= ASFS_SB(sb)-&gt;totalblocks)
+				ASFS_SB(sb)-&gt;block_rovingblockptr = 0;*/
+		}
+	}
+
+	asfs_debug("  addblocks: done.\n");
+
+	return errorcode;
+}
+#endif
diff -puN /dev/null fs/asfs/file.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/file.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,251 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * Copyright (C) 2003,2004  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/pagemap.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+static int
+asfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
+{
+	struct buffer_head *ebn_bh;
+	struct fsExtentBNode extent, *ebn_p;
+	u32 filedata;
+	unsigned long pos;
+	struct super_block *sb = inode-&gt;i_sb;
+#ifdef CONFIG_ASFS_RW
+	int error;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+#endif
+
+	asfs_debug("ASFS: get_block(%lu, %ld, %d)\n", inode-&gt;i_ino, block, create);
+
+	if (block &lt; 0) {
+		printk(KERN_ERR "ASFS: asfsget_block: requested block (%lld) &lt; 0!\n", (unsigned long long)block);
+		return -EIO;
+	} else if (block &gt;= inode-&gt;i_blocks &amp;&amp; !create) {
+		printk(KERN_ERR "ASFS: asfsget_block: strange block request %lld!\n", (unsigned long long)block);
+		return -EIO;
+	}
+
+	if (create)
+#ifdef CONFIG_ASFS_RW
+		ASFS_I(inode)-&gt;modified = TRUE;
+#else
+		return -EROFS;
+#endif
+
+	if (block &lt; inode-&gt;i_blocks)
+		create = 0;
+
+	lock_super(sb);
+
+#ifdef CONFIG_ASFS_RW
+	if (create) {
+		int blockstoadd;
+		u32 newspace, addedblocks;
+
+		blockstoadd = block - inode-&gt;i_blocks + 1;
+
+		if (blockstoadd &lt; ASFS_BLOCKCHUNKS)
+			blockstoadd = ASFS_BLOCKCHUNKS;
+
+		asfs_debug("ASFS get_block: Trying to add %d blocks to file\n", blockstoadd);
+
+		if ((error = asfs_readobject(sb, inode-&gt;i_ino, &amp;bh, &amp;obj)) != 0) {
+			unlock_super(sb);
+			return error;
+		}
+
+		if ((error = asfs_addblockstofile(sb, bh, obj, blockstoadd, &amp;newspace, &amp;addedblocks)) != 0) {
+			asfs_brelse(bh);
+			unlock_super(sb);
+			return error;
+		}
+		ASFS_I(inode)-&gt;mmu_private += addedblocks * sb-&gt;s_blocksize;
+		inode-&gt;i_blocks += addedblocks;
+		ASFS_I(inode)-&gt;ext_cache.key = 0;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.file.data);
+		asfs_brelse(bh);
+	}
+#endif
+
+	if (ASFS_I(inode)-&gt;ext_cache.key &gt; 0 &amp;&amp; ASFS_I(inode)-&gt;ext_cache.startblock &lt;= block) {
+		extent.key = ASFS_I(inode)-&gt;ext_cache.key;
+		extent.next = ASFS_I(inode)-&gt;ext_cache.next;
+		extent.blocks = ASFS_I(inode)-&gt;ext_cache.blocks;
+		pos = ASFS_I(inode)-&gt;ext_cache.startblock;
+	} else {
+		if (asfs_getextent(inode-&gt;i_sb, ASFS_I(inode)-&gt;firstblock, &amp;ebn_bh, &amp;ebn_p) != 0) {
+			unlock_super(sb);
+			return -EIO;
+		}
+		extent.key = be32_to_cpu(ebn_p-&gt;key);
+		extent.next = be32_to_cpu(ebn_p-&gt;next);
+		extent.blocks = be16_to_cpu(ebn_p-&gt;blocks);
+		pos = 0;
+		asfs_brelse(ebn_bh);
+	}
+	ebn_p = &amp;extent;
+	filedata = ebn_p-&gt;next;
+
+	while (pos + ebn_p-&gt;blocks &lt;= block &amp;&amp; ebn_p-&gt;next != 0 &amp;&amp; pos &lt; inode-&gt;i_blocks) {
+		pos += ebn_p-&gt;blocks;
+		if (asfs_getextent(inode-&gt;i_sb, filedata, &amp;ebn_bh, &amp;ebn_p) != 0) {
+			unlock_super(sb);
+			return -EIO;
+		}
+		extent.key = be32_to_cpu(ebn_p-&gt;key);
+		extent.next = be32_to_cpu(ebn_p-&gt;next);
+		extent.blocks = be16_to_cpu(ebn_p-&gt;blocks);
+		ebn_p = &amp;extent;
+		filedata = ebn_p-&gt;next;
+		asfs_brelse(ebn_bh);
+	}
+
+	unlock_super(sb);
+
+	map_bh(bh_result, inode-&gt;i_sb, (sector_t) (ebn_p-&gt;key + block - pos));
+
+	if (create)
+		set_buffer_new(bh_result);
+
+	asfs_debug("ASFS: get_block - mapped block %lu\n", ebn_p-&gt;key + block - pos);
+
+	ASFS_I(inode)-&gt;ext_cache.startblock = pos;
+	ASFS_I(inode)-&gt;ext_cache.key = ebn_p-&gt;key;
+	ASFS_I(inode)-&gt;ext_cache.next = ebn_p-&gt;next;
+	ASFS_I(inode)-&gt;ext_cache.blocks = ebn_p-&gt;blocks;
+
+	return 0;
+}
+
+int asfs_readpage(struct file *file, struct page *page)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return block_read_full_page(page, asfs_get_block);
+}
+
+sector_t asfs_bmap(struct address_space *mapping, sector_t block)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return generic_block_bmap(mapping,block,asfs_get_block);
+}
+
+#ifdef CONFIG_ASFS_RW
+
+int asfs_writepage(struct page *page, struct writeback_control *wbc)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return block_write_full_page(page, asfs_get_block, wbc);
+}
+
+int asfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+	return cont_prepare_write(page, from, to, asfs_get_block, &amp;ASFS_I(page-&gt;mapping-&gt;host)-&gt;mmu_private);
+}
+
+void asfs_truncate(struct inode *inode)
+{
+	struct super_block *sb = inode-&gt;i_sb;
+	struct buffer_head *bh;
+	struct fsObject *obj;
+
+	asfs_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n",
+		 (u32)inode-&gt;i_ino, (u32)ASFS_I(inode)-&gt;mmu_private, (u32)inode-&gt;i_size);
+
+	if (inode-&gt;i_size &gt; ASFS_I(inode)-&gt;mmu_private) {
+		printk("ASFS: enlarging file is not supported yet\n");
+		return;
+	}
+
+	lock_super(sb);
+
+	if ((asfs_readobject(sb, inode-&gt;i_ino, &amp;bh, &amp;obj)) != 0) {
+		unlock_super(sb);
+		return;
+	}
+
+	if (asfs_truncateblocksinfile(sb, bh, obj, inode-&gt;i_size) != 0) {
+		asfs_brelse(bh);
+		unlock_super(sb);
+		return;
+	}
+
+	obj-&gt;object.file.size = cpu_to_be32(inode-&gt;i_size);
+	ASFS_I(inode)-&gt;mmu_private = inode-&gt;i_size;
+	ASFS_I(inode)-&gt;modified = TRUE;
+	inode-&gt;i_blocks = (be32_to_cpu(obj-&gt;object.file.size) + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits;
+	asfs_bstore(sb, bh);
+	asfs_brelse(bh);
+
+	unlock_super(sb);
+}
+
+int asfs_file_open(struct inode *inode, struct file *filp)
+{
+	if (atomic_read(&amp;filp-&gt;f_count) != 1)
+		return 0;
+	asfs_debug("ASFS: file open (node %d)\n", (int)inode-&gt;i_ino);
+	return 0;
+}
+
+int asfs_file_release(struct inode *inode, struct file *filp)
+{
+	int error = 0;
+
+	asfs_debug("ASFS: file release (node %d oc %d)\n", (int)inode-&gt;i_ino, atomic_read(&amp;filp-&gt;f_count));
+
+	if (atomic_read(&amp;filp-&gt;f_count) != 0)
+		return 0;
+
+	if (ASFS_I(inode)-&gt;modified == TRUE) {
+		struct buffer_head *bh;
+		struct fsObject *obj;
+		lock_super(inode-&gt;i_sb);
+
+		if ((error = asfs_readobject(inode-&gt;i_sb, inode-&gt;i_ino, &amp;bh, &amp;obj)) != 0) {
+			unlock_super(inode-&gt;i_sb);
+			return error;
+		}
+
+		obj-&gt;datemodified = cpu_to_be32(inode-&gt;i_mtime.tv_sec - (365*8+2)*24*60*60);
+		if (inode-&gt;i_mode &amp; S_IFREG) {
+			error = asfs_truncateblocksinfile(inode-&gt;i_sb, bh, obj, (u32)inode-&gt;i_size);
+			obj-&gt;object.file.size = cpu_to_be32(inode-&gt;i_size);
+			ASFS_I(inode)-&gt;mmu_private = inode-&gt;i_size;
+			inode-&gt;i_blocks = (be32_to_cpu(obj-&gt;object.file.size) + inode-&gt;i_sb-&gt;s_blocksize - 1) &gt;&gt; inode-&gt;i_sb-&gt;s_blocksize_bits;
+		}
+		asfs_bstore(inode-&gt;i_sb, bh);
+
+		unlock_super(inode-&gt;i_sb);
+
+		asfs_brelse(bh);
+	}
+	ASFS_I(inode)-&gt;modified = FALSE;
+
+	return error;
+}
+
+#endif
diff -puN /dev/null fs/asfs/inode.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/inode.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,426 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta8
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/smp_lock.h&gt;
+#include &lt;linux/time.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include &lt;linux/dirent.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+/* Mapping from our types to the kernel */
+
+static struct address_space_operations asfs_aops = {
+	.readpage	= asfs_readpage,
+	.sync_page	= block_sync_page,
+	.bmap		= asfs_bmap,
+#ifdef CONFIG_ASFS_RW
+	.writepage	= asfs_writepage,
+	.prepare_write = asfs_prepare_write,
+	.commit_write = generic_commit_write,
+#endif
+};
+
+static struct file_operations asfs_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_file_read,
+	.mmap		= generic_file_mmap,
+#ifdef CONFIG_ASFS_RW
+	.write		= generic_file_write,
+	.open		= asfs_file_open,
+	.release	= asfs_file_release,
+	.fsync		= file_fsync,
+#endif
+};
+
+static struct file_operations asfs_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= asfs_readdir,
+};
+
+static struct inode_operations asfs_dir_inode_operations = {
+	.lookup		= asfs_lookup,
+#ifdef CONFIG_ASFS_RW
+	.create		= asfs_create,
+	.unlink		= asfs_unlink,
+	.symlink	= asfs_symlink,
+	.mkdir		= asfs_mkdir,
+	.rmdir		= asfs_rmdir,
+	.rename		= asfs_rename,
+/*	.setattr	= asfs_notify_change,*/
+#endif
+};
+
+static struct inode_operations asfs_file_inode_operations = {
+#ifdef CONFIG_ASFS_RW
+	.truncate	= asfs_truncate,
+/*	.setattr		= asfs_notify_change,*/
+#endif
+};
+
+static struct address_space_operations asfs_symlink_aops = {
+	.readpage	= asfs_symlink_readpage,
+};
+
+static struct inode_operations asfs_symlink_inode_operations = {
+	.readlink	= page_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+#ifdef CONFIG_ASFS_RW
+/*	.setattr	= asfs_notify_change,*/
+#endif
+};
+
+void asfs_read_locked_inode(struct inode *inode, void *arg)
+{
+	struct super_block *sb = inode-&gt;i_sb;
+	struct fsObject *obj = arg;
+
+	inode-&gt;i_mode = ASFS_SB(sb)-&gt;mode;
+	inode-&gt;i_mtime.tv_sec = inode-&gt;i_atime.tv_sec = inode-&gt;i_ctime.tv_sec = be32_to_cpu(obj-&gt;datemodified) + (365*8+2)*24*60*60;
+	/* Linux: seconds since 01-01-1970, AmigaSFS: seconds since 01-01-1978 */
+	inode-&gt;i_mtime.tv_nsec = inode-&gt;i_ctime.tv_nsec = inode-&gt;i_atime.tv_nsec = 0;
+	inode-&gt;i_uid = ASFS_SB(sb)-&gt;uid;
+	inode-&gt;i_gid = ASFS_SB(sb)-&gt;gid;
+
+	asfs_debug("asfs_read_inode2: Setting-up node %lu... ", inode-&gt;i_ino);
+
+	if (obj-&gt;bits &amp; OTYPE_DIR) {
+		asfs_debug("dir (FirstdirBlock: %u, HashTable %u)\n", \
+		           be32_to_cpu(obj-&gt;object.dir.firstdirblock), be32_to_cpu(obj-&gt;object.dir.hashtable));
+
+		inode-&gt;i_size = 0;
+		inode-&gt;i_op = &amp;asfs_dir_inode_operations;
+		inode-&gt;i_fop = &amp;asfs_dir_operations;
+		inode-&gt;i_mode |= S_IFDIR | ((inode-&gt;i_mode &amp; 0400) ? 0100 : 0) |
+		              ((inode-&gt;i_mode &amp; 0040) ? 0010 : 0) | ((inode-&gt;i_mode &amp; 0004) ? 0001 : 0);
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.dir.firstdirblock);
+		ASFS_I(inode)-&gt;hashtable = be32_to_cpu(obj-&gt;object.dir.hashtable);
+		ASFS_I(inode)-&gt;modified = 0;
+	} else if (obj-&gt;bits &amp; OTYPE_LINK &amp;&amp; !(obj-&gt;bits &amp; OTYPE_HARDLINK)) {
+		asfs_debug("symlink\n");
+		inode-&gt;i_size = 0;
+		inode-&gt;i_op = &amp;asfs_symlink_inode_operations;
+		inode-&gt;i_mapping-&gt;a_ops = &amp;asfs_symlink_aops;
+		inode-&gt;i_mode |= S_IFLNK | S_IRWXUGO;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.file.data);
+	} else {
+		asfs_debug("file (Size: %u, FirstBlock: %u)\n", be32_to_cpu(obj-&gt;object.file.size), be32_to_cpu(obj-&gt;object.file.data));
+		inode-&gt;i_size = be32_to_cpu(obj-&gt;object.file.size);
+		inode-&gt;i_blocks = (be32_to_cpu(obj-&gt;object.file.size) + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits;
+		inode-&gt;i_op = &amp;asfs_file_inode_operations;
+		inode-&gt;i_fop = &amp;asfs_file_operations;
+		inode-&gt;i_mapping-&gt;a_ops = &amp;asfs_aops;
+		inode-&gt;i_mode |= S_IFREG;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.file.data);
+		ASFS_I(inode)-&gt;ext_cache.startblock = 0;
+		ASFS_I(inode)-&gt;ext_cache.key = 0;
+		ASFS_I(inode)-&gt;mmu_private = inode-&gt;i_size;
+	}
+	return;
+}
+
+struct inode *asfs_get_root_inode(struct super_block *sb)
+{
+	struct inode *result = NULL;
+	struct fsObject *obj;
+	struct buffer_head *bh;
+
+	asfs_debug("asfs_get_root_inode\n");
+
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		obj = &amp;(((struct fsObjectContainer *)bh-&gt;b_data)-&gt;object[0]);
+		if (be32_to_cpu(obj-&gt;objectnode) &gt; 0)
+			result = iget_locked(sb, be32_to_cpu(obj-&gt;objectnode));
+
+		if (result != NULL &amp;&amp; result-&gt;i_state &amp; I_NEW) {
+			asfs_read_locked_inode(result, obj);
+			unlock_new_inode(result);
+		}
+		asfs_brelse(bh);
+	}
+	return result;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+static void asfs_sync_dir_inode(struct inode *dir, struct fsObject *obj)
+{
+	ASFS_I(dir)-&gt;firstblock = be32_to_cpu(obj-&gt;object.dir.firstdirblock);
+	ASFS_I(dir)-&gt;modified = 1;
+	dir-&gt;i_mtime = dir-&gt;i_atime = dir-&gt;i_ctime = CURRENT_TIME;
+	obj-&gt;datemodified = cpu_to_be32(dir-&gt;i_mtime.tv_sec - (365*8+2)*24*60*60);
+}
+
+enum { it_file, it_dir, it_link };
+
+static int asfs_create_object(struct inode *dir, struct dentry *dentry, int mode, int type, const char *symname)
+{
+	int error;
+	struct super_block *sb = dir-&gt;i_sb;
+	struct inode *inode;
+	struct buffer_head *bh, *dir_bh;
+	struct fsObject obj_data, *dir_obj, *obj;
+	u8 *name = (u8 *) dentry-&gt;d_name.name;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_debug("asfs_create_obj %s in dir node %d\n", name, (int)dir-&gt;i_ino);
+
+	asfs_translate(bufname, name, ASFS_SB(sb)-&gt;nls_disk, ASFS_SB(sb)-&gt;nls_io, ASFS_MAXFN_BUF);
+	if ((error = asfs_check_name(bufname, strlen(bufname))) != 0)
+		return error;
+
+	sb = dir-&gt;i_sb;
+	inode = new_inode(sb);
+	if (!inode)
+		return -ENOMEM;
+
+	memset(&amp;obj_data, 0, sizeof(struct fsObject));
+
+	obj_data.protection = cpu_to_be32(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
+	obj_data.datemodified = cpu_to_be32(inode-&gt;i_mtime.tv_sec - (365*8+2)*24*60*60);
+	switch (type) {
+	case it_dir:
+		obj_data.bits = OTYPE_DIR;
+		break;
+	case it_link:
+		obj_data.bits = OTYPE_LINK;
+		break;
+	default:
+		break;
+	}
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, dir-&gt;i_ino, &amp;dir_bh, &amp;dir_obj)) != 0) {
+		dec_count(inode);
+		unlock_super(sb);
+		return error;
+	}
+
+	bh = dir_bh;
+	obj = dir_obj;
+
+	if ((error = asfs_createobject(sb, &amp;bh, &amp;obj, &amp;obj_data, bufname, FALSE)) != 0) {
+		asfs_brelse(dir_bh);
+		dec_count(inode);
+		unlock_super(sb);
+		return error;
+	}
+
+	inode-&gt;i_ino = be32_to_cpu(obj-&gt;objectnode);
+	inode-&gt;i_mtime = inode-&gt;i_atime = inode-&gt;i_ctime = CURRENT_TIME;
+	inode-&gt;i_size = inode-&gt;i_blocks = inode-&gt;i_blksize = 0;
+	inode-&gt;i_uid = dir-&gt;i_uid;
+	inode-&gt;i_gid = dir-&gt;i_gid;
+	inode-&gt;i_mode = mode | ASFS_SB(sb)-&gt;mode;
+
+	switch (type) {
+	case it_dir:
+		inode-&gt;i_mode |= S_IFDIR;
+		inode-&gt;i_op = &amp;asfs_dir_inode_operations;
+		inode-&gt;i_fop = &amp;asfs_dir_operations;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.dir.firstdirblock);
+		ASFS_I(inode)-&gt;hashtable = be32_to_cpu(obj-&gt;object.dir.hashtable);
+		ASFS_I(inode)-&gt;modified = 0;
+		break;
+	case it_file:
+		inode-&gt;i_mode |= S_IFREG;
+		inode-&gt;i_op = &amp;asfs_file_inode_operations;
+		inode-&gt;i_fop = &amp;asfs_file_operations;
+		inode-&gt;i_mapping-&gt;a_ops = &amp;asfs_aops;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.file.data);
+		ASFS_I(inode)-&gt;ext_cache.startblock = 0;
+		ASFS_I(inode)-&gt;ext_cache.key = 0;
+		ASFS_I(inode)-&gt;mmu_private = inode-&gt;i_size;
+		break;
+	case it_link:
+		inode-&gt;i_mode = S_IFLNK | S_IRWXUGO;
+		inode-&gt;i_op = &amp;page_symlink_inode_operations;
+		inode-&gt;i_mapping-&gt;a_ops = &amp;asfs_symlink_aops;
+		ASFS_I(inode)-&gt;firstblock = be32_to_cpu(obj-&gt;object.file.data);
+		error = asfs_write_symlink(inode, symname);
+		break;
+	default:
+		break;
+	}
+
+	asfs_bstore(sb, bh);
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+	d_instantiate(dentry, inode);
+	asfs_sync_dir_inode(dir, dir_obj);
+	asfs_bstore(sb, dir_bh);
+
+	unlock_super(sb);
+	asfs_brelse(bh);
+	asfs_brelse(dir_bh);
+
+	return error;
+}
+
+int asfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
+{
+	return asfs_create_object(dir, dentry, mode, it_file, NULL);
+}
+
+int asfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	return asfs_create_object(dir, dentry, mode, it_dir, NULL);
+}
+
+int asfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+	return asfs_create_object(dir, dentry, 0, it_link, symname);
+}
+
+int asfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+
+	if (ASFS_I(dentry-&gt;d_inode)-&gt;firstblock != 0)
+		return -ENOTEMPTY;
+
+	return asfs_unlink(dir, dentry);
+}
+
+int asfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry-&gt;d_inode;
+	int error;
+	struct super_block *sb = dir-&gt;i_sb;
+	struct buffer_head *bh, *dir_bh;
+	struct fsObject *dir_obj, *obj;
+
+	asfs_debug("ASFS: %s\n", __FUNCTION__);
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, inode-&gt;i_ino, &amp;bh, &amp;obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_deleteobject(sb, bh, obj)) != 0) {
+		asfs_brelse(bh);
+		unlock_super(sb);
+		return error;
+	}
+	asfs_brelse(bh);
+
+	/* directory data could change after removing the object */
+	if ((error = asfs_readobject(sb, dir-&gt;i_ino, &amp;dir_bh, &amp;dir_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+
+	asfs_sync_dir_inode(dir, dir_obj);
+	asfs_bstore(sb, dir_bh);
+
+	dec_count(inode);
+	unlock_super(sb);
+	asfs_brelse(dir_bh);
+
+	return 0;
+}
+
+int asfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct super_block *sb = old_dir-&gt;i_sb;
+	struct buffer_head *src_bh, *old_bh, *new_bh;
+	int error;
+	struct fsObject *src_obj, *old_obj, *new_obj;
+	u8 bufname[ASFS_MAXFN_BUF];
+
+	asfs_debug("ASFS: rename (old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+		 (u32)old_dir-&gt;i_ino, (int)old_dentry-&gt;d_name.len, old_dentry-&gt;d_name.name,
+		 (u32)new_dir-&gt;i_ino, (int)new_dentry-&gt;d_name.len, new_dentry-&gt;d_name.name);
+
+	asfs_translate(bufname, (u8 *) new_dentry-&gt;d_name.name, ASFS_SB(sb)-&gt;nls_disk, ASFS_SB(sb)-&gt;nls_io, ASFS_MAXFN_BUF);
+	if ((error = asfs_check_name(bufname, strlen(bufname))) != 0)
+		return error;
+
+
+	/* Unlink destination if it already exists */
+	if (new_dentry-&gt;d_inode)
+		if ((error = asfs_unlink(new_dir, new_dentry)) != 0)
+			return error;
+
+	lock_super(sb);
+
+	if ((error = asfs_readobject(sb, old_dentry-&gt;d_inode-&gt;i_ino, &amp;src_bh, &amp;src_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_readobject(sb, new_dir-&gt;i_ino, &amp;new_bh, &amp;new_obj)) != 0) {
+		asfs_brelse(src_bh);
+		unlock_super(sb);
+		return error;
+	}
+
+	if ((error = asfs_renameobject(sb, src_bh, src_obj, new_bh, new_obj, bufname)) != 0) {
+		asfs_brelse(src_bh);
+		asfs_brelse(new_bh);
+		unlock_super(sb);
+		return error;
+	}
+	asfs_brelse(src_bh);
+	asfs_brelse(new_bh);
+
+	if ((error = asfs_readobject(sb, old_dir-&gt;i_ino, &amp;old_bh, &amp;old_obj)) != 0) {
+		unlock_super(sb);
+		return error;
+	}
+	if ((error = asfs_readobject(sb, new_dir-&gt;i_ino, &amp;new_bh, &amp;new_obj)) != 0) {
+		asfs_brelse(old_bh);
+		unlock_super(sb);
+		return error;
+	}
+
+	asfs_sync_dir_inode(old_dir, old_obj);
+	asfs_sync_dir_inode(new_dir, new_obj);
+
+	asfs_bstore(sb, new_bh);
+	asfs_bstore(sb, old_bh);
+
+	unlock_super(sb);
+	asfs_brelse(old_bh);
+	asfs_brelse(new_bh);
+
+	mark_inode_dirty(old_dir);
+	mark_inode_dirty(new_dir);
+
+	return 0;
+}
+
+/*
+int asfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry-&gt;d_inode;
+	int error = 0;
+
+	asfs_debug("ASFS: notify_change(%lu,0x%x)\n",inode-&gt;i_ino,attr-&gt;ia_valid);
+
+	error = inode_change_ok(inode,attr);
+
+	return error;
+}
+*/
+#endif
diff -puN /dev/null fs/asfs/Makefile
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/Makefile	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,8 @@
+#
+# Makefile for the linux asfs filesystem routines.
+#
+
+obj-$(CONFIG_ASFS_FS) += asfs.o
+
+asfs-y += dir.o extents.o file.o inode.o namei.o nodes.o objects.o super.o symlink.o
+asfs-$(CONFIG_ASFS_RW) += adminspace.o bitfuncs.o
diff -puN /dev/null fs/asfs/namei.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/namei.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,197 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta10
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/string.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include &lt;linux/string.h&gt;
+#include &lt;linux/nls.h&gt;
+#include "asfs_fs.h"
+
+static inline u8 asfs_upperchar(u8 c)
+{
+	if ((c &gt;= 224 &amp;&amp; c &lt;= 254 &amp;&amp; c != 247) || (c &gt;= 'a' &amp;&amp; c &lt;= 'z'))
+		c -= 32;
+	return (c);
+}
+
+u8 asfs_lowerchar(u8 c)
+{
+	if ((c &gt;= 192 &amp;&amp; c &lt;= 222 &amp;&amp; c != 215) || (c &gt;= 'A' &amp;&amp; c &lt;= 'Z'))
+		c += 32;
+	return (c);
+}
+
+static inline u8 asfs_nls_upperchar(u8 c, struct nls_table *t)
+{
+	if (t) {
+		u8 nc = t-&gt;charset2upper[c];
+		return nc ? nc : c;
+	} else
+		return asfs_upperchar(c);
+}
+
+/* Check if the name is valid for a asfs object. */
+
+inline int asfs_check_name(const u8 *name, int len)
+{
+	int i;
+
+	if (len &gt; ASFS_MAXFN)
+		return -ENAMETOOLONG;
+
+	for (i = 0; i &lt; len; i++)
+		if (name[i] &lt; ' ' || name[i] == ':' || (name[i] &gt; 0x7e &amp;&amp; name[i] &lt; 0xa0))
+			return -EINVAL;
+
+	return 0;
+}
+
+/* Note: the dentry argument is the parent dentry. */
+
+static int asfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+	struct super_block *sb = dentry-&gt;d_inode-&gt;i_sb;
+	const u8 *name = qstr-&gt;name;
+	unsigned long hash;
+	int i;
+	struct nls_table *nls_io = ASFS_SB(sb)-&gt;nls_io;
+
+	i = asfs_check_name(qstr-&gt;name,qstr-&gt;len);
+	if (i)
+		return i;
+
+	hash = init_name_hash();
+
+	if (ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE)
+		for (i=qstr-&gt;len; i &gt; 0; name++, i--)
+			hash = partial_name_hash(*name, hash);
+	else
+		for (i=qstr-&gt;len; i &gt; 0; name++, i--)
+			hash = partial_name_hash(asfs_nls_upperchar(*name, nls_io), hash);
+
+	qstr-&gt;hash = end_name_hash(hash);
+
+	return 0;
+}
+
+static int asfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+	struct super_block *sb = dentry-&gt;d_inode-&gt;i_sb;
+	const u8 *aname = a-&gt;name;
+	const u8 *bname = b-&gt;name;
+	int len;
+	struct nls_table *nls_io = ASFS_SB(sb)-&gt;nls_io;
+
+	/* 'a' is the qstr of an already existing dentry, so the name
+	 * must be valid. 'b' must be validated first.
+	 */
+
+	if (asfs_check_name(b-&gt;name,b-&gt;len))
+		return 1;
+
+	if (a-&gt;len != b-&gt;len)
+		return 1;
+
+	if (ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE) {
+		for (len=a-&gt;len; len &gt; 0; len--)
+			if (*aname++ != *bname++)
+				return 1;
+	} else {
+		for (len=a-&gt;len; len &gt; 0; len--)
+			if (asfs_nls_upperchar(*aname++, nls_io) != asfs_nls_upperchar(*bname++, nls_io))
+				return 1;
+	}
+
+	return 0;
+}
+
+struct dentry_operations asfs_dentry_operations = {
+	d_hash:		asfs_hash_dentry,
+	d_compare:	asfs_compare_dentry,
+};
+
+int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t)
+{
+	if (casesensitive) {
+		while (*s == *ct &amp;&amp; *ct != '\0' &amp;&amp; *ct != '/') {
+			s++;
+			ct++;
+		}
+	} else {
+		while (asfs_nls_upperchar(*s, t) == asfs_nls_upperchar(*ct, t) &amp;&amp; *ct != '\0'
+		       &amp;&amp; *ct != '/') {
+			s++;
+			ct++;
+		}
+	}
+	return (*s == '\0' &amp;&amp; (*ct == '\0' || *ct == '/')) ? 0 : *ct - *s;
+}
+
+u16 asfs_hash(u8 *name, int casesensitive)
+{
+	u16 hashval = 0;
+	while (name[hashval] != 0 &amp;&amp; name[hashval] != '/')
+		hashval++;
+	if (casesensitive) {
+		u8 c = *name;
+		while (c != 0 &amp;&amp; c != '/') {
+			hashval = hashval * 13 + c;
+			c = *++name;
+		}
+	} else {
+		u8 c = *name;
+		while (c != 0 &amp;&amp; c != '/') {
+			hashval = hashval * 13 + asfs_upperchar(c);
+			c = *++name;
+		}
+	}
+	return hashval;
+}
+
+void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from, int limit)
+{
+	wchar_t uni;
+	int i, len;
+	int from_len, to_len = limit;
+
+	if (nls_to) {
+		from_len = strlen(from);
+		for (i=0; i &lt; from_len &amp;&amp; to_len &gt; 1; ) {
+			len = nls_from-&gt;char2uni(&amp;from[i], from_len-i, &amp;uni);
+			if (len &gt; 0) {
+				i += len;
+				len = nls_to-&gt;uni2char(uni, to, to_len);
+				if (len &gt; 0) {
+					to += len;
+					to_len -= len;
+				}
+			} else
+				i++;
+			if (len &lt; 0) {
+				*to++ = '?';
+				to_len--;
+			}
+		}
+		*to = '\0';
+	} else {
+		strncpy (to, from, limit);
+		to[limit-1] = '\0';
+	}
+}
diff -puN /dev/null fs/asfs/nodes.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/nodes.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,455 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+/* Finds a specific node by number. */
+int asfs_getnode(struct super_block *sb, u32 nodeno, struct buffer_head **ret_bh, struct fsObjectNode **ret_node)
+{
+	struct buffer_head *bh;
+	struct fsNodeContainer *nodecont;
+	u32 nodeindex = ASFS_SB(sb)-&gt;objectnoderoot;
+
+	while ((bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) {
+		nodecont = (struct fsNodeContainer *) bh-&gt;b_data;
+
+		if (be32_to_cpu(nodecont-&gt;nodes) == 1) {
+			*ret_node = (struct fsObjectNode *) ((u8 *) nodecont-&gt;node + NODE_STRUCT_SIZE * (nodeno - be32_to_cpu(nodecont-&gt;nodenumber)));
+			*ret_bh = bh;
+			return 0;
+		} else {
+			u16 containerentry = (nodeno - be32_to_cpu(nodecont-&gt;nodenumber)) / be32_to_cpu(nodecont-&gt;nodes);
+			nodeindex = be32_to_cpu(nodecont-&gt;node[containerentry]) &gt;&gt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY);
+		}
+		asfs_brelse(bh);
+	}
+	if (bh == NULL)
+		return -EIO;
+	return -ENOENT;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+	/* Looks for the parent of the passed-in buffer_head (fsNodeContainer)
+	   starting from the root.  It returns an error if any error occured.
+	   If error is 0 and io_bh is NULL as well, then there was no parent (ie,
+	   you asked parent of the root).  Otherwise io_bh should contain the
+	   parent of the passed-in NodeContainer. */
+
+static int parentnodecontainer(struct super_block *sb, struct buffer_head **io_bh)
+{
+	u32 noderoot = ASFS_SB(sb)-&gt;objectnoderoot;
+	u32 childblock = be32_to_cpu(((struct fsBlockHeader *) (*io_bh)-&gt;b_data)-&gt;ownblock);
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (*io_bh)-&gt;b_data)-&gt;nodenumber);
+	int errorcode = 0;
+
+	if (noderoot == childblock) {
+		*io_bh = NULL;
+		return 0;
+	}
+
+	while ((*io_bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) {
+		struct fsNodeContainer *nc = (void *) (*io_bh)-&gt;b_data;
+
+		if (be32_to_cpu(nc-&gt;nodes) == 1) {
+			/* We've descended the tree to a leaf NodeContainer, something
+			   which should never happen if the passed-in io_bh had
+			   contained a valid fsNodeContainer. */
+			printk("ASFS: Failed to locate the parent NodeContainer - node tree is corrupted!\n");
+			*io_bh = NULL;
+			return -EIO;
+		} else {
+			u16 containerentry = (nodenumber - be32_to_cpu(nc-&gt;nodenumber)) / be32_to_cpu(nc-&gt;nodes);
+			noderoot = be32_to_cpu(nc-&gt;node[containerentry]) &gt;&gt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY);
+		}
+
+		if (noderoot == childblock)
+			break;
+
+		asfs_brelse(*io_bh);
+	}
+
+	if (*io_bh == NULL)
+		return -EIO;
+
+	return errorcode;
+}
+
+
+static int isfull(struct super_block *sb, struct fsNodeContainer *nc)
+{
+	u32 *p = nc-&gt;node;
+	s16 n = NODECONT_BLOCK_COUNT;
+
+	while (--n &gt;= 0) {
+		if (*p == 0 || (be32_to_cpu(*p) &amp; 0x00000001) == 0) {
+			break;
+		}
+		p++;
+	}
+
+	return n &lt; 0;
+}
+
+static int markparentfull(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) (bh-&gt;b_data))-&gt;nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &amp;bh)) == 0 &amp;&amp; bh != 0) {
+		struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+		u16 containerentry = (nodenumber - be32_to_cpu(nc-&gt;nodenumber)) / be32_to_cpu(nc-&gt;nodes);
+
+		nc-&gt;node[containerentry] = cpu_to_be32(be32_to_cpu(nc-&gt;node[containerentry]) | 0x00000001);
+
+		asfs_bstore(sb, bh);
+
+		if (isfull(sb, nc)) {	/* This container now is full as well!  Mark the next higher up container too then! */
+			return markparentfull(sb, bh);
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int addnewnodelevel(struct super_block *sb, u16 nodesize)
+{
+	struct buffer_head *bh;
+	u32 noderoot = ASFS_SB(sb)-&gt;objectnoderoot;
+	int errorcode;
+
+	/* Adds a new level to the Node tree. */
+
+	asfs_debug("addnewnodelevel: Entry\n");
+
+	if ((bh = asfs_breadcheck(sb, noderoot, ASFS_NODECONTAINER_ID))) {
+		struct buffer_head *newbh;
+		u32 newblock;
+
+		if ((errorcode = asfs_allocadminspace(sb, &amp;newblock)) == 0 &amp;&amp; (newbh = asfs_getzeroblk(sb, newblock))) {
+			struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+			struct fsNodeContainer *newnc = (void *) newbh-&gt;b_data;
+
+			/* The newly allocated block will become a copy of the current root. */
+
+			newnc-&gt;bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID);
+			newnc-&gt;bheader.ownblock = cpu_to_be32(newblock);
+			newnc-&gt;nodenumber = nc-&gt;nodenumber;
+			newnc-&gt;nodes = nc-&gt;nodes;
+			memcpy(newnc-&gt;node, nc-&gt;node, sb-&gt;s_blocksize - sizeof(struct fsNodeContainer));
+
+			asfs_bstore(sb, newbh);
+			asfs_brelse(newbh);
+
+			/* The current root will now be transformed into a new root. */
+
+			if (be32_to_cpu(nc-&gt;nodes) == 1)
+				nc-&gt;nodes = cpu_to_be32((sb-&gt;s_blocksize - sizeof(struct fsNodeContainer)) / nodesize);
+			else
+				nc-&gt;nodes = cpu_to_be32(be32_to_cpu(nc-&gt;nodes) * NODECONT_BLOCK_COUNT);
+
+			nc-&gt;node[0] = cpu_to_be32((newblock &lt;&lt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY)) + 1);	/* Tree is full from that point! */
+			memset(&amp;nc-&gt;node[1], 0, sb-&gt;s_blocksize - sizeof(struct fsNodeContainer) - 4);
+
+			asfs_bstore(sb, bh);
+		}
+		asfs_brelse(bh);
+	} else
+		errorcode = -EIO;
+
+	return errorcode;
+}
+
+static int createnodecontainer(struct super_block *sb, u32 nodenumber, u32 nodes, u32 * returned_block)
+{
+	struct buffer_head *bh;
+	int errorcode;
+	u32 newblock;
+
+	asfs_debug("createnodecontainer: nodenumber = %u, nodes = %u\n", nodenumber, nodes);
+
+	if ((errorcode = asfs_allocadminspace(sb, &amp;newblock)) == 0 &amp;&amp; (bh = asfs_getzeroblk(sb, newblock))) {
+		struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+
+		nc-&gt;bheader.id = cpu_to_be32(ASFS_NODECONTAINER_ID);
+		nc-&gt;bheader.ownblock = cpu_to_be32(newblock);
+
+		nc-&gt;nodenumber = cpu_to_be32(nodenumber);
+		nc-&gt;nodes = cpu_to_be32(nodes);
+
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+		*returned_block = newblock;
+	}
+
+	return errorcode;
+}
+
+	/* This function creates a new fsNode structure in a fsNodeContainer.  If needed
+	   it will create a new fsNodeContainers and a new fsNodeIndexContainer. */
+
+int asfs_createnode(struct super_block *sb, struct buffer_head **returned_bh, struct fsNode **returned_node, u32 * returned_nodeno)
+{
+	u16 nodecount = (sb-&gt;s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE;
+	u32 noderoot = ASFS_SB(sb)-&gt;objectnoderoot;
+	u32 nodeindex = noderoot;
+	int errorcode = 0;
+
+	while ((*returned_bh = asfs_breadcheck(sb, nodeindex, ASFS_NODECONTAINER_ID))) {
+		struct fsNodeContainer *nc = (void *) (*returned_bh)-&gt;b_data;
+
+		if (be32_to_cpu(nc-&gt;nodes) == 1) {	/* Is it a leaf-container? */
+			struct fsNode *n;
+			s16 i = nodecount;
+
+			n = (struct fsNode *) nc-&gt;node;
+
+			while (i-- &gt; 0) {
+				if (n-&gt;data == 0)
+					break;
+
+				n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+			}
+
+			if (i &gt;= 0) {
+				/* Found an empty fsNode structure! */
+				*returned_node = n;
+				*returned_nodeno = be32_to_cpu(nc-&gt;nodenumber) + ((u8 *) n - (u8 *) nc-&gt;node) / NODE_STRUCT_SIZE;
+
+				asfs_debug("createnode: Created Node %d\n", *returned_nodeno);
+
+				/* Below we continue to look through the NodeContainer block.  We skip the entry
+				   we found to be unused, and see if there are any more unused entries.  If we
+				   do not find any more unused entries then this container is now full. */
+
+				n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+
+				while (i-- &gt; 0) {
+					if (n-&gt;data == 0)
+						break;
+
+					n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+				}
+
+				if (i &lt; 0) {
+					/* No more empty fsNode structures in this block.  Mark parent full. */
+					errorcode = markparentfull(sb, *returned_bh);
+				}
+
+				return errorcode;
+			} else {
+				/* What happened now is that we found a leaf-container which was
+				   completely filled.  In practice this should only happen when there
+				   is only a single NodeContainer (only this container), or when there
+				   was an error in one of the full-bits in a higher level container. */
+
+				if (noderoot != nodeindex) {
+					/*** Hmmm... it looks like there was a damaged full-bit or something.
+					     In this case we'd probably better call markcontainerfull. */
+
+					printk("ASFS: Couldn't find empty Node in NodeContainer while NodeIndexContainer indicated there should be one!\n");
+
+					errorcode = -ENOSPC;
+					break;
+				} else {
+					/* Container is completely filled. */
+
+					if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0)
+						return errorcode;
+
+					nodeindex = noderoot;
+				}
+			}
+		} else {	/* This isn't a leaf container */
+			u32 *p = nc-&gt;node;
+			s16 i = NODECONT_BLOCK_COUNT;
+
+			/* We've read a normal container */
+
+			while (i-- &gt; 0) {
+				if (*p != 0 &amp;&amp; (be32_to_cpu(*p) &amp; 0x00000001) == 0)
+					break;
+
+				p++;
+			}
+
+			if (i &gt;= 0) {
+				/* Found a not completely filled Container */
+
+				nodeindex = be32_to_cpu(*p) &gt;&gt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY);
+			} else {
+				/* Everything in the NodeIndexContainer was completely filled.  There possibly
+				   are some unused pointers in this block however.  */
+
+				asfs_debug("createnode: NodeContainer at block has no empty Nodes.\n");
+
+				p = nc-&gt;node;
+				i = NODECONT_BLOCK_COUNT;
+
+				while (i-- &gt; 0) {
+					if (*p == 0)
+						break;
+
+					p++;
+				}
+
+				if (i &gt;= 0) {
+					u32 newblock;
+					u32 nodes;
+
+					/* Found an unused Container pointer */
+
+					if (be32_to_cpu(nc-&gt;nodes) == (sb-&gt;s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE) {
+						nodes = 1;
+					} else {
+						nodes = be32_to_cpu(nc-&gt;nodes) / NODECONT_BLOCK_COUNT;
+					}
+
+					if ((errorcode = createnodecontainer(sb, be32_to_cpu(nc-&gt;nodenumber) + (p - nc-&gt;node) * be32_to_cpu(nc-&gt;nodes), nodes, &amp;newblock)) != 0) {
+						break;
+					}
+
+					*p = cpu_to_be32(newblock &lt;&lt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY));
+
+					asfs_bstore(sb, *returned_bh);
+				} else {
+					/* Container is completely filled.  This must be the top-level NodeIndex container
+					   as otherwise the full-bit would have been wrong! */
+
+					if ((errorcode = addnewnodelevel(sb, NODE_STRUCT_SIZE)) != 0)
+						break;
+
+					nodeindex = noderoot;
+				}
+			}
+		}
+		asfs_brelse(*returned_bh);
+	}
+
+	if (*returned_bh == NULL)
+		return -EIO;
+
+	return (errorcode);
+}
+
+static int markparentempty(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh-&gt;b_data)-&gt;nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &amp;bh)) == 0 &amp;&amp; bh != 0) {
+		struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+		int wasfull;
+		u16 containerentry = (nodenumber - be32_to_cpu(nc-&gt;nodenumber)) / be32_to_cpu(nc-&gt;nodes);
+
+		wasfull = isfull(sb, nc);
+
+		nc-&gt;node[containerentry] = cpu_to_be32(be32_to_cpu(nc-&gt;node[containerentry]) &amp; ~0x00000001);
+
+		asfs_bstore(sb, bh);
+
+		if (wasfull) {
+			/* This container was completely full before!  Mark the next higher up container too then! */
+			return markparentempty(sb, bh);
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int freecontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	u32 nodenumber = be32_to_cpu(((struct fsNodeContainer *) bh-&gt;b_data)-&gt;nodenumber);
+	int errorcode;
+
+	if ((errorcode = parentnodecontainer(sb, &amp;bh)) == 0 &amp;&amp; bh != NULL) {	/* This line also prevents the freeing of the noderoot. */
+		struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+		u16 containerindex = (nodenumber - be32_to_cpu(nc-&gt;nodenumber)) / be32_to_cpu(nc-&gt;nodes);
+
+		if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(nc-&gt;node[containerindex]) &gt;&gt; (sb-&gt;s_blocksize_bits - ASFS_BLCKFACCURACY))) == 0) {
+			u32 *p = nc-&gt;node;
+			s16 n = NODECONT_BLOCK_COUNT;
+
+			nc-&gt;node[containerindex] = 0;
+			asfs_bstore(sb, bh);
+
+			while (n-- &gt; 0)
+				if (*p++ != 0)
+					break;
+
+			if (n &lt; 0) {	/* This container is now completely empty!  Free this NodeIndexContainer too then! */
+				return freecontainer(sb, bh);
+			}
+		}
+		asfs_brelse(bh);
+	}
+
+	return errorcode;
+}
+
+static int internaldeletenode(struct super_block *sb, struct buffer_head *bh, struct fsNode *n)
+{
+	struct fsNodeContainer *nc = (void *) bh-&gt;b_data;
+	u16 nodecount = (sb-&gt;s_blocksize - sizeof(struct fsNodeContainer)) / NODE_STRUCT_SIZE;
+	s16 i = nodecount;
+	s16 empty = 0;
+	int errorcode = 0;
+
+	n-&gt;data = 0;
+	n = (struct fsNode *) nc-&gt;node;
+
+	while (i-- &gt; 0) {
+		if (n-&gt;data == 0)
+			empty++;
+
+		n = (struct fsNode *) ((u8 *) n + NODE_STRUCT_SIZE);
+	}
+
+	asfs_bstore(sb, bh);
+
+	if (empty == 1)		/* NodeContainer was completely full before, so we need to mark it empty now. */
+		errorcode = markparentempty(sb, bh);
+	else if (empty == nodecount)	/* NodeContainer is now completely empty!  Free it! */
+		errorcode = freecontainer(sb, bh);
+
+	return (errorcode);
+}
+
+int asfs_deletenode(struct super_block *sb, u32 objectnode)
+{
+	struct buffer_head *bh;
+	struct fsObjectNode *on;
+	int errorcode;
+
+	asfs_debug("deletenode: Deleting Node %d\n", objectnode);
+
+	if ((errorcode = asfs_getnode(sb, objectnode, &amp;bh, &amp;on)) == 0)
+		errorcode = internaldeletenode(sb, bh, (struct fsNode *) on);
+
+	asfs_brelse(bh);
+	return (errorcode);
+}
+
+#endif
diff -puN /dev/null fs/asfs/objects.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/objects.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,765 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta7
+ *
+ * This file contains some parts of the original amiga version of
+ * SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber, and Marcin Kurek
+ *
+ * Adapted and modified by Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+
+struct fsObject *asfs_nextobject(struct fsObject *obj)
+{
+	int i;
+	u8 *p = obj-&gt;name;
+
+	for (i = 2; i &gt; 0; p++)
+		if (*p == '\0')
+			i--;
+	if ((p - (u8 *) obj) &amp; 0x01)
+		p++;
+
+	return ((struct fsObject *) p);
+}
+
+struct fsObject *asfs_find_obj_by_name(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name)
+{
+	struct fsObject *obj;
+
+	obj = &amp;(objcont-&gt;object[0]);
+	while (be32_to_cpu(obj-&gt;objectnode) &gt; 0 &amp;&amp; ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 &lt; sb-&gt;s_blocksize) {
+		if (asfs_namecmp(obj-&gt;name, name, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE, NULL) == 0) {
+			asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj-&gt;objectnode), obj-&gt;name, obj-&gt;bits, be32_to_cpu(objcont-&gt;bheader.ownblock));
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+struct fsObject *find_obj_by_node(struct super_block *sb, struct fsObjectContainer *objcont, u32 objnode)
+{
+	struct fsObject *obj;
+
+	obj = &amp;(objcont-&gt;object[0]);
+	while (be32_to_cpu(obj-&gt;objectnode) &gt; 0 &amp;&amp; ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 &lt; sb-&gt;s_blocksize) {
+		if (be32_to_cpu(obj-&gt;objectnode) == objnode) {
+			return obj;
+		}
+		obj = asfs_nextobject(obj);
+	}
+	return NULL;
+}
+
+int asfs_readobject(struct super_block *sb, u32 objectnode, struct buffer_head **bh, struct fsObject **returned_object)
+{
+	struct fsObjectNode *on;
+	int errorcode;
+	u32 contblock;
+
+	asfs_debug("Seaching object - node %d\n", objectnode);
+
+	if ((errorcode = asfs_getnode(sb, objectnode, bh, &amp;on)) != 0)
+		return errorcode;
+	contblock = be32_to_cpu(on-&gt;node.data);
+	asfs_brelse(*bh);
+
+	if (contblock &gt; 0 &amp;&amp; (*bh = asfs_breadcheck(sb, contblock, ASFS_OBJECTCONTAINER_ID))) {
+		*returned_object = find_obj_by_node(sb, (void *) (*bh)-&gt;b_data, objectnode);
+		if (*returned_object == NULL) {
+			brelse(*bh);
+			*bh = NULL;
+			return -ENOENT;
+		}
+		return 0;
+	} else
+		return -EIO;
+}
+
+static int removeobjectcontainer(struct super_block *sb, struct buffer_head *bh)
+{
+	struct fsObjectContainer *oc = (void *) bh-&gt;b_data;
+	int errorcode;
+	struct buffer_head *block;
+
+	asfs_debug("removeobjectcontainer: block %u\n", be32_to_cpu(oc-&gt;bheader.ownblock));
+
+	if (oc-&gt;next != 0 &amp;&amp; oc-&gt;next != oc-&gt;bheader.ownblock) {
+		struct fsObjectContainer *next_oc;
+
+		if ((block = asfs_breadcheck(sb, be32_to_cpu(oc-&gt;next), ASFS_OBJECTCONTAINER_ID)) == NULL)
+			return -EIO;
+
+		next_oc = (void *) block-&gt;b_data;
+		next_oc-&gt;previous = oc-&gt;previous;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	}
+
+	if (oc-&gt;previous != 0 &amp;&amp; oc-&gt;previous != oc-&gt;bheader.ownblock) {
+		struct fsObjectContainer *previous_oc;
+
+		if ((block = asfs_breadcheck(sb, be32_to_cpu(oc-&gt;previous), ASFS_OBJECTCONTAINER_ID)) == NULL)
+			return -EIO;
+
+		previous_oc = (void *) block-&gt;b_data;
+		previous_oc-&gt;next = oc-&gt;next;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	} else {
+		struct fsObject *parent_o;
+
+		if ((errorcode = asfs_readobject(sb, be32_to_cpu(oc-&gt;parent), &amp;block, &amp;parent_o)) != 0)
+			return (errorcode);
+
+		parent_o-&gt;object.dir.firstdirblock = oc-&gt;next;
+
+		asfs_bstore(sb, block);
+		asfs_brelse(block);
+	}
+
+	if ((errorcode = asfs_freeadminspace(sb, be32_to_cpu(oc-&gt;bheader.ownblock))) != 0)
+		return (errorcode);
+
+	return (0);
+}
+
+static int setrecycledinfodiff(struct super_block *sb, s32 deletedfiles, s32 deletedblocks)
+{
+	struct buffer_head *bh;
+
+	if ((bh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsRootInfo *ri = (struct fsRootInfo *) ((u8 *) bh-&gt;b_data + sb-&gt;s_blocksize - sizeof(struct fsRootInfo));
+
+		ri-&gt;deletedfiles = cpu_to_be32(be32_to_cpu(ri-&gt;deletedfiles) + deletedfiles);
+		ri-&gt;deletedblocks = cpu_to_be32(be32_to_cpu(ri-&gt;deletedblocks) + deletedblocks);
+
+		asfs_bstore(sb, bh);
+		asfs_brelse(bh);
+	} else
+		return -EIO;
+	return 0;
+}
+
+	/* This function removes the fsObject structure passed in from the passed
+	   buffer_head.  If the ObjectContainer becomes completely empty it will be
+	   delinked from the ObjectContainer chain and marked free for reuse.
+	   This function doesn't delink the object from the hashchain! */
+
+static int simpleremoveobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	struct fsObjectContainer *oc = (void *) bh-&gt;b_data;
+	int errorcode = 0;
+
+	asfs_debug("simpleremoveobject:\n");
+
+	if (be32_to_cpu(oc-&gt;parent) == ASFS_RECYCLEDNODE) {
+		/* This object is removed from the Recycled directory. */
+		if ((errorcode = setrecycledinfodiff(sb, -1, -((be32_to_cpu(o-&gt;object.file.size) + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits))) != 0)
+			return errorcode;
+	}
+
+	if ((asfs_nextobject(oc-&gt;object))-&gt;name[0] == '\0')
+		errorcode = removeobjectcontainer(sb, bh);
+	else {
+		struct fsObject *nexto;
+		int objlen;
+
+		nexto = asfs_nextobject(o);
+		objlen = (u8 *) nexto - (u8 *) o;
+
+		memmove(o, nexto, sb-&gt;s_blocksize - ((u8 *) nexto - (u8 *) oc));
+		memset((u8 *) oc + sb-&gt;s_blocksize - objlen, 0, objlen);
+
+		asfs_bstore(sb, bh);
+	}
+	return errorcode;
+}
+
+/* This function delinks the passed in ObjectNode from its hash-chain.  Handy when deleting
+   the object, or when renaming/moving it. */
+
+static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode)
+{
+	struct fsObject *o;
+	int errorcode = 0;
+	struct buffer_head *block;
+
+	asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode);
+
+	if ((errorcode = asfs_readobject(sb, parentobjectnode, &amp;block, &amp;o)) == 0 &amp;&amp; o-&gt;object.dir.hashtable != 0) {
+		u32 hashtable = be32_to_cpu(o-&gt;object.dir.hashtable);
+		asfs_brelse(block);
+
+		if ((block = asfs_breadcheck(sb, hashtable, ASFS_HASHTABLE_ID))) {
+			struct buffer_head *node_bh;
+			struct fsObjectNode *onptr, on;
+			struct fsHashTable *ht = (void *) block-&gt;b_data;
+			u32 nexthash;
+
+			if ((errorcode = asfs_getnode(sb, objectnode, &amp;node_bh, &amp;onptr)) == 0) {
+				u16 hashchain;
+
+				asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n");
+
+				hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE));
+				nexthash = be32_to_cpu(ht-&gt;hashentry[hashchain]);
+
+				if (nexthash == objectnode) {
+					/* The hashtable directly points to the fsObject to be delinked.  We simply
+					   modify the Hashtable to point to the new nexthash entry. */
+
+					asfs_debug("dehashobject: The hashtable points directly to the to be delinked object\n");
+
+					ht-&gt;hashentry[hashchain] = onptr-&gt;next;
+					asfs_bstore(sb, block);
+				} else {
+					struct fsObjectNode *onsearch = 0;
+
+					on = *onptr;
+
+					asfs_debug("dehashobject: Walking through hashchain\n");
+
+					while (nexthash != 0 &amp;&amp; nexthash != objectnode) {
+						asfs_brelse(node_bh);
+						if ((errorcode = asfs_getnode(sb, nexthash, &amp;node_bh, &amp;onsearch)) != 0)
+							break;
+						nexthash = be32_to_cpu(onsearch-&gt;next);
+					}
+
+					if (errorcode == 0) {
+						if (nexthash != 0) {
+							/* Previous fsObjectNode found in hash chain.  Modify the fsObjectNode to 'skip' the
+							   ObjectNode which is being delinked from the hash chain. */
+
+							onsearch-&gt;next = on.next;
+							asfs_bstore(sb, node_bh);
+						} else {
+							printk("ASFS: Hashchain of object %d is corrupt or incorrectly linked.", objectnode);
+
+							/*** This is strange.  We have been looking for the fsObjectNode which is located before the
+							     passed in fsObjectNode in the hash-chain.  However, we never found the
+							     fsObjectNode reffered to in the hash-chain!  Has to be somekind
+							     of internal error... */
+
+							errorcode = -ENOENT;
+						}
+					}
+				}
+				asfs_brelse(node_bh);
+			}
+			asfs_brelse(block);
+		}
+	}
+	return errorcode;
+}
+
+
+	/* This function removes an object from any directory.  It takes care
+	   of delinking the object from the hashchain and also frees the
+	   objectnode number. */
+
+static int removeobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	struct fsObjectContainer *oc = (void *) bh-&gt;b_data;
+	int errorcode;
+
+	asfs_debug("removeobject\n");
+
+	if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o-&gt;objectnode), o-&gt;name, be32_to_cpu(oc-&gt;parent))) == 0) {
+		u32 objectnode = be32_to_cpu(o-&gt;objectnode);
+
+		if ((errorcode = simpleremoveobject(sb, bh, o)) == 0)
+			errorcode = asfs_deletenode(sb, objectnode);
+	}
+
+	return (errorcode);
+}
+
+	/* This function deletes the specified object. */
+int asfs_deleteobject(struct super_block *sb, struct buffer_head *bh, struct fsObject *o)
+{
+	int errorcode = 0;
+
+	asfs_debug("deleteobject: Entry -- deleting object %d (%s)\n", be32_to_cpu(o-&gt;objectnode), o-&gt;name);
+
+	if ((o-&gt;bits &amp; OTYPE_DIR) == 0 || o-&gt;object.dir.firstdirblock == 0) {
+		u8 bits = o-&gt;bits;
+		u32 hashblckno = be32_to_cpu(o-&gt;object.dir.hashtable);
+		u32 extentbnode = be32_to_cpu(o-&gt;object.file.data);
+
+		if ((errorcode = removeobject(sb, bh, o)) == 0) {
+			if ((bits &amp; OTYPE_LINK) != 0) {
+				asfs_debug("deleteobject: Object is soft link!\n");
+				errorcode = asfs_freeadminspace(sb, extentbnode);
+			} else if ((bits &amp; OTYPE_DIR) != 0) {
+				asfs_debug("deleteobject: Object is a directory!\n");
+				errorcode = asfs_freeadminspace(sb, hashblckno);
+			} else {
+				asfs_debug("deleteobject: Object is a file\n");
+				if (extentbnode != 0)
+					errorcode = asfs_deleteextents(sb, extentbnode);
+			}
+		}
+	}
+
+	return (errorcode);
+}
+
+	/* This function takes a HashBlock pointer, an ObjectNode and an ObjectName.
+	   If there is a hashblock, then this function will correctly link the object
+	   into the hashchain.  If there isn't a hashblock (=0) then this function
+	   does nothing.  */
+
+static int hashobject(struct super_block *sb, u32 hashblock, struct fsObjectNode *on, u32 nodeno, u8 *objectname)
+{
+	struct buffer_head *hash_bh;
+
+	asfs_debug("hashobject, using hashblock %d\n", hashblock);
+	if (hashblock == 0)
+		return 0;
+
+	if ((hash_bh = asfs_breadcheck(sb, hashblock, ASFS_HASHTABLE_ID))) {
+		struct fsHashTable *ht = (void *) hash_bh-&gt;b_data;
+		u32 nexthash;
+		u16 hashvalue, hashchain;
+
+		hashvalue = asfs_hash(objectname, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE);
+		hashchain = HASHCHAIN(hashvalue);
+		nexthash = be32_to_cpu(ht-&gt;hashentry[hashchain]);
+
+		ht-&gt;hashentry[hashchain] = cpu_to_be32(nodeno);
+
+		asfs_bstore(sb, hash_bh);
+		asfs_brelse(hash_bh);
+
+		on-&gt;next = cpu_to_be32(nexthash);
+		on-&gt;hash16 = cpu_to_be16(hashvalue);
+	} else
+		return -EIO;
+
+	return 0;
+}
+
+	/* This function returns a pointer to the first unused byte in
+	   an ObjectContainer. */
+
+static u8 *emptyspaceinobjectcontainer(struct super_block *sb, struct fsObjectContainer *oc)
+{
+	struct fsObject *o = oc-&gt;object;
+	u8 *endadr;
+
+	endadr = (u8 *) oc + sb-&gt;s_blocksize - sizeof(struct fsObject) - 2;
+
+	while ((u8 *) o &lt; endadr &amp;&amp; o-&gt;name[0] != 0)
+		o = asfs_nextobject(o);
+
+	return (u8 *) o;
+}
+
+	/* This function will look in the directory indicated by io_o
+	   for an ObjectContainer block which contains bytesneeded free
+	   bytes.  If none is found then this function simply creates a
+	   new ObjectContainer and adds that to the indicated directory. */
+
+static int findobjectspace(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, u32 bytesneeded)
+{
+	struct buffer_head *bhparent = *io_bh;
+	struct fsObject *oparent = *io_o;
+	struct buffer_head *bh;
+	u32 nextblock = be32_to_cpu(oparent-&gt;object.dir.firstdirblock);
+	int errorcode = 0;
+
+	asfs_debug("findobjectspace: Looking for %u bytes in directory with ObjectNode number %d (in block %d)\n", bytesneeded, be32_to_cpu((*io_o)-&gt;objectnode),
+		   be32_to_cpu(((struct fsBlockHeader *) (*io_bh)-&gt;b_data)-&gt;ownblock));
+
+	while (nextblock != 0 &amp;&amp; (bh = asfs_breadcheck(sb, nextblock, ASFS_OBJECTCONTAINER_ID))) {
+		struct fsObjectContainer *oc = (void *) bh-&gt;b_data;
+		u8 *emptyspace;
+
+		/* We need to find out how much free space this ObjectContainer has */
+
+		emptyspace = emptyspaceinobjectcontainer(sb, oc);
+
+		if ((u8 *) oc + sb-&gt;s_blocksize - emptyspace &gt;= bytesneeded) {
+			/* We found enough space in one of the ObjectContainer blocks!!
+			   We return a struct fsObject *. */
+			*io_bh = bh;
+			*io_o = (struct fsObject *) emptyspace;
+			break;
+		}
+		nextblock = be32_to_cpu(oc-&gt;next);
+		asfs_brelse(bh);
+	}
+
+	if (nextblock == 0) {
+		u32 newcontblock;
+		/* If we get here, we traversed the *entire* directory (ough!) and found no empty
+		   space large enough for our entry.  We allocate new space and add it to this
+		   directory. */
+
+		if ((errorcode = asfs_allocadminspace(sb, &amp;newcontblock)) == 0 &amp;&amp; (bh = asfs_getzeroblk(sb, newcontblock))) {
+			struct fsObjectContainer *oc = (void *) bh-&gt;b_data;
+			struct buffer_head *bhnext;
+
+			asfs_debug("findobjectspace: No room was found, allocated new block at %u\n", newcontblock);
+
+			/* Allocated new block.  We will now link it to the START of the directory chain
+			   so the new free space can be found quickly when more entries need to be added. */
+
+			oc-&gt;bheader.id = cpu_to_be32(ASFS_OBJECTCONTAINER_ID);
+			oc-&gt;bheader.ownblock = cpu_to_be32(newcontblock);
+			oc-&gt;parent = oparent-&gt;objectnode;
+			oc-&gt;next = oparent-&gt;object.dir.firstdirblock;
+			oc-&gt;previous = 0;
+
+			oparent-&gt;object.dir.firstdirblock = cpu_to_be32(newcontblock);
+
+			asfs_bstore(sb, bhparent);
+
+			if (oc-&gt;next != 0 &amp;&amp; (bhnext = asfs_breadcheck(sb, be32_to_cpu(oc-&gt;next), ASFS_OBJECTCONTAINER_ID))) {
+				struct fsObjectContainer *ocnext = (void *) bhnext-&gt;b_data;
+				ocnext-&gt;previous = cpu_to_be32(newcontblock);
+				asfs_bstore(sb, bhnext);
+				asfs_brelse(bhnext);
+			}
+
+			*io_bh = bh;
+			*io_o = oc-&gt;object;
+		}
+	}
+
+	asfs_debug("findobjectspace: new object will be in container block %u\n", be32_to_cpu(((struct fsBlockHeader *) (*io_bh)-&gt;b_data)-&gt;ownblock));
+
+	return (errorcode);
+}
+
+/* io_bh &amp; io_o refer to the direct parent of the new object.  Objectname is the
+	name of the new object (name only). Does not realese io_bh !!! */
+
+int asfs_createobject(struct super_block *sb, struct buffer_head **io_bh, struct fsObject **io_o, struct fsObject *src_o, u8 *objectname, int force)
+{
+	int errorcode;
+	u32 object_size;
+	u32 hashblock = be32_to_cpu((*io_o)-&gt;object.dir.hashtable);
+
+	asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)-&gt;name);
+
+	if (!force &amp;&amp; ASFS_SB(sb)-&gt;freeblocks &lt; ASFS_ALWAYSFREE)
+		return -ENOSPC;
+
+	if (!force &amp;&amp; be32_to_cpu((*io_o)-&gt;objectnode) == ASFS_RECYCLEDNODE)
+		return -EINVAL;
+
+	object_size = sizeof(struct fsObject) + strlen(objectname) + 2;
+
+	if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) {
+		struct fsObject *o2 = *io_o;
+		u8 *name = o2-&gt;name;
+		u8 *objname = objectname;
+		struct buffer_head *node_bh;
+		struct fsObjectNode *on;
+		u32 nodeno;
+
+		**io_o = *src_o;	/* Copying whole object data... */
+
+		while (*objname != 0)	/* Copying name */
+			*name++ = *objname++;
+
+		*name++ = 0;
+		*name = 0;	/* zero byte for comment */
+
+		if (o2-&gt;objectnode != 0)	/* ObjectNode reuse or creation */
+			errorcode = asfs_getnode(sb, o2-&gt;objectnode, &amp;node_bh, &amp;on);
+		else {
+			if ((errorcode = asfs_createnode(sb, &amp;node_bh, (struct fsNode **) &amp;on, &amp;nodeno)) == 0) {
+				on-&gt;hash16 = cpu_to_be16(asfs_hash(o2-&gt;name, ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE));
+				o2-&gt;objectnode = cpu_to_be32(nodeno);
+			}
+			asfs_debug("createnode returned with errorcode: %d\n", errorcode);
+		}
+
+		if (errorcode == 0) {	/* in io_bh there is a container with created object */
+			on-&gt;node.data = ((struct fsBlockHeader *) (*io_bh)-&gt;b_data)-&gt;ownblock;
+			if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2-&gt;objectnode), objectname)) == 0) {
+				asfs_bstore(sb, node_bh);
+				asfs_brelse(node_bh);
+			} else
+				errorcode = -EIO;
+		}
+
+		if (errorcode == 0) {	/* HashBlock reuse or creation:*/
+
+			if ((o2-&gt;bits &amp; OTYPE_DIR) != 0 &amp;&amp; o2-&gt;object.dir.hashtable == 0) {
+				struct buffer_head *hashbh;
+				u32 hashblock;
+
+				asfs_debug("creating Hashblock\n");
+
+				if ((errorcode = asfs_allocadminspace(sb, &amp;hashblock)) == 0 &amp;&amp; (hashbh = asfs_getzeroblk(sb, hashblock))) {
+					struct fsHashTable *ht = (void *) hashbh-&gt;b_data;
+
+					o2-&gt;object.dir.hashtable = cpu_to_be32(hashblock);
+
+					ht-&gt;bheader.id = cpu_to_be32(ASFS_HASHTABLE_ID);
+					ht-&gt;bheader.ownblock = cpu_to_be32(hashblock);
+					ht-&gt;parent = o2-&gt;objectnode;
+
+					asfs_bstore(sb, hashbh);
+					asfs_brelse(hashbh);
+				}
+			}
+		}
+
+		if (errorcode == 0) {	/* SoftLink creation: */
+			if ((o2-&gt;bits &amp; (OTYPE_LINK | OTYPE_HARDLINK)) == OTYPE_LINK &amp;&amp; o2-&gt;object.file.data == 0) {
+				struct buffer_head *bh2;
+				u32 slinkblock;
+
+				if ((errorcode = asfs_allocadminspace(sb, &amp;slinkblock)) == 0 &amp;&amp; (bh2 = asfs_getzeroblk(sb, slinkblock))) {
+					struct fsSoftLink *sl = (void *) bh2-&gt;b_data;
+					o2-&gt;object.file.data = cpu_to_be32(slinkblock);
+					sl-&gt;bheader.id = cpu_to_be32(ASFS_SOFTLINK_ID);
+					sl-&gt;bheader.ownblock = cpu_to_be32(slinkblock);
+					sl-&gt;parent = o2-&gt;objectnode;
+					sl-&gt;next = 0;
+					sl-&gt;previous = 0;
+					asfs_bstore(sb, bh2);
+					asfs_brelse(bh2);
+				}
+			}
+		}
+	}
+	asfs_debug("createobject: done.\n");
+
+	return (errorcode);
+}
+
+	/* This function extends the file object 'o' with a number  of blocks
+		(hopefully, if any blocks has been found!). Only new Extents will
+      be created -- the size of the file will not be altered, and changing
+		it is left up to the caller.  If the file did not have any blocks
+		yet, then the o-&gt;object.file.data will be set to the first (new)
+		ExtentBNode. It returns the number of added blocks through
+		addedblocks pointer */
+
+int asfs_addblockstofile(struct super_block *sb, struct buffer_head *objbh, struct fsObject *o, u32 blocks, u32 * newspace, u32 * addedblocks)
+{
+	u32 lastextentbnode;
+	int errorcode = 0;
+	struct fsExtentBNode *ebnp;
+	struct buffer_head *block = NULL;
+
+
+	asfs_debug("extendblocksinfile: Trying to increasing number of blocks by %d.\n", blocks);
+
+	lastextentbnode = be32_to_cpu(o-&gt;object.file.data);
+
+	if (lastextentbnode != 0) {
+		while (lastextentbnode != 0 &amp;&amp; errorcode == 0) {
+			if (block != NULL)
+				asfs_brelse(block);
+			errorcode = asfs_getextent(sb, lastextentbnode, &amp;block, &amp;ebnp);
+			lastextentbnode = be32_to_cpu(ebnp-&gt;next);
+		}
+		lastextentbnode = be32_to_cpu(ebnp-&gt;key);
+	}
+
+	if (errorcode == 0) {
+		u32 searchstart;
+
+		u32 found_block;
+		u32 found_blocks;
+
+		*addedblocks = 0;
+		*newspace = 0;
+
+		if (lastextentbnode != 0)
+			searchstart = be32_to_cpu(ebnp-&gt;key) + be16_to_cpu(ebnp-&gt;blocks);
+		else
+			searchstart = 0; //ASFS_SB(sb)-&gt;block_rovingblockptr;
+
+		if ((errorcode = asfs_findspace(sb, blocks, searchstart, searchstart, &amp;found_block, &amp;found_blocks)) != 0) {
+			asfs_brelse(block);
+			asfs_debug("extendblocksinfile: findspace returned %s\n", errorcode == -ENOSPC ? "ENOSPC" : "error");
+			return errorcode;
+		}
+
+		blocks = found_blocks;
+		errorcode = asfs_markspace(sb, found_block, found_blocks);
+		*addedblocks = found_blocks;
+		*newspace = found_block;
+
+		asfs_debug("extendblocksinfile: block = %u, lastextentbnode = %u, extentblocks = %d\n", found_block, lastextentbnode, blocks);
+
+		if ((errorcode = asfs_addblocks(sb, blocks, found_block, be32_to_cpu(o-&gt;objectnode), &amp;lastextentbnode)) != 0) {
+			asfs_debug("extendblocksinfile: addblocks returned errorcode %d\n", errorcode);
+			return errorcode;
+		}
+
+		if (o-&gt;object.file.data == 0)
+			o-&gt;object.file.data = cpu_to_be32(lastextentbnode);
+	}
+
+	if (block)
+		asfs_brelse(block);
+	asfs_bstore(sb, objbh);
+
+	asfs_debug("addblockstofile: done. added %d blocks\n", *addedblocks);
+
+	return errorcode;
+}
+
+	/* The Object indicated by bh1 &amp; o1, gets renamed to newname and placed
+	   in the directory indicated by bhparent &amp; oparent. */
+
+int asfs_renameobject(struct super_block *sb, struct buffer_head *bh1, struct fsObject *o1, struct buffer_head *bhparent, struct fsObject *oparent, u8 * newname)
+{
+	struct fsObject object;
+	u32 oldparentnode = be32_to_cpu(((struct fsObjectContainer *) bh1-&gt;b_data)-&gt;parent);
+	u8 oldname[107];
+	int errorcode;
+
+	asfs_debug("renameobject: Renaming '%s' to '%s' in dir '%s'\n", o1-&gt;name, newname, oparent-&gt;name);
+
+	object = *o1;
+	strcpy(oldname, o1-&gt;name);
+
+	if ((errorcode = dehashobjectquick(sb, be32_to_cpu(o1-&gt;objectnode), o1-&gt;name, oldparentnode)) == 0) {
+		u32 parentobjectnode = be32_to_cpu(oparent-&gt;objectnode);
+
+		if ((errorcode = simpleremoveobject(sb, bh1, o1)) == 0) {
+			struct buffer_head *bh2 = bhparent;
+			struct fsObject *o2;
+
+			/* oparent might changed after simpleremoveobject */
+			oparent = o2 = find_obj_by_node(sb, (struct fsObjectContainer *) bhparent-&gt;b_data, parentobjectnode);
+
+			/* In goes the Parent bh &amp; o, out comes the New object's bh &amp; o :-) */
+			if ((errorcode = asfs_createobject(sb, &amp;bh2, &amp;o2, &amp;object, newname, TRUE)) == 0) {
+				asfs_bstore(sb, bh2);
+				if (be32_to_cpu(oparent-&gt;objectnode) == ASFS_RECYCLEDNODE) {
+					asfs_debug("renameobject: Updating recycled dir info\n");
+					if ((errorcode = setrecycledinfodiff(sb, 1, (be32_to_cpu(o2-&gt;object.file.size) + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits)) != 0) {
+						brelse(bh2);
+						return errorcode;
+					}
+				}
+				brelse(bh2);
+				asfs_debug("renameobject: Succesfully created &amp; stored new object.\n");
+			} else { /* recreate object in old place, maybe this will not fail, but who knows... */
+				asfs_debug("renameobject: Creating new object failed. Trying to recreate it in source directory.\n");
+				if (asfs_readobject(sb, oldparentnode, &amp;bh1, &amp;o1) == 0) {
+					struct buffer_head *bh2 = bh1;
+					if (asfs_createobject(sb, &amp;bh2, &amp;o1, &amp;object, oldname, TRUE) == 0) {
+						asfs_bstore(sb, bh2);
+						if (oldparentnode == ASFS_RECYCLEDNODE) {
+							asfs_debug("renameobject: Updating recycled dir info\n");
+							setrecycledinfodiff(sb, 1, (be32_to_cpu(o1-&gt;object.file.size) + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits);
+						}
+						brelse(bh2);
+					}
+					brelse(bh1);
+				}
+			}
+		}
+	}
+	return errorcode;
+}
+
+		/* Truncates the specified file to /newsize/ bytes */
+
+int asfs_truncateblocksinfile(struct super_block *sb, struct buffer_head *bh, struct fsObject *o, u32 newsize)
+{
+	struct buffer_head *ebh;
+	struct fsExtentBNode *ebn;
+	int errorcode;
+	u32 pos = 0;
+	u32 newblocks = (newsize + sb-&gt;s_blocksize - 1) &gt;&gt; sb-&gt;s_blocksize_bits;
+	u32 filedata = be32_to_cpu(o-&gt;object.file.data);
+	u32 eprev, ekey;
+	u16 eblocks;
+
+	asfs_debug("trucateblocksinfile: newsize %u\n", newsize);
+
+	if (filedata == 0)
+		return 0;
+
+	for (;;) {
+		if ((errorcode = asfs_getextent(sb, filedata, &amp;ebh, &amp;ebn)) != 0)
+			return errorcode;
+		if (pos + be16_to_cpu(ebn-&gt;blocks) &gt;= newblocks)
+			break;
+		pos += be16_to_cpu(ebn-&gt;blocks);
+		if ((filedata = be32_to_cpu(ebn-&gt;next)) == 0)
+			break;
+		asfs_brelse(ebh);
+	};
+
+	eblocks = newblocks - pos;
+	ekey = be32_to_cpu(ebn-&gt;key);
+	eprev = be32_to_cpu(ebn-&gt;prev);
+
+	if (be16_to_cpu(ebn-&gt;blocks) &lt; eblocks) {
+		printk("ASFS: Extent chain is too short or damaged!\n");
+		asfs_brelse(ebh);
+		return -ENOENT;
+	}
+	if (be16_to_cpu(ebn-&gt;blocks) - eblocks &gt; 0 &amp;&amp; (errorcode = asfs_freespace(sb, be32_to_cpu(ebn-&gt;key) + eblocks, be16_to_cpu(ebn-&gt;blocks) - eblocks)) != 0) {
+		asfs_brelse(ebh);
+		return errorcode;
+	}
+	if (be32_to_cpu(ebn-&gt;next) &gt; 0 &amp;&amp; (errorcode = asfs_deleteextents(sb, be32_to_cpu(ebn-&gt;next))) != 0) {
+		asfs_brelse(ebh);
+		return errorcode;
+	}
+	ebn-&gt;blocks = cpu_to_be16(eblocks);
+	ebn-&gt;next = 0;
+	asfs_bstore(sb, ebh);
+
+	if (eblocks == 0) {
+		if (eprev &amp; MSB_MASK) {
+			o-&gt;object.file.data = 0;
+			asfs_bstore(sb, bh);
+		} else {
+			struct buffer_head *ebhp;
+			struct fsExtentBNode *ebnp;
+
+			if ((errorcode = asfs_getextent(sb, eprev &amp; !MSB_MASK, &amp;ebhp, &amp;ebnp)) != 0) {
+				asfs_brelse(ebh);
+				return errorcode;
+			}
+
+			ebnp-&gt;next = 0;
+			asfs_bstore(sb, ebhp);
+			asfs_brelse(ebhp);
+		}
+		if ((errorcode = asfs_deletebnode(sb, ebh, ekey)) != 0) {
+			asfs_brelse(ebh);
+			return errorcode;
+		}
+	}
+	asfs_brelse(ebh);
+
+	return 0;
+}
+#endif
diff -puN /dev/null fs/asfs/super.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/super.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,488 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ *
+ * version: 1.0beta10 for 2.6.xx kernel
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ * NLS support by Pavel Fedin (C) 2005
+ *
+ *
+ * Thanks to Marcin Kurek (Morgoth/Dreamolers-CAPS) for help and parts
+ * of original amiga version of SmartFilesystem source code.
+ *
+ * SmartFilesystem is copyrighted (C) 2003 by: John Hendrikx,
+ * Ralph Schmidt, Emmanuel Lesueur, David Gerber and Marcin Kurek
+ *
+ *
+ * ASFS is based on the Amiga FFS filesystem for Linux
+ * Copyright (C) 1993  Ray Burr
+ * Copyright (C) 1996  Hans-Joachim Widmaier
+ *
+ * Earlier versions were based on the Linux implementation of
+ * the ROMFS file system
+ * Copyright (C) 1997-1999  Janos Farkas &lt;chexum@shadow.banki.hu&gt;
+ *
+ * ASFS used some parts of the smbfs filesystem:
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * and parts of the Minix filesystem additionally
+ * Copyright (C) 1991, 1992  Linus Torvalds
+ * Copyright (C) 1996  Gertjan van Wingerde
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+/* todo:
+ * - remove bugs
+ * - add missing features (maybe safe-delete, other...)
+ * - create other fs tools like mkfs.asfs and fsck.asfs, some data-recovery tools
+ */
+
+#define ASFS_VERSION "1.0beta10 (13.06.2005)"
+
+#include &lt;linux/module.h&gt;
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/init.h&gt;
+#include &lt;linux/smp_lock.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include &lt;linux/parser.h&gt;
+#include &lt;linux/nls.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+#include &lt;asm/uaccess.h&gt;
+
+static char asfs_default_codepage[] = CONFIG_ASFS_DEFAULT_CODEPAGE;
+static char asfs_default_iocharset[] = CONFIG_NLS_DEFAULT;
+
+u32 asfs_calcchecksum(void *block, u32 blocksize)
+{
+	u32 *data = block, checksum = 1;
+	while (blocksize &gt; 0) {
+		checksum += be32_to_cpu(*data++);
+		blocksize -= 4;
+	}
+	checksum -= be32_to_cpu(((struct fsBlockHeader *)block)-&gt;checksum);
+	return -checksum;
+}
+
+static struct super_operations asfs_ops = {
+	.alloc_inode	= asfs_alloc_inode,
+	.destroy_inode	= asfs_destroy_inode,
+	.put_super		= asfs_put_super,
+	.statfs			= asfs_statfs,
+#ifdef CONFIG_ASFS_RW
+	.remount_fs		= asfs_remount,
+#endif
+};
+
+extern struct dentry_operations asfs_dentry_operations;
+
+enum {
+	Opt_mode, Opt_setgid, Opt_setuid, Opt_prefix, Opt_volume,
+	Opt_lcvol, Opt_iocharset, Opt_codepage, Opt_ignore, Opt_err
+};
+
+static match_table_t tokens = {
+	{Opt_mode, "mode=%o"},
+	{Opt_setgid, "setgid=%u"},
+	{Opt_setuid, "setuid=%u"},
+	{Opt_prefix, "prefix=%s"},
+	{Opt_volume, "volume=%s"},
+	{Opt_lcvol, "lowercasevol"},
+	{Opt_iocharset, "iocharset=%s"},
+	{Opt_codepage, "codepage=%s"},
+	{Opt_ignore, "grpquota"},
+	{Opt_ignore, "noquota"},
+	{Opt_ignore, "quota"},
+	{Opt_ignore, "usrquota"},
+	{Opt_err, NULL},
+};
+
+static int asfs_parse_options(char *options, struct super_block *sb)
+{
+	char *p;
+	substring_t args[MAX_OPT_ARGS];
+
+	if (!options)
+		return 1;
+	while ((p = strsep(&amp;options, ",")) != NULL) {
+		int token, option;
+		if (!*p)
+			continue;
+		token = match_token(p, tokens, args);
+
+		switch (token) {
+		case Opt_mode:
+			if (match_octal(&amp;args[0], &amp;option))
+				goto no_arg;
+			ASFS_SB(sb)-&gt;mode = option &amp; 0777;
+			break;
+		case Opt_setgid:
+			if (match_int(&amp;args[0], &amp;option))
+				goto no_arg;
+			ASFS_SB(sb)-&gt;gid = option;
+			break;
+		case Opt_setuid:
+			if (match_int(&amp;args[0], &amp;option))
+				goto no_arg;
+			ASFS_SB(sb)-&gt;uid = option;
+			break;
+		case Opt_prefix:
+			if (ASFS_SB(sb)-&gt;prefix) {
+				kfree(ASFS_SB(sb)-&gt;prefix);
+				ASFS_SB(sb)-&gt;prefix = NULL;
+			}
+			ASFS_SB(sb)-&gt;prefix = match_strdup(&amp;args[0]);
+			if (! ASFS_SB(sb)-&gt;prefix)
+				return 0;
+			break;
+		case Opt_volume:
+			if (ASFS_SB(sb)-&gt;root_volume) {
+				kfree(ASFS_SB(sb)-&gt;root_volume);
+				ASFS_SB(sb)-&gt;root_volume = NULL;
+			}
+			ASFS_SB(sb)-&gt;root_volume = match_strdup(&amp;args[0]);
+			if (! ASFS_SB(sb)-&gt;root_volume)
+				return 0;
+			break;
+		case Opt_lcvol:
+			ASFS_SB(sb)-&gt;flags |= ASFS_VOL_LOWERCASE;
+			break;
+		case Opt_iocharset:
+			if (ASFS_SB(sb)-&gt;iocharset != asfs_default_iocharset)
+				kfree(ASFS_SB(sb)-&gt;iocharset);
+			ASFS_SB(sb)-&gt;iocharset = match_strdup(&amp;args[0]);
+			if (!ASFS_SB(sb)-&gt;iocharset)
+				return 0;
+			break;
+		case Opt_codepage:
+			if (ASFS_SB(sb)-&gt;codepage != asfs_default_codepage)
+				kfree(ASFS_SB(sb)-&gt;codepage);
+			ASFS_SB(sb)-&gt;codepage = match_strdup(&amp;args[0]);
+			if (!ASFS_SB(sb)-&gt;codepage)
+				return 0;
+		case Opt_ignore:
+		 	/* Silently ignore the quota options */
+			break;
+		default:
+no_arg:
+			printk("ASFS: Unrecognized mount option \"%s\" "
+					"or missing value\n", p);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int asfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct asfs_sb_info *sbi;
+	struct buffer_head *bh;
+	struct fsRootBlock *rootblock;
+	struct inode *rootinode;
+
+	sbi = kmalloc(sizeof(struct asfs_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	sb-&gt;s_fs_info = sbi;
+
+	/* Fill in defaults */
+	ASFS_SB(sb)-&gt;uid = ASFS_DEFAULT_UID;
+	ASFS_SB(sb)-&gt;gid = ASFS_DEFAULT_GID;
+	ASFS_SB(sb)-&gt;mode = ASFS_DEFAULT_MODE;
+	ASFS_SB(sb)-&gt;prefix = NULL;
+	ASFS_SB(sb)-&gt;root_volume = NULL;
+	ASFS_SB(sb)-&gt;flags = 0;
+	ASFS_SB(sb)-&gt;iocharset = asfs_default_iocharset;
+	ASFS_SB(sb)-&gt;codepage = asfs_default_codepage;
+
+	if (!asfs_parse_options(data, sb)) {
+		printk(KERN_ERR "ASFS: Error parsing options\n");
+		return -EINVAL;
+	}
+
+	if (!sb_set_blocksize(sb, 512))
+		return -EINVAL;
+	sb-&gt;s_maxbytes = ASFS_MAXFILESIZE;
+
+	bh = sb_bread(sb, 0);
+	if (!bh) {
+		printk(KERN_ERR "ASFS: unable to read superblock\n");
+		return -EINVAL;
+	}
+
+	rootblock = (struct fsRootBlock *)bh-&gt;b_data;
+
+	if (be32_to_cpu(rootblock-&gt;bheader.id) == ASFS_ROOTID &amp;&amp;
+		be16_to_cpu(rootblock-&gt;version) == ASFS_STRUCTURE_VERISON) {
+
+		sb-&gt;s_blocksize = be32_to_cpu(rootblock-&gt;blocksize);
+		ASFS_SB(sb)-&gt;totalblocks = be32_to_cpu(rootblock-&gt;totalblocks);
+		ASFS_SB(sb)-&gt;rootobjectcontainer = be32_to_cpu(rootblock-&gt;rootobjectcontainer);
+		ASFS_SB(sb)-&gt;extentbnoderoot = be32_to_cpu(rootblock-&gt;extentbnoderoot);
+		ASFS_SB(sb)-&gt;objectnoderoot = be32_to_cpu(rootblock-&gt;objectnoderoot);
+		ASFS_SB(sb)-&gt;flags |= 0xff &amp; rootblock-&gt;bits;
+		ASFS_SB(sb)-&gt;adminspacecontainer = be32_to_cpu(rootblock-&gt;adminspacecontainer);
+		ASFS_SB(sb)-&gt;bitmapbase = be32_to_cpu(rootblock-&gt;bitmapbase);
+		ASFS_SB(sb)-&gt;blocks_inbitmap = (sb-&gt;s_blocksize - sizeof(struct fsBitmap))&lt;&lt;3;  /* must be a multiple of 32 !! */
+		ASFS_SB(sb)-&gt;blocks_bitmap = (ASFS_SB(sb)-&gt;totalblocks + ASFS_SB(sb)-&gt;blocks_inbitmap - 1) / ASFS_SB(sb)-&gt;blocks_inbitmap;
+		ASFS_SB(sb)-&gt;block_rovingblockptr = 0;
+		asfs_brelse(bh);
+
+		if (!sb_set_blocksize(sb, sb-&gt;s_blocksize)) {
+			printk(KERN_ERR "ASFS: Found Amiga SFS RootBlock on dev %s, but blocksize %ld is not supported!\n", \
+			       sb-&gt;s_id, sb-&gt;s_blocksize);
+			return -EINVAL;
+		}
+
+		bh = sb_bread(sb, 0);
+		if (!bh) {
+			printk(KERN_ERR "ASFS: unable to read superblock\n");
+			goto out;
+		}
+		rootblock = (struct fsRootBlock *)bh-&gt;b_data;
+
+		if (asfs_check_block((void *)rootblock, sb-&gt;s_blocksize, 0, ASFS_ROOTID)) {
+#ifdef CONFIG_ASFS_RW
+			struct buffer_head *tmpbh;
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;rootobjectcontainer, ASFS_OBJECTCONTAINER_ID))) {
+				struct fsRootInfo *ri = (struct fsRootInfo *)((u8 *)tmpbh-&gt;b_data + sb-&gt;s_blocksize - sizeof(struct fsRootInfo));
+				ASFS_SB(sb)-&gt;freeblocks = be32_to_cpu(ri-&gt;freeblocks);
+				asfs_brelse(tmpbh);
+			} else
+				ASFS_SB(sb)-&gt;freeblocks = 0;
+
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;rootobjectcontainer+2, ASFS_TRANSACTIONFAILURE_ID))) {
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but it has unfinished transaction. Mounting read-only.\n", sb-&gt;s_id);
+				ASFS_SB(sb)-&gt;flags |= ASFS_READONLY;
+				asfs_brelse(tmpbh);
+			}
+
+			if ((tmpbh = asfs_breadcheck(sb, ASFS_SB(sb)-&gt;totalblocks-1, ASFS_ROOTID)) == NULL) {
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s, but there is no second RootBlock! Mounting read-only.\n", sb-&gt;s_id);
+				ASFS_SB(sb)-&gt;flags |= ASFS_READONLY;
+				asfs_brelse(tmpbh);
+			}
+			if (!(ASFS_SB(sb)-&gt;flags &amp; ASFS_READONLY))
+				printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb-&gt;s_id);
+#else
+			ASFS_SB(sb)-&gt;freeblocks = 0;
+			ASFS_SB(sb)-&gt;flags |= ASFS_READONLY;
+			printk(KERN_NOTICE "VFS: Found Amiga SFS RootBlock on dev %s.\n", sb-&gt;s_id);
+#endif
+		} else {
+			if (!silent)
+				printk(KERN_ERR "VFS: Found Amiga SFS RootBlock on dev %s, but it has checksum error!\n", \
+				       sb-&gt;s_id);
+			goto out;
+		}
+	} else {
+		if (!silent)
+			printk(KERN_ERR "VFS: Can't find a valid Amiga SFS filesystem on dev %s.\n", \
+			       sb-&gt;s_id);
+		goto out;
+	}
+
+	asfs_brelse(bh);
+
+	sb-&gt;s_magic = ASFS_MAGIC;
+	sb-&gt;s_flags |= MS_NODEV | MS_NOSUID;
+	if (ASFS_SB(sb)-&gt;flags &amp; ASFS_READONLY)
+		sb-&gt;s_flags |= MS_RDONLY;
+	sb-&gt;s_op = &amp;asfs_ops;
+	asfs_debug("Case sensitive: %s\n", (ASFS_SB(sb)-&gt;flags &amp; ASFS_ROOTBITS_CASESENSITIVE) ? "yes" : "no");
+
+	if (ASFS_SB(sb)-&gt;codepage[0] != '\0' &amp;&amp; strcmp(ASFS_SB(sb)-&gt;codepage, "none") != 0) {
+		ASFS_SB(sb)-&gt;nls_disk = load_nls(ASFS_SB(sb)-&gt;codepage);
+		if (!ASFS_SB(sb)-&gt;nls_disk) {
+			printk(KERN_ERR "ASFS: codepage %s not found\n", ASFS_SB(sb)-&gt;codepage);
+			return -EINVAL;
+		}
+		ASFS_SB(sb)-&gt;nls_io = load_nls(ASFS_SB(sb)-&gt;iocharset);
+		if (!ASFS_SB(sb)-&gt;nls_io) {
+			printk(KERN_ERR "ASFS: IO charset %s not found\n", ASFS_SB(sb)-&gt;iocharset);
+			goto out2;
+		}
+	} else {
+		ASFS_SB(sb)-&gt;nls_io = NULL;
+		ASFS_SB(sb)-&gt;nls_disk = NULL;
+	}
+
+	if ((rootinode = asfs_get_root_inode(sb))) {
+		if ((sb-&gt;s_root = d_alloc_root(rootinode))) {
+			sb-&gt;s_root-&gt;d_op = &amp;asfs_dentry_operations;
+			return 0;
+		}
+		iput(rootinode);
+	}
+	unload_nls(ASFS_SB(sb)-&gt;nls_io);
+out2:
+	unload_nls(ASFS_SB(sb)-&gt;nls_disk);
+	return -EINVAL;
+
+out:
+	asfs_brelse(bh);
+	return -EINVAL;
+
+}
+
+#ifdef CONFIG_ASFS_RW
+int asfs_remount(struct super_block *sb, int *flags, char *data)
+{
+	asfs_debug("ASFS: remount (flags=0x%x, opts=\"%s\")\n",*flags,data);
+
+	if (!asfs_parse_options(data,sb))
+		return -EINVAL;
+
+	if ((*flags &amp; MS_RDONLY) == (sb-&gt;s_flags &amp; MS_RDONLY))
+		return 0;
+
+	if (*flags &amp; MS_RDONLY) {
+		sb-&gt;s_flags |= MS_RDONLY;
+	} else if (!(ASFS_SB(sb)-&gt;flags &amp; ASFS_READONLY)) {
+		sb-&gt;s_flags &amp;= ~MS_RDONLY;
+	} else {
+		printk("VFS: Can't remount Amiga SFS on dev %s read/write because of errors.", sb-&gt;s_id);
+		return -EINVAL;
+	}
+	return 0;
+}
+#endif
+
+void asfs_put_super(struct super_block *sb)
+{
+	struct asfs_sb_info *sbi = ASFS_SB(sb);
+
+	if (ASFS_SB(sb)-&gt;prefix)
+		kfree(ASFS_SB(sb)-&gt;prefix);
+	if (ASFS_SB(sb)-&gt;root_volume)
+		kfree(ASFS_SB(sb)-&gt;root_volume);
+	if (ASFS_SB(sb)-&gt;nls_disk)
+		unload_nls(ASFS_SB(sb)-&gt;nls_disk);
+	if (ASFS_SB(sb)-&gt;nls_io)
+		unload_nls(ASFS_SB(sb)-&gt;nls_io);
+	if (ASFS_SB(sb)-&gt;iocharset != asfs_default_iocharset)
+		kfree(ASFS_SB(sb)-&gt;iocharset);
+	if (ASFS_SB(sb)-&gt;codepage != asfs_default_codepage)
+		kfree(ASFS_SB(sb)-&gt;codepage);
+
+	kfree(sbi);
+	sb-&gt;s_fs_info = NULL;
+	return;
+}
+
+/* That's simple too. */
+int asfs_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	buf-&gt;f_type = ASFS_MAGIC;
+	buf-&gt;f_bsize = sb-&gt;s_blocksize;
+	buf-&gt;f_bfree = buf-&gt;f_bavail = ASFS_SB(sb)-&gt;freeblocks;
+	buf-&gt;f_blocks = ASFS_SB(sb)-&gt;totalblocks;
+	buf-&gt;f_namelen = ASFS_MAXFN;
+	return 0;
+}
+
+/* --- new in 2.6.x --- */
+static kmem_cache_t * asfs_inode_cachep;
+
+struct inode *asfs_alloc_inode(struct super_block *sb)
+{
+	struct asfs_inode_info *ei;
+	ei = (struct asfs_inode_info *)kmem_cache_alloc(asfs_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &amp;ei-&gt;vfs_inode;
+}
+
+void asfs_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(asfs_inode_cachep, ASFS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct asfs_inode_info *ei = (struct asfs_inode_info *) foo;
+
+	if ((flags &amp; (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		inode_init_once(&amp;ei-&gt;vfs_inode);
+	}
+}
+
+static int init_inodecache(void)
+{
+	asfs_inode_cachep = kmem_cache_create("asfs_inode_cache",
+					     sizeof(struct asfs_inode_info),
+					     0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+					     init_once, NULL);
+	if (asfs_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(asfs_inode_cachep))
+		printk(KERN_INFO "asfs_inode_cache: not all structures were freed\n");
+}
+
+static struct super_block *asfs_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data, asfs_fill_super);
+}
+
+static struct file_system_type asfs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "asfs",
+	.get_sb		= asfs_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static int __init init_asfs_fs(void)
+{
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&amp;asfs_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
+}
+
+static void __exit exit_asfs_fs(void)
+{
+	unregister_filesystem(&amp;asfs_fs_type);
+	destroy_inodecache();
+}
+
+/* Yes, works even as a module... :) */
+
+#ifdef CONFIG_ASFS_RW
+MODULE_DESCRIPTION("Amiga Smart File System (read/write) support for Linux kernel 2.6.x v" ASFS_VERSION);
+#else
+MODULE_DESCRIPTION("Amiga Smart File System (read-only) support for Linux kernel 2.6.x v" ASFS_VERSION);
+#endif
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Szyprowski &lt;marek@amiga.pl&gt;");
+
+module_init(init_asfs_fs)
+module_exit(exit_asfs_fs)
diff -puN /dev/null fs/asfs/symlink.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/fs/asfs/symlink.c	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,235 @@
+/*
+ *
+ * Amiga Smart File System, Linux implementation
+ * version: 1.0beta9
+ *
+ * Copyright (C) 2003,2004,2005  Marek 'March' Szyprowski &lt;marek@amiga.pl&gt;
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include &lt;linux/types.h&gt;
+#include &lt;linux/errno.h&gt;
+#include &lt;linux/slab.h&gt;
+#include &lt;linux/fs.h&gt;
+#include &lt;linux/buffer_head.h&gt;
+#include &lt;linux/vfs.h&gt;
+#include &lt;linux/pagemap.h&gt;
+#include &lt;linux/nls.h&gt;
+#include "asfs_fs.h"
+
+#include &lt;asm/byteorder.h&gt;
+#include &lt;asm/uaccess.h&gt;
+
+int asfs_symlink_readpage(struct file *file, struct page *page)
+{
+	struct buffer_head *bh;
+	struct fsSoftLink *slinkcont;
+	struct inode *inode = page-&gt;mapping-&gt;host;
+	struct super_block *sb = inode-&gt;i_sb;
+	struct nls_table *nls_io = ASFS_SB(sb)-&gt;nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)-&gt;nls_disk;
+	char *link = kmap(page);
+	int i = 0, j = 0;
+	char c, lc = 0, *prefix, *lf, *p;
+	wchar_t uni;
+	int clen;
+
+	if (!(bh = asfs_breadcheck(sb, ASFS_I(inode)-&gt;firstblock, ASFS_SOFTLINK_ID))) {
+		SetPageError(page);
+		kunmap(page);
+		unlock_page(page);
+		return -EIO;
+	}
+	slinkcont = (struct fsSoftLink *) bh-&gt;b_data;
+
+	lf = slinkcont-&gt;string;
+	prefix = ASFS_SB(sb)-&gt;prefix ? ASFS_SB(sb)-&gt;prefix : "/";
+
+	if ((p = strchr(lf,':'))) {	/* Handle assign or volume name */
+		if (ASFS_SB(sb)-&gt;root_volume &amp;&amp;
+		    strncmp(lf, ASFS_SB(sb)-&gt;root_volume, strlen(ASFS_SB(sb)-&gt;root_volume)) == 0) {
+			/* global root volume name found */
+			link[i++] = '/';
+			lf = p+1;
+		} else {
+			/* adding volume prefix */
+			while (i &lt; 1023 &amp;&amp; (c = prefix[i]))
+				link[i++] = c;
+			while (i &lt; 1023 &amp;&amp; lf[j] != ':')
+			{
+				c = lf[j++];
+				if (ASFS_SB(sb)-&gt;flags &amp; ASFS_VOL_LOWERCASE)
+					c = asfs_lowerchar(c);
+				if (nls_io)
+				{
+					clen = nls_disk-&gt;char2uni(&amp;c, 1, &amp;uni);
+					if (clen&gt;0) {
+						clen = nls_io-&gt;uni2char(uni, &amp;link[i], NLS_MAX_CHARSET_SIZE);
+						if (clen&gt;0)
+							i += clen;
+					}
+					if (clen&lt;0)
+						link[i++] = '?';
+				} else
+					link[i++] = c;
+			}
+			if (i &lt; 1023)
+				link[i++] = '/';
+			j++;
+		}
+		lc = '/';
+	}
+
+	while (i &lt; 1023 &amp;&amp; (c = lf[j])) {
+		if (c == '/' &amp;&amp; lc == '/' &amp;&amp; i &lt; 1020) {	/* parent dir */
+			link[i++] = '.';
+			link[i++] = '.';
+		}
+		lc = c;
+		if (nls_io)
+		{
+			clen = nls_disk-&gt;char2uni(&amp;c, 1, &amp;uni);
+			if (clen&gt;0) {
+				clen = nls_io-&gt;uni2char(uni, &amp;link[i], NLS_MAX_CHARSET_SIZE);
+				if (clen&gt;0)
+					i += clen;
+			}
+			if (clen&lt;0)
+				link[i++] = '?';
+		} else
+			link[i++] = c;
+		j++;
+	}
+	link[i] = '\0';
+	SetPageUptodate(page);
+	kunmap(page);
+	unlock_page(page);
+	asfs_brelse(bh);
+	return 0;
+}
+
+#ifdef CONFIG_ASFS_RW
+
+int asfs_write_symlink(struct inode *symfile, const char *symname)
+{
+	struct super_block *sb = symfile-&gt;i_sb;
+	struct buffer_head *bh;
+	struct fsSoftLink *slinkcont;
+	struct nls_table *nls_io = ASFS_SB(sb)-&gt;nls_io;
+	struct nls_table *nls_disk = ASFS_SB(sb)-&gt;nls_disk;
+	char *p, c, lc;
+	int i, maxlen, pflen;
+	wchar_t uni;
+	int clen, blen;
+
+	asfs_debug("asfs_write_symlink %s to node %d\n", symname, (int)symfile-&gt;i_ino);
+
+	if (!(bh = asfs_breadcheck(sb, ASFS_I(symfile)-&gt;firstblock, ASFS_SOFTLINK_ID))) {
+		unlock_super(sb);
+		return -EIO;
+	}
+	slinkcont = (struct fsSoftLink *) bh-&gt;b_data;
+
+	/* translating symlink target path */
+
+	maxlen = sb-&gt;s_blocksize - sizeof(struct fsSoftLink) - 2;
+	i  = 0;
+	p  = slinkcont-&gt;string;
+	lc = '/';
+
+	if (*symname == '/') {
+		while (*symname == '/')
+			symname++;
+		if (ASFS_SB(sb)-&gt;prefix &amp;&amp;
+		    strncmp(symname-1, ASFS_SB(sb)-&gt;prefix, (pflen = strlen(ASFS_SB(sb)-&gt;prefix))) == 0) {
+			/* found volume prefix, ommiting it */
+			symname += pflen;
+			blen = strlen(symname);
+			while (*symname != '/' &amp;&amp; *symname != '\0') {
+				clen = nls_io-&gt;char2uni(symname, blen, &amp;uni);
+				if (clen&gt;0) {
+					symname += clen;
+					blen -= clen;
+					clen = nls_disk-&gt;uni2char(uni, p, NLS_MAX_CHARSET_SIZE);
+					if (clen&gt;0)
+						p += clen;
+				}
+				else
+				{
+					symname++;
+					blen--;
+				}
+				if (clen&lt;0)
+					*p++ = '?';
+				i++;
+			}
+			symname++;
+			*p++ = ':';
+		} else if (ASFS_SB(sb)-&gt;root_volume) {	/* adding root volume name */
+			while (ASFS_SB(sb)-&gt;root_volume[i])
+				*p++ = ASFS_SB(sb)-&gt;root_volume[i++];
+			*p++ = ':';
+		} else {	/* do nothing */
+			*p++ = '/';
+		}
+		i++;
+	}
+
+	blen = strlen(symname);
+	while (i &lt; maxlen &amp;&amp; (c = *symname)) {
+		if (c == '.' &amp;&amp; lc == '/' &amp;&amp; symname[1] == '.' &amp;&amp; symname[2] == '/') {
+			*p++ = '/';
+			i++;
+			symname += 3;
+			blen -= 3;
+			lc = '/';
+		} else if (c == '.' &amp;&amp; lc == '/' &amp;&amp; symname[1] == '/') {
+			symname += 2;
+			blen -= 2;
+			lc = '/';
+		} else {
+			clen = nls_io-&gt;char2uni(symname, blen, &amp;uni);
+			if (clen&gt;0) {
+				symname += clen;
+				blen -= clen;
+				clen = nls_disk-&gt;uni2char(uni, p, NLS_MAX_CHARSET_SIZE);
+				if (clen&gt;0)
+					lc = *p;
+					p += clen;
+			}
+			else
+			{
+				symname++;
+				blen--;
+			}
+			if (clen&lt;0)
+			{
+				*p++ = '?';
+				lc = '?';
+			}
+			i++;
+		}
+		if (lc == '/')
+			while (*symname == '/')
+			{
+				symname++;
+				blen--;
+			}
+	}
+	*p = 0;
+
+	asfs_bstore(sb, bh);
+	asfs_brelse(bh);
+
+	unlock_super(sb);
+
+	return 0;
+}
+
+#endif
diff -puN fs/Kconfig~asfs-filesystem-driver fs/Kconfig
--- devel/fs/Kconfig~asfs-filesystem-driver	2005-09-12 03:34:53.000000000 -0700
+++ devel-akpm/fs/Kconfig	2005-09-12 03:34:53.000000000 -0700
@@ -937,6 +937,53 @@ config AFFS_FS
 	  To compile this file system support as a module, choose M here: the
 	  module will be called affs.  If unsure, say N.
 
+config ASFS_FS
+	tristate "Amiga SFS file system support (EXPERIMENTAL)"
+	select NLS
+	depends on EXPERIMENTAL
+	help
+
+	  The Amiga Smart FileSystem (SFS) is the file system used on hard
+	  disks by Amiga(tm) and MorphOS(tm) systems.  Say Y if you want
+	  to be able to read files from an Amiga SFS partition on your hard
+	  drive.
+
+	  For more information read &lt;file:Documentation/filesystems/asfs.txt&gt;
+
+	  To compile this file system support as a module, choose M here: the
+	  module will be called asfs.
+
+	  If unsure, say N.
+
+config ASFS_DEFAULT_CODEPAGE
+	string "Default codepage for SFS"
+	depends on ASFS_FS
+	default ""
+	help
+	  This option should be set to the codepage of your SFS filesystems.
+	  It can be overridden with the 'codepage' mount option. Leave it blank
+	  or enter 'none' to disable filename converting.
+
+	  Use full codepage name (for example 'cp1251' instead of '1251') here,
+	  this allows to specify any character set, not only numbered one (like
+	  'iso8859-2').
+
+	  If unsure, leave it blank.
+
+config ASFS_RW
+	bool "Amiga SFS write support (DANGEROUS)"
+	depends on ASFS_FS
+	help
+
+	  If you say Y here, you will be able to write to ASFS file
+	  systems as well as read from them. The read-write support in ASFS
+	  is in beta stage. This means that useing it to write files to SFS
+	  partitions is DANGEROUS and COULD corrupt the filesystem.
+
+	  For more information read &lt;file:Documentation/filesystems/asfs.txt&gt;
+
+	  If unsure, say N.
+
 config HFS_FS
 	tristate "Apple Macintosh file system support (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff -puN fs/Makefile~asfs-filesystem-driver fs/Makefile
--- devel/fs/Makefile~asfs-filesystem-driver	2005-09-12 03:34:53.000000000 -0700
+++ devel-akpm/fs/Makefile	2005-09-12 03:34:53.000000000 -0700
@@ -84,6 +84,7 @@ obj-$(CONFIG_EFS_FS)		+= efs/
 obj-$(CONFIG_JFFS_FS)		+= jffs/
 obj-$(CONFIG_JFFS2_FS)		+= jffs2/
 obj-$(CONFIG_AFFS_FS)		+= affs/
+obj-$(CONFIG_ASFS_FS)		+= asfs/
 obj-$(CONFIG_ROMFS_FS)		+= romfs/
 obj-$(CONFIG_QNX4FS_FS)		+= qnx4/
 obj-$(CONFIG_AUTOFS_FS)		+= autofs/
diff -puN /dev/null include/linux/amigasfs.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ devel-akpm/include/linux/amigasfs.h	2005-09-12 03:34:53.000000000 -0700
@@ -0,0 +1,276 @@
+#ifndef __LINUX_AMIGASFS_H
+#define __LINUX_AMIGASFS_H
+
+#include &lt;linux/types.h&gt;
+
+/* some helper macros... */
+#define ASFS_MAKE_ID(a,b,c,d) (((a)&amp;0xff)&lt;&lt;24|((b)&amp;0xff)&lt;&lt;16|((c)&amp;0xff)&lt;&lt;8|((d)&amp;0xff))
+
+/* Amiga SFS block IDs */
+#define ASFS_ROOTID                 ASFS_MAKE_ID('S','F','S','\0')
+#define ASFS_OBJECTCONTAINER_ID     ASFS_MAKE_ID('O','B','J','C')
+#define ASFS_BNODECONTAINER_ID      ASFS_MAKE_ID('B','N','D','C')
+#define ASFS_NODECONTAINER_ID       ASFS_MAKE_ID('N','D','C',' ')
+#define ASFS_HASHTABLE_ID           ASFS_MAKE_ID('H','T','A','B')
+#define ASFS_SOFTLINK_ID            ASFS_MAKE_ID('S','L','N','K')
+#define ASFS_ADMINSPACECONTAINER_ID ASFS_MAKE_ID('A','D','M','C')
+#define ASFS_BITMAP_ID              ASFS_MAKE_ID('B','T','M','P')
+#define ASFS_TRANSACTIONFAILURE_ID  ASFS_MAKE_ID('T','R','F','A')
+
+/* Amiga SFS defines and magic values */
+
+#define ASFS_MAGIC 0xa0ff
+#define ASFS_MAXFN (105u)
+#define ASFS_MAXFILESIZE 0x8FFFFFFE
+
+#define ASFS_STRUCTURE_VERISON (3)
+#define ASFS_BLCKFACCURACY	(5)
+
+#define ASFS_ROOTBITS_CASESENSITIVE (128)
+#define ASFS_READONLY (512)
+#define ASFS_VOL_LOWERCASE (1024)
+
+#define ASFS_ROOTNODE   (1)
+#define ASFS_RECYCLEDNODE (2)
+
+#define OTYPE_HIDDEN      (1)
+#define OTYPE_HARDLINK    (32)
+#define OTYPE_LINK        (64)
+#define OTYPE_DIR         (128)
+
+#define MSB_MASK (1ul &lt;&lt; 31)
+
+#define NODE_STRUCT_SIZE (10)	/* (sizeof(struct fsObjectNode)) */
+#define NODECONT_BLOCK_COUNT ((sb-&gt;s_blocksize - sizeof(struct fsNodeContainer)) / sizeof(u32))
+
+#define ASFS_ALWAYSFREE (16)		/* keep this amount of blocks free */
+
+#define ASFS_BLOCKCHUNKS (16)		/* try to allocate this number of blocks in one request */
+
+#ifndef TRUE
+#define TRUE		1
+#endif
+#ifndef FALSE
+#define FALSE		0
+#endif
+
+/* amigados protection bits */
+
+#define FIBB_SCRIPT    6	/* program is a script (execute) file */
+#define FIBB_PURE      5	/* program is reentrant and rexecutable */
+#define FIBB_ARCHIVE   4	/* cleared whenever file is changed */
+#define FIBB_READ      3	/* ignored by old filesystem */
+#define FIBB_WRITE     2	/* ignored by old filesystem */
+#define FIBB_EXECUTE   1	/* ignored by system, used by Shell */
+#define FIBB_DELETE    0	/* prevent file from being deleted */
+
+#define FIBF_SCRIPT    (1&lt;&lt;FIBB_SCRIPT)
+#define FIBF_PURE      (1&lt;&lt;FIBB_PURE)
+#define FIBF_ARCHIVE   (1&lt;&lt;FIBB_ARCHIVE)
+#define FIBF_READ      (1&lt;&lt;FIBB_READ)
+#define FIBF_WRITE     (1&lt;&lt;FIBB_WRITE)
+#define FIBF_EXECUTE   (1&lt;&lt;FIBB_EXECUTE)
+#define FIBF_DELETE    (1&lt;&lt;FIBB_DELETE)
+
+/* name hashing macro */
+
+#define HASHCHAIN(x) (u16)(x % (u16)(((sb-&gt;s_blocksize) - sizeof(struct fsHashTable))&gt;&gt;2))
+
+/* Each block has its own header with checksum and id, its called fsBlockHeader */
+
+struct fsBlockHeader {
+	u32 id;			/* 4 character id string of this block */
+	u32 checksum;		/* The checksum */
+	u32 ownblock;		/* The blocknumber of the block this block is stored at */
+};
+
+/* On-disk "super block", called fsRootBlock */
+
+struct fsRootBlock {
+	struct fsBlockHeader bheader;
+
+	u16 version;		/* Version number of the filesystem block structure */
+	u16 sequencenumber;	/* The Root with the highest sequencenumber is valid */
+
+	u32 datecreated;	/* Creation date (when first formatted).  Cannot be changed. */
+	u8 bits;		/* various settings, see defines below. */
+	u8 pad1;
+	u16 pad2;
+
+	u32 reserved1[2];
+
+	u32 firstbyteh;		/* The first byte of our partition from the start of the */
+	u32 firstbyte;		/* disk.  firstbyteh = upper 32 bits, firstbyte = lower 32 bits. */
+
+	u32 lastbyteh;		/* The last byte of our partition, excluding this one. */
+	u32 lastbyte;
+
+	u32 totalblocks;	/* size of this partition in blocks */
+	u32 blocksize;		/* blocksize used */
+
+	u32 reserved2[2];
+	u32 reserved3[8];
+
+	u32 bitmapbase;		/* location of the bitmap */
+	u32 adminspacecontainer;	/* location of first adminspace container */
+	u32 rootobjectcontainer;	/* location of the root objectcontainer */
+	u32 extentbnoderoot;	/* location of the root of the extentbnode B-tree */
+	u32 objectnoderoot;	/* location of the root of the objectnode tree */
+
+	u32 reserved4[3];
+};
+
+/* On disk inode, called fsObject */
+
+struct fsObject {
+	u16 owneruid;
+	u16 ownergid;
+	u32 objectnode;
+	u32 protection;
+
+	union {
+		struct {
+			u32 data;
+			u32 size;
+		} file;
+
+		struct {
+			u32 hashtable;	/* for directories &amp; root, 0 means no hashblock */
+			u32 firstdirblock;
+		} dir;
+	} object;
+
+	u32 datemodified;
+	u8 bits;
+
+	u8 name[0];
+	u8 comment[0];
+};
+
+/* On disk block containging a number of fsObjects */
+
+struct fsObjectContainer {
+	struct fsBlockHeader bheader;
+
+	u32 parent;
+	u32 next;
+	u32 previous;		/* 0 for the first block in the directory chain */
+
+	struct fsObject object[0];
+};
+
+/* BTree structures, used to collect file data position on disk */
+
+struct fsExtentBNode {
+	u32 key;		/* data! */
+	u32 next;
+	u32 prev;
+	u16 blocks;		/* The size in blocks of the region this Extent controls */
+};
+
+struct BNode {
+	u32 key;
+	u32 data;
+};
+
+struct BTreeContainer {
+	u16 nodecount;
+	u8 isleaf;
+	u8 nodesize;		/* Must be a multiple of 2 */
+
+	struct BNode bnode[0];
+};
+
+/* On disk block with BTreeContainer */
+
+struct fsBNodeContainer {
+	struct fsBlockHeader bheader;
+	struct BTreeContainer btc;
+};
+
+/* On disk block  with soft link data */
+
+struct fsSoftLink {
+	struct fsBlockHeader bheader;
+	u32 parent;
+	u32 next;
+	u32 previous;
+	u8 string[0];
+};
+
+/* On disk block with hashtable data */
+
+struct fsHashTable {
+	struct fsBlockHeader bheader;
+	u32 parent;
+	u32 hashentry[0];
+};
+
+/* On disk block with node index and some helper structures */
+
+struct fsNodeContainer {
+	struct fsBlockHeader bheader;
+	u32 nodenumber;
+	u32 nodes;
+	u32 node[0];
+};
+
+struct fsNode {
+	u32 data;
+};
+
+struct fsObjectNode {
+	struct fsNode node;
+	u32 next;
+	u16 hash16;
+} __attribute__ ((packed));
+
+/* Some adminspace and bitmap block structures */
+
+struct fsAdminSpace {
+	u32 space;
+	u32 bits;
+/* Set bits are used blocks, bit 31 is	the first block in the AdminSpace. */
+};
+
+struct fsAdminSpaceContainer {
+	struct fsBlockHeader bheader;
+
+	u32 next;
+	u32 previous;
+
+	u8 bits;
+	u8 pad1;
+	u16 pad2;
+
+	struct fsAdminSpace adminspace[0];
+};
+
+struct fsBitmap {
+	struct fsBlockHeader bheader;
+
+	u32 bitmap[0];
+
+/* Bits are 1 if the block is free, and 0 if full.
+   Bitmap must consist of an integral number of longwords. */
+};
+
+/* The fsRootInfo structure has all kinds of information about the format
+   of the disk. */
+
+struct fsRootInfo {
+	u32 deletedblocks;	/* Amount in blocks which deleted files consume. */
+	u32 deletedfiles;	/* Number of deleted files in recycled. */
+	u32 freeblocks;		/* Cached number of free blocks on disk. */
+
+	u32 datecreated;
+
+	u32 lastallocatedblock;	/* Block which was most recently allocated */
+	u32 lastallocatedadminspace;	/* AdminSpaceContainer which most recently was used to allocate a block */
+	u32 lastallocatedextentnode;	/* ExtentNode which was most recently created */
+	u32 lastallocatedobjectnode;	/* ObjectNode which was most recently created */
+
+	u32 rovingpointer;
+};
+
+#endif
_
</pre></body></html>