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/minix/Makefile b/fs/minix/Makefile
new file mode 100644
index 0000000..3063015
--- /dev/null
+++ b/fs/minix/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux minix filesystem routines.
+#
+
+obj-$(CONFIG_MINIX_FS) += minix.o
+
+minix-objs := bitmap.o itree_v1.o itree_v2.o namei.o inode.o file.o dir.o
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
new file mode 100644
index 0000000..dc6a4e4
--- /dev/null
+++ b/fs/minix/bitmap.c
@@ -0,0 +1,269 @@
+/*
+ *  linux/fs/minix/bitmap.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * Modified for 680x0 by Hamish Macdonald
+ * Fixed for 680x0 by Andreas Schwab
+ */
+
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+
+#include "minix.h"
+#include <linux/smp_lock.h>
+#include <linux/buffer_head.h>
+#include <linux/bitops.h>
+
+static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
+
+static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits)
+{
+	unsigned i, j, sum = 0;
+	struct buffer_head *bh;
+  
+	for (i=0; i<numblocks-1; i++) {
+		if (!(bh=map[i])) 
+			return(0);
+		for (j=0; j<BLOCK_SIZE; j++)
+			sum += nibblemap[bh->b_data[j] & 0xf]
+				+ nibblemap[(bh->b_data[j]>>4) & 0xf];
+	}
+
+	if (numblocks==0 || !(bh=map[numblocks-1]))
+		return(0);
+	i = ((numbits-(numblocks-1)*BLOCK_SIZE*8)/16)*2;
+	for (j=0; j<i; j++) {
+		sum += nibblemap[bh->b_data[j] & 0xf]
+			+ nibblemap[(bh->b_data[j]>>4) & 0xf];
+	}
+
+	i = numbits%16;
+	if (i!=0) {
+		i = *(__u16 *)(&bh->b_data[j]) | ~((1<<i) - 1);
+		sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf];
+		sum += nibblemap[(i>>8) & 0xf] + nibblemap[(i>>12) & 0xf];
+	}
+	return(sum);
+}
+
+void minix_free_block(struct inode * inode, int block)
+{
+	struct super_block * sb = inode->i_sb;
+	struct minix_sb_info * sbi = minix_sb(sb);
+	struct buffer_head * bh;
+	unsigned int bit,zone;
+
+	if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
+		printk("trying to free block not in datazone\n");
+		return;
+	}
+	zone = block - sbi->s_firstdatazone + 1;
+	bit = zone & 8191;
+	zone >>= 13;
+	if (zone >= sbi->s_zmap_blocks) {
+		printk("minix_free_block: nonexistent bitmap buffer\n");
+		return;
+	}
+	bh = sbi->s_zmap[zone];
+	lock_kernel();
+	if (!minix_test_and_clear_bit(bit,bh->b_data))
+		printk("free_block (%s:%d): bit already cleared\n",
+		       sb->s_id, block);
+	unlock_kernel();
+	mark_buffer_dirty(bh);
+	return;
+}
+
+int minix_new_block(struct inode * inode)
+{
+	struct minix_sb_info *sbi = minix_sb(inode->i_sb);
+	int i;
+
+	for (i = 0; i < sbi->s_zmap_blocks; i++) {
+		struct buffer_head *bh = sbi->s_zmap[i];
+		int j;
+
+		lock_kernel();
+		if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192) {
+			minix_set_bit(j,bh->b_data);
+			unlock_kernel();
+			mark_buffer_dirty(bh);
+			j += i*8192 + sbi->s_firstdatazone-1;
+			if (j < sbi->s_firstdatazone || j >= sbi->s_nzones)
+				break;
+			return j;
+		}
+		unlock_kernel();
+	}
+	return 0;
+}
+
+unsigned long minix_count_free_blocks(struct minix_sb_info *sbi)
+{
+	return (count_free(sbi->s_zmap, sbi->s_zmap_blocks,
+		sbi->s_nzones - sbi->s_firstdatazone + 1)
+		<< sbi->s_log_zone_size);
+}
+
+struct minix_inode *
+minix_V1_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
+{
+	int block;
+	struct minix_sb_info *sbi = minix_sb(sb);
+	struct minix_inode *p;
+
+	if (!ino || ino > sbi->s_ninodes) {
+		printk("Bad inode number on dev %s: %ld is out of range\n",
+		       sb->s_id, (long)ino);
+		return NULL;
+	}
+	ino--;
+	block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
+		 ino / MINIX_INODES_PER_BLOCK;
+	*bh = sb_bread(sb, block);
+	if (!*bh) {
+		printk("unable to read i-node block\n");
+		return NULL;
+	}
+	p = (void *)(*bh)->b_data;
+	return p + ino % MINIX_INODES_PER_BLOCK;
+}
+
+struct minix2_inode *
+minix_V2_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
+{
+	int block;
+	struct minix_sb_info *sbi = minix_sb(sb);
+	struct minix2_inode *p;
+
+	*bh = NULL;
+	if (!ino || ino > sbi->s_ninodes) {
+		printk("Bad inode number on dev %s: %ld is out of range\n",
+		       sb->s_id, (long)ino);
+		return NULL;
+	}
+	ino--;
+	block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
+		 ino / MINIX2_INODES_PER_BLOCK;
+	*bh = sb_bread(sb, block);
+	if (!*bh) {
+		printk("unable to read i-node block\n");
+		return NULL;
+	}
+	p = (void *)(*bh)->b_data;
+	return p + ino % MINIX2_INODES_PER_BLOCK;
+}
+
+/* Clear the link count and mode of a deleted inode on disk. */
+
+static void minix_clear_inode(struct inode *inode)
+{
+	struct buffer_head *bh = NULL;
+
+	if (INODE_VERSION(inode) == MINIX_V1) {
+		struct minix_inode *raw_inode;
+		raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+		if (raw_inode) {
+			raw_inode->i_nlinks = 0;
+			raw_inode->i_mode = 0;
+		}
+	} else {
+		struct minix2_inode *raw_inode;
+		raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+		if (raw_inode) {
+			raw_inode->i_nlinks = 0;
+			raw_inode->i_mode = 0;
+		}
+	}
+	if (bh) {
+		mark_buffer_dirty(bh);
+		brelse (bh);
+	}
+}
+
+void minix_free_inode(struct inode * inode)
+{
+	struct minix_sb_info *sbi = minix_sb(inode->i_sb);
+	struct buffer_head * bh;
+	unsigned long ino;
+
+	ino = inode->i_ino;
+	if (ino < 1 || ino > sbi->s_ninodes) {
+		printk("minix_free_inode: inode 0 or nonexistent inode\n");
+		goto out;
+	}
+	if ((ino >> 13) >= sbi->s_imap_blocks) {
+		printk("minix_free_inode: nonexistent imap in superblock\n");
+		goto out;
+	}
+
+	minix_clear_inode(inode);	/* clear on-disk copy */
+
+	bh = sbi->s_imap[ino >> 13];
+	lock_kernel();
+	if (!minix_test_and_clear_bit(ino & 8191, bh->b_data))
+		printk("minix_free_inode: bit %lu already cleared.\n", ino);
+	unlock_kernel();
+	mark_buffer_dirty(bh);
+ out:
+	clear_inode(inode);		/* clear in-memory copy */
+}
+
+struct inode * minix_new_inode(const struct inode * dir, int * error)
+{
+	struct super_block *sb = dir->i_sb;
+	struct minix_sb_info *sbi = minix_sb(sb);
+	struct inode *inode = new_inode(sb);
+	struct buffer_head * bh;
+	int i,j;
+
+	if (!inode) {
+		*error = -ENOMEM;
+		return NULL;
+	}
+	j = 8192;
+	bh = NULL;
+	*error = -ENOSPC;
+	lock_kernel();
+	for (i = 0; i < sbi->s_imap_blocks; i++) {
+		bh = sbi->s_imap[i];
+		if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192)
+			break;
+	}
+	if (!bh || j >= 8192) {
+		unlock_kernel();
+		iput(inode);
+		return NULL;
+	}
+	if (minix_test_and_set_bit(j,bh->b_data)) {	/* shouldn't happen */
+		printk("new_inode: bit already set");
+		unlock_kernel();
+		iput(inode);
+		return NULL;
+	}
+	unlock_kernel();
+	mark_buffer_dirty(bh);
+	j += i*8192;
+	if (!j || j > sbi->s_ninodes) {
+		iput(inode);
+		return NULL;
+	}
+	inode->i_uid = current->fsuid;
+	inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+	inode->i_ino = j;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_blocks = inode->i_blksize = 0;
+	memset(&minix_i(inode)->u, 0, sizeof(minix_i(inode)->u));
+	insert_inode_hash(inode);
+	mark_inode_dirty(inode);
+
+	*error = 0;
+	return inode;
+}
+
+unsigned long minix_count_free_inodes(struct minix_sb_info *sbi)
+{
+	return count_free(sbi->s_imap, sbi->s_imap_blocks, sbi->s_ninodes + 1);
+}
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
new file mode 100644
index 0000000..732502a
--- /dev/null
+++ b/fs/minix/dir.c
@@ -0,0 +1,409 @@
+/*
+ *  linux/fs/minix/dir.c
+ *
+ *  Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *  minix directory handling functions
+ */
+
+#include "minix.h"
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+
+typedef struct minix_dir_entry minix_dirent;
+
+static int minix_readdir(struct file *, void *, filldir_t);
+
+struct file_operations minix_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= minix_readdir,
+	.fsync		= minix_sync_file,
+};
+
+static inline void dir_put_page(struct page *page)
+{
+	kunmap(page);
+	page_cache_release(page);
+}
+
+/*
+ * Return the offset into page `page_nr' of the last valid
+ * byte in that page, plus one.
+ */
+static unsigned
+minix_last_byte(struct inode *inode, unsigned long page_nr)
+{
+	unsigned last_byte = PAGE_CACHE_SIZE;
+
+	if (page_nr == (inode->i_size >> PAGE_CACHE_SHIFT))
+		last_byte = inode->i_size & (PAGE_CACHE_SIZE - 1);
+	return last_byte;
+}
+
+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 inline void *minix_next_entry(void *de, struct minix_sb_info *sbi)
+{
+	return (void*)((char*)de + sbi->s_dirsize);
+}
+
+static int minix_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);
+	struct minix_sb_info *sbi = minix_sb(sb);
+	unsigned chunk_size = sbi->s_dirsize;
+
+	lock_kernel();
+
+	pos = (pos + chunk_size-1) & ~(chunk_size-1);
+	if (pos >= inode->i_size)
+		goto done;
+
+	for ( ; n < npages; n++, offset = 0) {
+		char *p, *kaddr, *limit;
+		struct page *page = dir_get_page(inode, n);
+
+		if (IS_ERR(page))
+			continue;
+		kaddr = (char *)page_address(page);
+		p = kaddr+offset;
+		limit = kaddr + minix_last_byte(inode, n) - chunk_size;
+		for ( ; p <= limit ; p = minix_next_entry(p, sbi)) {
+			minix_dirent *de = (minix_dirent *)p;
+			if (de->inode) {
+				int over;
+				unsigned l = strnlen(de->name,sbi->s_namelen);
+
+				offset = p - kaddr;
+				over = filldir(dirent, de->name, l,
+						(n<<PAGE_CACHE_SHIFT) | offset,
+						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;
+}
+
+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);
+}
+
+/*
+ *	minix_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.
+ */
+minix_dirent *minix_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;
+	struct super_block * sb = dir->i_sb;
+	struct minix_sb_info * sbi = minix_sb(sb);
+	unsigned long n;
+	unsigned long npages = dir_pages(dir);
+	struct page *page = NULL;
+	struct minix_dir_entry *de;
+
+	*res_page = NULL;
+
+	for (n = 0; n < npages; n++) {
+		char *kaddr;
+		page = dir_get_page(dir, n);
+		if (IS_ERR(page))
+			continue;
+
+		kaddr = (char*)page_address(page);
+		de = (struct minix_dir_entry *) kaddr;
+		kaddr += minix_last_byte(dir, n) - sbi->s_dirsize;
+		for ( ; (char *) de <= kaddr ; de = minix_next_entry(de,sbi)) {
+			if (!de->inode)
+				continue;
+			if (namecompare(namelen,sbi->s_namelen,name,de->name))
+				goto found;
+		}
+		dir_put_page(page);
+	}
+	return NULL;
+
+found:
+	*res_page = page;
+	return de;
+}
+
+int minix_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 super_block * sb = dir->i_sb;
+	struct minix_sb_info * sbi = minix_sb(sb);
+	struct page *page = NULL;
+	struct minix_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
+	 * This code plays outside i_size, so it locks the page
+	 * to protect that region.
+	 */
+	for (n = 0; n <= npages; n++) {
+		char *dir_end;
+
+		page = dir_get_page(dir, n);
+		err = PTR_ERR(page);
+		if (IS_ERR(page))
+			goto out;
+		lock_page(page);
+		kaddr = (char*)page_address(page);
+		dir_end = kaddr + minix_last_byte(dir, n);
+		de = (minix_dirent *)kaddr;
+		kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+		while ((char *)de <= kaddr) {
+			if ((char *)de == dir_end) {
+				/* We hit i_size */
+				de->inode = 0;
+				goto got_it;
+			}
+			if (!de->inode)
+				goto got_it;
+			err = -EEXIST;
+			if (namecompare(namelen,sbi->s_namelen,name,de->name))
+				goto out_unlock;
+			de = minix_next_entry(de, sbi);
+		}
+		unlock_page(page);
+		dir_put_page(page);
+	}
+	BUG();
+	return -EINVAL;
+
+got_it:
+	from = (char*)de - (char*)page_address(page);
+	to = from + sbi->s_dirsize;
+	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, sbi->s_dirsize - namelen - 2);
+	de->inode = inode->i_ino;
+	err = dir_commit_chunk(page, from, to);
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(dir);
+out_put:
+	dir_put_page(page);
+out:
+	return err;
+out_unlock:
+	unlock_page(page);
+	goto out_put;
+}
+
+int minix_delete_entry(struct minix_dir_entry *de, struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode = (struct inode*)mapping->host;
+	char *kaddr = page_address(page);
+	unsigned from = (char*)de - kaddr;
+	unsigned to = from + minix_sb(inode->i_sb)->s_dirsize;
+	int err;
+
+	lock_page(page);
+	err = mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (err == 0) {
+		de->inode = 0;
+		err = dir_commit_chunk(page, from, to);
+	} else {
+		unlock_page(page);
+	}
+	dir_put_page(page);
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+	return err;
+}
+
+int minix_make_empty(struct inode *inode, struct inode *dir)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page = grab_cache_page(mapping, 0);
+	struct minix_sb_info * sbi = minix_sb(inode->i_sb);
+	struct minix_dir_entry * de;
+	char *kaddr;
+	int err;
+
+	if (!page)
+		return -ENOMEM;
+	err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * sbi->s_dirsize);
+	if (err) {
+		unlock_page(page);
+		goto fail;
+	}
+
+	kaddr = kmap_atomic(page, KM_USER0);
+	memset(kaddr, 0, PAGE_CACHE_SIZE);
+
+	de = (struct minix_dir_entry *)kaddr;
+	de->inode = inode->i_ino;
+	strcpy(de->name,".");
+	de = minix_next_entry(de, sbi);
+	de->inode = dir->i_ino;
+	strcpy(de->name,"..");
+	kunmap_atomic(kaddr, KM_USER0);
+
+	err = dir_commit_chunk(page, 0, 2 * sbi->s_dirsize);
+fail:
+	page_cache_release(page);
+	return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int minix_empty_dir(struct inode * inode)
+{
+	struct page *page = NULL;
+	unsigned long i, npages = dir_pages(inode);
+	struct minix_sb_info *sbi = minix_sb(inode->i_sb);
+
+	for (i = 0; i < npages; i++) {
+		char *kaddr;
+		minix_dirent * de;
+		page = dir_get_page(inode, i);
+
+		if (IS_ERR(page))
+			continue;
+
+		kaddr = (char *)page_address(page);
+		de = (minix_dirent *)kaddr;
+		kaddr += minix_last_byte(inode, i) - sbi->s_dirsize;
+
+		while ((char *)de <= kaddr) {
+			if (de->inode != 0) {
+				/* check for . and .. */
+				if (de->name[0] != '.')
+					goto not_empty;
+				if (!de->name[1]) {
+					if (de->inode != inode->i_ino)
+						goto not_empty;
+				} else if (de->name[1] != '.')
+					goto not_empty;
+				else if (de->name[2])
+					goto not_empty;
+			}
+			de = minix_next_entry(de, sbi);
+		}
+		dir_put_page(page);
+	}
+	return 1;
+
+not_empty:
+	dir_put_page(page);
+	return 0;
+}
+
+/* Releases the page */
+void minix_set_link(struct minix_dir_entry *de, struct page *page,
+	struct inode *inode)
+{
+	struct inode *dir = (struct inode*)page->mapping->host;
+	struct minix_sb_info *sbi = minix_sb(dir->i_sb);
+	unsigned from = (char *)de-(char*)page_address(page);
+	unsigned to = from + sbi->s_dirsize;
+	int err;
+
+	lock_page(page);
+	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+	if (err == 0) {
+		de->inode = inode->i_ino;
+		err = dir_commit_chunk(page, from, to);
+	} else {
+		unlock_page(page);
+	}
+	dir_put_page(page);
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(dir);
+}
+
+struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p)
+{
+	struct page *page = dir_get_page(dir, 0);
+	struct minix_sb_info *sbi = minix_sb(dir->i_sb);
+	struct minix_dir_entry *de = NULL;
+
+	if (!IS_ERR(page)) {
+		de = minix_next_entry(page_address(page), sbi);
+		*p = page;
+	}
+	return de;
+}
+
+ino_t minix_inode_by_name(struct dentry *dentry)
+{
+	struct page *page;
+	struct minix_dir_entry *de = minix_find_entry(dentry, &page);
+	ino_t res = 0;
+
+	if (de) {
+		res = de->inode;
+		dir_put_page(page);
+	}
+	return res;
+}
diff --git a/fs/minix/file.c b/fs/minix/file.c
new file mode 100644
index 0000000..f1d77ac
--- /dev/null
+++ b/fs/minix/file.c
@@ -0,0 +1,45 @@
+/*
+ *  linux/fs/minix/file.c
+ *
+ *  Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *  minix regular file handling primitives
+ */
+
+#include <linux/buffer_head.h>		/* for fsync_inode_buffers() */
+#include "minix.h"
+
+/*
+ * We have mostly NULLs here: the current defaults are OK for
+ * the minix filesystem.
+ */
+int minix_sync_file(struct file *, struct dentry *, int);
+
+struct file_operations minix_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_file_read,
+	.write		= generic_file_write,
+	.mmap		= generic_file_mmap,
+	.fsync		= minix_sync_file,
+	.sendfile	= generic_file_sendfile,
+};
+
+struct inode_operations minix_file_inode_operations = {
+	.truncate	= minix_truncate,
+	.getattr	= minix_getattr,
+};
+
+int minix_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 |= minix_sync_inode(inode);
+	return err ? -EIO : 0;
+}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
new file mode 100644
index 0000000..3f18c21
--- /dev/null
+++ b/fs/minix/inode.c
@@ -0,0 +1,598 @@
+/*
+ *  linux/fs/minix/inode.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Copyright (C) 1996  Gertjan van Wingerde    (gertjan@cs.vu.nl)
+ *	Minix V2 fs support.
+ *
+ *  Modified for 680x0 by Andreas Schwab
+ */
+
+#include <linux/module.h>
+#include "minix.h"
+#include <linux/buffer_head.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/vfs.h>
+
+static void minix_read_inode(struct inode * inode);
+static int minix_write_inode(struct inode * inode, int wait);
+static int minix_statfs(struct super_block *sb, struct kstatfs *buf);
+static int minix_remount (struct super_block * sb, int * flags, char * data);
+
+static void minix_delete_inode(struct inode *inode)
+{
+	inode->i_size = 0;
+	minix_truncate(inode);
+	minix_free_inode(inode);
+}
+
+static void minix_put_super(struct super_block *sb)
+{
+	int i;
+	struct minix_sb_info *sbi = minix_sb(sb);
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		sbi->s_ms->s_state = sbi->s_mount_state;
+		mark_buffer_dirty(sbi->s_sbh);
+	}
+	for (i = 0; i < sbi->s_imap_blocks; i++)
+		brelse(sbi->s_imap[i]);
+	for (i = 0; i < sbi->s_zmap_blocks; i++)
+		brelse(sbi->s_zmap[i]);
+	brelse (sbi->s_sbh);
+	kfree(sbi->s_imap);
+	sb->s_fs_info = NULL;
+	kfree(sbi);
+
+	return;
+}
+
+static kmem_cache_t * minix_inode_cachep;
+
+static struct inode *minix_alloc_inode(struct super_block *sb)
+{
+	struct minix_inode_info *ei;
+	ei = (struct minix_inode_info *)kmem_cache_alloc(minix_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void minix_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(minix_inode_cachep, minix_i(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct minix_inode_info *ei = (struct minix_inode_info *) foo;
+
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR)
+		inode_init_once(&ei->vfs_inode);
+}
+ 
+static int init_inodecache(void)
+{
+	minix_inode_cachep = kmem_cache_create("minix_inode_cache",
+					     sizeof(struct minix_inode_info),
+					     0, SLAB_RECLAIM_ACCOUNT,
+					     init_once, NULL);
+	if (minix_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	if (kmem_cache_destroy(minix_inode_cachep))
+		printk(KERN_INFO "minix_inode_cache: not all structures were freed\n");
+}
+
+static struct super_operations minix_sops = {
+	.alloc_inode	= minix_alloc_inode,
+	.destroy_inode	= minix_destroy_inode,
+	.read_inode	= minix_read_inode,
+	.write_inode	= minix_write_inode,
+	.delete_inode	= minix_delete_inode,
+	.put_super	= minix_put_super,
+	.statfs		= minix_statfs,
+	.remount_fs	= minix_remount,
+};
+
+static int minix_remount (struct super_block * sb, int * flags, char * data)
+{
+	struct minix_sb_info * sbi = minix_sb(sb);
+	struct minix_super_block * ms;
+
+	ms = sbi->s_ms;
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+	if (*flags & MS_RDONLY) {
+		if (ms->s_state & MINIX_VALID_FS ||
+		    !(sbi->s_mount_state & MINIX_VALID_FS))
+			return 0;
+		/* Mounting a rw partition read-only. */
+		ms->s_state = sbi->s_mount_state;
+		mark_buffer_dirty(sbi->s_sbh);
+	} else {
+	  	/* Mount a partition which is read-only, read-write. */
+		sbi->s_mount_state = ms->s_state;
+		ms->s_state &= ~MINIX_VALID_FS;
+		mark_buffer_dirty(sbi->s_sbh);
+
+		if (!(sbi->s_mount_state & MINIX_VALID_FS))
+			printk ("MINIX-fs warning: remounting unchecked fs, "
+				"running fsck is recommended.\n");
+		else if ((sbi->s_mount_state & MINIX_ERROR_FS))
+			printk ("MINIX-fs warning: remounting fs with errors, "
+				"running fsck is recommended.\n");
+	}
+	return 0;
+}
+
+static int minix_fill_super(struct super_block *s, void *data, int silent)
+{
+	struct buffer_head *bh;
+	struct buffer_head **map;
+	struct minix_super_block *ms;
+	int i, block;
+	struct inode *root_inode;
+	struct minix_sb_info *sbi;
+
+	sbi = kmalloc(sizeof(struct minix_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	s->s_fs_info = sbi;
+	memset(sbi, 0, sizeof(struct minix_sb_info));
+
+	/* N.B. These should be compile-time tests.
+	   Unfortunately that is impossible. */
+	if (32 != sizeof (struct minix_inode))
+		panic("bad V1 i-node size");
+	if (64 != sizeof(struct minix2_inode))
+		panic("bad V2 i-node size");
+
+	if (!sb_set_blocksize(s, BLOCK_SIZE))
+		goto out_bad_hblock;
+
+	if (!(bh = sb_bread(s, 1)))
+		goto out_bad_sb;
+
+	ms = (struct minix_super_block *) bh->b_data;
+	sbi->s_ms = ms;
+	sbi->s_sbh = bh;
+	sbi->s_mount_state = ms->s_state;
+	sbi->s_ninodes = ms->s_ninodes;
+	sbi->s_nzones = ms->s_nzones;
+	sbi->s_imap_blocks = ms->s_imap_blocks;
+	sbi->s_zmap_blocks = ms->s_zmap_blocks;
+	sbi->s_firstdatazone = ms->s_firstdatazone;
+	sbi->s_log_zone_size = ms->s_log_zone_size;
+	sbi->s_max_size = ms->s_max_size;
+	s->s_magic = ms->s_magic;
+	if (s->s_magic == MINIX_SUPER_MAGIC) {
+		sbi->s_version = MINIX_V1;
+		sbi->s_dirsize = 16;
+		sbi->s_namelen = 14;
+		sbi->s_link_max = MINIX_LINK_MAX;
+	} else if (s->s_magic == MINIX_SUPER_MAGIC2) {
+		sbi->s_version = MINIX_V1;
+		sbi->s_dirsize = 32;
+		sbi->s_namelen = 30;
+		sbi->s_link_max = MINIX_LINK_MAX;
+	} else if (s->s_magic == MINIX2_SUPER_MAGIC) {
+		sbi->s_version = MINIX_V2;
+		sbi->s_nzones = ms->s_zones;
+		sbi->s_dirsize = 16;
+		sbi->s_namelen = 14;
+		sbi->s_link_max = MINIX2_LINK_MAX;
+	} else if (s->s_magic == MINIX2_SUPER_MAGIC2) {
+		sbi->s_version = MINIX_V2;
+		sbi->s_nzones = ms->s_zones;
+		sbi->s_dirsize = 32;
+		sbi->s_namelen = 30;
+		sbi->s_link_max = MINIX2_LINK_MAX;
+	} else
+		goto out_no_fs;
+
+	/*
+	 * Allocate the buffer map to keep the superblock small.
+	 */
+	i = (sbi->s_imap_blocks + sbi->s_zmap_blocks) * sizeof(bh);
+	map = kmalloc(i, GFP_KERNEL);
+	if (!map)
+		goto out_no_map;
+	memset(map, 0, i);
+	sbi->s_imap = &map[0];
+	sbi->s_zmap = &map[sbi->s_imap_blocks];
+
+	block=2;
+	for (i=0 ; i < sbi->s_imap_blocks ; i++) {
+		if (!(sbi->s_imap[i]=sb_bread(s, block)))
+			goto out_no_bitmap;
+		block++;
+	}
+	for (i=0 ; i < sbi->s_zmap_blocks ; i++) {
+		if (!(sbi->s_zmap[i]=sb_bread(s, block)))
+			goto out_no_bitmap;
+		block++;
+	}
+
+	minix_set_bit(0,sbi->s_imap[0]->b_data);
+	minix_set_bit(0,sbi->s_zmap[0]->b_data);
+
+	/* set up enough so that it can read an inode */
+	s->s_op = &minix_sops;
+	root_inode = iget(s, MINIX_ROOT_INO);
+	if (!root_inode || is_bad_inode(root_inode))
+		goto out_no_root;
+
+	s->s_root = d_alloc_root(root_inode);
+	if (!s->s_root)
+		goto out_iput;
+
+	if (!NO_TRUNCATE)
+		s->s_root->d_op = &minix_dentry_operations;
+
+	if (!(s->s_flags & MS_RDONLY)) {
+		ms->s_state &= ~MINIX_VALID_FS;
+		mark_buffer_dirty(bh);
+	}
+	if (!(sbi->s_mount_state & MINIX_VALID_FS))
+		printk ("MINIX-fs: mounting unchecked file system, "
+			"running fsck is recommended.\n");
+ 	else if (sbi->s_mount_state & MINIX_ERROR_FS)
+		printk ("MINIX-fs: mounting file system with errors, "
+			"running fsck is recommended.\n");
+	return 0;
+
+out_iput:
+	iput(root_inode);
+	goto out_freemap;
+
+out_no_root:
+	if (!silent)
+		printk("MINIX-fs: get root inode failed\n");
+	goto out_freemap;
+
+out_no_bitmap:
+	printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
+    out_freemap:
+	for (i = 0; i < sbi->s_imap_blocks; i++)
+		brelse(sbi->s_imap[i]);
+	for (i = 0; i < sbi->s_zmap_blocks; i++)
+		brelse(sbi->s_zmap[i]);
+	kfree(sbi->s_imap);
+	goto out_release;
+
+out_no_map:
+	if (!silent)
+		printk ("MINIX-fs: can't allocate map\n");
+	goto out_release;
+
+out_no_fs:
+	if (!silent)
+		printk("VFS: Can't find a Minix or Minix V2 filesystem on device "
+		       "%s.\n", s->s_id);
+    out_release:
+	brelse(bh);
+	goto out;
+
+out_bad_hblock:
+	printk("MINIX-fs: blocksize too small for device.\n");
+	goto out;
+
+out_bad_sb:
+	printk("MINIX-fs: unable to read superblock\n");
+ out:
+	s->s_fs_info = NULL;
+	kfree(sbi);
+	return -EINVAL;
+}
+
+static int minix_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct minix_sb_info *sbi = minix_sb(sb);
+	buf->f_type = sb->s_magic;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = (sbi->s_nzones - sbi->s_firstdatazone) << sbi->s_log_zone_size;
+	buf->f_bfree = minix_count_free_blocks(sbi);
+	buf->f_bavail = buf->f_bfree;
+	buf->f_files = sbi->s_ninodes;
+	buf->f_ffree = minix_count_free_inodes(sbi);
+	buf->f_namelen = sbi->s_namelen;
+	return 0;
+}
+
+static int minix_get_block(struct inode *inode, sector_t block,
+		    struct buffer_head *bh_result, int create)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_get_block(inode, block, bh_result, create);
+	else
+		return V2_minix_get_block(inode, block, bh_result, create);
+}
+
+static int minix_writepage(struct page *page, struct writeback_control *wbc)
+{
+	return block_write_full_page(page, minix_get_block, wbc);
+}
+static int minix_readpage(struct file *file, struct page *page)
+{
+	return block_read_full_page(page,minix_get_block);
+}
+static int minix_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+	return block_prepare_write(page,from,to,minix_get_block);
+}
+static sector_t minix_bmap(struct address_space *mapping, sector_t block)
+{
+	return generic_block_bmap(mapping,block,minix_get_block);
+}
+static struct address_space_operations minix_aops = {
+	.readpage = minix_readpage,
+	.writepage = minix_writepage,
+	.sync_page = block_sync_page,
+	.prepare_write = minix_prepare_write,
+	.commit_write = generic_commit_write,
+	.bmap = minix_bmap
+};
+
+static struct inode_operations minix_symlink_inode_operations = {
+	.readlink	= generic_readlink,
+	.follow_link	= page_follow_link_light,
+	.put_link	= page_put_link,
+	.getattr	= minix_getattr,
+};
+
+void minix_set_inode(struct inode *inode, dev_t rdev)
+{
+	if (S_ISREG(inode->i_mode)) {
+		inode->i_op = &minix_file_inode_operations;
+		inode->i_fop = &minix_file_operations;
+		inode->i_mapping->a_ops = &minix_aops;
+	} else if (S_ISDIR(inode->i_mode)) {
+		inode->i_op = &minix_dir_inode_operations;
+		inode->i_fop = &minix_dir_operations;
+		inode->i_mapping->a_ops = &minix_aops;
+	} else if (S_ISLNK(inode->i_mode)) {
+		inode->i_op = &minix_symlink_inode_operations;
+		inode->i_mapping->a_ops = &minix_aops;
+	} else
+		init_special_inode(inode, inode->i_mode, rdev);
+}
+
+/*
+ * The minix V1 function to read an inode.
+ */
+static void V1_minix_read_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix_inode * raw_inode;
+	struct minix_inode_info *minix_inode = minix_i(inode);
+	int i;
+
+	raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+	if (!raw_inode) {
+		make_bad_inode(inode);
+		return;
+	}
+	inode->i_mode = raw_inode->i_mode;
+	inode->i_uid = (uid_t)raw_inode->i_uid;
+	inode->i_gid = (gid_t)raw_inode->i_gid;
+	inode->i_nlink = raw_inode->i_nlinks;
+	inode->i_size = raw_inode->i_size;
+	inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = raw_inode->i_time;
+	inode->i_mtime.tv_nsec = 0;
+	inode->i_atime.tv_nsec = 0;
+	inode->i_ctime.tv_nsec = 0;
+	inode->i_blocks = inode->i_blksize = 0;
+	for (i = 0; i < 9; i++)
+		minix_inode->u.i1_data[i] = raw_inode->i_zone[i];
+	minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0]));
+	brelse(bh);
+}
+
+/*
+ * The minix V2 function to read an inode.
+ */
+static void V2_minix_read_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix2_inode * raw_inode;
+	struct minix_inode_info *minix_inode = minix_i(inode);
+	int i;
+
+	raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+	if (!raw_inode) {
+		make_bad_inode(inode);
+		return;
+	}
+	inode->i_mode = raw_inode->i_mode;
+	inode->i_uid = (uid_t)raw_inode->i_uid;
+	inode->i_gid = (gid_t)raw_inode->i_gid;
+	inode->i_nlink = raw_inode->i_nlinks;
+	inode->i_size = raw_inode->i_size;
+	inode->i_mtime.tv_sec = raw_inode->i_mtime;
+	inode->i_atime.tv_sec = raw_inode->i_atime;
+	inode->i_ctime.tv_sec = raw_inode->i_ctime;
+	inode->i_mtime.tv_nsec = 0;
+	inode->i_atime.tv_nsec = 0;
+	inode->i_ctime.tv_nsec = 0;
+	inode->i_blocks = inode->i_blksize = 0;
+	for (i = 0; i < 10; i++)
+		minix_inode->u.i2_data[i] = raw_inode->i_zone[i];
+	minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0]));
+	brelse(bh);
+}
+
+/*
+ * The global function to read an inode.
+ */
+static void minix_read_inode(struct inode * inode)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		V1_minix_read_inode(inode);
+	else
+		V2_minix_read_inode(inode);
+}
+
+/*
+ * The minix V1 function to synchronize an inode.
+ */
+static struct buffer_head * V1_minix_update_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix_inode * raw_inode;
+	struct minix_inode_info *minix_inode = minix_i(inode);
+	int i;
+
+	raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+	if (!raw_inode)
+		return NULL;
+	raw_inode->i_mode = inode->i_mode;
+	raw_inode->i_uid = fs_high2lowuid(inode->i_uid);
+	raw_inode->i_gid = fs_high2lowgid(inode->i_gid);
+	raw_inode->i_nlinks = inode->i_nlink;
+	raw_inode->i_size = inode->i_size;
+	raw_inode->i_time = inode->i_mtime.tv_sec;
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		raw_inode->i_zone[0] = old_encode_dev(inode->i_rdev);
+	else for (i = 0; i < 9; i++)
+		raw_inode->i_zone[i] = minix_inode->u.i1_data[i];
+	mark_buffer_dirty(bh);
+	return bh;
+}
+
+/*
+ * The minix V2 function to synchronize an inode.
+ */
+static struct buffer_head * V2_minix_update_inode(struct inode * inode)
+{
+	struct buffer_head * bh;
+	struct minix2_inode * raw_inode;
+	struct minix_inode_info *minix_inode = minix_i(inode);
+	int i;
+
+	raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+	if (!raw_inode)
+		return NULL;
+	raw_inode->i_mode = inode->i_mode;
+	raw_inode->i_uid = fs_high2lowuid(inode->i_uid);
+	raw_inode->i_gid = fs_high2lowgid(inode->i_gid);
+	raw_inode->i_nlinks = inode->i_nlink;
+	raw_inode->i_size = inode->i_size;
+	raw_inode->i_mtime = inode->i_mtime.tv_sec;
+	raw_inode->i_atime = inode->i_atime.tv_sec;
+	raw_inode->i_ctime = inode->i_ctime.tv_sec;
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		raw_inode->i_zone[0] = old_encode_dev(inode->i_rdev);
+	else for (i = 0; i < 10; i++)
+		raw_inode->i_zone[i] = minix_inode->u.i2_data[i];
+	mark_buffer_dirty(bh);
+	return bh;
+}
+
+static struct buffer_head *minix_update_inode(struct inode *inode)
+{
+	if (INODE_VERSION(inode) == MINIX_V1)
+		return V1_minix_update_inode(inode);
+	else
+		return V2_minix_update_inode(inode);
+}
+
+static int minix_write_inode(struct inode * inode, int wait)
+{
+	brelse(minix_update_inode(inode));
+	return 0;
+}
+
+int minix_sync_inode(struct inode * inode)
+{
+	int err = 0;
+	struct buffer_head *bh;
+
+	bh = minix_update_inode(inode);
+	if (bh && buffer_dirty(bh))
+	{
+		sync_dirty_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+		{
+			printk ("IO error syncing minix inode [%s:%08lx]\n",
+				inode->i_sb->s_id, inode->i_ino);
+			err = -1;
+		}
+	}
+	else if (!bh)
+		err = -1;
+	brelse (bh);
+	return err;
+}
+
+int minix_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
+{
+	generic_fillattr(dentry->d_inode, stat);
+	if (INODE_VERSION(dentry->d_inode) == MINIX_V1)
+		stat->blocks = (BLOCK_SIZE / 512) * V1_minix_blocks(stat->size);
+	else
+		stat->blocks = (BLOCK_SIZE / 512) * V2_minix_blocks(stat->size);
+	stat->blksize = BLOCK_SIZE;
+	return 0;
+}
+
+/*
+ * The function that is called for file truncation.
+ */
+void minix_truncate(struct inode * inode)
+{
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
+		return;
+	if (INODE_VERSION(inode) == MINIX_V1)
+		V1_minix_truncate(inode);
+	else
+		V2_minix_truncate(inode);
+}
+
+static struct super_block *minix_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, minix_fill_super);
+}
+
+static struct file_system_type minix_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "minix",
+	.get_sb		= minix_get_sb,
+	.kill_sb	= kill_block_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+
+static int __init init_minix_fs(void)
+{
+	int err = init_inodecache();
+	if (err)
+		goto out1;
+	err = register_filesystem(&minix_fs_type);
+	if (err)
+		goto out;
+	return 0;
+out:
+	destroy_inodecache();
+out1:
+	return err;
+}
+
+static void __exit exit_minix_fs(void)
+{
+        unregister_filesystem(&minix_fs_type);
+	destroy_inodecache();
+}
+
+module_init(init_minix_fs)
+module_exit(exit_minix_fs)
+MODULE_LICENSE("GPL");
+
diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c
new file mode 100644
index 0000000..429baf8
--- /dev/null
+++ b/fs/minix/itree_common.c
@@ -0,0 +1,362 @@
+/* Generic part */
+
+typedef struct {
+	block_t	*p;
+	block_t	key;
+	struct buffer_head *bh;
+} Indirect;
+
+static DEFINE_RWLOCK(pointers_lock);
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, block_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 block_t *block_end(struct buffer_head *bh)
+{
+	return (block_t *)((char*)bh->b_data + BLOCK_SIZE);
+}
+
+static inline Indirect *get_branch(struct inode *inode,
+					int depth,
+					int *offsets,
+					Indirect chain[DEPTH],
+					int *err)
+{
+	struct super_block *sb = inode->i_sb;
+	Indirect *p = chain;
+	struct buffer_head *bh;
+
+	*err = 0;
+	/* i_data is not going away, no lock needed */
+	add_chain (chain, NULL, i_data(inode) + *offsets);
+	if (!p->key)
+		goto no_block;
+	while (--depth) {
+		bh = sb_bread(sb, block_to_cpu(p->key));
+		if (!bh)
+			goto failure;
+		read_lock(&pointers_lock);
+		if (!verify_chain(chain, p))
+			goto changed;
+		add_chain(++p, bh, (block_t *)bh->b_data + *++offsets);
+		read_unlock(&pointers_lock);
+		if (!p->key)
+			goto no_block;
+	}
+	return NULL;
+
+changed:
+	read_unlock(&pointers_lock);
+	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 n = 0;
+	int i;
+	int parent = minix_new_block(inode);
+
+	branch[0].key = cpu_to_block(parent);
+	if (parent) for (n = 1; n < num; n++) {
+		struct buffer_head *bh;
+		/* Allocate the next block */
+		int nr = minix_new_block(inode);
+		if (!nr)
+			break;
+		branch[n].key = cpu_to_block(nr);
+		bh = sb_getblk(inode->i_sb, parent);
+		lock_buffer(bh);
+		memset(bh->b_data, 0, BLOCK_SIZE);
+		branch[n].bh = bh;
+		branch[n].p = (block_t*) bh->b_data + offsets[n];
+		*branch[n].p = branch[n].key;
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		mark_buffer_dirty_inode(bh, inode);
+		parent = nr;
+	}
+	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++)
+		minix_free_block(inode, block_to_cpu(branch[i].key));
+	return -ENOSPC;
+}
+
+static inline int splice_branch(struct inode *inode,
+				     Indirect chain[DEPTH],
+				     Indirect *where,
+				     int num)
+{
+	int i;
+
+	write_lock(&pointers_lock);
+
+	/* Verify that place we are splicing to is still there and vacant */
+	if (!verify_chain(chain, where-1) || *where->p)
+		goto changed;
+
+	*where->p = where->key;
+
+	write_unlock(&pointers_lock);
+
+	/* We are done with atomic stuff, now do the rest of housekeeping */
+
+	inode->i_ctime = CURRENT_TIME_SEC;
+
+	/* had we spliced it onto indirect block? */
+	if (where->bh)
+		mark_buffer_dirty_inode(where->bh, inode);
+
+	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++)
+		minix_free_block(inode, block_to_cpu(where[i].key));
+	return -EAGAIN;
+}
+
+static inline int get_block(struct inode * inode, sector_t block,
+			struct buffer_head *bh, int create)
+{
+	int err = -EIO;
+	int offsets[DEPTH];
+	Indirect chain[DEPTH];
+	Indirect *partial;
+	int left;
+	int depth = block_to_path(inode, block, offsets);
+
+	if (depth == 0)
+		goto out;
+
+reread:
+	partial = get_branch(inode, depth, offsets, chain, &err);
+
+	/* Simplest case - block found, no allocation needed */
+	if (!partial) {
+got_it:
+		map_bh(bh, inode->i_sb, block_to_cpu(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);
+	goto got_it;
+
+changed:
+	while (partial > chain) {
+		brelse(partial->bh);
+		partial--;
+	}
+	goto reread;
+}
+
+static inline int all_zeroes(block_t *p, block_t *q)
+{
+	while (p < q)
+		if (*p++)
+			return 0;
+	return 1;
+}
+
+static Indirect *find_shared(struct inode *inode,
+				int depth,
+				int offsets[DEPTH],
+				Indirect chain[DEPTH],
+				block_t *top)
+{
+	Indirect *partial, *p;
+	int k, err;
+
+	*top = 0;
+	for (k = depth; k > 1 && !offsets[k-1]; k--)
+		;
+	partial = get_branch(inode, k, offsets, chain, &err);
+
+	write_lock(&pointers_lock);
+	if (!partial)
+		partial = chain + k-1;
+	if (!partial->key && *partial->p) {
+		write_unlock(&pointers_lock);
+		goto no_top;
+	}
+	for (p=partial;p>chain && all_zeroes((block_t*)p->bh->b_data,p->p);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, block_t *p, block_t *q)
+{
+	unsigned long nr;
+
+	for ( ; p < q ; p++) {
+		nr = block_to_cpu(*p);
+		if (nr) {
+			*p = 0;
+			minix_free_block(inode, nr);
+		}
+	}
+}
+
+static void free_branches(struct inode *inode, block_t *p, block_t *q, int depth)
+{
+	struct buffer_head * bh;
+	unsigned long nr;
+
+	if (depth--) {
+		for ( ; p < q ; p++) {
+			nr = block_to_cpu(*p);
+			if (!nr)
+				continue;
+			*p = 0;
+			bh = sb_bread(inode->i_sb, nr);
+			if (!bh)
+				continue;
+			free_branches(inode, (block_t*)bh->b_data,
+				      block_end(bh), depth);
+			bforget(bh);
+			minix_free_block(inode, nr);
+			mark_inode_dirty(inode);
+		}
+	} else
+		free_data(inode, p, q);
+}
+
+static inline void truncate (struct inode * inode)
+{
+	block_t *idata = i_data(inode);
+	int offsets[DEPTH];
+	Indirect chain[DEPTH];
+	Indirect *partial;
+	block_t nr = 0;
+	int n;
+	int first_whole;
+	long iblock;
+
+	iblock = (inode->i_size + BLOCK_SIZE-1) >> 10;
+	block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+
+	n = block_to_path(inode, iblock, offsets);
+	if (!n)
+		return;
+
+	if (n == 1) {
+		free_data(inode, idata+offsets[0], idata + DIRECT);
+		first_whole = 0;
+		goto do_indirects;
+	}
+
+	first_whole = offsets[0] + 1 - DIRECT;
+	partial = find_shared(inode, n, offsets, chain, &nr);
+	if (nr) {
+		if (partial == chain)
+			mark_inode_dirty(inode);
+		else
+			mark_buffer_dirty_inode(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);
+		mark_buffer_dirty_inode(partial->bh, inode);
+		brelse (partial->bh);
+		partial--;
+	}
+do_indirects:
+	/* Kill the remaining (whole) subtrees */
+	while (first_whole < DEPTH-1) {
+		nr = idata[DIRECT+first_whole];
+		if (nr) {
+			idata[DIRECT+first_whole] = 0;
+			mark_inode_dirty(inode);
+			free_branches(inode, &nr, &nr+1, first_whole+1);
+		}
+		first_whole++;
+	}
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	mark_inode_dirty(inode);
+}
+
+static inline unsigned nblocks(loff_t size)
+{
+	unsigned blocks, res, direct = DIRECT, i = DEPTH;
+	blocks = (size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
+	res = blocks;
+	while (--i && blocks > direct) {
+		blocks -= direct;
+		blocks += BLOCK_SIZE/sizeof(block_t) - 1;
+		blocks /= BLOCK_SIZE/sizeof(block_t);
+		res += blocks;
+		direct = 1;
+	}
+	return res;
+}
diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c
new file mode 100644
index 0000000..ba06aef
--- /dev/null
+++ b/fs/minix/itree_v1.c
@@ -0,0 +1,61 @@
+#include <linux/buffer_head.h>
+#include "minix.h"
+
+enum {DEPTH = 3, DIRECT = 7};	/* Only double indirect */
+
+typedef u16 block_t;	/* 16 bit, host order */
+
+static inline unsigned long block_to_cpu(block_t n)
+{
+	return n;
+}
+
+static inline block_t cpu_to_block(unsigned long n)
+{
+	return n;
+}
+
+static inline block_t *i_data(struct inode *inode)
+{
+	return (block_t *)minix_i(inode)->u.i1_data;
+}
+
+static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
+{
+	int n = 0;
+
+	if (block < 0) {
+		printk("minix_bmap: block<0");
+	} else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) {
+		printk("minix_bmap: block>big");
+	} else if (block < 7) {
+		offsets[n++] = block;
+	} else if ((block -= 7) < 512) {
+		offsets[n++] = 7;
+		offsets[n++] = block;
+	} else {
+		block -= 512;
+		offsets[n++] = 8;
+		offsets[n++] = block>>9;
+		offsets[n++] = block & 511;
+	}
+	return n;
+}
+
+#include "itree_common.c"
+
+int V1_minix_get_block(struct inode * inode, long block,
+			struct buffer_head *bh_result, int create)
+{
+	return get_block(inode, block, bh_result, create);
+}
+
+void V1_minix_truncate(struct inode * inode)
+{
+	truncate(inode);
+}
+
+unsigned V1_minix_blocks(loff_t size)
+{
+	return nblocks(size);
+}
diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c
new file mode 100644
index 0000000..3adc767
--- /dev/null
+++ b/fs/minix/itree_v2.c
@@ -0,0 +1,66 @@
+#include <linux/buffer_head.h>
+#include "minix.h"
+
+enum {DIRECT = 7, DEPTH = 4};	/* Have triple indirect */
+
+typedef u32 block_t;	/* 32 bit, host order */
+
+static inline unsigned long block_to_cpu(block_t n)
+{
+	return n;
+}
+
+static inline block_t cpu_to_block(unsigned long n)
+{
+	return n;
+}
+
+static inline block_t *i_data(struct inode *inode)
+{
+	return (block_t *)minix_i(inode)->u.i2_data;
+}
+
+static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
+{
+	int n = 0;
+
+	if (block < 0) {
+		printk("minix_bmap: block<0");
+	} else if (block >= (minix_sb(inode->i_sb)->s_max_size/BLOCK_SIZE)) {
+		printk("minix_bmap: block>big");
+	} else if (block < 7) {
+		offsets[n++] = block;
+	} else if ((block -= 7) < 256) {
+		offsets[n++] = 7;
+		offsets[n++] = block;
+	} else if ((block -= 256) < 256*256) {
+		offsets[n++] = 8;
+		offsets[n++] = block>>8;
+		offsets[n++] = block & 255;
+	} else {
+		block -= 256*256;
+		offsets[n++] = 9;
+		offsets[n++] = block>>16;
+		offsets[n++] = (block>>8) & 255;
+		offsets[n++] = block & 255;
+	}
+	return n;
+}
+
+#include "itree_common.c"
+
+int V2_minix_get_block(struct inode * inode, long block,
+			struct buffer_head *bh_result, int create)
+{
+	return get_block(inode, block, bh_result, create);
+}
+
+void V2_minix_truncate(struct inode * inode)
+{
+	truncate(inode);
+}
+
+unsigned V2_minix_blocks(loff_t size)
+{
+	return nblocks(size);
+}
diff --git a/fs/minix/minix.h b/fs/minix/minix.h
new file mode 100644
index 0000000..e42a8bb
--- /dev/null
+++ b/fs/minix/minix.h
@@ -0,0 +1,96 @@
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/minix_fs.h>
+
+/*
+ * change the define below to 0 if you want names > info->s_namelen chars to be
+ * truncated. Else they will be disallowed (ENAMETOOLONG).
+ */
+#define NO_TRUNCATE 1
+
+#define INODE_VERSION(inode)	minix_sb(inode->i_sb)->s_version
+
+#define MINIX_V1		0x0001		/* original minix fs */
+#define MINIX_V2		0x0002		/* minix V2 fs */
+
+/*
+ * minix fs inode data in memory
+ */
+struct minix_inode_info {
+	union {
+		__u16 i1_data[16];
+		__u32 i2_data[16];
+	} u;
+	struct inode vfs_inode;
+};
+
+/*
+ * minix super-block data in memory
+ */
+struct minix_sb_info {
+	unsigned long s_ninodes;
+	unsigned long s_nzones;
+	unsigned long s_imap_blocks;
+	unsigned long s_zmap_blocks;
+	unsigned long s_firstdatazone;
+	unsigned long s_log_zone_size;
+	unsigned long s_max_size;
+	int s_dirsize;
+	int s_namelen;
+	int s_link_max;
+	struct buffer_head ** s_imap;
+	struct buffer_head ** s_zmap;
+	struct buffer_head * s_sbh;
+	struct minix_super_block * s_ms;
+	unsigned short s_mount_state;
+	unsigned short s_version;
+};
+
+extern struct minix_inode * minix_V1_raw_inode(struct super_block *, ino_t, struct buffer_head **);
+extern struct minix2_inode * minix_V2_raw_inode(struct super_block *, ino_t, struct buffer_head **);
+extern struct inode * minix_new_inode(const struct inode * dir, int * error);
+extern void minix_free_inode(struct inode * inode);
+extern unsigned long minix_count_free_inodes(struct minix_sb_info *sbi);
+extern int minix_new_block(struct inode * inode);
+extern void minix_free_block(struct inode * inode, int block);
+extern unsigned long minix_count_free_blocks(struct minix_sb_info *sbi);
+
+extern int minix_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+
+extern void V2_minix_truncate(struct inode *);
+extern void V1_minix_truncate(struct inode *);
+extern void V2_minix_truncate(struct inode *);
+extern void minix_truncate(struct inode *);
+extern int minix_sync_inode(struct inode *);
+extern void minix_set_inode(struct inode *, dev_t);
+extern int V1_minix_get_block(struct inode *, long, struct buffer_head *, int);
+extern int V2_minix_get_block(struct inode *, long, struct buffer_head *, int);
+extern unsigned V1_minix_blocks(loff_t);
+extern unsigned V2_minix_blocks(loff_t);
+
+extern struct minix_dir_entry *minix_find_entry(struct dentry*, struct page**);
+extern int minix_add_link(struct dentry*, struct inode*);
+extern int minix_delete_entry(struct minix_dir_entry*, struct page*);
+extern int minix_make_empty(struct inode*, struct inode*);
+extern int minix_empty_dir(struct inode*);
+extern void minix_set_link(struct minix_dir_entry*, struct page*, struct inode*);
+extern struct minix_dir_entry *minix_dotdot(struct inode*, struct page**);
+extern ino_t minix_inode_by_name(struct dentry*);
+
+extern int minix_sync_file(struct file *, struct dentry *, int);
+
+extern struct inode_operations minix_file_inode_operations;
+extern struct inode_operations minix_dir_inode_operations;
+extern struct file_operations minix_file_operations;
+extern struct file_operations minix_dir_operations;
+extern struct dentry_operations minix_dentry_operations;
+
+static inline struct minix_sb_info *minix_sb(struct super_block *sb)
+{
+	return sb->s_fs_info;
+}
+
+static inline struct minix_inode_info *minix_i(struct inode *inode)
+{
+	return list_entry(inode, struct minix_inode_info, vfs_inode);
+}
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
new file mode 100644
index 0000000..b25bca5
--- /dev/null
+++ b/fs/minix/namei.c
@@ -0,0 +1,317 @@
+/*
+ *  linux/fs/minix/namei.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include "minix.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 = minix_add_link(dentry, inode);
+	if (!err) {
+		d_instantiate(dentry, inode);
+		return 0;
+	}
+	dec_count(inode);
+	iput(inode);
+	return err;
+}
+
+static int minix_hash(struct dentry *dentry, struct qstr *qstr)
+{
+	unsigned long hash;
+	int i;
+	const unsigned char *name;
+
+	i = minix_sb(dentry->d_inode->i_sb)->s_namelen;
+	if (i >= qstr->len)
+		return 0;
+	/* Truncate the name in place, avoids having to define a compare
+	   function. */
+	qstr->len = i;
+	name = qstr->name;
+	hash = init_name_hash();
+	while (i--)
+		hash = partial_name_hash(*name++, hash);
+	qstr->hash = end_name_hash(hash);
+	return 0;
+}
+
+struct dentry_operations minix_dentry_operations = {
+	.d_hash		= minix_hash,
+};
+
+static struct dentry *minix_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 > minix_sb(dir->i_sb)->s_namelen)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	ino = minix_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 minix_mknod(struct inode * dir, struct dentry *dentry, int mode, dev_t rdev)
+{
+	int error;
+	struct inode *inode;
+
+	if (!old_valid_dev(rdev))
+		return -EINVAL;
+
+	inode = minix_new_inode(dir, &error);
+
+	if (inode) {
+		inode->i_mode = mode;
+		minix_set_inode(inode, rdev);
+		mark_inode_dirty(inode);
+		error = add_nondir(dentry, inode);
+	}
+	return error;
+}
+
+static int minix_create(struct inode * dir, struct dentry *dentry, int mode,
+		struct nameidata *nd)
+{
+	return minix_mknod(dir, dentry, mode, 0);
+}
+
+static int minix_symlink(struct inode * dir, struct dentry *dentry,
+	  const char * symname)
+{
+	int err = -ENAMETOOLONG;
+	int i = strlen(symname)+1;
+	struct inode * inode;
+
+	if (i > dir->i_sb->s_blocksize)
+		goto out;
+
+	inode = minix_new_inode(dir, &err);
+	if (!inode)
+		goto out;
+
+	inode->i_mode = S_IFLNK | 0777;
+	minix_set_inode(inode, 0);
+	err = page_symlink(inode, symname, i);
+	if (err)
+		goto out_fail;
+
+	err = add_nondir(dentry, inode);
+out:
+	return err;
+
+out_fail:
+	dec_count(inode);
+	iput(inode);
+	goto out;
+}
+
+static int minix_link(struct dentry * old_dentry, struct inode * dir,
+	struct dentry *dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+
+	if (inode->i_nlink >= minix_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 minix_mkdir(struct inode * dir, struct dentry *dentry, int mode)
+{
+	struct inode * inode;
+	int err = -EMLINK;
+
+	if (dir->i_nlink >= minix_sb(dir->i_sb)->s_link_max)
+		goto out;
+
+	inc_count(dir);
+
+	inode = minix_new_inode(dir, &err);
+	if (!inode)
+		goto out_dir;
+
+	inode->i_mode = S_IFDIR | mode;
+	if (dir->i_mode & S_ISGID)
+		inode->i_mode |= S_ISGID;
+	minix_set_inode(inode, 0);
+
+	inc_count(inode);
+
+	err = minix_make_empty(inode, dir);
+	if (err)
+		goto out_fail;
+
+	err = minix_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 minix_unlink(struct inode * dir, struct dentry *dentry)
+{
+	int err = -ENOENT;
+	struct inode * inode = dentry->d_inode;
+	struct page * page;
+	struct minix_dir_entry * de;
+
+	de = minix_find_entry(dentry, &page);
+	if (!de)
+		goto end_unlink;
+
+	err = minix_delete_entry(de, page);
+	if (err)
+		goto end_unlink;
+
+	inode->i_ctime = dir->i_ctime;
+	dec_count(inode);
+end_unlink:
+	return err;
+}
+
+static int minix_rmdir(struct inode * dir, struct dentry *dentry)
+{
+	struct inode * inode = dentry->d_inode;
+	int err = -ENOTEMPTY;
+
+	if (minix_empty_dir(inode)) {
+		err = minix_unlink(dir, dentry);
+		if (!err) {
+			dec_count(dir);
+			dec_count(inode);
+		}
+	}
+	return err;
+}
+
+static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
+			   struct inode * new_dir, struct dentry *new_dentry)
+{
+	struct minix_sb_info * info = minix_sb(old_dir->i_sb);
+	struct inode * old_inode = old_dentry->d_inode;
+	struct inode * new_inode = new_dentry->d_inode;
+	struct page * dir_page = NULL;
+	struct minix_dir_entry * dir_de = NULL;
+	struct page * old_page;
+	struct minix_dir_entry * old_de;
+	int err = -ENOENT;
+
+	old_de = minix_find_entry(old_dentry, &old_page);
+	if (!old_de)
+		goto out;
+
+	if (S_ISDIR(old_inode->i_mode)) {
+		err = -EIO;
+		dir_de = minix_dotdot(old_inode, &dir_page);
+		if (!dir_de)
+			goto out_old;
+	}
+
+	if (new_inode) {
+		struct page * new_page;
+		struct minix_dir_entry * new_de;
+
+		err = -ENOTEMPTY;
+		if (dir_de && !minix_empty_dir(new_inode))
+			goto out_dir;
+
+		err = -ENOENT;
+		new_de = minix_find_entry(new_dentry, &new_page);
+		if (!new_de)
+			goto out_dir;
+		inc_count(old_inode);
+		minix_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 >= info->s_link_max)
+				goto out_dir;
+		}
+		inc_count(old_inode);
+		err = minix_add_link(new_dentry, old_inode);
+		if (err) {
+			dec_count(old_inode);
+			goto out_dir;
+		}
+		if (dir_de)
+			inc_count(new_dir);
+	}
+
+	minix_delete_entry(old_de, old_page);
+	dec_count(old_inode);
+
+	if (dir_de) {
+		minix_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 minix_dir_inode_operations = {
+	.create		= minix_create,
+	.lookup		= minix_lookup,
+	.link		= minix_link,
+	.unlink		= minix_unlink,
+	.symlink	= minix_symlink,
+	.mkdir		= minix_mkdir,
+	.rmdir		= minix_rmdir,
+	.mknod		= minix_mknod,
+	.rename		= minix_rename,
+	.getattr	= minix_getattr,
+};