Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES
new file mode 100644
index 0000000..66ea6e9
--- /dev/null
+++ b/fs/sysv/CHANGES
@@ -0,0 +1,60 @@
+Mon, 15 Dec 1997	  Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+	*    namei.c: struct sysv_dir_inode_operations updated to use dentries.
+
+Fri, 23 Jan 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+	*    inode.c: corrected 1 track offset setting (in sb->sv_block_base).
+		      Originally it was overridden (by setting to zero)
+		      in detected_[xenix,sysv4,sysv2,coherent]. Thanks
+		      to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+		      for identifying the problem.
+
+Tue, 27 Jan 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+        *    inode.c: added 2048-byte block support to SystemV FS.
+		      Merged detected_bs[512,1024,2048]() into one function:
+		      void detected_bs (u_char type, struct super_block *sb).
+		      Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+		      for the patch.
+
+Wed, 4 Feb 1998   Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+	*    namei.c: removed static subdir(); is_subdir() from dcache.c
+		      is used instead. Cosmetic changes.
+
+Thu, 3 Dec 1998   Al Viro (viro@parcelfarce.linux.theplanet.co.uk)
+	*    namei.c (sysv_rmdir):
+		      Bugectomy: old check for victim being busy
+		      (inode->i_count) wasn't replaced (with checking
+		      dentry->d_count) and escaped Linus in the last round
+		      of changes. Shot and buried.
+
+Wed, 9 Dec 1998   AV
+	*    namei.c (do_sysv_rename):
+		       Fixed incorrect check for other owners + race.
+		       Removed checks that went to VFS.
+	*    namei.c (sysv_unlink):
+		       Removed checks that went to VFS.
+
+Thu, 10 Dec 1998   AV
+	*    namei.c (do_mknod):
+			Removed dead code - mknod is never asked to
+			create a symlink or directory. Incidentially,
+			it wouldn't do it right if it would be called.
+
+Sat, 26 Dec 1998   KGB
+	*    inode.c (detect_sysv4):
+			Added detection of expanded s_type field (0x10,
+			0x20 and 0x30).  Forced read-only access in this case.
+
+Sun, 21 Mar 1999   AV
+	*    namei.c (sysv_link):
+			Fixed i_count usage that resulted in dcache corruption.
+	*    inode.c:
+			Filled ->delete_inode() method with sysv_delete_inode().
+			sysv_put_inode() is gone, as it tried to do ->delete_
+			_inode()'s job.
+	*    ialloc.c: (sysv_free_inode):
+			Fixed race.
+
+Sun, 30 Apr 1999   AV
+	*    namei.c (sysv_mknod):
+			Removed dead code (S_IFREG case is now passed to
+			->create() by VFS).
diff --git a/fs/sysv/ChangeLog b/fs/sysv/ChangeLog
new file mode 100644
index 0000000..18e3487
--- /dev/null
+++ b/fs/sysv/ChangeLog
@@ -0,0 +1,106 @@
+Thu Feb 14 2002  Andrew Morton  <akpm@zip.com.au>
+
+	* dir_commit_chunk(): call writeout_one_page() as well as
+	  waitfor_one_page() for IS_SYNC directories, so that we
+	  actually do sync the directory. (forward-port from 2.4).
+
+Thu Feb  7 2002  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+
+	* super.c: switched to ->get_sb()
+	* ChangeLog: fixed dates ;-)
+
+2002-01-24  David S. Miller  <davem@redhat.com>
+
+	* inode.c: Include linux/init.h
+
+Mon Jan 21 2002  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+	* ialloc.c (sysv_new_inode): zero SYSV_I(inode)->i_data out.
+	* i_vnode renamed to vfs_inode.  Sorry, but let's keep that
+	  consistent.
+
+Sat Jan 19 2002  Christoph Hellwig  <hch@infradead.org>
+
+	* include/linux/sysv_fs.h (SYSV_I): Get fs-private inode data using
+		list_entry() instead of inode->u.
+	* include/linux/sysv_fs_i.h: Add 'struct inode  i_vnode' field to
+		sysv_inode_info structure.
+	* inode.c: Include <linux/slab.h>, implement alloc_inode/destroy_inode
+		sop methods, add infrastructure for per-fs inode slab cache.
+	* super.c (init_sysv_fs): Initialize inode cache, recover properly
+		in the case of failed register_filesystem for V7.
+	(exit_sysv_fs): Destroy inode cache.
+
+Sat Jan 19 2002  Christoph Hellwig  <hch@infradead.org>
+
+	* include/linux/sysv_fs.h: Include <linux/sysv_fs_i.h>, declare SYSV_I().
+	* dir.c (sysv_find_entry): Use SYSV_I() instead of ->u.sysv_i to
+		access fs-private inode data.
+	* ialloc.c (sysv_new_inode): Likewise.
+	* inode.c (sysv_read_inode): Likewise.
+	(sysv_update_inode): Likewise.
+	* itree.c (get_branch): Likewise.
+	(sysv_truncate): Likewise.
+	* symlink.c (sysv_readlink): Likewise.
+	(sysv_follow_link): Likewise.
+
+Fri Jan  4 2002  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+
+	* ialloc.c (sysv_free_inode): Use sb->s_id instead of bdevname().
+	* inode.c (sysv_read_inode): Likewise.
+	  (sysv_update_inode): Likewise.
+	  (sysv_sync_inode): Likewise.
+	* super.c (detect_sysv): Likewise.
+	  (complete_read_super): Likewise.
+	  (sysv_read_super): Likewise.
+	  (v7_read_super): Likewise.
+
+Sun Dec 30 2001  Manfred Spraul  <manfreds@colorfullife.com>
+
+	* dir.c (dir_commit_chunk): Do not set dir->i_version.
+	(sysv_readdir): Likewise.
+
+Thu Dec 27 2001  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+
+	* itree.c (get_block): Use map_bh() to fill out bh_result.
+
+Tue Dec 25 2001  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+
+	* super.c (sysv_read_super): Use sb_set_blocksize() to set blocksize.
+	  (v7_read_super): Likewise.
+
+Tue Nov 27 2001  Alexander Viro  <viro@parcelfarce.linux.theplanet.co.uk>
+
+	* itree.c (get_block): Change type for iblock argument to sector_t.
+	* super.c (sysv_read_super): Set s_blocksize early.
+	  (v7_read_super): Likewise.
+	* balloc.c (sysv_new_block): Use sb_bread(). instead of bread().
+	  (sysv_count_free_blocks): Likewise.
+	* ialloc.c (sysv_raw_inode): Likewise.
+	* itree.c (get_branch): Likewise.
+	  (free_branches): Likewise.
+	* super.c (sysv_read_super): Likewise.
+	  (v7_read_super): Likewise.
+
+Sat Dec 15 2001  Christoph Hellwig  <hch@infradead.org>
+
+	* inode.c (sysv_read_inode): Mark inode as bad in case of failure.
+	* super.c (complete_read_super): Check for bad root inode.
+
+Wed Nov 21 2001  Andrew Morton  <andrewm@uow.edu.au>
+
+	* file.c (sysv_sync_file): Call fsync_inode_data_buffers.
+
+Fri Oct 26 2001  Christoph Hellwig  <hch@infradead.org>
+
+	* dir.c, ialloc.c, namei.c, include/linux/sysv_fs_i.h:
+	Implement per-Inode lookup offset cache.
+	Modelled after Ted's ext2 patch.
+
+Fri Oct 26 2001  Christoph Hellwig  <hch@infradead.org>
+
+	* inode.c, super.c, include/linux/sysv_fs.h,
+	  include/linux/sysv_fs_sb.h:
+	Remove symlink faking.	Noone really wants to use these as
+	linux filesystems and native OSes don't support it anyway.
+
+
diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO
new file mode 100644
index 0000000..de4e4d1
--- /dev/null
+++ b/fs/sysv/INTRO
@@ -0,0 +1,182 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It grew out of separate filesystem implementations
+
+    Xenix FS      Doug Evans <dje@cygnus.com>  June 1992
+    SystemV FS    Paul B. Monday <pmonday@eecs.wsu.edu> March-June 1993
+    Coherent FS   B. Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> June 1993
+
+and was merged together in July 1993.
+
+These filesystems are rather similar. Here is a comparison with Minix FS:
+
+* Linux fdisk reports on partitions
+  - Minix FS     0x81 Linux/Minix
+  - Xenix FS     ??
+  - SystemV FS   ??
+  - Coherent FS  0x08 AIX bootable
+
+* Size of a block or zone (data allocation unit on disk)
+  - Minix FS     1024
+  - Xenix FS     1024 (also 512 ??)
+  - SystemV FS   1024 (also 512 and 2048)
+  - Coherent FS   512
+
+* General layout: all have one boot block, one super block and
+  separate areas for inodes and for directories/data.
+  On SystemV Release 2 FS (e.g. Microport) the first track is reserved and
+  all the block numbers (including the super block) are offset by one track.
+
+* Byte ordering of "short" (16 bit entities) on disk:
+  - Minix FS     little endian  0 1
+  - Xenix FS     little endian  0 1
+  - SystemV FS   little endian  0 1
+  - Coherent FS  little endian  0 1
+  Of course, this affects only the file system, not the data of files on it!
+
+* Byte ordering of "long" (32 bit entities) on disk:
+  - Minix FS     little endian  0 1 2 3
+  - Xenix FS     little endian  0 1 2 3
+  - SystemV FS   little endian  0 1 2 3
+  - Coherent FS  PDP-11         2 3 0 1
+  Of course, this affects only the file system, not the data of files on it!
+
+* Inode on disk: "short", 0 means non-existent, the root dir ino is:
+  - Minix FS                            1
+  - Xenix FS, SystemV FS, Coherent FS   2
+
+* Maximum number of hard links to a file:
+  - Minix FS     250
+  - Xenix FS     ??
+  - SystemV FS   ??
+  - Coherent FS  >=10000
+
+* Free inode management:
+  - Minix FS                             a bitmap
+  - Xenix FS, SystemV FS, Coherent FS
+      There is a cache of a certain number of free inodes in the super-block.
+      When it is exhausted, new free inodes are found using a linear search.
+
+* Free block management:
+  - Minix FS                             a bitmap
+  - Xenix FS, SystemV FS, Coherent FS
+      Free blocks are organized in a "free list". Maybe a misleading term,
+      since it is not true that every free block contains a pointer to
+      the next free block. Rather, the free blocks are organized in chunks
+      of limited size, and every now and then a free block contains pointers
+      to the free blocks pertaining to the next chunk; the first of these
+      contains pointers and so on. The list terminates with a "block number"
+      0 on Xenix FS and SystemV FS, with a block zeroed out on Coherent FS.
+
+* Super-block location:
+  - Minix FS     block 1 = bytes 1024..2047
+  - Xenix FS     block 1 = bytes 1024..2047
+  - SystemV FS   bytes 512..1023
+  - Coherent FS  block 1 = bytes 512..1023
+
+* Super-block layout:
+  - Minix FS
+                    unsigned short s_ninodes;
+                    unsigned short s_nzones;
+                    unsigned short s_imap_blocks;
+                    unsigned short s_zmap_blocks;
+                    unsigned short s_firstdatazone;
+                    unsigned short s_log_zone_size;
+                    unsigned long s_max_size;
+                    unsigned short s_magic;
+  - Xenix FS, SystemV FS, Coherent FS
+                    unsigned short s_firstdatazone;
+                    unsigned long  s_nzones;
+                    unsigned short s_fzone_count;
+                    unsigned long  s_fzones[NICFREE];
+                    unsigned short s_finode_count;
+                    unsigned short s_finodes[NICINOD];
+                    char           s_flock;
+                    char           s_ilock;
+                    char           s_modified;
+                    char           s_rdonly;
+                    unsigned long  s_time;
+                    short          s_dinfo[4]; -- SystemV FS only
+                    unsigned long  s_free_zones;
+                    unsigned short s_free_inodes;
+                    short          s_dinfo[4]; -- Xenix FS only
+                    unsigned short s_interleave_m,s_interleave_n; -- Coherent FS only
+                    char           s_fname[6];
+                    char           s_fpack[6];
+    then they differ considerably:
+        Xenix FS
+                    char           s_clean;
+                    char           s_fill[371];
+                    long           s_magic;
+                    long           s_type;
+        SystemV FS
+                    long           s_fill[12 or 14];
+                    long           s_state;
+                    long           s_magic;
+                    long           s_type;
+        Coherent FS
+                    unsigned long  s_unique;
+    Note that Coherent FS has no magic.
+
+* Inode layout:
+  - Minix FS
+                    unsigned short i_mode;
+                    unsigned short i_uid;
+                    unsigned long  i_size;
+                    unsigned long  i_time;
+                    unsigned char  i_gid;
+                    unsigned char  i_nlinks;
+                    unsigned short i_zone[7+1+1];
+  - Xenix FS, SystemV FS, Coherent FS
+                    unsigned short i_mode;
+                    unsigned short i_nlink;
+                    unsigned short i_uid;
+                    unsigned short i_gid;
+                    unsigned long  i_size;
+                    unsigned char  i_zone[3*(10+1+1+1)];
+                    unsigned long  i_atime;
+                    unsigned long  i_mtime;
+                    unsigned long  i_ctime;
+
+* Regular file data blocks are organized as
+  - Minix FS
+               7 direct blocks
+               1 indirect block (pointers to blocks)
+               1 double-indirect block (pointer to pointers to blocks)
+  - Xenix FS, SystemV FS, Coherent FS
+              10 direct blocks
+               1 indirect block (pointers to blocks)
+               1 double-indirect block (pointer to pointers to blocks)
+               1 triple-indirect block (pointer to pointers to pointers to blocks)
+
+* Inode size, inodes per block
+  - Minix FS        32   32
+  - Xenix FS        64   16
+  - SystemV FS      64   16
+  - Coherent FS     64    8
+
+* Directory entry on disk
+  - Minix FS
+                    unsigned short inode;
+                    char name[14/30];
+  - Xenix FS, SystemV FS, Coherent FS
+                    unsigned short inode;
+                    char name[14];
+
+* Dir entry size, dir entries per block
+  - Minix FS     16/32    64/32
+  - Xenix FS     16       64
+  - SystemV FS   16       64
+  - Coherent FS  16       32
+
+* How to implement symbolic links such that the host fsck doesn't scream:
+  - Minix FS     normal
+  - Xenix FS     kludge: as regular files with  chmod 1000
+  - SystemV FS   ??
+  - Coherent FS  kludge: as regular files with  chmod 1000
+
+
+Notation: We often speak of a "block" but mean a zone (the allocation unit)
+and not the disk driver's notion of "block".
+
+
+Bruno Haible  <haible@ma2s2.mathematik.uni-karlsruhe.de>
diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile
new file mode 100644
index 0000000..3591f9d
--- /dev/null
+++ b/fs/sysv/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the Linux SystemV/Coherent filesystem routines.
+#
+
+obj-$(CONFIG_SYSV_FS) += sysv.o
+
+sysv-objs := ialloc.o balloc.o inode.o itree.o file.o dir.o \
+	     namei.o super.o symlink.o
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
new file mode 100644
index 0000000..9a6ad96a
--- /dev/null
+++ b/fs/sysv/balloc.c
@@ -0,0 +1,239 @@
+/*
+ *  linux/fs/sysv/balloc.c
+ *
+ *  minix/bitmap.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  ext/freelists.c
+ *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
+ *
+ *  xenix/alloc.c
+ *  Copyright (C) 1992  Doug Evans
+ *
+ *  coh/alloc.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/balloc.c
+ *  Copyright (C) 1993  Bruno Haible
+ *
+ *  This file contains code for allocating/freeing blocks.
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/string.h>
+#include "sysv.h"
+
+/* We don't trust the value of
+   sb->sv_sbd2->s_tfree = *sb->sv_free_blocks
+   but we nevertheless keep it up to date. */
+
+static inline sysv_zone_t *get_chunk(struct super_block *sb, struct buffer_head *bh)
+{
+	char *bh_data = bh->b_data;
+
+	if (SYSV_SB(sb)->s_type == FSTYPE_SYSV4)
+		return (sysv_zone_t*)(bh_data+4);
+	else
+		return (sysv_zone_t*)(bh_data+2);
+}
+
+/* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */
+
+void sysv_free_block(struct super_block * sb, sysv_zone_t nr)
+{
+	struct sysv_sb_info * sbi = SYSV_SB(sb);
+	struct buffer_head * bh;
+	sysv_zone_t *blocks = sbi->s_bcache;
+	unsigned count;
+	unsigned block = fs32_to_cpu(sbi, nr);
+
+	/*
+	 * This code does not work at all for AFS (it has a bitmap
+	 * free list).  As AFS is supposed to be read-only no one
+	 * should call this for an AFS filesystem anyway...
+	 */
+	if (sbi->s_type == FSTYPE_AFS)
+		return;
+
+	if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
+		printk("sysv_free_block: trying to free block not in datazone\n");
+		return;
+	}
+
+	lock_super(sb);
+	count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
+
+	if (count > sbi->s_flc_size) {
+		printk("sysv_free_block: flc_count > flc_size\n");
+		unlock_super(sb);
+		return;
+	}
+	/* If the free list head in super-block is full, it is copied
+	 * into this block being freed, ditto if it's completely empty
+	 * (applies only on Coherent).
+	 */
+	if (count == sbi->s_flc_size || count == 0) {
+		block += sbi->s_block_base;
+		bh = sb_getblk(sb, block);
+		if (!bh) {
+			printk("sysv_free_block: getblk() failed\n");
+			unlock_super(sb);
+			return;
+		}
+		memset(bh->b_data, 0, sb->s_blocksize);
+		*(__fs16*)bh->b_data = cpu_to_fs16(sbi, count);
+		memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
+		mark_buffer_dirty(bh);
+		set_buffer_uptodate(bh);
+		brelse(bh);
+		count = 0;
+	}
+	sbi->s_bcache[count++] = nr;
+
+	*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
+	fs32_add(sbi, sbi->s_free_blocks, 1);
+	dirty_sb(sb);
+	unlock_super(sb);
+}
+
+sysv_zone_t sysv_new_block(struct super_block * sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	unsigned int block;
+	sysv_zone_t nr;
+	struct buffer_head * bh;
+	unsigned count;
+
+	lock_super(sb);
+	count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
+
+	if (count == 0) /* Applies only to Coherent FS */
+		goto Enospc;
+	nr = sbi->s_bcache[--count];
+	if (nr == 0)  /* Applies only to Xenix FS, SystemV FS */
+		goto Enospc;
+
+	block = fs32_to_cpu(sbi, nr);
+
+	*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
+
+	if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
+		printk("sysv_new_block: new block %d is not in data zone\n",
+			block);
+		goto Enospc;
+	}
+
+	if (count == 0) { /* the last block continues the free list */
+		unsigned count;
+
+		block += sbi->s_block_base;
+		if (!(bh = sb_bread(sb, block))) {
+			printk("sysv_new_block: cannot read free-list block\n");
+			/* retry this same block next time */
+			*sbi->s_bcache_count = cpu_to_fs16(sbi, 1);
+			goto Enospc;
+		}
+		count = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
+		if (count > sbi->s_flc_size) {
+			printk("sysv_new_block: free-list block with >flc_size entries\n");
+			brelse(bh);
+			goto Enospc;
+		}
+		*sbi->s_bcache_count = cpu_to_fs16(sbi, count);
+		memcpy(sbi->s_bcache, get_chunk(sb, bh),
+				count * sizeof(sysv_zone_t));
+		brelse(bh);
+	}
+	/* Now the free list head in the superblock is valid again. */
+	fs32_add(sbi, sbi->s_free_blocks, -1);
+	dirty_sb(sb);
+	unlock_super(sb);
+	return nr;
+
+Enospc:
+	unlock_super(sb);
+	return 0;
+}
+
+unsigned long sysv_count_free_blocks(struct super_block * sb)
+{
+	struct sysv_sb_info * sbi = SYSV_SB(sb);
+	int sb_count;
+	int count;
+	struct buffer_head * bh = NULL;
+	sysv_zone_t *blocks;
+	unsigned block;
+	int n;
+
+	/*
+	 * This code does not work at all for AFS (it has a bitmap
+	 * free list).  As AFS is supposed to be read-only we just
+	 * lie and say it has no free block at all.
+	 */
+	if (sbi->s_type == FSTYPE_AFS)
+		return 0;
+
+	lock_super(sb);
+	sb_count = fs32_to_cpu(sbi, *sbi->s_free_blocks);
+
+	if (0)
+		goto trust_sb;
+
+	/* this causes a lot of disk traffic ... */
+	count = 0;
+	n = fs16_to_cpu(sbi, *sbi->s_bcache_count);
+	blocks = sbi->s_bcache;
+	while (1) {
+		sysv_zone_t zone;
+		if (n > sbi->s_flc_size)
+			goto E2big;
+		zone = 0;
+		while (n && (zone = blocks[--n]) != 0)
+			count++;
+		if (zone == 0)
+			break;
+
+		block = fs32_to_cpu(sbi, zone);
+		if (bh)
+			brelse(bh);
+
+		if (block < sbi->s_firstdatazone || block >= sbi->s_nzones)
+			goto Einval;
+		block += sbi->s_block_base;
+		bh = sb_bread(sb, block);
+		if (!bh)
+			goto Eio;
+		n = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
+		blocks = get_chunk(sb, bh);
+	}
+	if (bh)
+		brelse(bh);
+	if (count != sb_count)
+		goto Ecount;
+done:
+	unlock_super(sb);
+	return count;
+
+Einval:
+	printk("sysv_count_free_blocks: new block %d is not in data zone\n",
+		block);
+	goto trust_sb;
+Eio:
+	printk("sysv_count_free_blocks: cannot read free-list block\n");
+	goto trust_sb;
+E2big:
+	printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
+	if (bh)
+		brelse(bh);
+trust_sb:
+	count = sb_count;
+	goto done;
+Ecount:
+	printk("sysv_count_free_blocks: free block count was %d, "
+		"correcting to %d\n", sb_count, count);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		*sbi->s_free_blocks = cpu_to_fs32(sbi, count);
+		dirty_sb(sb);
+	}
+	goto done;
+}
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
new file mode 100644
index 0000000..69a085a
--- /dev/null
+++ b/fs/sysv/dir.c
@@ -0,0 +1,388 @@
+/*
+ *  linux/fs/sysv/dir.c
+ *
+ *  minix/dir.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  coh/dir.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/dir.c
+ *  Copyright (C) 1993  Bruno Haible
+ *
+ *  SystemV/Coherent directory handling functions
+ */
+
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+#include "sysv.h"
+
+static int sysv_readdir(struct file *, void *, filldir_t);
+
+struct file_operations sysv_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= sysv_readdir,
+	.fsync		= sysv_sync_file,
+};
+
+static inline void dir_put_page(struct page *page)
+{
+	kunmap(page);
+	page_cache_release(page);
+}
+
+static inline unsigned long dir_pages(struct inode *inode)
+{
+	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
+}
+
+static int dir_commit_chunk(struct page *page, unsigned from, unsigned to)
+{
+	struct inode *dir = (struct inode *)page->mapping->host;
+	int err = 0;
+
+	page->mapping->a_ops->commit_write(NULL, page, from, to);
+	if (IS_DIRSYNC(dir))
+		err = write_one_page(page, 1);
+	else
+		unlock_page(page);
+	return err;
+}
+
+static struct page * dir_get_page(struct inode *dir, unsigned long n)
+{
+	struct address_space *mapping = dir->i_mapping;
+	struct page *page = read_cache_page(mapping, n,
+				(filler_t*)mapping->a_ops->readpage, NULL);
+	if (!IS_ERR(page)) {
+		wait_on_page_locked(page);
+		kmap(page);
+		if (!PageUptodate(page))
+			goto fail;
+	}
+	return page;
+
+fail:
+	dir_put_page(page);
+	return ERR_PTR(-EIO);
+}
+
+static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+	unsigned long pos = filp->f_pos;
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	unsigned offset = pos & ~PAGE_CACHE_MASK;
+	unsigned long n = pos >> PAGE_CACHE_SHIFT;
+	unsigned long npages = dir_pages(inode);
+
+	lock_kernel();
+
+	pos = (pos + SYSV_DIRSIZE-1) & ~(SYSV_DIRSIZE-1);
+	if (pos >= inode->i_size)
+		goto done;
+
+	for ( ; n < npages; n++, offset = 0) {
+		char *kaddr, *limit;
+		struct sysv_dir_entry *de;
+		struct page *page = dir_get_page(inode, n);
+
+		if (IS_ERR(page))
+			continue;
+		kaddr = (char *)page_address(page);
+		de = (struct sysv_dir_entry *)(kaddr+offset);
+		limit = kaddr + PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+		for ( ;(char*)de <= limit; de++) {
+			char *name = de->name;
+			int over;
+
+			if (!de->inode)
+				continue;
+
+			offset = (char *)de - kaddr;
+
+			over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN),
+					(n<<PAGE_CACHE_SHIFT) | offset,
+					fs16_to_cpu(SYSV_SB(sb), de->inode),
+					DT_UNKNOWN);
+			if (over) {
+				dir_put_page(page);
+				goto done;
+			}
+		}
+		dir_put_page(page);
+	}
+
+done:
+	filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
+	unlock_kernel();
+	return 0;
+}
+
+/* compare strings: name[0..len-1] (not zero-terminated) and
+ * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
+ */
+static inline int namecompare(int len, int maxlen,
+	const char * name, const char * buffer)
+{
+	if (len < maxlen && buffer[len])
+		return 0;
+	return !memcmp(name, buffer, len);
+}
+
+/*
+ *	sysv_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page)
+{
+	const char * name = dentry->d_name.name;
+	int namelen = dentry->d_name.len;
+	struct inode * dir = dentry->d_parent->d_inode;
+	unsigned long start, n;
+	unsigned long npages = dir_pages(dir);
+	struct page *page = NULL;
+	struct sysv_dir_entry *de;
+
+	*res_page = NULL;
+
+	start = SYSV_I(dir)->i_dir_start_lookup;
+	if (start >= npages)
+		start = 0;
+	n = start;
+
+	do {
+		char *kaddr;
+		page = dir_get_page(dir, n);
+		if (!IS_ERR(page)) {
+			kaddr = (char*)page_address(page);
+			de = (struct sysv_dir_entry *) kaddr;
+			kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+			for ( ; (char *) de <= kaddr ; de++) {
+				if (!de->inode)
+					continue;
+				if (namecompare(namelen, SYSV_NAMELEN,
+							name, de->name))
+					goto found;
+			}
+		}
+		dir_put_page(page);
+
+		if (++n >= npages)
+			n = 0;
+	} while (n != start);
+
+	return NULL;
+
+found:
+	SYSV_I(dir)->i_dir_start_lookup = n;
+	*res_page = page;
+	return de;
+}
+
+int sysv_add_link(struct dentry *dentry, struct inode *inode)
+{
+	struct inode *dir = dentry->d_parent->d_inode;
+	const char * name = dentry->d_name.name;
+	int namelen = dentry->d_name.len;
+	struct page *page = NULL;
+	struct sysv_dir_entry * de;
+	unsigned long npages = dir_pages(dir);
+	unsigned long n;
+	char *kaddr;
+	unsigned from, to;
+	int err;
+
+	/* We take care of directory expansion in the same loop */
+	for (n = 0; n <= npages; n++) {
+		page = dir_get_page(dir, n);
+		err = PTR_ERR(page);
+		if (IS_ERR(page))
+			goto out;
+		kaddr = (char*)page_address(page);
+		de = (struct sysv_dir_entry *)kaddr;
+		kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+		while ((char *)de <= kaddr) {
+			if (!de->inode)
+				goto got_it;
+			err = -EEXIST;
+			if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) 
+				goto out_page;
+			de++;
+		}
+		dir_put_page(page);
+	}
+	BUG();
+	return -EINVAL;
+
+got_it:
+	from = (char*)de - (char*)page_address(page);
+	to = from + SYSV_DIRSIZE;
+	lock_page(page);
+	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (err)
+		goto out_unlock;
+	memcpy (de->name, name, namelen);
+	memset (de->name + namelen, 0, SYSV_DIRSIZE - namelen - 2);
+	de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino);
+	err = dir_commit_chunk(page, from, to);
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(dir);
+out_page:
+	dir_put_page(page);
+out:
+	return err;
+out_unlock:
+	unlock_page(page);
+	goto out_page;
+}
+
+int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = (struct inode*)mapping->host;
+	char *kaddr = (char*)page_address(page);
+	unsigned from = (char*)de - kaddr;
+	unsigned to = from + SYSV_DIRSIZE;
+	int err;
+
+	lock_page(page);
+	err = mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (err)
+		BUG();
+	de->inode = 0;
+	err = dir_commit_chunk(page, from, to);
+	dir_put_page(page);
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	return err;
+}
+
+int sysv_make_empty(struct inode *inode, struct inode *dir)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page = grab_cache_page(mapping, 0);
+	struct sysv_dir_entry * de;
+	char *base;
+	int err;
+
+	if (!page)
+		return -ENOMEM;
+	kmap(page);
+	err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * SYSV_DIRSIZE);
+	if (err) {
+		unlock_page(page);
+		goto fail;
+	}
+
+	base = (char*)page_address(page);
+	memset(base, 0, PAGE_CACHE_SIZE);
+
+	de = (struct sysv_dir_entry *) base;
+	de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino);
+	strcpy(de->name,".");
+	de++;
+	de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), dir->i_ino);
+	strcpy(de->name,"..");
+
+	err = dir_commit_chunk(page, 0, 2 * SYSV_DIRSIZE);
+fail:
+	kunmap(page);
+	page_cache_release(page);
+	return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int sysv_empty_dir(struct inode * inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct page *page = NULL;
+	unsigned long i, npages = dir_pages(inode);
+
+	for (i = 0; i < npages; i++) {
+		char *kaddr;
+		struct sysv_dir_entry * de;
+		page = dir_get_page(inode, i);
+
+		if (IS_ERR(page))
+			continue;
+
+		kaddr = (char *)page_address(page);
+		de = (struct sysv_dir_entry *)kaddr;
+		kaddr += PAGE_CACHE_SIZE-SYSV_DIRSIZE;
+
+		for ( ;(char *)de <= kaddr; de++) {
+			if (!de->inode)
+				continue;
+			/* check for . and .. */
+			if (de->name[0] != '.')
+				goto not_empty;
+			if (!de->name[1]) {
+				if (de->inode == cpu_to_fs16(SYSV_SB(sb),
+							inode->i_ino))
+					continue;
+				goto not_empty;
+			}
+			if (de->name[1] != '.' || de->name[2])
+				goto not_empty;
+		}
+		dir_put_page(page);
+	}
+	return 1;
+
+not_empty:
+	dir_put_page(page);
+	return 0;
+}
+
+/* Releases the page */
+void sysv_set_link(struct sysv_dir_entry *de, struct page *page,
+	struct inode *inode)
+{
+	struct inode *dir = (struct inode*)page->mapping->host;
+	unsigned from = (char *)de-(char*)page_address(page);
+	unsigned to = from + SYSV_DIRSIZE;
+	int err;
+
+	lock_page(page);
+	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (err)
+		BUG();
+	de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino);
+	err = dir_commit_chunk(page, from, to);
+	dir_put_page(page);
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(dir);
+}
+
+struct sysv_dir_entry * sysv_dotdot (struct inode *dir, struct page **p)
+{
+	struct page *page = dir_get_page(dir, 0);
+	struct sysv_dir_entry *de = NULL;
+
+	if (!IS_ERR(page)) {
+		de = (struct sysv_dir_entry*) page_address(page) + 1;
+		*p = page;
+	}
+	return de;
+}
+
+ino_t sysv_inode_by_name(struct dentry *dentry)
+{
+	struct page *page;
+	struct sysv_dir_entry *de = sysv_find_entry (dentry, &page);
+	ino_t res = 0;
+	
+	if (de) {
+		res = fs16_to_cpu(SYSV_SB(dentry->d_sb), de->inode);
+		dir_put_page(page);
+	}
+	return res;
+}
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
new file mode 100644
index 0000000..da69abc
--- /dev/null
+++ b/fs/sysv/file.c
@@ -0,0 +1,49 @@
+/*
+ *  linux/fs/sysv/file.c
+ *
+ *  minix/file.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  coh/file.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/file.c
+ *  Copyright (C) 1993  Bruno Haible
+ *
+ *  SystemV/Coherent regular file handling primitives
+ */
+
+#include "sysv.h"
+
+/*
+ * We have mostly NULLs here: the current defaults are OK for
+ * the coh filesystem.
+ */
+struct file_operations sysv_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_file_read,
+	.write		= generic_file_write,
+	.mmap		= generic_file_mmap,
+	.fsync		= sysv_sync_file,
+	.sendfile	= generic_file_sendfile,
+};
+
+struct inode_operations sysv_file_inode_operations = {
+	.truncate	= sysv_truncate,
+	.getattr	= sysv_getattr,
+};
+
+int sysv_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+	struct inode *inode = dentry->d_inode;
+	int err;
+
+	err = sync_mapping_buffers(inode->i_mapping);
+	if (!(inode->i_state & I_DIRTY))
+		return err;
+	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+		return err;
+	
+	err |= sysv_sync_inode(inode);
+	return err ? -EIO : 0;
+}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
new file mode 100644
index 0000000..9b585d1
--- /dev/null
+++ b/fs/sysv/ialloc.c
@@ -0,0 +1,240 @@
+/*
+ *  linux/fs/sysv/ialloc.c
+ *
+ *  minix/bitmap.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  ext/freelists.c
+ *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
+ *
+ *  xenix/alloc.c
+ *  Copyright (C) 1992  Doug Evans
+ *
+ *  coh/alloc.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/ialloc.c
+ *  Copyright (C) 1993  Bruno Haible
+ *
+ *  This file contains code for allocating/freeing inodes.
+ */
+
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+#include "sysv.h"
+
+/* We don't trust the value of
+   sb->sv_sbd2->s_tinode = *sb->sv_sb_total_free_inodes
+   but we nevertheless keep it up to date. */
+
+/* An inode on disk is considered free if both i_mode == 0 and i_nlink == 0. */
+
+/* return &sb->sv_sb_fic_inodes[i] = &sbd->s_inode[i]; */
+static inline sysv_ino_t *
+sv_sb_fic_inode(struct super_block * sb, unsigned int i)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+
+	if (sbi->s_bh1 == sbi->s_bh2)
+		return &sbi->s_sb_fic_inodes[i];
+	else {
+		/* 512 byte Xenix FS */
+		unsigned int offset = offsetof(struct xenix_super_block, s_inode[i]);
+		if (offset < 512)
+			return (sysv_ino_t*)(sbi->s_sbd1 + offset);
+		else
+			return (sysv_ino_t*)(sbi->s_sbd2 + offset);
+	}
+}
+
+struct sysv_inode *
+sysv_raw_inode(struct super_block *sb, unsigned ino, struct buffer_head **bh)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	struct sysv_inode *res;
+	int block = sbi->s_firstinodezone + sbi->s_block_base;
+
+	block += (ino-1) >> sbi->s_inodes_per_block_bits;
+	*bh = sb_bread(sb, block);
+	if (!*bh)
+		return NULL;
+	res = (struct sysv_inode *)(*bh)->b_data;
+	return res + ((ino-1) & sbi->s_inodes_per_block_1);
+}
+
+static int refill_free_cache(struct super_block *sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	struct buffer_head * bh;
+	struct sysv_inode * raw_inode;
+	int i = 0, ino;
+
+	ino = SYSV_ROOT_INO+1;
+	raw_inode = sysv_raw_inode(sb, ino, &bh);
+	if (!raw_inode)
+		goto out;
+	while (ino <= sbi->s_ninodes) {
+		if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0) {
+			*sv_sb_fic_inode(sb,i++) = cpu_to_fs16(SYSV_SB(sb), ino);
+			if (i == sbi->s_fic_size)
+				break;
+		}
+		if ((ino++ & sbi->s_inodes_per_block_1) == 0) {
+			brelse(bh);
+			raw_inode = sysv_raw_inode(sb, ino, &bh);
+			if (!raw_inode)
+				goto out;
+		} else
+			raw_inode++;
+	}
+	brelse(bh);
+out:
+	return i;
+}
+
+void sysv_free_inode(struct inode * inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	unsigned int ino;
+	struct buffer_head * bh;
+	struct sysv_inode * raw_inode;
+	unsigned count;
+
+	sb = inode->i_sb;
+	ino = inode->i_ino;
+	if (ino <= SYSV_ROOT_INO || ino > sbi->s_ninodes) {
+		printk("sysv_free_inode: inode 0,1,2 or nonexistent inode\n");
+		return;
+	}
+	raw_inode = sysv_raw_inode(sb, ino, &bh);
+	clear_inode(inode);
+	if (!raw_inode) {
+		printk("sysv_free_inode: unable to read inode block on device "
+		       "%s\n", inode->i_sb->s_id);
+		return;
+	}
+	lock_super(sb);
+	count = fs16_to_cpu(sbi, *sbi->s_sb_fic_count);
+	if (count < sbi->s_fic_size) {
+		*sv_sb_fic_inode(sb,count++) = cpu_to_fs16(sbi, ino);
+		*sbi->s_sb_fic_count = cpu_to_fs16(sbi, count);
+	}
+	fs16_add(sbi, sbi->s_sb_total_free_inodes, 1);
+	dirty_sb(sb);
+	memset(raw_inode, 0, sizeof(struct sysv_inode));
+	mark_buffer_dirty(bh);
+	unlock_super(sb);
+	brelse(bh);
+}
+
+struct inode * sysv_new_inode(const struct inode * dir, mode_t mode)
+{
+	struct super_block *sb = dir->i_sb;
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	struct inode *inode;
+	sysv_ino_t ino;
+	unsigned count;
+
+	inode = new_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	lock_super(sb);
+	count = fs16_to_cpu(sbi, *sbi->s_sb_fic_count);
+	if (count == 0 || (*sv_sb_fic_inode(sb,count-1) == 0)) {
+		count = refill_free_cache(sb);
+		if (count == 0) {
+			iput(inode);
+			unlock_super(sb);
+			return ERR_PTR(-ENOSPC);
+		}
+	}
+	/* Now count > 0. */
+	ino = *sv_sb_fic_inode(sb,--count);
+	*sbi->s_sb_fic_count = cpu_to_fs16(sbi, count);
+	fs16_add(sbi, sbi->s_sb_total_free_inodes, -1);
+	dirty_sb(sb);
+	
+	if (dir->i_mode & S_ISGID) {
+		inode->i_gid = dir->i_gid;
+		if (S_ISDIR(mode))
+			mode |= S_ISGID;
+	} else
+		inode->i_gid = current->fsgid;
+
+	inode->i_uid = current->fsuid;
+	inode->i_ino = fs16_to_cpu(sbi, ino);
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_blocks = inode->i_blksize = 0;
+	memset(SYSV_I(inode)->i_data, 0, sizeof(SYSV_I(inode)->i_data));
+	SYSV_I(inode)->i_dir_start_lookup = 0;
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+
+	inode->i_mode = mode;		/* for sysv_write_inode() */
+	sysv_write_inode(inode, 0);	/* ensure inode not allocated again */
+	mark_inode_dirty(inode);	/* cleared by sysv_write_inode() */
+	/* That's it. */
+	unlock_super(sb);
+	return inode;
+}
+
+unsigned long sysv_count_free_inodes(struct super_block * sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	struct buffer_head * bh;
+	struct sysv_inode * raw_inode;
+	int ino, count, sb_count;
+
+	lock_super(sb);
+
+	sb_count = fs16_to_cpu(sbi, *sbi->s_sb_total_free_inodes);
+
+	if (0)
+		goto trust_sb;
+
+	/* this causes a lot of disk traffic ... */
+	count = 0;
+	ino = SYSV_ROOT_INO+1;
+	raw_inode = sysv_raw_inode(sb, ino, &bh);
+	if (!raw_inode)
+		goto Eio;
+	while (ino <= sbi->s_ninodes) {
+		if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+			count++;
+		if ((ino++ & sbi->s_inodes_per_block_1) == 0) {
+			brelse(bh);
+			raw_inode = sysv_raw_inode(sb, ino, &bh);
+			if (!raw_inode)
+				goto Eio;
+		} else
+			raw_inode++;
+	}
+	brelse(bh);
+	if (count != sb_count)
+		goto Einval;
+out:
+	unlock_super(sb);
+	return count;
+
+Einval:
+	printk("sysv_count_free_inodes: "
+		"free inode count was %d, correcting to %d\n",
+		sb_count, count);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		*sbi->s_sb_total_free_inodes = cpu_to_fs16(SYSV_SB(sb), count);
+		dirty_sb(sb);
+	}
+	goto out;
+
+Eio:
+	printk("sysv_count_free_inodes: unable to read inode table\n");
+trust_sb:
+	count = sb_count;
+	goto out;
+}
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
new file mode 100644
index 0000000..0530077
--- /dev/null
+++ b/fs/sysv/inode.c
@@ -0,0 +1,354 @@
+/*
+ *  linux/fs/sysv/inode.c
+ *
+ *  minix/inode.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  xenix/inode.c
+ *  Copyright (C) 1992  Doug Evans
+ *
+ *  coh/inode.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/inode.c
+ *  Copyright (C) 1993  Paul B. Monday
+ *
+ *  sysv/inode.c
+ *  Copyright (C) 1993  Bruno Haible
+ *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
+ *
+ *  This file contains code for allocating/freeing inodes and for read/writing
+ *  the superblock.
+ */
+
+#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <asm/byteorder.h>
+#include "sysv.h"
+
+/* This is only called on sync() and umount(), when s_dirt=1. */
+static void sysv_write_super(struct super_block *sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	unsigned long time = get_seconds(), old_time;
+
+	lock_kernel();
+	if (sb->s_flags & MS_RDONLY)
+		goto clean;
+
+	/*
+	 * If we are going to write out the super block,
+	 * then attach current time stamp.
+	 * But if the filesystem was marked clean, keep it clean.
+	 */
+	old_time = fs32_to_cpu(sbi, *sbi->s_sb_time);
+	if (sbi->s_type == FSTYPE_SYSV4) {
+		if (*sbi->s_sb_state == cpu_to_fs32(sbi, 0x7c269d38 - old_time))
+			*sbi->s_sb_state = cpu_to_fs32(sbi, 0x7c269d38 - time);
+		*sbi->s_sb_time = cpu_to_fs32(sbi, time);
+		mark_buffer_dirty(sbi->s_bh2);
+	}
+clean:
+	sb->s_dirt = 0;
+	unlock_kernel();
+}
+
+static int sysv_remount(struct super_block *sb, int *flags, char *data)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	if (sbi->s_forced_ro)
+		*flags |= MS_RDONLY;
+	if (!(*flags & MS_RDONLY))
+		sb->s_dirt = 1;
+	return 0;
+}
+
+static void sysv_put_super(struct super_block *sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		/* XXX ext2 also updates the state here */
+		mark_buffer_dirty(sbi->s_bh1);
+		if (sbi->s_bh1 != sbi->s_bh2)
+			mark_buffer_dirty(sbi->s_bh2);
+	}
+
+	brelse(sbi->s_bh1);
+	if (sbi->s_bh1 != sbi->s_bh2)
+		brelse(sbi->s_bh2);
+
+	kfree(sbi);
+}
+
+static int sysv_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+
+	buf->f_type = sb->s_magic;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = sbi->s_ndatazones;
+	buf->f_bavail = buf->f_bfree = sysv_count_free_blocks(sb);
+	buf->f_files = sbi->s_ninodes;
+	buf->f_ffree = sysv_count_free_inodes(sb);
+	buf->f_namelen = SYSV_NAMELEN;
+	return 0;
+}
+
+/* 
+ * NXI <-> N0XI for PDP, XIN <-> XIN0 for le32, NIX <-> 0NIX for be32
+ */
+static inline void read3byte(struct sysv_sb_info *sbi,
+	unsigned char * from, unsigned char * to)
+{
+	if (sbi->s_bytesex == BYTESEX_PDP) {
+		to[0] = from[0];
+		to[1] = 0;
+		to[2] = from[1];
+		to[3] = from[2];
+	} else if (sbi->s_bytesex == BYTESEX_LE) {
+		to[0] = from[0];
+		to[1] = from[1];
+		to[2] = from[2];
+		to[3] = 0;
+	} else {
+		to[0] = 0;
+		to[1] = from[0];
+		to[2] = from[1];
+		to[3] = from[2];
+	}
+}
+
+static inline void write3byte(struct sysv_sb_info *sbi,
+	unsigned char * from, unsigned char * to)
+{
+	if (sbi->s_bytesex == BYTESEX_PDP) {
+		to[0] = from[0];
+		to[1] = from[2];
+		to[2] = from[3];
+	} else if (sbi->s_bytesex == BYTESEX_LE) {
+		to[0] = from[0];
+		to[1] = from[1];
+		to[2] = from[2];
+	} else {
+		to[0] = from[1];
+		to[1] = from[2];
+		to[2] = from[3];
+	}
+}
+
+static struct inode_operations sysv_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+	.getattr	= sysv_getattr,
+};
+
+void sysv_set_inode(struct inode *inode, dev_t rdev)
+{
+	if (S_ISREG(inode->i_mode)) {
+		inode->i_op = &sysv_file_inode_operations;
+		inode->i_fop = &sysv_file_operations;
+		inode->i_mapping->a_ops = &sysv_aops;
+	} else if (S_ISDIR(inode->i_mode)) {
+		inode->i_op = &sysv_dir_inode_operations;
+		inode->i_fop = &sysv_dir_operations;
+		inode->i_mapping->a_ops = &sysv_aops;
+	} else if (S_ISLNK(inode->i_mode)) {
+		if (inode->i_blocks) {
+			inode->i_op = &sysv_symlink_inode_operations;
+			inode->i_mapping->a_ops = &sysv_aops;
+		} else
+			inode->i_op = &sysv_fast_symlink_inode_operations;
+	} else
+		init_special_inode(inode, inode->i_mode, rdev);
+}
+
+static void sysv_read_inode(struct inode *inode)
+{
+	struct super_block * sb = inode->i_sb;
+	struct sysv_sb_info * sbi = SYSV_SB(sb);
+	struct buffer_head * bh;
+	struct sysv_inode * raw_inode;
+	struct sysv_inode_info * si;
+	unsigned int block, ino = inode->i_ino;
+
+	if (!ino || ino > sbi->s_ninodes) {
+		printk("Bad inode number on dev %s: %d is out of range\n",
+		       inode->i_sb->s_id, ino);
+		goto bad_inode;
+	}
+	raw_inode = sysv_raw_inode(sb, ino, &bh);
+	if (!raw_inode) {
+		printk("Major problem: unable to read inode from dev %s\n",
+		       inode->i_sb->s_id);
+		goto bad_inode;
+	}
+	/* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
+	inode->i_mode = fs16_to_cpu(sbi, raw_inode->i_mode);
+	inode->i_uid = (uid_t)fs16_to_cpu(sbi, raw_inode->i_uid);
+	inode->i_gid = (gid_t)fs16_to_cpu(sbi, raw_inode->i_gid);
+	inode->i_nlink = fs16_to_cpu(sbi, raw_inode->i_nlink);
+	inode->i_size = fs32_to_cpu(sbi, raw_inode->i_size);
+	inode->i_atime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_atime);
+	inode->i_mtime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_mtime);
+	inode->i_ctime.tv_sec = fs32_to_cpu(sbi, raw_inode->i_ctime);
+	inode->i_ctime.tv_nsec = 0;
+	inode->i_atime.tv_nsec = 0;
+	inode->i_mtime.tv_nsec = 0;
+	inode->i_blocks = inode->i_blksize = 0;
+
+	si = SYSV_I(inode);
+	for (block = 0; block < 10+1+1+1; block++)
+		read3byte(sbi, &raw_inode->i_data[3*block],
+				(u8 *)&si->i_data[block]);
+	brelse(bh);
+	si->i_dir_start_lookup = 0;
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		sysv_set_inode(inode,
+			       old_decode_dev(fs32_to_cpu(sbi, si->i_data[0])));
+	else
+		sysv_set_inode(inode, 0);
+	return;
+
+bad_inode:
+	make_bad_inode(inode);
+	return;
+}
+
+static struct buffer_head * sysv_update_inode(struct inode * inode)
+{
+	struct super_block * sb = inode->i_sb;
+	struct sysv_sb_info * sbi = SYSV_SB(sb);
+	struct buffer_head * bh;
+	struct sysv_inode * raw_inode;
+	struct sysv_inode_info * si;
+	unsigned int ino, block;
+
+	ino = inode->i_ino;
+	if (!ino || ino > sbi->s_ninodes) {
+		printk("Bad inode number on dev %s: %d is out of range\n",
+		       inode->i_sb->s_id, ino);
+		return NULL;
+	}
+	raw_inode = sysv_raw_inode(sb, ino, &bh);
+	if (!raw_inode) {
+		printk("unable to read i-node block\n");
+		return NULL;
+	}
+
+	raw_inode->i_mode = cpu_to_fs16(sbi, inode->i_mode);
+	raw_inode->i_uid = cpu_to_fs16(sbi, fs_high2lowuid(inode->i_uid));
+	raw_inode->i_gid = cpu_to_fs16(sbi, fs_high2lowgid(inode->i_gid));
+	raw_inode->i_nlink = cpu_to_fs16(sbi, inode->i_nlink);
+	raw_inode->i_size = cpu_to_fs32(sbi, inode->i_size);
+	raw_inode->i_atime = cpu_to_fs32(sbi, inode->i_atime.tv_sec);
+	raw_inode->i_mtime = cpu_to_fs32(sbi, inode->i_mtime.tv_sec);
+	raw_inode->i_ctime = cpu_to_fs32(sbi, inode->i_ctime.tv_sec);
+
+	si = SYSV_I(inode);
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		si->i_data[0] = cpu_to_fs32(sbi, old_encode_dev(inode->i_rdev));
+	for (block = 0; block < 10+1+1+1; block++)
+		write3byte(sbi, (u8 *)&si->i_data[block],
+			&raw_inode->i_data[3*block]);
+	mark_buffer_dirty(bh);
+	return bh;
+}
+
+int sysv_write_inode(struct inode * inode, int wait)
+{
+	struct buffer_head *bh;
+	lock_kernel();
+	bh = sysv_update_inode(inode);
+	brelse(bh);
+	unlock_kernel();
+	return 0;
+}
+
+int sysv_sync_inode(struct inode * inode)
+{
+        int err = 0;
+        struct buffer_head *bh;
+
+        bh = sysv_update_inode(inode);
+        if (bh && buffer_dirty(bh)) {
+                sync_dirty_buffer(bh);
+                if (buffer_req(bh) && !buffer_uptodate(bh)) {
+                        printk ("IO error syncing sysv inode [%s:%08lx]\n",
+                                inode->i_sb->s_id, inode->i_ino);
+                        err = -1;
+                }
+        }
+        else if (!bh)
+                err = -1;
+        brelse (bh);
+        return err;
+}
+
+static void sysv_delete_inode(struct inode *inode)
+{
+	inode->i_size = 0;
+	sysv_truncate(inode);
+	lock_kernel();
+	sysv_free_inode(inode);
+	unlock_kernel();
+}
+
+static kmem_cache_t *sysv_inode_cachep;
+
+static struct inode *sysv_alloc_inode(struct super_block *sb)
+{
+	struct sysv_inode_info *si;
+
+	si = kmem_cache_alloc(sysv_inode_cachep, SLAB_KERNEL);
+	if (!si)
+		return NULL;
+	return &si->vfs_inode;
+}
+
+static void sysv_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(sysv_inode_cachep, SYSV_I(inode));
+}
+
+static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags)
+{
+	struct sysv_inode_info *si = (struct sysv_inode_info *)p;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+			SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&si->vfs_inode);
+}
+
+struct super_operations sysv_sops = {
+	.alloc_inode	= sysv_alloc_inode,
+	.destroy_inode	= sysv_destroy_inode,
+	.read_inode	= sysv_read_inode,
+	.write_inode	= sysv_write_inode,
+	.delete_inode	= sysv_delete_inode,
+	.put_super	= sysv_put_super,
+	.write_super	= sysv_write_super,
+	.remount_fs	= sysv_remount,
+	.statfs		= sysv_statfs,
+};
+
+int __init sysv_init_icache(void)
+{
+	sysv_inode_cachep = kmem_cache_create("sysv_inode_cache",
+			sizeof(struct sysv_inode_info), 0,
+			SLAB_RECLAIM_ACCOUNT,
+			init_once, NULL);
+	if (!sysv_inode_cachep)
+		return -ENOMEM;
+	return 0;
+}
+
+void sysv_destroy_icache(void)
+{
+	kmem_cache_destroy(sysv_inode_cachep);
+}
diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c
new file mode 100644
index 0000000..86f5f8d
--- /dev/null
+++ b/fs/sysv/itree.c
@@ -0,0 +1,475 @@
+/*
+ *  linux/fs/sysv/itree.c
+ *
+ *  Handling of indirect blocks' trees.
+ *  AV, Sep--Dec 2000
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/mount.h>
+#include <linux/string.h>
+#include "sysv.h"
+
+enum {DIRECT = 10, DEPTH = 4};	/* Have triple indirect */
+
+static inline void dirty_indirect(struct buffer_head *bh, struct inode *inode)
+{
+	mark_buffer_dirty_inode(bh, inode);
+	if (IS_SYNC(inode))
+		sync_dirty_buffer(bh);
+}
+
+static int block_to_path(struct inode *inode, long block, int offsets[DEPTH])
+{
+	struct super_block *sb = inode->i_sb;
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	int ptrs_bits = sbi->s_ind_per_block_bits;
+	unsigned long	indirect_blocks = sbi->s_ind_per_block,
+			double_blocks = sbi->s_ind_per_block_2;
+	int n = 0;
+
+	if (block < 0) {
+		printk("sysv_block_map: block < 0\n");
+	} else if (block < DIRECT) {
+		offsets[n++] = block;
+	} else if ( (block -= DIRECT) < indirect_blocks) {
+		offsets[n++] = DIRECT;
+		offsets[n++] = block;
+	} else if ((block -= indirect_blocks) < double_blocks) {
+		offsets[n++] = DIRECT+1;
+		offsets[n++] = block >> ptrs_bits;
+		offsets[n++] = block & (indirect_blocks - 1);
+	} else if (((block -= double_blocks) >> (ptrs_bits * 2)) < indirect_blocks) {
+		offsets[n++] = DIRECT+2;
+		offsets[n++] = block >> (ptrs_bits * 2);
+		offsets[n++] = (block >> ptrs_bits) & (indirect_blocks - 1);
+		offsets[n++] = block & (indirect_blocks - 1);
+	} else {
+		/* nothing */;
+	}
+	return n;
+}
+
+static inline int block_to_cpu(struct sysv_sb_info *sbi, sysv_zone_t nr)
+{
+	return sbi->s_block_base + fs32_to_cpu(sbi, nr);
+}
+
+typedef struct {
+	sysv_zone_t     *p;
+	sysv_zone_t     key;
+	struct buffer_head *bh;
+} Indirect;
+
+static DEFINE_RWLOCK(pointers_lock);
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, sysv_zone_t *v)
+{
+	p->key = *(p->p = v);
+	p->bh = bh;
+}
+
+static inline int verify_chain(Indirect *from, Indirect *to)
+{
+	while (from <= to && from->key == *from->p)
+		from++;
+	return (from > to);
+}
+
+static inline sysv_zone_t *block_end(struct buffer_head *bh)
+{
+	return (sysv_zone_t*)((char*)bh->b_data + bh->b_size);
+}
+
+/*
+ * Requires read_lock(&pointers_lock) or write_lock(&pointers_lock)
+ */
+static Indirect *get_branch(struct inode *inode,
+			    int depth,
+			    int offsets[],
+			    Indirect chain[],
+			    int *err)
+{
+	struct super_block *sb = inode->i_sb;
+	Indirect *p = chain;
+	struct buffer_head *bh;
+
+	*err = 0;
+	add_chain(chain, NULL, SYSV_I(inode)->i_data + *offsets);
+	if (!p->key)
+		goto no_block;
+	while (--depth) {
+		int block = block_to_cpu(SYSV_SB(sb), p->key);
+		bh = sb_bread(sb, block);
+		if (!bh)
+			goto failure;
+		if (!verify_chain(chain, p))
+			goto changed;
+		add_chain(++p, bh, (sysv_zone_t*)bh->b_data + *++offsets);
+		if (!p->key)
+			goto no_block;
+	}
+	return NULL;
+
+changed:
+	brelse(bh);
+	*err = -EAGAIN;
+	goto no_block;
+failure:
+	*err = -EIO;
+no_block:
+	return p;
+}
+
+static int alloc_branch(struct inode *inode,
+			int num,
+			int *offsets,
+			Indirect *branch)
+{
+	int blocksize = inode->i_sb->s_blocksize;
+	int n = 0;
+	int i;
+
+	branch[0].key = sysv_new_block(inode->i_sb);
+	if (branch[0].key) for (n = 1; n < num; n++) {
+		struct buffer_head *bh;
+		int parent;
+		/* Allocate the next block */
+		branch[n].key = sysv_new_block(inode->i_sb);
+		if (!branch[n].key)
+			break;
+		/*
+		 * Get buffer_head for parent block, zero it out and set 
+		 * the pointer to new one, then send parent to disk.
+		 */
+		parent = block_to_cpu(SYSV_SB(inode->i_sb), branch[n-1].key);
+		bh = sb_getblk(inode->i_sb, parent);
+		lock_buffer(bh);
+		memset(bh->b_data, 0, blocksize);
+		branch[n].bh = bh;
+		branch[n].p = (sysv_zone_t*) bh->b_data + offsets[n];
+		*branch[n].p = branch[n].key;
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		dirty_indirect(bh, inode);
+	}
+	if (n == num)
+		return 0;
+
+	/* Allocation failed, free what we already allocated */
+	for (i = 1; i < n; i++)
+		bforget(branch[i].bh);
+	for (i = 0; i < n; i++)
+		sysv_free_block(inode->i_sb, branch[i].key);
+	return -ENOSPC;
+}
+
+static inline int splice_branch(struct inode *inode,
+				Indirect chain[],
+				Indirect *where,
+				int num)
+{
+	int i;
+
+	/* Verify that place we are splicing to is still there and vacant */
+	write_lock(&pointers_lock);
+	if (!verify_chain(chain, where-1) || *where->p)
+		goto changed;
+	*where->p = where->key;
+	write_unlock(&pointers_lock);
+
+	inode->i_ctime = CURRENT_TIME_SEC;
+
+	/* had we spliced it onto indirect block? */
+	if (where->bh)
+		dirty_indirect(where->bh, inode);
+
+	if (IS_SYNC(inode))
+		sysv_sync_inode(inode);
+	else
+		mark_inode_dirty(inode);
+	return 0;
+
+changed:
+	write_unlock(&pointers_lock);
+	for (i = 1; i < num; i++)
+		bforget(where[i].bh);
+	for (i = 0; i < num; i++)
+		sysv_free_block(inode->i_sb, where[i].key);
+	return -EAGAIN;
+}
+
+static int get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
+{
+	int err = -EIO;
+	int offsets[DEPTH];
+	Indirect chain[DEPTH];
+	struct super_block *sb = inode->i_sb;
+	Indirect *partial;
+	int left;
+	int depth = block_to_path(inode, iblock, offsets);
+
+	if (depth == 0)
+		goto out;
+
+reread:
+	read_lock(&pointers_lock);
+	partial = get_branch(inode, depth, offsets, chain, &err);
+	read_unlock(&pointers_lock);
+
+	/* Simplest case - block found, no allocation needed */
+	if (!partial) {
+got_it:
+		map_bh(bh_result, sb, block_to_cpu(SYSV_SB(sb),
+					chain[depth-1].key));
+		/* Clean up and exit */
+		partial = chain+depth-1; /* the whole chain */
+		goto cleanup;
+	}
+
+	/* Next simple case - plain lookup or failed read of indirect block */
+	if (!create || err == -EIO) {
+cleanup:
+		while (partial > chain) {
+			brelse(partial->bh);
+			partial--;
+		}
+out:
+		return err;
+	}
+
+	/*
+	 * Indirect block might be removed by truncate while we were
+	 * reading it. Handling of that case (forget what we've got and
+	 * reread) is taken out of the main path.
+	 */
+	if (err == -EAGAIN)
+		goto changed;
+
+	left = (chain + depth) - partial;
+	err = alloc_branch(inode, left, offsets+(partial-chain), partial);
+	if (err)
+		goto cleanup;
+
+	if (splice_branch(inode, chain, partial, left) < 0)
+		goto changed;
+
+	set_buffer_new(bh_result);
+	goto got_it;
+
+changed:
+	while (partial > chain) {
+		brelse(partial->bh);
+		partial--;
+	}
+	goto reread;
+}
+
+static inline int all_zeroes(sysv_zone_t *p, sysv_zone_t *q)
+{
+	while (p < q)
+		if (*p++)
+			return 0;
+	return 1;
+}
+
+static Indirect *find_shared(struct inode *inode,
+				int depth,
+				int offsets[],
+				Indirect chain[],
+				sysv_zone_t *top)
+{
+	Indirect *partial, *p;
+	int k, err;
+
+	*top = 0;
+	for (k = depth; k > 1 && !offsets[k-1]; k--)
+		;
+
+	write_lock(&pointers_lock);
+	partial = get_branch(inode, k, offsets, chain, &err);
+	if (!partial)
+		partial = chain + k-1;
+	/*
+	 * If the branch acquired continuation since we've looked at it -
+	 * fine, it should all survive and (new) top doesn't belong to us.
+	 */
+	if (!partial->key && *partial->p) {
+		write_unlock(&pointers_lock);
+		goto no_top;
+	}
+	for (p=partial; p>chain && all_zeroes((sysv_zone_t*)p->bh->b_data,p->p); p--)
+		;
+	/*
+	 * OK, we've found the last block that must survive. The rest of our
+	 * branch should be detached before unlocking. However, if that rest
+	 * of branch is all ours and does not grow immediately from the inode
+	 * it's easier to cheat and just decrement partial->p.
+	 */
+	if (p == chain + k - 1 && p > chain) {
+		p->p--;
+	} else {
+		*top = *p->p;
+		*p->p = 0;
+	}
+	write_unlock(&pointers_lock);
+
+	while (partial > p) {
+		brelse(partial->bh);
+		partial--;
+	}
+no_top:
+	return partial;
+}
+
+static inline void free_data(struct inode *inode, sysv_zone_t *p, sysv_zone_t *q)
+{
+	for ( ; p < q ; p++) {
+		sysv_zone_t nr = *p;
+		if (nr) {
+			*p = 0;
+			sysv_free_block(inode->i_sb, nr);
+			mark_inode_dirty(inode);
+		}
+	}
+}
+
+static void free_branches(struct inode *inode, sysv_zone_t *p, sysv_zone_t *q, int depth)
+{
+	struct buffer_head * bh;
+	struct super_block *sb = inode->i_sb;
+
+	if (depth--) {
+		for ( ; p < q ; p++) {
+			int block;
+			sysv_zone_t nr = *p;
+			if (!nr)
+				continue;
+			*p = 0;
+			block = block_to_cpu(SYSV_SB(sb), nr);
+			bh = sb_bread(sb, block);
+			if (!bh)
+				continue;
+			free_branches(inode, (sysv_zone_t*)bh->b_data,
+					block_end(bh), depth);
+			bforget(bh);
+			sysv_free_block(sb, nr);
+			mark_inode_dirty(inode);
+		}
+	} else
+		free_data(inode, p, q);
+}
+
+void sysv_truncate (struct inode * inode)
+{
+	sysv_zone_t *i_data = SYSV_I(inode)->i_data;
+	int offsets[DEPTH];
+	Indirect chain[DEPTH];
+	Indirect *partial;
+	sysv_zone_t nr = 0;
+	int n;
+	long iblock;
+	unsigned blocksize;
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	    S_ISLNK(inode->i_mode)))
+		return;
+
+	blocksize = inode->i_sb->s_blocksize;
+	iblock = (inode->i_size + blocksize-1)
+					>> inode->i_sb->s_blocksize_bits;
+
+	block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+
+	n = block_to_path(inode, iblock, offsets);
+	if (n == 0)
+		return;
+
+	if (n == 1) {
+		free_data(inode, i_data+offsets[0], i_data + DIRECT);
+		goto do_indirects;
+	}
+
+	partial = find_shared(inode, n, offsets, chain, &nr);
+	/* Kill the top of shared branch (already detached) */
+	if (nr) {
+		if (partial == chain)
+			mark_inode_dirty(inode);
+		else
+			dirty_indirect(partial->bh, inode);
+		free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
+	}
+	/* Clear the ends of indirect blocks on the shared branch */
+	while (partial > chain) {
+		free_branches(inode, partial->p + 1, block_end(partial->bh),
+				(chain+n-1) - partial);
+		dirty_indirect(partial->bh, inode);
+		brelse (partial->bh);
+		partial--;
+	}
+do_indirects:
+	/* Kill the remaining (whole) subtrees (== subtrees deeper than...) */
+	while (n < DEPTH) {
+		nr = i_data[DIRECT + n - 1];
+		if (nr) {
+			i_data[DIRECT + n - 1] = 0;
+			mark_inode_dirty(inode);
+			free_branches(inode, &nr, &nr+1, n);
+		}
+		n++;
+	}
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	if (IS_SYNC(inode))
+		sysv_sync_inode (inode);
+	else
+		mark_inode_dirty(inode);
+}
+
+static unsigned sysv_nblocks(struct super_block *s, loff_t size)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(s);
+	int ptrs_bits = sbi->s_ind_per_block_bits;
+	unsigned blocks, res, direct = DIRECT, i = DEPTH;
+	blocks = (size + s->s_blocksize - 1) >> s->s_blocksize_bits;
+	res = blocks;
+	while (--i && blocks > direct) {
+		blocks = ((blocks - direct - 1) >> ptrs_bits) + 1;
+		res += blocks;
+		direct = 1;
+	}
+	return blocks;
+}
+
+int sysv_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	struct super_block *s = mnt->mnt_sb;
+	generic_fillattr(dentry->d_inode, stat);
+	stat->blocks = (s->s_blocksize / 512) * sysv_nblocks(s, stat->size);
+	stat->blksize = s->s_blocksize;
+	return 0;
+}
+
+static int sysv_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page,get_block,wbc);
+}
+static int sysv_readpage(struct file *file, struct page *page)
+{
+	return block_read_full_page(page,get_block);
+}
+static int sysv_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	return block_prepare_write(page,from,to,get_block);
+}
+static sector_t sysv_bmap(struct address_space *mapping, sector_t block)
+{
+	return generic_block_bmap(mapping,block,get_block);
+}
+struct address_space_operations sysv_aops = {
+	.readpage = sysv_readpage,
+	.writepage = sysv_writepage,
+	.sync_page = block_sync_page,
+	.prepare_write = sysv_prepare_write,
+	.commit_write = generic_commit_write,
+	.bmap = sysv_bmap
+};
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
new file mode 100644
index 0000000..7f0e4b5
--- /dev/null
+++ b/fs/sysv/namei.c
@@ -0,0 +1,318 @@
+/*
+ *  linux/fs/sysv/namei.c
+ *
+ *  minix/namei.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  coh/namei.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/namei.c
+ *  Copyright (C) 1993  Bruno Haible
+ *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
+ */
+
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include "sysv.h"
+
+static inline void inc_count(struct inode *inode)
+{
+	inode->i_nlink++;
+	mark_inode_dirty(inode);
+}
+
+static inline void dec_count(struct inode *inode)
+{
+	inode->i_nlink--;
+	mark_inode_dirty(inode);
+}
+
+static int add_nondir(struct dentry *dentry, struct inode *inode)
+{
+	int err = sysv_add_link(dentry, inode);
+	if (!err) {
+		d_instantiate(dentry, inode);
+		return 0;
+	}
+	dec_count(inode);
+	iput(inode);
+	return err;
+}
+
+static int sysv_hash(struct dentry *dentry, struct qstr *qstr)
+{
+	/* Truncate the name in place, avoids having to define a compare
+	   function. */
+	if (qstr->len > SYSV_NAMELEN) {
+		qstr->len = SYSV_NAMELEN;
+		qstr->hash = full_name_hash(qstr->name, qstr->len);
+	}
+	return 0;
+}
+
+struct dentry_operations sysv_dentry_operations = {
+	.d_hash		= sysv_hash,
+};
+
+static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
+{
+	struct inode * inode = NULL;
+	ino_t ino;
+
+	dentry->d_op = dir->i_sb->s_root->d_op;
+	if (dentry->d_name.len > SYSV_NAMELEN)
+		return ERR_PTR(-ENAMETOOLONG);
+	ino = sysv_inode_by_name(dentry);
+
+	if (ino) {
+		inode = iget(dir->i_sb, ino);
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	}
+	d_add(dentry, inode);
+	return NULL;
+}
+
+static int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, dev_t rdev)
+{
+	struct inode * inode;
+	int err;
+
+	if (!old_valid_dev(rdev))
+		return -EINVAL;
+
+	inode = sysv_new_inode(dir, mode);
+	err = PTR_ERR(inode);
+
+	if (!IS_ERR(inode)) {
+		sysv_set_inode(inode, rdev);
+		mark_inode_dirty(inode);
+		err = add_nondir(dentry, inode);
+	}
+	return err;
+}
+
+static int sysv_create(struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd)
+{
+	return sysv_mknod(dir, dentry, mode, 0);
+}
+
+static int sysv_symlink(struct inode * dir, struct dentry * dentry, 
+	const char * symname)
+{
+	int err = -ENAMETOOLONG;
+	int l = strlen(symname)+1;
+	struct inode * inode;
+
+	if (l > dir->i_sb->s_blocksize)
+		goto out;
+
+	inode = sysv_new_inode(dir, S_IFLNK|0777);
+	err = PTR_ERR(inode);
+	if (IS_ERR(inode))
+		goto out;
+	
+	sysv_set_inode(inode, 0);
+	err = page_symlink(inode, symname, l);
+	if (err)
+		goto out_fail;
+
+	mark_inode_dirty(inode);
+	err = add_nondir(dentry, inode);
+out:
+	return err;
+
+out_fail:
+	dec_count(inode);
+	iput(inode);
+	goto out;
+}
+
+static int sysv_link(struct dentry * old_dentry, struct inode * dir, 
+	struct dentry * dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+
+	if (inode->i_nlink >= SYSV_SB(inode->i_sb)->s_link_max)
+		return -EMLINK;
+
+	inode->i_ctime = CURRENT_TIME_SEC;
+	inc_count(inode);
+	atomic_inc(&inode->i_count);
+
+	return add_nondir(dentry, inode);
+}
+
+static int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode)
+{
+	struct inode * inode;
+	int err = -EMLINK;
+
+	if (dir->i_nlink >= SYSV_SB(dir->i_sb)->s_link_max) 
+		goto out;
+	inc_count(dir);
+
+	inode = sysv_new_inode(dir, S_IFDIR|mode);
+	err = PTR_ERR(inode);
+	if (IS_ERR(inode))
+		goto out_dir;
+
+	sysv_set_inode(inode, 0);
+
+	inc_count(inode);
+
+	err = sysv_make_empty(inode, dir);
+	if (err)
+		goto out_fail;
+
+	err = sysv_add_link(dentry, inode);
+	if (err)
+		goto out_fail;
+
+        d_instantiate(dentry, inode);
+out:
+	return err;
+
+out_fail:
+	dec_count(inode);
+	dec_count(inode);
+	iput(inode);
+out_dir:
+	dec_count(dir);
+	goto out;
+}
+
+static int sysv_unlink(struct inode * dir, struct dentry * dentry)
+{
+	struct inode * inode = dentry->d_inode;
+	struct page * page;
+	struct sysv_dir_entry * de;
+	int err = -ENOENT;
+
+	de = sysv_find_entry(dentry, &page);
+	if (!de)
+		goto out;
+
+	err = sysv_delete_entry (de, page);
+	if (err)
+		goto out;
+
+	inode->i_ctime = dir->i_ctime;
+	dec_count(inode);
+out:
+	return err;
+}
+
+static int sysv_rmdir(struct inode * dir, struct dentry * dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	int err = -ENOTEMPTY;
+
+	if (sysv_empty_dir(inode)) {
+		err = sysv_unlink(dir, dentry);
+		if (!err) {
+			inode->i_size = 0;
+			dec_count(inode);
+			dec_count(dir);
+		}
+	}
+	return err;
+}
+
+/*
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
+		  struct inode * new_dir, struct dentry * new_dentry)
+{
+	struct inode * old_inode = old_dentry->d_inode;
+	struct inode * new_inode = new_dentry->d_inode;
+	struct page * dir_page = NULL;
+	struct sysv_dir_entry * dir_de = NULL;
+	struct page * old_page;
+	struct sysv_dir_entry * old_de;
+	int err = -ENOENT;
+
+	old_de = sysv_find_entry(old_dentry, &old_page);
+	if (!old_de)
+		goto out;
+
+	if (S_ISDIR(old_inode->i_mode)) {
+		err = -EIO;
+		dir_de = sysv_dotdot(old_inode, &dir_page);
+		if (!dir_de)
+			goto out_old;
+	}
+
+	if (new_inode) {
+		struct page * new_page;
+		struct sysv_dir_entry * new_de;
+
+		err = -ENOTEMPTY;
+		if (dir_de && !sysv_empty_dir(new_inode))
+			goto out_dir;
+
+		err = -ENOENT;
+		new_de = sysv_find_entry(new_dentry, &new_page);
+		if (!new_de)
+			goto out_dir;
+		inc_count(old_inode);
+		sysv_set_link(new_de, new_page, old_inode);
+		new_inode->i_ctime = CURRENT_TIME_SEC;
+		if (dir_de)
+			new_inode->i_nlink--;
+		dec_count(new_inode);
+	} else {
+		if (dir_de) {
+			err = -EMLINK;
+			if (new_dir->i_nlink >= SYSV_SB(new_dir->i_sb)->s_link_max)
+				goto out_dir;
+		}
+		inc_count(old_inode);
+		err = sysv_add_link(new_dentry, old_inode);
+		if (err) {
+			dec_count(old_inode);
+			goto out_dir;
+		}
+		if (dir_de)
+			inc_count(new_dir);
+	}
+
+	sysv_delete_entry(old_de, old_page);
+	dec_count(old_inode);
+
+	if (dir_de) {
+		sysv_set_link(dir_de, dir_page, new_dir);
+		dec_count(old_dir);
+	}
+	return 0;
+
+out_dir:
+	if (dir_de) {
+		kunmap(dir_page);
+		page_cache_release(dir_page);
+	}
+out_old:
+	kunmap(old_page);
+	page_cache_release(old_page);
+out:
+	return err;
+}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations sysv_dir_inode_operations = {
+	.create		= sysv_create,
+	.lookup		= sysv_lookup,
+	.link		= sysv_link,
+	.unlink		= sysv_unlink,
+	.symlink	= sysv_symlink,
+	.mkdir		= sysv_mkdir,
+	.rmdir		= sysv_rmdir,
+	.mknod		= sysv_mknod,
+	.rename		= sysv_rename,
+	.getattr	= sysv_getattr,
+};
diff --git a/fs/sysv/super.c b/fs/sysv/super.c
new file mode 100644
index 0000000..59e76b5
--- /dev/null
+++ b/fs/sysv/super.c
@@ -0,0 +1,572 @@
+/*
+ *  linux/fs/sysv/inode.c
+ *
+ *  minix/inode.c
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  xenix/inode.c
+ *  Copyright (C) 1992  Doug Evans
+ *
+ *  coh/inode.c
+ *  Copyright (C) 1993  Pascal Haible, Bruno Haible
+ *
+ *  sysv/inode.c
+ *  Copyright (C) 1993  Paul B. Monday
+ *
+ *  sysv/inode.c
+ *  Copyright (C) 1993  Bruno Haible
+ *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
+ *
+ *  This file contains code for read/parsing the superblock.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+#include "sysv.h"
+
+/*
+ * The following functions try to recognize specific filesystems.
+ *
+ * We recognize:
+ * - Xenix FS by its magic number.
+ * - SystemV FS by its magic number.
+ * - Coherent FS by its funny fname/fpack field.
+ * - SCO AFS by s_nfree == 0xffff
+ * - V7 FS has no distinguishing features.
+ *
+ * We discriminate among SystemV4 and SystemV2 FS by the assumption that
+ * the time stamp is not < 01-01-1980.
+ */
+
+enum {
+	JAN_1_1980 = (10*365 + 2) * 24 * 60 * 60
+};
+
+static void detected_xenix(struct sysv_sb_info *sbi)
+{
+	struct buffer_head *bh1 = sbi->s_bh1;
+	struct buffer_head *bh2 = sbi->s_bh2;
+	struct xenix_super_block * sbd1;
+	struct xenix_super_block * sbd2;
+
+	if (bh1 != bh2)
+		sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
+	else {
+		/* block size = 512, so bh1 != bh2 */
+		sbd1 = (struct xenix_super_block *) bh1->b_data;
+		sbd2 = (struct xenix_super_block *) (bh2->b_data - 512);
+	}
+
+	sbi->s_link_max = XENIX_LINK_MAX;
+	sbi->s_fic_size = XENIX_NICINOD;
+	sbi->s_flc_size = XENIX_NICFREE;
+	sbi->s_sbd1 = (char *)sbd1;
+	sbi->s_sbd2 = (char *)sbd2;
+	sbi->s_sb_fic_count = &sbd1->s_ninode;
+	sbi->s_sb_fic_inodes = &sbd1->s_inode[0];
+	sbi->s_sb_total_free_inodes = &sbd2->s_tinode;
+	sbi->s_bcache_count = &sbd1->s_nfree;
+	sbi->s_bcache = &sbd1->s_free[0];
+	sbi->s_free_blocks = &sbd2->s_tfree;
+	sbi->s_sb_time = &sbd2->s_time;
+	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd1->s_isize);
+	sbi->s_nzones = fs32_to_cpu(sbi, sbd1->s_fsize);
+}
+
+static void detected_sysv4(struct sysv_sb_info *sbi)
+{
+	struct sysv4_super_block * sbd;
+	struct buffer_head *bh1 = sbi->s_bh1;
+	struct buffer_head *bh2 = sbi->s_bh2;
+
+	if (bh1 == bh2)
+		sbd = (struct sysv4_super_block *) (bh1->b_data + BLOCK_SIZE/2);
+	else
+		sbd = (struct sysv4_super_block *) bh2->b_data;
+
+	sbi->s_link_max = SYSV_LINK_MAX;
+	sbi->s_fic_size = SYSV_NICINOD;
+	sbi->s_flc_size = SYSV_NICFREE;
+	sbi->s_sbd1 = (char *)sbd;
+	sbi->s_sbd2 = (char *)sbd;
+	sbi->s_sb_fic_count = &sbd->s_ninode;
+	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
+	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
+	sbi->s_bcache_count = &sbd->s_nfree;
+	sbi->s_bcache = &sbd->s_free[0];
+	sbi->s_free_blocks = &sbd->s_tfree;
+	sbi->s_sb_time = &sbd->s_time;
+	sbi->s_sb_state = &sbd->s_state;
+	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
+	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
+}
+
+static void detected_sysv2(struct sysv_sb_info *sbi)
+{
+	struct sysv2_super_block *sbd;
+	struct buffer_head *bh1 = sbi->s_bh1;
+	struct buffer_head *bh2 = sbi->s_bh2;
+
+	if (bh1 == bh2)
+		sbd = (struct sysv2_super_block *) (bh1->b_data + BLOCK_SIZE/2);
+	else
+		sbd = (struct sysv2_super_block *) bh2->b_data;
+
+	sbi->s_link_max = SYSV_LINK_MAX;
+	sbi->s_fic_size = SYSV_NICINOD;
+	sbi->s_flc_size = SYSV_NICFREE;
+	sbi->s_sbd1 = (char *)sbd;
+	sbi->s_sbd2 = (char *)sbd;
+	sbi->s_sb_fic_count = &sbd->s_ninode;
+	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
+	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
+	sbi->s_bcache_count = &sbd->s_nfree;
+	sbi->s_bcache = &sbd->s_free[0];
+	sbi->s_free_blocks = &sbd->s_tfree;
+	sbi->s_sb_time = &sbd->s_time;
+	sbi->s_sb_state = &sbd->s_state;
+	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
+	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
+}
+
+static void detected_coherent(struct sysv_sb_info *sbi)
+{
+	struct coh_super_block * sbd;
+	struct buffer_head *bh1 = sbi->s_bh1;
+
+	sbd = (struct coh_super_block *) bh1->b_data;
+
+	sbi->s_link_max = COH_LINK_MAX;
+	sbi->s_fic_size = COH_NICINOD;
+	sbi->s_flc_size = COH_NICFREE;
+	sbi->s_sbd1 = (char *)sbd;
+	sbi->s_sbd2 = (char *)sbd;
+	sbi->s_sb_fic_count = &sbd->s_ninode;
+	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
+	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
+	sbi->s_bcache_count = &sbd->s_nfree;
+	sbi->s_bcache = &sbd->s_free[0];
+	sbi->s_free_blocks = &sbd->s_tfree;
+	sbi->s_sb_time = &sbd->s_time;
+	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
+	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
+}
+
+static void detected_v7(struct sysv_sb_info *sbi)
+{
+	struct buffer_head *bh2 = sbi->s_bh2;
+	struct v7_super_block *sbd = (struct v7_super_block *)bh2->b_data;
+
+	sbi->s_link_max = V7_LINK_MAX;
+	sbi->s_fic_size = V7_NICINOD;
+	sbi->s_flc_size = V7_NICFREE;
+	sbi->s_sbd1 = (char *)sbd;
+	sbi->s_sbd2 = (char *)sbd;
+	sbi->s_sb_fic_count = &sbd->s_ninode;
+	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
+	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
+	sbi->s_bcache_count = &sbd->s_nfree;
+	sbi->s_bcache = &sbd->s_free[0];
+	sbi->s_free_blocks = &sbd->s_tfree;
+	sbi->s_sb_time = &sbd->s_time;
+	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
+	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
+}
+
+static int detect_xenix(struct sysv_sb_info *sbi, struct buffer_head *bh)
+{
+	struct xenix_super_block *sbd = (struct xenix_super_block *)bh->b_data;
+	if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0x2b5544))
+		sbi->s_bytesex = BYTESEX_LE;
+	else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0x2b5544))
+		sbi->s_bytesex = BYTESEX_BE;
+	else
+		return 0;
+	switch (fs32_to_cpu(sbi, sbd->s_type)) {
+	case 1:
+		sbi->s_type = FSTYPE_XENIX;
+		return 1;
+	case 2:
+		sbi->s_type = FSTYPE_XENIX;
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+static int detect_sysv(struct sysv_sb_info *sbi, struct buffer_head *bh)
+{
+	struct super_block *sb = sbi->s_sb;
+	/* All relevant fields are at the same offsets in R2 and R4 */
+	struct sysv4_super_block * sbd;
+	u32 type;
+
+	sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
+	if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0xfd187e20))
+		sbi->s_bytesex = BYTESEX_LE;
+	else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0xfd187e20))
+		sbi->s_bytesex = BYTESEX_BE;
+	else
+		return 0;
+
+	type = fs32_to_cpu(sbi, sbd->s_type);
+ 
+ 	if (fs16_to_cpu(sbi, sbd->s_nfree) == 0xffff) {
+ 		sbi->s_type = FSTYPE_AFS;
+		sbi->s_forced_ro = 1;
+ 		if (!(sb->s_flags & MS_RDONLY)) {
+ 			printk("SysV FS: SCO EAFS on %s detected, " 
+ 				"forcing read-only mode.\n", 
+ 				sb->s_id);
+ 		}
+ 		return type;
+ 	}
+ 
+	if (fs32_to_cpu(sbi, sbd->s_time) < JAN_1_1980) {
+		/* this is likely to happen on SystemV2 FS */
+		if (type > 3 || type < 1)
+			return 0;
+		sbi->s_type = FSTYPE_SYSV2;
+		return type;
+	}
+	if ((type > 3 || type < 1) && (type > 0x30 || type < 0x10))
+		return 0;
+
+	/* On Interactive Unix (ISC) Version 4.0/3.x s_type field = 0x10,
+	   0x20 or 0x30 indicates that symbolic links and the 14-character
+	   filename limit is gone. Due to lack of information about this
+           feature read-only mode seems to be a reasonable approach... -KGB */
+
+	if (type >= 0x10) {
+		printk("SysV FS: can't handle long file names on %s, "
+		       "forcing read-only mode.\n", sb->s_id);
+		sbi->s_forced_ro = 1;
+	}
+
+	sbi->s_type = FSTYPE_SYSV4;
+	return type >= 0x10 ? type >> 4 : type;
+}
+
+static int detect_coherent(struct sysv_sb_info *sbi, struct buffer_head *bh)
+{
+	struct coh_super_block * sbd;
+
+	sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
+	if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
+	    || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
+		return 0;
+	sbi->s_bytesex = BYTESEX_PDP;
+	sbi->s_type = FSTYPE_COH;
+	return 1;
+}
+
+static int detect_sysv_odd(struct sysv_sb_info *sbi, struct buffer_head *bh)
+{
+	int size = detect_sysv(sbi, bh);
+
+	return size>2 ? 0 : size;
+}
+
+static struct {
+	int block;
+	int (*test)(struct sysv_sb_info *, struct buffer_head *);
+} flavours[] = {
+	{1, detect_xenix},
+	{0, detect_sysv},
+	{0, detect_coherent},
+	{9, detect_sysv_odd},
+	{15,detect_sysv_odd},
+	{18,detect_sysv},
+};
+
+static char *flavour_names[] = {
+	[FSTYPE_XENIX]	= "Xenix",
+	[FSTYPE_SYSV4]	= "SystemV",
+	[FSTYPE_SYSV2]	= "SystemV Release 2",
+	[FSTYPE_COH]	= "Coherent",
+	[FSTYPE_V7]	= "V7",
+	[FSTYPE_AFS]	= "AFS",
+};
+
+static void (*flavour_setup[])(struct sysv_sb_info *) = {
+	[FSTYPE_XENIX]	= detected_xenix,
+	[FSTYPE_SYSV4]	= detected_sysv4,
+	[FSTYPE_SYSV2]	= detected_sysv2,
+	[FSTYPE_COH]	= detected_coherent,
+	[FSTYPE_V7]	= detected_v7,
+	[FSTYPE_AFS]	= detected_sysv4,
+};
+
+static int complete_read_super(struct super_block *sb, int silent, int size)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+	struct inode *root_inode;
+	char *found = flavour_names[sbi->s_type];
+	u_char n_bits = size+8;
+	int bsize = 1 << n_bits;
+	int bsize_4 = bsize >> 2;
+
+	sbi->s_firstinodezone = 2;
+
+	flavour_setup[sbi->s_type](sbi);
+	
+	sbi->s_truncate = 1;
+	sbi->s_ndatazones = sbi->s_nzones - sbi->s_firstdatazone;
+	sbi->s_inodes_per_block = bsize >> 6;
+	sbi->s_inodes_per_block_1 = (bsize >> 6)-1;
+	sbi->s_inodes_per_block_bits = n_bits-6;
+	sbi->s_ind_per_block = bsize_4;
+	sbi->s_ind_per_block_2 = bsize_4*bsize_4;
+	sbi->s_toobig_block = 10 + bsize_4 * (1 + bsize_4 * (1 + bsize_4));
+	sbi->s_ind_per_block_bits = n_bits-2;
+
+	sbi->s_ninodes = (sbi->s_firstdatazone - sbi->s_firstinodezone)
+		<< sbi->s_inodes_per_block_bits;
+
+	if (!silent)
+		printk("VFS: Found a %s FS (block size = %ld) on device %s\n",
+		       found, sb->s_blocksize, sb->s_id);
+
+	sb->s_magic = SYSV_MAGIC_BASE + sbi->s_type;
+	/* set up enough so that it can read an inode */
+	sb->s_op = &sysv_sops;
+	root_inode = iget(sb,SYSV_ROOT_INO);
+	if (!root_inode || is_bad_inode(root_inode)) {
+		printk("SysV FS: get root inode failed\n");
+		return 0;
+	}
+	sb->s_root = d_alloc_root(root_inode);
+	if (!sb->s_root) {
+		iput(root_inode);
+		printk("SysV FS: get root dentry failed\n");
+		return 0;
+	}
+	if (sbi->s_forced_ro)
+		sb->s_flags |= MS_RDONLY;
+	if (sbi->s_truncate)
+		sb->s_root->d_op = &sysv_dentry_operations;
+	sb->s_dirt = 1;
+	return 1;
+}
+
+static int sysv_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct buffer_head *bh1, *bh = NULL;
+	struct sysv_sb_info *sbi;
+	unsigned long blocknr;
+	int size = 0, i;
+	
+	if (1024 != sizeof (struct xenix_super_block))
+		panic("Xenix FS: bad superblock size");
+	if (512 != sizeof (struct sysv4_super_block))
+		panic("SystemV FS: bad superblock size");
+	if (512 != sizeof (struct sysv2_super_block))
+		panic("SystemV FS: bad superblock size");
+	if (500 != sizeof (struct coh_super_block))
+		panic("Coherent FS: bad superblock size");
+	if (64 != sizeof (struct sysv_inode))
+		panic("sysv fs: bad inode size");
+
+	sbi = kmalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	memset(sbi, 0, sizeof(struct sysv_sb_info));
+
+	sbi->s_sb = sb;
+	sbi->s_block_base = 0;
+	sb->s_fs_info = sbi;
+	
+	sb_set_blocksize(sb, BLOCK_SIZE);
+
+	for (i = 0; i < sizeof(flavours)/sizeof(flavours[0]) && !size; i++) {
+		brelse(bh);
+		bh = sb_bread(sb, flavours[i].block);
+		if (!bh)
+			continue;
+		size = flavours[i].test(SYSV_SB(sb), bh);
+	}
+
+	if (!size)
+		goto Eunknown;
+
+	switch (size) {
+		case 1:
+			blocknr = bh->b_blocknr << 1;
+			brelse(bh);
+			sb_set_blocksize(sb, 512);
+			bh1 = sb_bread(sb, blocknr);
+			bh = sb_bread(sb, blocknr + 1);
+			break;
+		case 2:
+			bh1 = bh;
+			break;
+		case 3:
+			blocknr = bh->b_blocknr >> 1;
+			brelse(bh);
+			sb_set_blocksize(sb, 2048);
+			bh1 = bh = sb_bread(sb, blocknr);
+			break;
+		default:
+			goto Ebadsize;
+	}
+
+	if (bh && bh1) {
+		sbi->s_bh1 = bh1;
+		sbi->s_bh2 = bh;
+		if (complete_read_super(sb, silent, size))
+			return 0;
+	}
+
+	brelse(bh1);
+	brelse(bh);
+	sb_set_blocksize(sb, BLOCK_SIZE);
+	printk("oldfs: cannot read superblock\n");
+failed:
+	kfree(sbi);
+	return -EINVAL;
+
+Eunknown:
+	brelse(bh);
+	if (!silent)
+		printk("VFS: unable to find oldfs superblock on device %s\n",
+			sb->s_id);
+	goto failed;
+Ebadsize:
+	brelse(bh);
+	if (!silent)
+		printk("VFS: oldfs: unsupported block size (%dKb)\n",
+			1<<(size-2));
+	goto failed;
+}
+
+static int v7_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct sysv_sb_info *sbi;
+	struct buffer_head *bh, *bh2 = NULL;
+	struct v7_super_block *v7sb;
+	struct sysv_inode *v7i;
+
+	if (440 != sizeof (struct v7_super_block))
+		panic("V7 FS: bad super-block size");
+	if (64 != sizeof (struct sysv_inode))
+		panic("sysv fs: bad i-node size");
+
+	sbi = kmalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	memset(sbi, 0, sizeof(struct sysv_sb_info));
+
+	sbi->s_sb = sb;
+	sbi->s_block_base = 0;
+	sbi->s_type = FSTYPE_V7;
+	sbi->s_bytesex = BYTESEX_PDP;
+	sb->s_fs_info = sbi;
+	
+	sb_set_blocksize(sb, 512);
+
+	if ((bh = sb_bread(sb, 1)) == NULL) {
+		if (!silent)
+			printk("VFS: unable to read V7 FS superblock on "
+			       "device %s.\n", sb->s_id);
+		goto failed;
+	}
+
+	/* plausibility check on superblock */
+	v7sb = (struct v7_super_block *) bh->b_data;
+	if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE ||
+	    fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD ||
+	    fs32_to_cpu(sbi, v7sb->s_time) == 0)
+		goto failed;
+
+	/* plausibility check on root inode: it is a directory,
+	   with a nonzero size that is a multiple of 16 */
+	if ((bh2 = sb_bread(sb, 2)) == NULL)
+		goto failed;
+	v7i = (struct sysv_inode *)(bh2->b_data + 64);
+	if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR ||
+	    (fs32_to_cpu(sbi, v7i->i_size) == 0) ||
+	    (fs32_to_cpu(sbi, v7i->i_size) & 017) != 0)
+		goto failed;
+	brelse(bh2);
+	bh2 = NULL;
+
+	sbi->s_bh1 = bh;
+	sbi->s_bh2 = bh;
+	if (complete_read_super(sb, silent, 1))
+		return 0;
+
+failed:
+	brelse(bh2);
+	brelse(bh);
+	kfree(sbi);
+	return -EINVAL;
+}
+
+/* Every kernel module contains stuff like this. */
+
+static struct super_block *sysv_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, sysv_fill_super);
+}
+
+static struct super_block *v7_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, v7_fill_super);
+}
+
+static struct file_system_type sysv_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "sysv",
+	.get_sb		= sysv_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static struct file_system_type v7_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "v7",
+	.get_sb		= v7_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+extern int sysv_init_icache(void) __init;
+extern void sysv_destroy_icache(void);
+
+static int __init init_sysv_fs(void)
+{
+	int error;
+
+	error = sysv_init_icache();
+	if (error)
+		goto out;
+	error = register_filesystem(&sysv_fs_type);
+	if (error)
+		goto destroy_icache;
+	error = register_filesystem(&v7_fs_type);
+	if (error)
+		goto unregister;
+	return 0;
+
+unregister:
+	unregister_filesystem(&sysv_fs_type);
+destroy_icache:
+	sysv_destroy_icache();
+out:
+	return error;
+}
+
+static void __exit exit_sysv_fs(void)
+{
+	unregister_filesystem(&sysv_fs_type);
+	unregister_filesystem(&v7_fs_type);
+	sysv_destroy_icache();
+}
+
+module_init(init_sysv_fs)
+module_exit(exit_sysv_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
new file mode 100644
index 0000000..ed637db
--- /dev/null
+++ b/fs/sysv/symlink.c
@@ -0,0 +1,20 @@
+/*
+ *  linux/fs/sysv/symlink.c
+ *
+ *  Handling of System V filesystem fast symlinks extensions.
+ *  Aug 2001, Christoph Hellwig (hch@infradead.org)
+ */
+
+#include "sysv.h"
+#include <linux/namei.h>
+
+static int sysv_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	nd_set_link(nd, (char *)SYSV_I(dentry->d_inode)->i_data);
+	return 0;
+}
+
+struct inode_operations sysv_fast_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= sysv_follow_link,
+};
diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h
new file mode 100644
index 0000000..b7f9b4a
--- /dev/null
+++ b/fs/sysv/sysv.h
@@ -0,0 +1,244 @@
+#ifndef _SYSV_H
+#define _SYSV_H
+
+#include <linux/buffer_head.h>
+
+typedef __u16 __bitwise __fs16;
+typedef __u32 __bitwise __fs32;
+
+#include <linux/sysv_fs.h>
+
+/*
+ * SystemV/V7/Coherent super-block data in memory
+ *
+ * The SystemV/V7/Coherent superblock contains dynamic data (it gets modified
+ * while the system is running). This is in contrast to the Minix and Berkeley
+ * filesystems (where the superblock is never modified). This affects the
+ * sync() operation: we must keep the superblock in a disk buffer and use this
+ * one as our "working copy".
+ */
+
+struct sysv_sb_info {
+	struct super_block *s_sb;	/* VFS superblock */
+	int	       s_type;		/* file system type: FSTYPE_{XENIX|SYSV|COH} */
+	char	       s_bytesex;	/* bytesex (le/be/pdp) */
+	char	       s_truncate;	/* if 1: names > SYSV_NAMELEN chars are truncated */
+					/* if 0: they are disallowed (ENAMETOOLONG) */
+	nlink_t        s_link_max;	/* max number of hard links to a file */
+	unsigned int   s_inodes_per_block;	/* number of inodes per block */
+	unsigned int   s_inodes_per_block_1;	/* inodes_per_block - 1 */
+	unsigned int   s_inodes_per_block_bits;	/* log2(inodes_per_block) */
+	unsigned int   s_ind_per_block;		/* number of indirections per block */
+	unsigned int   s_ind_per_block_bits;	/* log2(ind_per_block) */
+	unsigned int   s_ind_per_block_2;	/* ind_per_block ^ 2 */
+	unsigned int   s_toobig_block;		/* 10 + ipb + ipb^2 + ipb^3 */
+	unsigned int   s_block_base;	/* physical block number of block 0 */
+	unsigned short s_fic_size;	/* free inode cache size, NICINOD */
+	unsigned short s_flc_size;	/* free block list chunk size, NICFREE */
+	/* The superblock is kept in one or two disk buffers: */
+	struct buffer_head *s_bh1;
+	struct buffer_head *s_bh2;
+	/* These are pointers into the disk buffer, to compensate for
+	   different superblock layout. */
+	char *         s_sbd1;		/* entire superblock data, for part 1 */
+	char *         s_sbd2;		/* entire superblock data, for part 2 */
+	__fs16         *s_sb_fic_count;	/* pointer to s_sbd->s_ninode */
+        sysv_ino_t     *s_sb_fic_inodes; /* pointer to s_sbd->s_inode */
+	__fs16         *s_sb_total_free_inodes; /* pointer to s_sbd->s_tinode */
+	__fs16         *s_bcache_count;	/* pointer to s_sbd->s_nfree */
+	sysv_zone_t    *s_bcache;	/* pointer to s_sbd->s_free */
+	__fs32         *s_free_blocks;	/* pointer to s_sbd->s_tfree */
+	__fs32         *s_sb_time;	/* pointer to s_sbd->s_time */
+	__fs32         *s_sb_state;	/* pointer to s_sbd->s_state, only FSTYPE_SYSV */
+	/* We keep those superblock entities that don't change here;
+	   this saves us an indirection and perhaps a conversion. */
+	u32            s_firstinodezone; /* index of first inode zone */
+	u32            s_firstdatazone;	/* same as s_sbd->s_isize */
+	u32            s_ninodes;	/* total number of inodes */
+	u32            s_ndatazones;	/* total number of data zones */
+	u32            s_nzones;	/* same as s_sbd->s_fsize */
+	u16	       s_namelen;       /* max length of dir entry */
+	int	       s_forced_ro;
+};
+
+/*
+ * SystemV/V7/Coherent FS inode data in memory
+ */
+struct sysv_inode_info {
+	__fs32		i_data[13];
+	u32		i_dir_start_lookup;
+	struct inode	vfs_inode;
+};
+
+
+static inline struct sysv_inode_info *SYSV_I(struct inode *inode)
+{
+	return list_entry(inode, struct sysv_inode_info, vfs_inode);
+}
+
+static inline struct sysv_sb_info *SYSV_SB(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+
+/* identify the FS in memory */
+enum {
+	FSTYPE_NONE = 0,
+	FSTYPE_XENIX,
+	FSTYPE_SYSV4,
+	FSTYPE_SYSV2,
+	FSTYPE_COH,
+	FSTYPE_V7,
+	FSTYPE_AFS,
+	FSTYPE_END,
+};
+
+#define SYSV_MAGIC_BASE		0x012FF7B3
+
+#define XENIX_SUPER_MAGIC	(SYSV_MAGIC_BASE+FSTYPE_XENIX)
+#define SYSV4_SUPER_MAGIC	(SYSV_MAGIC_BASE+FSTYPE_SYSV4)
+#define SYSV2_SUPER_MAGIC	(SYSV_MAGIC_BASE+FSTYPE_SYSV2)
+#define COH_SUPER_MAGIC		(SYSV_MAGIC_BASE+FSTYPE_COH)
+
+
+/* Admissible values for i_nlink: 0.._LINK_MAX */
+enum {
+	XENIX_LINK_MAX	=	126,	/* ?? */
+	SYSV_LINK_MAX	=	126,	/* 127? 251? */
+	V7_LINK_MAX     =	126,	/* ?? */
+	COH_LINK_MAX	=	10000,
+};
+
+
+static inline void dirty_sb(struct super_block *sb)
+{
+	struct sysv_sb_info *sbi = SYSV_SB(sb);
+
+	mark_buffer_dirty(sbi->s_bh1);
+	if (sbi->s_bh1 != sbi->s_bh2)
+		mark_buffer_dirty(sbi->s_bh2);
+	sb->s_dirt = 1;
+}
+
+
+/* ialloc.c */
+extern struct sysv_inode *sysv_raw_inode(struct super_block *, unsigned,
+			struct buffer_head **);
+extern struct inode * sysv_new_inode(const struct inode *, mode_t);
+extern void sysv_free_inode(struct inode *);
+extern unsigned long sysv_count_free_inodes(struct super_block *);
+
+/* balloc.c */
+extern sysv_zone_t sysv_new_block(struct super_block *);
+extern void sysv_free_block(struct super_block *, sysv_zone_t);
+extern unsigned long sysv_count_free_blocks(struct super_block *);
+
+/* itree.c */
+extern void sysv_truncate(struct inode *);
+
+/* inode.c */
+extern int sysv_write_inode(struct inode *, int);
+extern int sysv_sync_inode(struct inode *);
+extern int sysv_sync_file(struct file *, struct dentry *, int);
+extern void sysv_set_inode(struct inode *, dev_t);
+extern int sysv_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+
+/* dir.c */
+extern struct sysv_dir_entry *sysv_find_entry(struct dentry *, struct page **);
+extern int sysv_add_link(struct dentry *, struct inode *);
+extern int sysv_delete_entry(struct sysv_dir_entry *, struct page *);
+extern int sysv_make_empty(struct inode *, struct inode *);
+extern int sysv_empty_dir(struct inode *);
+extern void sysv_set_link(struct sysv_dir_entry *, struct page *,
+			struct inode *);
+extern struct sysv_dir_entry *sysv_dotdot(struct inode *, struct page **);
+extern ino_t sysv_inode_by_name(struct dentry *);
+
+
+extern struct inode_operations sysv_file_inode_operations;
+extern struct inode_operations sysv_dir_inode_operations;
+extern struct inode_operations sysv_fast_symlink_inode_operations;
+extern struct file_operations sysv_file_operations;
+extern struct file_operations sysv_dir_operations;
+extern struct address_space_operations sysv_aops;
+extern struct super_operations sysv_sops;
+extern struct dentry_operations sysv_dentry_operations;
+
+
+enum {
+	BYTESEX_LE,
+	BYTESEX_PDP,
+	BYTESEX_BE,
+};
+
+static inline u32 PDP_swab(u32 x)
+{
+#ifdef __LITTLE_ENDIAN
+	return ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16);
+#else
+#ifdef __BIG_ENDIAN
+	return ((x & 0xff00ff) << 8) | ((x & 0xff00ff00) >> 8);
+#else
+#error BYTESEX
+#endif
+#endif
+}
+
+static inline __u32 fs32_to_cpu(struct sysv_sb_info *sbi, __fs32 n)
+{
+	if (sbi->s_bytesex == BYTESEX_PDP)
+		return PDP_swab((__force __u32)n);
+	else if (sbi->s_bytesex == BYTESEX_LE)
+		return le32_to_cpu((__force __le32)n);
+	else
+		return be32_to_cpu((__force __be32)n);
+}
+
+static inline __fs32 cpu_to_fs32(struct sysv_sb_info *sbi, __u32 n)
+{
+	if (sbi->s_bytesex == BYTESEX_PDP)
+		return (__force __fs32)PDP_swab(n);
+	else if (sbi->s_bytesex == BYTESEX_LE)
+		return (__force __fs32)cpu_to_le32(n);
+	else
+		return (__force __fs32)cpu_to_be32(n);
+}
+
+static inline __fs32 fs32_add(struct sysv_sb_info *sbi, __fs32 *n, int d)
+{
+	if (sbi->s_bytesex == BYTESEX_PDP)
+		*(__u32*)n = PDP_swab(PDP_swab(*(__u32*)n)+d);
+	else if (sbi->s_bytesex == BYTESEX_LE)
+		*(__le32*)n = cpu_to_le32(le32_to_cpu(*(__le32*)n)+d);
+	else
+		*(__be32*)n = cpu_to_be32(be32_to_cpu(*(__be32*)n)+d);
+	return *n;
+}
+
+static inline __u16 fs16_to_cpu(struct sysv_sb_info *sbi, __fs16 n)
+{
+	if (sbi->s_bytesex != BYTESEX_BE)
+		return le16_to_cpu((__force __le16)n);
+	else
+		return be16_to_cpu((__force __be16)n);
+}
+
+static inline __fs16 cpu_to_fs16(struct sysv_sb_info *sbi, __u16 n)
+{
+	if (sbi->s_bytesex != BYTESEX_BE)
+		return (__force __fs16)cpu_to_le16(n);
+	else
+		return (__force __fs16)cpu_to_be16(n);
+}
+
+static inline __fs16 fs16_add(struct sysv_sb_info *sbi, __fs16 *n, int d)
+{
+	if (sbi->s_bytesex != BYTESEX_BE)
+		*(__le16*)n = cpu_to_le16(le16_to_cpu(*(__le16 *)n)+d);
+	else
+		*(__be16*)n = cpu_to_be16(be16_to_cpu(*(__be16 *)n)+d);
+	return *n;
+}
+
+#endif /* _SYSV_H */