summaryrefslogtreecommitdiffstats
path: root/fs/ext
diff options
context:
space:
mode:
Diffstat (limited to 'fs/ext')
-rw-r--r--fs/ext/Makefile31
-rw-r--r--fs/ext/dir.c131
-rw-r--r--fs/ext/file.c258
-rw-r--r--fs/ext/freelists.c341
-rw-r--r--fs/ext/fsync.c185
-rw-r--r--fs/ext/inode.c444
-rw-r--r--fs/ext/namei.c893
-rw-r--r--fs/ext/symlink.c108
-rw-r--r--fs/ext/truncate.c252
9 files changed, 2643 insertions, 0 deletions
diff --git a/fs/ext/Makefile b/fs/ext/Makefile
new file mode 100644
index 000000000..5e23319c8
--- /dev/null
+++ b/fs/ext/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the linux ext-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= freelists.o truncate.o namei.o inode.o \
+ file.o dir.o symlink.o fsync.o
+
+ext.o: $(OBJS)
+ $(LD) -r -o ext.o $(OBJS)
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/ext/dir.c b/fs/ext/dir.c
new file mode 100644
index 000000000..10e30fafa
--- /dev/null
+++ b/fs/ext/dir.c
@@ -0,0 +1,131 @@
+/*
+ * linux/fs/ext/dir.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/dir.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/ext_fs.h>
+#include <linux/stat.h>
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+static int ext_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
+static int ext_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations ext_dir_operations = {
+ NULL, /* lseek - default */
+ ext_dir_read, /* read */
+ NULL, /* write - bad */
+ ext_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ file_fsync /* fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations ext_dir_inode_operations = {
+ &ext_dir_operations, /* default directory file-ops */
+ ext_create, /* create */
+ ext_lookup, /* lookup */
+ ext_link, /* link */
+ ext_unlink, /* unlink */
+ ext_symlink, /* symlink */
+ ext_mkdir, /* mkdir */
+ ext_rmdir, /* rmdir */
+ ext_mknod, /* mknod */
+ ext_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ ext_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int ext_readdir(struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ unsigned int i;
+ unsigned int ret;
+ off_t offset;
+ char c;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ if ((filp->f_pos & 7) != 0)
+ return -EBADF;
+ ret = 0;
+ while (!ret && filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & 1023;
+ bh = ext_bread(inode,(filp->f_pos)>>BLOCK_SIZE_BITS,0);
+ if (!bh) {
+ filp->f_pos += 1024-offset;
+ continue;
+ }
+ for (i = 0; i < 1024 && i < offset; ) {
+ de = (struct ext_dir_entry *) (bh->b_data + i);
+ if (!de->rec_len)
+ break;
+ i += de->rec_len;
+ }
+ offset = i;
+ de = (struct ext_dir_entry *) (offset + bh->b_data);
+ while (!ret && offset < 1024 && filp->f_pos < inode->i_size) {
+ if (de->rec_len < 8 || de->rec_len % 8 != 0 ||
+ de->rec_len < de->name_len + 8 ||
+ (de->rec_len + (off_t) filp->f_pos - 1) / 1024 > ((off_t) filp->f_pos / 1024)) {
+ printk ("ext_readdir: bad dir entry, skipping\n");
+ printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
+ inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+ filp->f_pos += 1024-offset;
+ if (filp->f_pos > inode->i_size)
+ filp->f_pos = inode->i_size;
+ continue;
+ }
+ offset += de->rec_len;
+ filp->f_pos += de->rec_len;
+ if (de->inode) {
+ for (i = 0; i < de->name_len; i++)
+ if ((c = de->name[i]) != 0)
+ put_fs_byte(c,i+dirent->d_name);
+ else
+ break;
+ if (i) {
+ put_fs_long(de->inode,&dirent->d_ino);
+ put_fs_byte(0,i+dirent->d_name);
+ put_fs_word(i,&dirent->d_reclen);
+ ret = ROUND_UP(NAME_OFFSET(dirent)+i+1);
+ break;
+ }
+ }
+ de = (struct ext_dir_entry *) ((char *) de
+ + de->rec_len);
+ }
+ brelse(bh);
+ }
+ return ret;
+}
diff --git a/fs/ext/file.c b/fs/ext/file.c
new file mode 100644
index 000000000..f32cdd898
--- /dev/null
+++ b/fs/ext/file.c
@@ -0,0 +1,258 @@
+/*
+ * linux/fs/ext/file.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/ext_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#define NBUF 32
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/ext_fs.h>
+
+static int ext_file_read(struct inode *, struct file *, char *, int);
+static int ext_file_write(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ext filesystem.
+ */
+static struct file_operations ext_file_operations = {
+ NULL, /* lseek - default */
+ ext_file_read, /* read */
+ ext_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ generic_mmap, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ ext_sync_file /* fsync */
+};
+
+struct inode_operations ext_file_inode_operations = {
+ &ext_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ ext_bmap, /* bmap */
+ ext_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int ext_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ int read,left,chars;
+ int block, blocks, offset;
+ int bhrequest, uptodate;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+ unsigned int size;
+
+ if (!inode) {
+ printk("ext_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("ext_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ offset = filp->f_pos;
+ size = inode->i_size;
+ if (offset > size)
+ left = 0;
+ else
+ left = size - offset;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = offset >> BLOCK_SIZE_BITS;
+ offset &= BLOCK_SIZE-1;
+ size = (size + (BLOCK_SIZE-1)) >> BLOCK_SIZE_BITS;
+ blocks = (left + offset + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
+ bhb = bhe = buflist;
+ if (filp->f_reada) {
+ if(blocks < read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9))
+ blocks = read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
+ if (block + blocks > size)
+ blocks = size - block;
+ }
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk.
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ *bhb = ext_getblk(inode, block++, 0);
+ if (*bhb && !(*bhb)->b_uptodate) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ }
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing. */
+ if (uptodate)
+ break;
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block(READ, bhrequest, bhreq);
+
+ do { /* Finish off all I/O that has actually completed */
+ if (*bhe) {
+ wait_on_buffer(*bhe);
+ if (!(*bhe)->b_uptodate) { /* read error? */
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ left = 0;
+ break;
+ }
+ }
+ if (left < BLOCK_SIZE - offset)
+ chars = left;
+ else
+ chars = BLOCK_SIZE - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ brelse(*bhe);
+ buf += chars;
+ } else {
+ while (chars-->0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0);
+
+/* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ };
+ if (!read)
+ return -EIO;
+ filp->f_reada = 1;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+ return read;
+}
+
+static int ext_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ off_t pos;
+ int written,c;
+ struct buffer_head * bh;
+ char * p;
+
+ if (!inode) {
+ printk("ext_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("ext_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written<count) {
+ bh = ext_getblk(inode,pos/BLOCK_SIZE,1);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = BLOCK_SIZE - (pos % BLOCK_SIZE);
+ if (c > count-written)
+ c = count-written;
+ if (c != BLOCK_SIZE && !bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ p = (pos % BLOCK_SIZE) + bh->b_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs(p,buf,c);
+ buf += c;
+ bh->b_uptodate = 1;
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ return written;
+}
diff --git a/fs/ext/freelists.c b/fs/ext/freelists.c
new file mode 100644
index 000000000..29c4c4289
--- /dev/null
+++ b/fs/ext/freelists.c
@@ -0,0 +1,341 @@
+/*
+ * linux/fs/ext/freelists.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ */
+
+/* freelists.c contains the code that handles the inode and block free lists */
+
+
+/*
+
+ The free blocks are managed by a linked list. The super block contains the
+ number of the first free block. This block contains 254 numbers of other
+ free blocks and the number of the next block in the list.
+
+ When an ext fs is mounted, the number of the first free block is stored
+ in s->u.ext_sb.s_firstfreeblocknumber and the block header is stored in
+ s->u.ext_sb.s_firstfreeblock. u.ext_sb.s_freeblockscount contains the count
+ of free blocks.
+
+ The free inodes are also managed by a linked list in a similar way. The
+ super block contains the number of the first free inode. This inode contains
+ 14 numbers of other free inodes and the number of the next inode in the list.
+
+ The number of the first free inode is stored in
+ s->u.ext_sb.s_firstfreeinodenumber and the header of the block containing
+ the inode is stored in s->u.ext_sb.s_firstfreeinodeblock.
+ u.ext_sb.s_freeinodescount contains the count of free inodes.
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ext_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+void ext_free_block(struct super_block * sb, int block)
+{
+ struct buffer_head * bh;
+ struct ext_free_block * efb;
+
+ if (!sb) {
+ printk("trying to free block on non-existent device\n");
+ return;
+ }
+ lock_super (sb);
+ if (block < sb->u.ext_sb.s_firstdatazone ||
+ block >= sb->u.ext_sb.s_nzones) {
+ printk("trying to free block not in datazone\n");
+ return;
+ }
+ bh = get_hash_table(sb->s_dev, block, sb->s_blocksize);
+ if (bh)
+ bh->b_dirt=0;
+ brelse(bh);
+ if (sb->u.ext_sb.s_firstfreeblock)
+ efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
+ if (!sb->u.ext_sb.s_firstfreeblock || efb->count == 254) {
+#ifdef EXTFS_DEBUG
+printk("ext_free_block: block full, skipping to %d\n", block);
+#endif
+ if (sb->u.ext_sb.s_firstfreeblock)
+ brelse (sb->u.ext_sb.s_firstfreeblock);
+ if (!(sb->u.ext_sb.s_firstfreeblock = bread (sb->s_dev,
+ block, sb->s_blocksize)))
+ panic ("ext_free_block: unable to read block to free\n");
+ efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
+ efb->next = sb->u.ext_sb.s_firstfreeblocknumber;
+ efb->count = 0;
+ sb->u.ext_sb.s_firstfreeblocknumber = block;
+ } else {
+ efb->free[efb->count++] = block;
+ }
+ sb->u.ext_sb.s_freeblockscount ++;
+ sb->s_dirt = 1;
+ mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1);
+ unlock_super (sb);
+ return;
+}
+
+int ext_new_block(struct super_block * sb)
+{
+ struct buffer_head * bh;
+ struct ext_free_block * efb;
+ int j;
+
+ if (!sb) {
+ printk("trying to get new block from non-existent device\n");
+ return 0;
+ }
+ if (!sb->u.ext_sb.s_firstfreeblock)
+ return 0;
+ lock_super (sb);
+ efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
+ if (efb->count) {
+ j = efb->free[--efb->count];
+ mark_buffer_dirty(sb->u.ext_sb.s_firstfreeblock, 1);
+ } else {
+#ifdef EXTFS_DEBUG
+printk("ext_new_block: block empty, skipping to %d\n", efb->next);
+#endif
+ j = sb->u.ext_sb.s_firstfreeblocknumber;
+ sb->u.ext_sb.s_firstfreeblocknumber = efb->next;
+ brelse (sb->u.ext_sb.s_firstfreeblock);
+ if (!sb->u.ext_sb.s_firstfreeblocknumber) {
+ sb->u.ext_sb.s_firstfreeblock = NULL;
+ } else {
+ if (!(sb->u.ext_sb.s_firstfreeblock = bread (sb->s_dev,
+ sb->u.ext_sb.s_firstfreeblocknumber,
+ sb->s_blocksize)))
+ panic ("ext_new_block: unable to read next free block\n");
+ }
+ }
+ if (j < sb->u.ext_sb.s_firstdatazone || j > sb->u.ext_sb.s_nzones) {
+ printk ("ext_new_block: blk = %d\n", j);
+ printk("allocating block not in data zone\n");
+ return 0;
+ }
+ sb->u.ext_sb.s_freeblockscount --;
+ sb->s_dirt = 1;
+
+ if (!(bh=getblk(sb->s_dev, j, sb->s_blocksize))) {
+ printk("new_block: cannot get block");
+ return 0;
+ }
+ memset(bh->b_data, 0, BLOCK_SIZE);
+ bh->b_uptodate = 1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+#ifdef EXTFS_DEBUG
+printk("ext_new_block: allocating block %d\n", j);
+#endif
+ unlock_super (sb);
+ return j;
+}
+
+unsigned long ext_count_free_blocks(struct super_block *sb)
+{
+#ifdef EXTFS_DEBUG
+ struct buffer_head * bh;
+ struct ext_free_block * efb;
+ unsigned long count, block;
+
+ lock_super (sb);
+ if (!sb->u.ext_sb.s_firstfreeblock)
+ count = 0;
+ else {
+ efb = (struct ext_free_block *) sb->u.ext_sb.s_firstfreeblock->b_data;
+ count = efb->count + 1;
+ block = efb->next;
+ while (block) {
+ if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) {
+ printk ("ext_count_free: error while reading free blocks list\n");
+ block = 0;
+ } else {
+ efb = (struct ext_free_block *) bh->b_data;
+ count += efb->count + 1;
+ block = efb->next;
+ brelse (bh);
+ }
+ }
+ }
+printk("ext_count_free_blocks: stored = %d, computed = %d\n",
+ sb->u.ext_sb.s_freeblockscount, count);
+ unlock_super (sb);
+ return count;
+#else
+ return sb->u.ext_sb.s_freeblockscount;
+#endif
+}
+
+void ext_free_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext_free_inode * efi;
+ struct super_block * sb;
+ unsigned long block;
+ unsigned long ino;
+ dev_t dev;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev) {
+ printk("free_inode: inode has no device\n");
+ return;
+ }
+ if (inode->i_count != 1) {
+ printk("free_inode: inode has count=%d\n",inode->i_count);
+ return;
+ }
+ if (inode->i_nlink) {
+ printk("free_inode: inode has nlink=%d\n",inode->i_nlink);
+ return;
+ }
+ if (!inode->i_sb) {
+ printk("free_inode: inode on non-existent device\n");
+ return;
+ }
+ sb = inode->i_sb;
+ ino = inode->i_ino;
+ dev = inode->i_dev;
+ clear_inode(inode);
+ lock_super (sb);
+ if (ino < 1 || ino > sb->u.ext_sb.s_ninodes) {
+ printk("free_inode: inode 0 or non-existent inode\n");
+ unlock_super (sb);
+ return;
+ }
+ if (sb->u.ext_sb.s_firstfreeinodeblock)
+ efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) +
+ (sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK;
+ if (!sb->u.ext_sb.s_firstfreeinodeblock || efi->count == 14) {
+#ifdef EXTFS_DEBUG
+printk("ext_free_inode: inode full, skipping to %d\n", ino);
+#endif
+ if (sb->u.ext_sb.s_firstfreeinodeblock)
+ brelse (sb->u.ext_sb.s_firstfreeinodeblock);
+ block = 2 + (ino - 1) / EXT_INODES_PER_BLOCK;
+ if (!(bh = bread(dev, block, sb->s_blocksize)))
+ panic("ext_free_inode: unable to read inode block\n");
+ efi = ((struct ext_free_inode *) bh->b_data) +
+ (ino - 1) % EXT_INODES_PER_BLOCK;
+ efi->next = sb->u.ext_sb.s_firstfreeinodenumber;
+ efi->count = 0;
+ sb->u.ext_sb.s_firstfreeinodenumber = ino;
+ sb->u.ext_sb.s_firstfreeinodeblock = bh;
+ } else {
+ efi->free[efi->count++] = ino;
+ }
+ sb->u.ext_sb.s_freeinodescount ++;
+ sb->s_dirt = 1;
+ mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1);
+ unlock_super (sb);
+}
+
+struct inode * ext_new_inode(const struct inode * dir)
+{
+ struct super_block * sb;
+ struct inode * inode;
+ struct ext_free_inode * efi;
+ unsigned long block;
+ int j;
+
+ if (!dir || !(inode=get_empty_inode()))
+ return NULL;
+ sb = dir->i_sb;
+ inode->i_sb = sb;
+ inode->i_flags = sb->s_flags;
+ if (!sb->u.ext_sb.s_firstfreeinodeblock)
+ return 0;
+ lock_super (sb);
+ efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) +
+ (sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK;
+ if (efi->count) {
+ j = efi->free[--efi->count];
+ mark_buffer_dirty(sb->u.ext_sb.s_firstfreeinodeblock, 1);
+ } else {
+#ifdef EXTFS_DEBUG
+printk("ext_free_inode: inode empty, skipping to %d\n", efi->next);
+#endif
+ j = sb->u.ext_sb.s_firstfreeinodenumber;
+ if (efi->next > sb->u.ext_sb.s_ninodes) {
+ printk ("efi->next = %ld\n", efi->next);
+ panic ("ext_new_inode: bad inode number in free list\n");
+ }
+ sb->u.ext_sb.s_firstfreeinodenumber = efi->next;
+ block = 2 + (((unsigned long) efi->next) - 1) / EXT_INODES_PER_BLOCK;
+ brelse (sb->u.ext_sb.s_firstfreeinodeblock);
+ if (!sb->u.ext_sb.s_firstfreeinodenumber) {
+ sb->u.ext_sb.s_firstfreeinodeblock = NULL;
+ } else {
+ if (!(sb->u.ext_sb.s_firstfreeinodeblock =
+ bread(sb->s_dev, block, sb->s_blocksize)))
+ panic ("ext_new_inode: unable to read next free inode block\n");
+ }
+ }
+ sb->u.ext_sb.s_freeinodescount --;
+ sb->s_dirt = 1;
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_dirt = 1;
+ inode->i_ino = j;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = NULL;
+ inode->i_blocks = inode->i_blksize = 0;
+ insert_inode_hash(inode);
+#ifdef EXTFS_DEBUG
+printk("ext_new_inode : allocating inode %d\n", inode->i_ino);
+#endif
+ unlock_super (sb);
+ return inode;
+}
+
+unsigned long ext_count_free_inodes(struct super_block *sb)
+{
+#ifdef EXTFS_DEBUG
+ struct buffer_head * bh;
+ struct ext_free_inode * efi;
+ unsigned long count, block, ino;
+
+ lock_super (sb);
+ if (!sb->u.ext_sb.s_firstfreeinodeblock)
+ count = 0;
+ else {
+ efi = ((struct ext_free_inode *) sb->u.ext_sb.s_firstfreeinodeblock->b_data) +
+ ((sb->u.ext_sb.s_firstfreeinodenumber-1)%EXT_INODES_PER_BLOCK);
+ count = efi->count + 1;
+ ino = efi->next;
+ while (ino) {
+ if (ino < 1 || ino > sb->u.ext_sb.s_ninodes) {
+ printk ("u.ext_sb.s_firstfreeinodenumber = %d, ino = %d\n",
+ (int) sb->u.ext_sb.s_firstfreeinodenumber,ino);
+ panic ("ext_count_fre_inodes: bad inode number in free list\n");
+ }
+ block = 2 + ((ino - 1) / EXT_INODES_PER_BLOCK);
+ if (!(bh = bread (sb->s_dev, block, sb->s_blocksize))) {
+ printk ("ext_count_free_inodes: error while reading free inodes list\n");
+ block = 0;
+ } else {
+ efi = ((struct ext_free_inode *) bh->b_data) +
+ ((ino - 1) % EXT_INODES_PER_BLOCK);
+ count += efi->count + 1;
+ ino = efi->next;
+ brelse (bh);
+ }
+ }
+ }
+printk("ext_count_free_inodes: stored = %d, computed = %d\n",
+ sb->u.ext_sb.s_freeinodescount, count);
+ unlock_super (sb);
+ return count;
+#else
+ return sb->u.ext_sb.s_freeinodescount;
+#endif
+}
diff --git a/fs/ext/fsync.c b/fs/ext/fsync.c
new file mode 100644
index 000000000..bb20383cc
--- /dev/null
+++ b/fs/ext/fsync.c
@@ -0,0 +1,185 @@
+
+/*
+ * linux/fs/ext/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ * from
+ * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * extfs fsync primitive
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+
+#include <linux/fs.h>
+#include <linux/ext_fs.h>
+
+
+#define blocksize BLOCK_SIZE
+#define addr_per_block 256
+
+static int sync_block (struct inode * inode, unsigned long * block, int wait)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = get_hash_table(inode->i_dev, *block, blocksize);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse (bh);
+ return 1;
+ }
+ if (wait && bh->b_req && !bh->b_uptodate) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !bh->b_uptodate || !bh->b_dirt)
+ {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ bh->b_count--;
+ return 0;
+}
+
+static int sync_iblock (struct inode * inode, unsigned long * iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc, tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block (inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 9; i++) {
+ rc = sync_block (inode, inode->u.ext_i.i_data + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+static int sync_indirect(struct inode *inode, unsigned long *iblock, int wait)
+{
+ int i;
+ struct buffer_head * ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_block (inode,
+ ((unsigned long *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned long *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect (inode,
+ ((unsigned long *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+static int sync_tindirect(struct inode *inode, unsigned long *tiblock,
+ int wait)
+{
+ int i;
+ struct buffer_head * tind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock (inode, tiblock, &tind_bh, wait);
+ if (rc || !tind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_dindirect (inode,
+ ((unsigned long *) tind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
+int ext_sync_file(struct inode * inode, struct file *file)
+{
+ int wait, err = 0;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+ for (wait=0; wait<=1; wait++)
+ {
+ err |= sync_direct(inode, wait);
+ err |= sync_indirect(inode, inode->u.ext_i.i_data+9, wait);
+ err |= sync_dindirect(inode, inode->u.ext_i.i_data+10, wait);
+ err |= sync_tindirect(inode, inode->u.ext_i.i_data+11, wait);
+ }
+ err |= ext_sync_inode (inode);
+ return (err < 0) ? -EIO : 0;
+}
diff --git a/fs/ext/inode.c b/fs/ext/inode.c
new file mode 100644
index 000000000..b3ca2e2cf
--- /dev/null
+++ b/fs/ext/inode.c
@@ -0,0 +1,444 @@
+/*
+ * linux/fs/ext/inode.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+void ext_put_inode(struct inode *inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+ ext_truncate(inode);
+ ext_free_inode(inode);
+}
+
+void ext_put_super(struct super_block *sb)
+{
+
+ lock_super(sb);
+ sb->s_dev = 0;
+ if (sb->u.ext_sb.s_firstfreeinodeblock)
+ brelse (sb->u.ext_sb.s_firstfreeinodeblock);
+ if (sb->u.ext_sb.s_firstfreeblock)
+ brelse (sb->u.ext_sb.s_firstfreeblock);
+ unlock_super(sb);
+ return;
+}
+
+static struct super_operations ext_sops = {
+ ext_read_inode,
+ NULL,
+ ext_write_inode,
+ ext_put_inode,
+ ext_put_super,
+ ext_write_super,
+ ext_statfs,
+ NULL
+};
+
+struct super_block *ext_read_super(struct super_block *s,void *data,
+ int silent)
+{
+ struct buffer_head *bh;
+ struct ext_super_block *es;
+ int dev = s->s_dev,block;
+
+ lock_super(s);
+ set_blocksize(dev, BLOCK_SIZE);
+ if (!(bh = bread(dev, 1, BLOCK_SIZE))) {
+ s->s_dev=0;
+ unlock_super(s);
+ printk("EXT-fs: unable to read superblock\n");
+ return NULL;
+ }
+ es = (struct ext_super_block *) bh->b_data;
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->u.ext_sb.s_ninodes = es->s_ninodes;
+ s->u.ext_sb.s_nzones = es->s_nzones;
+ s->u.ext_sb.s_firstdatazone = es->s_firstdatazone;
+ s->u.ext_sb.s_log_zone_size = es->s_log_zone_size;
+ s->u.ext_sb.s_max_size = es->s_max_size;
+ s->s_magic = es->s_magic;
+ s->u.ext_sb.s_firstfreeblocknumber = es->s_firstfreeblock;
+ s->u.ext_sb.s_freeblockscount = es->s_freeblockscount;
+ s->u.ext_sb.s_firstfreeinodenumber = es->s_firstfreeinode;
+ s->u.ext_sb.s_freeinodescount = es->s_freeinodescount;
+ brelse(bh);
+ if (s->s_magic != EXT_SUPER_MAGIC) {
+ s->s_dev = 0;
+ unlock_super(s);
+ if (!silent)
+ printk("VFS: Can't find an extfs filesystem on dev 0x%04x.\n",
+ dev);
+ return NULL;
+ }
+ if (!s->u.ext_sb.s_firstfreeblocknumber)
+ s->u.ext_sb.s_firstfreeblock = NULL;
+ else
+ if (!(s->u.ext_sb.s_firstfreeblock = bread(dev,
+ s->u.ext_sb.s_firstfreeblocknumber, BLOCK_SIZE))) {
+ printk("ext_read_super: unable to read first free block\n");
+ s->s_dev = 0;
+ unlock_super(s);
+ return NULL;
+ }
+ if (!s->u.ext_sb.s_firstfreeinodenumber)
+ s->u.ext_sb.s_firstfreeinodeblock = NULL;
+ else {
+ block = 2 + (s->u.ext_sb.s_firstfreeinodenumber - 1) / EXT_INODES_PER_BLOCK;
+ if (!(s->u.ext_sb.s_firstfreeinodeblock = bread(dev, block, BLOCK_SIZE))) {
+ printk("ext_read_super: unable to read first free inode block\n");
+ brelse(s->u.ext_sb.s_firstfreeblock);
+ s->s_dev = 0;
+ unlock_super (s);
+ return NULL;
+ }
+ }
+ unlock_super(s);
+ /* set up enough so that it can read an inode */
+ s->s_dev = dev;
+ s->s_op = &ext_sops;
+ if (!(s->s_mounted = iget(s,EXT_ROOT_INO))) {
+ s->s_dev=0;
+ printk("EXT-fs: get root inode failed\n");
+ return NULL;
+ }
+ return s;
+}
+
+void ext_write_super (struct super_block *sb)
+{
+ struct buffer_head * bh;
+ struct ext_super_block * es;
+
+ if (!(bh = bread(sb->s_dev, 1, BLOCK_SIZE))) {
+ printk ("ext_write_super: bread failed\n");
+ return;
+ }
+ es = (struct ext_super_block *) bh->b_data;
+ es->s_firstfreeblock = sb->u.ext_sb.s_firstfreeblocknumber;
+ es->s_freeblockscount = sb->u.ext_sb.s_freeblockscount;
+ es->s_firstfreeinode = sb->u.ext_sb.s_firstfreeinodenumber;
+ es->s_freeinodescount = sb->u.ext_sb.s_freeinodescount;
+ mark_buffer_dirty(bh, 1);
+ brelse (bh);
+ sb->s_dirt = 0;
+}
+
+void ext_statfs (struct super_block *sb, struct statfs *buf)
+{
+ long tmp;
+
+ put_fs_long(EXT_SUPER_MAGIC, &buf->f_type);
+ put_fs_long(1024, &buf->f_bsize);
+ put_fs_long(sb->u.ext_sb.s_nzones << sb->u.ext_sb.s_log_zone_size,
+ &buf->f_blocks);
+ tmp = ext_count_free_blocks(sb);
+ put_fs_long(tmp, &buf->f_bfree);
+ put_fs_long(tmp, &buf->f_bavail);
+ put_fs_long(sb->u.ext_sb.s_ninodes, &buf->f_files);
+ put_fs_long(ext_count_free_inodes(sb), &buf->f_ffree);
+ put_fs_long(EXT_NAME_LEN, &buf->f_namelen);
+ /* Don't know what value to put in buf->f_fsid */
+}
+
+#define inode_bmap(inode,nr) ((inode)->u.ext_i.i_data[(nr)])
+
+static int block_bmap(struct buffer_head * bh, int nr)
+{
+ int tmp;
+
+ if (!bh)
+ return 0;
+ tmp = ((unsigned long *) bh->b_data)[nr];
+ brelse(bh);
+ return tmp;
+}
+
+int ext_bmap(struct inode * inode,int block)
+{
+ int i;
+
+ if (block<0) {
+ printk("ext_bmap: block<0");
+ return 0;
+ }
+ if (block >= 9+256+256*256+256*256*256) {
+ printk("ext_bmap: block>big");
+ return 0;
+ }
+ if (block<9)
+ return inode_bmap(inode,block);
+ block -= 9;
+ if (block<256) {
+ i = inode_bmap(inode,9);
+ if (!i)
+ return 0;
+ return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block);
+ }
+ block -= 256;
+ if (block<256*256) {
+ i = inode_bmap(inode,10);
+ if (!i)
+ return 0;
+ i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>8);
+ if (!i)
+ return 0;
+ return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
+ }
+ block -= 256*256;
+ i = inode_bmap(inode,11);
+ if (!i)
+ return 0;
+ i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block>>16);
+ if (!i)
+ return 0;
+ i = block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),(block>>8) & 255);
+ if (!i)
+ return 0;
+ return block_bmap(bread(inode->i_dev,i,BLOCK_SIZE),block & 255);
+}
+
+static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create)
+{
+ int tmp;
+ unsigned long * p;
+ struct buffer_head * result;
+
+ p = inode->u.ext_i.i_data + nr;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+ if (tmp == *p)
+ return result;
+ brelse(result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = ext_new_block(inode->i_sb);
+ if (!tmp)
+ return NULL;
+ result = getblk(inode->i_dev, tmp, BLOCK_SIZE);
+ if (*p) {
+ ext_free_block(inode->i_sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ return result;
+}
+
+static struct buffer_head * block_getblk(struct inode * inode,
+ struct buffer_head * bh, int nr, int create)
+{
+ int tmp;
+ unsigned long * p;
+ struct buffer_head * result;
+
+ if (!bh)
+ return NULL;
+ if (!bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ return NULL;
+ }
+ }
+ p = nr + (unsigned long *) bh->b_data;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+ if (tmp == *p) {
+ brelse(bh);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse(bh);
+ return NULL;
+ }
+ tmp = ext_new_block(inode->i_sb);
+ if (!tmp) {
+ brelse(bh);
+ return NULL;
+ }
+ result = getblk(bh->b_dev, tmp, BLOCK_SIZE);
+ if (*p) {
+ ext_free_block(inode->i_sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *p = tmp;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ return result;
+}
+
+struct buffer_head * ext_getblk(struct inode * inode, int block, int create)
+{
+ struct buffer_head * bh;
+
+ if (block<0) {
+ printk("ext_getblk: block<0\n");
+ return NULL;
+ }
+ if (block >= 9+256+256*256+256*256*256) {
+ printk("ext_getblk: block>big\n");
+ return NULL;
+ }
+ if (block<9)
+ return inode_getblk(inode,block,create);
+ block -= 9;
+ if (block<256) {
+ bh = inode_getblk(inode,9,create);
+ return block_getblk(inode,bh,block,create);
+ }
+ block -= 256;
+ if (block<256*256) {
+ bh = inode_getblk(inode,10,create);
+ bh = block_getblk(inode,bh,block>>8,create);
+ return block_getblk(inode,bh,block & 255,create);
+ }
+ block -= 256*256;
+ bh = inode_getblk(inode,11,create);
+ bh = block_getblk(inode,bh,block>>16,create);
+ bh = block_getblk(inode,bh,(block>>8) & 255,create);
+ return block_getblk(inode,bh,block & 255,create);
+}
+
+struct buffer_head * ext_bread(struct inode * inode, int block, int create)
+{
+ struct buffer_head * bh;
+
+ bh = ext_getblk(inode,block,create);
+ if (!bh || bh->b_uptodate)
+ return bh;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse(bh);
+ return NULL;
+}
+
+void ext_read_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext_inode * raw_inode;
+ int block;
+
+ block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE)))
+ panic("unable to read i-node block");
+ raw_inode = ((struct ext_inode *) bh->b_data) +
+ (inode->i_ino-1)%EXT_INODES_PER_BLOCK;
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlinks;
+ inode->i_size = raw_inode->i_size;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
+ inode->i_blocks = inode->i_blksize = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = raw_inode->i_zone[0];
+ else for (block = 0; block < 12; block++)
+ inode->u.ext_i.i_data[block] = raw_inode->i_zone[block];
+ brelse(bh);
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &ext_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &ext_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &ext_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+}
+
+static struct buffer_head * ext_update_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext_inode * raw_inode;
+ int block;
+
+ block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE)))
+ panic("unable to read i-node block");
+ raw_inode = ((struct ext_inode *)bh->b_data) +
+ (inode->i_ino-1)%EXT_INODES_PER_BLOCK;
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = inode->i_uid;
+ raw_inode->i_gid = inode->i_gid;
+ raw_inode->i_nlinks = inode->i_nlink;
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_time = inode->i_mtime;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_zone[0] = inode->i_rdev;
+ else for (block = 0; block < 12; block++)
+ raw_inode->i_zone[block] = inode->u.ext_i.i_data[block];
+ mark_buffer_dirty(bh, 1);
+ inode->i_dirt=0;
+ return bh;
+}
+
+void ext_write_inode(struct inode * inode)
+{
+ struct buffer_head *bh;
+ bh = ext_update_inode (inode);
+ brelse(bh);
+}
+
+int ext_sync_inode (struct inode *inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = ext_update_inode(inode);
+ if (bh && bh->b_dirt)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_req && !bh->b_uptodate)
+ {
+ printk ("IO error syncing ext inode [%04x:%08lx]\n",
+ inode->i_dev, inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
diff --git a/fs/ext/namei.c b/fs/ext/namei.c
new file mode 100644
index 000000000..85a411e94
--- /dev/null
+++ b/fs/ext/namei.c
@@ -0,0 +1,893 @@
+/*
+ * linux/fs/ext/namei.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+/*
+ * comment out this line if you want names > EXT_NAME_LEN chars to be
+ * truncated. Else they will be disallowed.
+ */
+/* #define NO_TRUNCATE */
+
+/*
+ * EXT_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a power of 2 and must be greater or equal than 8
+ * because a directory entry needs 8 bytes for its fixed part
+ * (4 bytes for the inode, 2 bytes for the entry length and 2 bytes
+ * for the name length)
+ */
+#define EXT_DIR_PAD 8
+
+/*
+ *
+ * EXT_DIR_MIN_SIZE is the minimal size of a directory entry
+ *
+ * During allocations, a directory entry is split into 2 ones
+ * *ONLY* if the size of the unused part is greater than or
+ * equal to EXT_DIR_MIN_SIZE
+ */
+#define EXT_DIR_MIN_SIZE 12
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use ext_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, ext_match returns 1 for success, 0 for failure.
+ */
+static int ext_match(int len,const char * name,struct ext_dir_entry * de)
+{
+ if (!de || !de->inode || len > EXT_NAME_LEN)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
+ return 1;
+ if (len != de->name_len)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+/*
+ * ext_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.
+ *
+ * addition for the ext file system : this function returns the previous
+ * and next directory entries in the parameters prev_dir and next_dir
+ */
+static struct buffer_head * ext_find_entry(struct inode * dir,
+ const char * name, int namelen, struct ext_dir_entry ** res_dir,
+ struct ext_dir_entry ** prev_dir, struct ext_dir_entry ** next_dir)
+{
+ long offset;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+#ifdef NO_TRUNCATE
+ if (namelen > EXT_NAME_LEN)
+ return NULL;
+#else
+ if (namelen > EXT_NAME_LEN)
+ namelen = EXT_NAME_LEN;
+#endif
+ bh = ext_bread(dir,0,0);
+ if (!bh)
+ return NULL;
+ if (prev_dir)
+ *prev_dir = NULL;
+ if (next_dir)
+ *next_dir = NULL;
+ offset = 0;
+ de = (struct ext_dir_entry *) bh->b_data;
+ while (offset < dir->i_size) {
+ if ((char *)de >= BLOCK_SIZE+bh->b_data) {
+ brelse(bh);
+ bh = NULL;
+ bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
+ if (!bh)
+ continue;
+ de = (struct ext_dir_entry *) bh->b_data;
+ if (prev_dir)
+ *prev_dir = NULL;
+ }
+ if (de->rec_len < 8 || de->rec_len % 8 != 0 ||
+ de->rec_len < de->name_len + 8 ||
+ (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
+ printk ("ext_find_entry: bad dir entry\n");
+ printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
+ dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+ de = (struct ext_dir_entry *) (bh->b_data+BLOCK_SIZE);
+ offset = ((offset / BLOCK_SIZE) + 1) * BLOCK_SIZE;
+ continue;
+/* brelse (bh);
+ return NULL; */
+ }
+ if (ext_match(namelen,name,de)) {
+ *res_dir = de;
+ if (next_dir)
+ if (offset + de->rec_len < dir->i_size &&
+ ((char *)de) + de->rec_len < BLOCK_SIZE+bh->b_data)
+ *next_dir = (struct ext_dir_entry *)
+ ((char *) de + de->rec_len);
+ else
+ *next_dir = NULL;
+ return bh;
+ }
+ offset += de->rec_len;
+ if (prev_dir)
+ *prev_dir = de;
+ de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse(bh);
+ return NULL;
+}
+
+int ext_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result)
+{
+ int ino;
+ struct ext_dir_entry * de;
+ struct buffer_head * bh;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!(bh = ext_find_entry(dir,name,len,&de,NULL,NULL))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ ino = de->inode;
+ brelse(bh);
+ if (!(*result = iget(dir->i_sb,ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+ iput(dir);
+ return 0;
+}
+
+/*
+ * ext_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as ext_find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * ext_add_entry(struct inode * dir,
+ const char * name, int namelen, struct ext_dir_entry ** res_dir)
+{
+ int i;
+ long offset;
+ unsigned short rec_len;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de, * de1;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+#ifdef NO_TRUNCATE
+ if (namelen > EXT_NAME_LEN)
+ return NULL;
+#else
+ if (namelen > EXT_NAME_LEN)
+ namelen = EXT_NAME_LEN;
+#endif
+ if (!namelen)
+ return NULL;
+ bh = ext_bread(dir,0,0);
+ if (!bh)
+ return NULL;
+ rec_len = ((8 + namelen + EXT_DIR_PAD - 1) / EXT_DIR_PAD) * EXT_DIR_PAD;
+ offset = 0;
+ de = (struct ext_dir_entry *) bh->b_data;
+ while (1) {
+ if ((char *)de >= BLOCK_SIZE+bh->b_data && offset < dir->i_size) {
+#ifdef EXTFS_DEBUG
+printk ("ext_add_entry: skipping to next block\n");
+#endif
+ brelse(bh);
+ bh = NULL;
+ bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,0);
+ if (!bh)
+ return NULL;
+ de = (struct ext_dir_entry *) bh->b_data;
+ }
+ if (offset >= dir->i_size) {
+ /* Check that the directory entry fits in the block */
+ if (offset % BLOCK_SIZE == 0 ||
+ (BLOCK_SIZE - (offset % BLOCK_SIZE)) < rec_len) {
+ if ((offset % BLOCK_SIZE) != 0) {
+ /* If the entry does not fit in the
+ block, the remainder of the block
+ becomes an unused entry */
+ de->inode = 0;
+ de->rec_len = BLOCK_SIZE
+ - (offset & (BLOCK_SIZE - 1));
+ de->name_len = 0;
+ offset += de->rec_len;
+ dir->i_size += de->rec_len;
+ dir->i_dirt = 1;
+#if 0
+ dir->i_ctime = CURRENT_TIME;
+#endif
+ mark_buffer_dirty(bh, 1);
+ }
+ brelse (bh);
+ bh = NULL;
+#ifdef EXTFS_DEBUG
+printk ("ext_add_entry : creating next block\n");
+#endif
+ bh = ext_bread(dir,offset>>BLOCK_SIZE_BITS,1);
+ if (!bh)
+ return NULL; /* Other thing to do ??? */
+ de = (struct ext_dir_entry *) bh->b_data;
+ }
+ /* Allocate the entry */
+ de->inode=0;
+ de->rec_len = rec_len;
+ dir->i_size += de->rec_len;
+ dir->i_dirt = 1;
+#if 0
+ dir->i_ctime = CURRENT_TIME;
+#endif
+ }
+ if (de->rec_len < 8 || de->rec_len % 4 != 0 ||
+ de->rec_len < de->name_len + 8 ||
+ (((char *) de) + de->rec_len-1 >= BLOCK_SIZE+bh->b_data)) {
+ printk ("ext_addr_entry: bad dir entry\n");
+ printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
+ dir->i_dev, dir->i_ino, offset, de->rec_len, de->name_len);
+ brelse (bh);
+ return NULL;
+ }
+ if (!de->inode && de->rec_len >= rec_len) {
+ if (de->rec_len > rec_len
+ && de->rec_len - rec_len >= EXT_DIR_MIN_SIZE) {
+ /* The found entry is too big : it is split
+ into 2 ones :
+ - the 1st one will be used to hold the name,
+ - the 2nd one is unused */
+ de1 = (struct ext_dir_entry *) ((char *) de + rec_len);
+ de1->inode = 0;
+ de1->rec_len = de->rec_len - rec_len;
+ de1->name_len = 0;
+ de->rec_len = rec_len;
+ }
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ de->name_len = namelen;
+ for (i=0; i < namelen ; i++)
+ de->name[i] = name[i];
+ mark_buffer_dirty(bh, 1);
+ *res_dir = de;
+ return bh;
+ }
+ offset += de->rec_len;
+ de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse(bh);
+ return NULL;
+}
+
+int ext_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ inode = ext_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &ext_file_inode_operations;
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ bh = ext_add_entry(dir,name,len,&de);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ *result = inode;
+ return 0;
+}
+
+int ext_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de;
+
+ if (!dir)
+ return -ENOENT;
+ bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = ext_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_uid = current->fsuid;
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &ext_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ext_dir_inode_operations;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ }
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &ext_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode))
+ init_fifo(inode);
+ if (S_ISBLK(mode) || S_ISCHR(mode))
+ inode->i_rdev = rdev;
+#if 0
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
+ inode->i_dirt = 1;
+ bh = ext_add_entry(dir,name,len,&de);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int ext_mkdir(struct inode * dir, const char * name, int len, int mode)
+{
+ struct inode * inode;
+ struct buffer_head * bh, *dir_block;
+ struct ext_dir_entry * de;
+
+ bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = ext_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &ext_dir_inode_operations;
+ inode->i_size = 2 * 16; /* Each entry is coded on 16 bytes for "." and ".."
+ - 4 bytes for the inode number,
+ - 2 bytes for the record length
+ - 2 bytes for the name length
+ - 8 bytes for the name */
+#if 0
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+#endif
+ dir_block = ext_bread(inode,0,1);
+ if (!dir_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de = (struct ext_dir_entry *) dir_block->b_data;
+ de->inode=inode->i_ino;
+ de->rec_len=16;
+ de->name_len=1;
+ strcpy(de->name,".");
+ de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
+ de->inode = dir->i_ino;
+ de->rec_len=16;
+ de->name_len=2;
+ strcpy(de->name,"..");
+ inode->i_nlink = 2;
+ mark_buffer_dirty(dir_block, 1);
+ brelse(dir_block);
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ inode->i_dirt = 1;
+ bh = ext_add_entry(dir,name,len,&de);
+ if (!bh) {
+ iput(dir);
+ inode->i_nlink=0;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ dir->i_nlink++;
+ dir->i_dirt = 1;
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct inode * inode)
+{
+ unsigned long offset;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de, * de1;
+
+ if (inode->i_size < 2 * 12 || !(bh = ext_bread(inode,0,0))) {
+ printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ return 1;
+ }
+ de = (struct ext_dir_entry *) bh->b_data;
+ de1 = (struct ext_dir_entry *) ((char *) de + de->rec_len);
+ if (de->inode != inode->i_ino || !de1->inode ||
+ strcmp(".",de->name) || strcmp("..",de1->name)) {
+ printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ return 1;
+ }
+ offset = de->rec_len + de1->rec_len;
+ de = (struct ext_dir_entry *) ((char *) de1 + de1->rec_len);
+ while (offset < inode->i_size ) {
+ if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
+ brelse(bh);
+ bh = ext_bread(inode, offset >> BLOCK_SIZE_BITS,1);
+ if (!bh) {
+ offset += BLOCK_SIZE;
+ continue;
+ }
+ de = (struct ext_dir_entry *) bh->b_data;
+ }
+ if (de->rec_len < 8 || de->rec_len %4 != 0 ||
+ de->rec_len < de->name_len + 8) {
+ printk ("empty_dir: bad dir entry\n");
+ printk ("dev=%d, dir=%ld, offset=%ld, rec_len=%d, name_len=%d\n",
+ inode->i_dev, inode->i_ino, offset, de->rec_len, de->name_len);
+ brelse (bh);
+ return 1;
+ }
+ if (de->inode) {
+ brelse(bh);
+ return 0;
+ }
+ offset += de->rec_len;
+ de = (struct ext_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse(bh);
+ return 1;
+}
+
+static inline void ext_merge_entries (struct ext_dir_entry * de,
+ struct ext_dir_entry * pde, struct ext_dir_entry * nde)
+{
+ if (nde && !nde->inode)
+ de->rec_len += nde->rec_len;
+ if (pde && !pde->inode)
+ pde->rec_len += de->rec_len;
+}
+
+int ext_rmdir(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de, * pde, * nde;
+
+ inode = NULL;
+ bh = ext_find_entry(dir,name,len,&de,&pde,&nde);
+ retval = -ENOENT;
+ if (!bh)
+ goto end_rmdir;
+ retval = -EPERM;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_rmdir;
+ if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto end_rmdir;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto end_rmdir;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto end_rmdir;
+ }
+ if (!empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto end_rmdir;
+ }
+ if (inode->i_nlink != 2)
+ printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
+ de->inode = 0;
+ de->name_len = 0;
+ ext_merge_entries (de, pde, nde);
+ mark_buffer_dirty(bh, 1);
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ dir->i_nlink--;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt=1;
+ retval = 0;
+end_rmdir:
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return retval;
+}
+
+int ext_unlink(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext_dir_entry * de, * pde, * nde;
+
+ retval = -ENOENT;
+ inode = NULL;
+ bh = ext_find_entry(dir,name,len,&de,&pde,&nde);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget(dir->i_sb, de->inode)))
+ goto end_unlink;
+ retval = -EPERM;
+ if ((dir->i_mode & S_ISVTX) && !fsuser() &&
+ current->fsuid != inode->i_uid &&
+ current->fsuid != dir->i_uid)
+ goto end_unlink;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (!inode->i_nlink) {
+ printk("Deleting nonexistent file (%04x:%ld), %d\n",
+ inode->i_dev,inode->i_ino,inode->i_nlink);
+ inode->i_nlink=1;
+ }
+ de->inode = 0;
+ de->name_len = 0;
+ ext_merge_entries (de, pde, nde);
+ mark_buffer_dirty(bh, 1);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ inode->i_ctime = CURRENT_TIME;
+ dir->i_ctime = dir->i_mtime = inode->i_ctime;
+ dir->i_dirt = 1;
+ retval = 0;
+end_unlink:
+ brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int ext_symlink(struct inode * dir, const char * name, int len, const char * symname)
+{
+ struct ext_dir_entry * de;
+ struct inode * inode = NULL;
+ struct buffer_head * bh = NULL, * name_block = NULL;
+ int i;
+ char c;
+
+ if (!(inode = ext_new_inode(dir))) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &ext_symlink_inode_operations;
+ name_block = ext_bread(inode,0,1);
+ if (!name_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ i = 0;
+ while (i < 1023 && (c = *(symname++)))
+ name_block->b_data[i++] = c;
+ name_block->b_data[i] = 0;
+ mark_buffer_dirty(name_block, 1);
+ brelse(name_block);
+ inode->i_size = i;
+ inode->i_dirt = 1;
+ bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
+ if (bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ bh = ext_add_entry(dir,name,len,&de);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
+{
+ struct ext_dir_entry * de;
+ struct buffer_head * bh;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput(oldinode);
+ iput(dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink > 32000) {
+ iput(oldinode);
+ iput(dir);
+ return -EMLINK;
+ }
+ bh = ext_find_entry(dir,name,len,&de,NULL,NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ iput(oldinode);
+ return -EEXIST;
+ }
+ bh = ext_add_entry(dir,name,len,&de);
+ if (!bh) {
+ iput(dir);
+ iput(oldinode);
+ return -ENOSPC;
+ }
+ de->inode = oldinode->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ iput(dir);
+ oldinode->i_nlink++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput(oldinode);
+ return 0;
+}
+
+static int subdir(struct inode * new_inode, struct inode * old_inode)
+{
+ int ino;
+ int result;
+
+ new_inode->i_count++;
+ result = 0;
+ for (;;) {
+ if (new_inode == old_inode) {
+ result = 1;
+ break;
+ }
+ if (new_inode->i_dev != old_inode->i_dev)
+ break;
+ ino = new_inode->i_ino;
+ if (ext_lookup(new_inode,"..",2,&new_inode))
+ break;
+ if (new_inode->i_ino == ino)
+ break;
+ }
+ iput(new_inode);
+ return result;
+}
+
+#define PARENT_INO(buffer) \
+((struct ext_dir_entry *) ((char *) buffer + \
+((struct ext_dir_entry *) buffer)->rec_len))->inode
+
+#define PARENT_NAME(buffer) \
+((struct ext_dir_entry *) ((char *) buffer + \
+((struct ext_dir_entry *) buffer)->rec_len))->name
+
+/*
+ * rename uses retrying to avoid race-conditions: at least they should be minimal.
+ * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
+ * checks fail, it tries to restart itself again. Very practical - no changes
+ * are done until we know everything works ok.. and then all the changes can be
+ * done in one fell swoop when we have claimed all the buffers needed.
+ *
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int do_ext_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ struct ext_dir_entry * old_de, * new_de, * pde, * nde;
+ int retval;
+
+ goto start_up;
+try_again:
+ brelse(old_bh);
+ brelse(new_bh);
+ brelse(dir_bh);
+ iput(old_inode);
+ iput(new_inode);
+ current->counter = 0;
+ schedule();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = dir_bh = NULL;
+ old_bh = ext_find_entry(old_dir,old_name,old_len,&old_de,&pde,&nde);
+ retval = -ENOENT;
+ if (!old_bh)
+ goto end_rename;
+ old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */
+ if (!old_inode)
+ goto end_rename;
+ retval = -EPERM;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->fsuid != old_inode->i_uid &&
+ current->fsuid != old_dir->i_uid && !fsuser())
+ goto end_rename;
+ new_bh = ext_find_entry(new_dir,new_name,new_len,&new_de,NULL,NULL);
+ if (new_bh) {
+ new_inode = __iget(new_dir->i_sb, new_de->inode,0); /* don't cross mnt-points */
+ if (!new_inode) {
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) {
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EEXIST;
+ goto end_rename;
+ }
+ retval = -EPERM;
+ if (new_inode && (new_dir->i_mode & S_ISVTX) &&
+ current->fsuid != new_inode->i_uid &&
+ current->fsuid != new_dir->i_uid && !fsuser())
+ goto end_rename;
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -EEXIST;
+ if (new_bh)
+ goto end_rename;
+ retval = -EACCES;
+ if (!permission(old_inode, MAY_WRITE))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -EIO;
+ dir_bh = ext_bread(old_inode,0,0);
+ if (!dir_bh)
+ goto end_rename;
+ if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+ goto end_rename;
+ }
+ if (!new_bh)
+ new_bh = ext_add_entry(new_dir,new_name,new_len,&new_de);
+ retval = -ENOSPC;
+ if (!new_bh)
+ goto end_rename;
+/* sanity checking before doing the rename - avoid races */
+ if (new_inode && (new_de->inode != new_inode->i_ino))
+ goto try_again;
+ if (new_de->inode && !new_inode)
+ goto try_again;
+ if (old_de->inode != old_inode->i_ino)
+ goto try_again;
+/* ok, that's it */
+ old_de->inode = 0;
+ old_de->name_len = 0;
+ new_de->inode = old_inode->i_ino;
+ ext_merge_entries (old_de, pde, nde);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ }
+ mark_buffer_dirty(old_bh, 1);
+ mark_buffer_dirty(new_bh, 1);
+ if (dir_bh) {
+ PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+ mark_buffer_dirty(dir_bh, 1);
+ old_dir->i_nlink--;
+ new_dir->i_nlink++;
+ old_dir->i_dirt = 1;
+ new_dir->i_dirt = 1;
+ }
+ retval = 0;
+end_rename:
+ brelse(dir_bh);
+ brelse(old_bh);
+ brelse(new_bh);
+ iput(old_inode);
+ iput(new_inode);
+ iput(old_dir);
+ iput(new_dir);
+ return retval;
+}
+
+/*
+ * Ok, rename also locks out other renames, as they can change the parent of
+ * a directory, and we don't want any races. Other races are checked for by
+ * "do_rename()", which restarts if there are inconsistencies.
+ *
+ * Note that there is no race between different filesystems: it's only within
+ * the same device that races occur: many renames can happen at once, as long
+ * as they are on different partitions.
+ */
+int ext_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ static struct wait_queue * wait = NULL;
+ static int lock = 0;
+ int result;
+
+ while (lock)
+ sleep_on(&wait);
+ lock = 1;
+ result = do_ext_rename(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ lock = 0;
+ wake_up(&wait);
+ return result;
+}
diff --git a/fs/ext/symlink.c b/fs/ext/symlink.c
new file mode 100644
index 000000000..8c84bc622
--- /dev/null
+++ b/fs/ext/symlink.c
@@ -0,0 +1,108 @@
+/*
+ * linux/fs/ext/symlink.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/symlink.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext symlink handling code
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ext_fs.h>
+#include <linux/stat.h>
+
+static int ext_readlink(struct inode *, char *, int);
+static int ext_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations ext_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ ext_readlink, /* readlink */
+ ext_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int ext_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ struct buffer_head * bh;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput(dir);
+ iput(inode);
+ return -ELOOP;
+ }
+ if (!(bh = ext_bread(inode, 0, 0))) {
+ iput(inode);
+ iput(dir);
+ return -EIO;
+ }
+ iput(inode);
+ current->link_count++;
+ error = open_namei(bh->b_data,flag,mode,res_inode,dir);
+ current->link_count--;
+ brelse(bh);
+ return error;
+}
+
+static int ext_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ if (buflen > 1023)
+ buflen = 1023;
+ bh = ext_bread(inode, 0, 0);
+ iput(inode);
+ if (!bh)
+ return 0;
+ i = 0;
+ while (i<buflen && (c = bh->b_data[i])) {
+ i++;
+ put_fs_byte(c,buffer++);
+ }
+ brelse(bh);
+ return i;
+}
diff --git a/fs/ext/truncate.c b/fs/ext/truncate.c
new file mode 100644
index 000000000..a2b485821
--- /dev/null
+++ b/fs/ext/truncate.c
@@ -0,0 +1,252 @@
+/*
+ * linux/fs/ext/truncate.c
+ *
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/truncate.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext_fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+/*
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ */
+
+static int trunc_direct(struct inode * inode)
+{
+ int i, tmp;
+ unsigned long * p;
+ struct buffer_head * bh;
+ int retry = 0;
+#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10)
+
+repeat:
+ for (i = DIRECT_BLOCK ; i < 9 ; i++) {
+ p = inode->u.ext_i.i_data+i;
+ if (!(tmp = *p))
+ continue;
+ bh = getblk(inode->i_dev,tmp,BLOCK_SIZE);
+ if (i < DIRECT_BLOCK) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *p) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *p = 0;
+ inode->i_dirt = 1;
+ brelse(bh);
+ ext_free_block(inode->i_sb,tmp);
+ }
+ return retry;
+}
+
+static int trunc_indirect(struct inode * inode, int offset, unsigned long * p)
+{
+ int i, tmp;
+ struct buffer_head * bh;
+ struct buffer_head * ind_bh;
+ unsigned long * ind;
+ int retry = 0;
+#define INDIRECT_BLOCK (DIRECT_BLOCK-offset)
+
+ tmp = *p;
+ if (!tmp)
+ return 0;
+ ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+ if (tmp != *p) {
+ brelse(ind_bh);
+ return 1;
+ }
+ if (!ind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = INDIRECT_BLOCK ; i < 256 ; i++) {
+ if (i < 0)
+ i = 0;
+ if (i < INDIRECT_BLOCK)
+ goto repeat;
+ ind = i+(unsigned long *) ind_bh->b_data;
+ tmp = *ind;
+ if (!tmp)
+ continue;
+ bh = getblk(inode->i_dev,tmp,BLOCK_SIZE);
+ if (i < INDIRECT_BLOCK) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *ind) {
+ retry = 1;
+ brelse(bh);
+ continue;
+ }
+ *ind = 0;
+ mark_buffer_dirty(ind_bh, 1);
+ brelse(bh);
+ ext_free_block(inode->i_sb,tmp);
+ }
+ ind = (unsigned long *) ind_bh->b_data;
+ for (i = 0; i < 256; i++)
+ if (*(ind++))
+ break;
+ if (i >= 256)
+ if (ind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_dirt = 1;
+ ext_free_block(inode->i_sb,tmp);
+ }
+ brelse(ind_bh);
+ return retry;
+}
+
+static int trunc_dindirect(struct inode * inode, int offset, unsigned long * p)
+{
+ int i,tmp;
+ struct buffer_head * dind_bh;
+ unsigned long * dind;
+ int retry = 0;
+#define DINDIRECT_BLOCK ((DIRECT_BLOCK-offset)>>8)
+
+ tmp = *p;
+ if (!tmp)
+ return 0;
+ dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+ if (tmp != *p) {
+ brelse(dind_bh);
+ return 1;
+ }
+ if (!dind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = DINDIRECT_BLOCK ; i < 256 ; i ++) {
+ if (i < 0)
+ i = 0;
+ if (i < DINDIRECT_BLOCK)
+ goto repeat;
+ dind = i+(unsigned long *) dind_bh->b_data;
+ tmp = *dind;
+ if (!tmp)
+ continue;
+ retry |= trunc_indirect(inode,offset+(i<<8),dind);
+ mark_buffer_dirty(dind_bh, 1);
+ }
+ dind = (unsigned long *) dind_bh->b_data;
+ for (i = 0; i < 256; i++)
+ if (*(dind++))
+ break;
+ if (i >= 256)
+ if (dind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_dirt = 1;
+ ext_free_block(inode->i_sb,tmp);
+ }
+ brelse(dind_bh);
+ return retry;
+}
+
+static int trunc_tindirect(struct inode * inode)
+{
+ int i,tmp;
+ struct buffer_head * tind_bh;
+ unsigned long * tind, * p;
+ int retry = 0;
+#define TINDIRECT_BLOCK ((DIRECT_BLOCK-(256*256+256+9))>>16)
+
+ p = inode->u.ext_i.i_data+11;
+ if (!(tmp = *p))
+ return 0;
+ tind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE);
+ if (tmp != *p) {
+ brelse(tind_bh);
+ return 1;
+ }
+ if (!tind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = TINDIRECT_BLOCK ; i < 256 ; i ++) {
+ if (i < 0)
+ i = 0;
+ if (i < TINDIRECT_BLOCK)
+ goto repeat;
+ tind = i+(unsigned long *) tind_bh->b_data;
+ retry |= trunc_dindirect(inode,9+256+256*256+(i<<16),tind);
+ mark_buffer_dirty(tind_bh, 1);
+ }
+ tind = (unsigned long *) tind_bh->b_data;
+ for (i = 0; i < 256; i++)
+ if (*(tind++))
+ break;
+ if (i >= 256)
+ if (tind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_dirt = 1;
+ ext_free_block(inode->i_sb,tmp);
+ }
+ brelse(tind_bh);
+ return retry;
+}
+
+void ext_truncate(struct inode * inode)
+{
+ int retry;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ while (1) {
+ retry = trunc_direct(inode);
+ retry |= trunc_indirect(inode,9,inode->u.ext_i.i_data+9);
+ retry |= trunc_dindirect(inode,9+256,inode->u.ext_i.i_data+10);
+ retry |= trunc_tindirect(inode);
+ if (!retry)
+ break;
+ current->counter = 0;
+ schedule();
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}
+
+/*
+ * Called when a inode is released. Note that this is different
+ * from ext_open: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+void ext_release(struct inode * inode, struct file * filp)
+{
+ printk("ext_release not implemented\n");
+}