patch-2.3.9 linux/fs/sysv/inode.c

Next file: linux/fs/sysv/symlink.c
Previous file: linux/fs/sysv/fsync.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.8/linux/fs/sysv/inode.c linux/fs/sysv/inode.c
@@ -31,6 +31,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/init.h>
+#include <linux/smp_lock.h>
 #include <asm/byteorder.h>
 #include <asm/uaccess.h>
 
@@ -62,6 +63,11 @@
 	sysv_free_inode(inode);
 }
 
+static void sysv_put_super(struct super_block *);
+static void sysv_write_super(struct super_block *);
+static void sysv_read_inode(struct inode *);
+static int sysv_notify_change(struct dentry *, struct iattr *);
+static int sysv_statfs(struct super_block *, struct statfs *, int);
 
 static struct super_operations sysv_sops = {
 	sysv_read_inode,
@@ -338,8 +344,8 @@
 	return sb;
 }
 
-struct super_block *sysv_read_super(struct super_block *sb,void *data,
-				     int silent)
+static struct super_block *sysv_read_super(struct super_block *sb,
+					   void *data, int silent)
 {
 	struct buffer_head *bh;
 	const char *found;
@@ -519,7 +525,7 @@
 }
 
 /* This is only called on sync() and umount(), when s_dirt=1. */
-void sysv_write_super (struct super_block *sb)
+static void sysv_write_super(struct super_block *sb)
 {
 	lock_super(sb);
 	if (buffer_dirty(sb->sv_bh1) || buffer_dirty(sb->sv_bh2)) {
@@ -542,7 +548,7 @@
 	unlock_super(sb);
 }
 
-void sysv_put_super(struct super_block *sb)
+static void sysv_put_super(struct super_block *sb)
 {
 	/* we can assume sysv_write_super() has already been called,
 	   and that the superblock is locked */
@@ -555,7 +561,7 @@
 	MOD_DEC_USE_COUNT;
 }
 
-int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+static int sysv_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
 	struct statfs tmp;
 
@@ -597,59 +603,72 @@
 	return tmp + sb->sv_block_base;
 }
 
-int sysv_bmap(struct inode * inode,int block_nr)
+static unsigned int sysv_block_map(struct inode *inode, unsigned int block)
 {
-	unsigned int block = block_nr;
-	struct super_block * sb = inode->i_sb;
-	int convert;
-	int i;
-	struct buffer_head * bh;
+	struct super_block *sb;
+	int i, ret, convert;
 
-	if (block < 10)
-		return inode_bmap(sb,inode,block);
+	ret = 0;
+	lock_kernel();
+	sb = inode->i_sb;
+	if (block < 10) {
+		ret = inode_bmap(sb, inode, block);
+		goto out;
+	}
 	block -= 10;
 	convert = sb->sv_convert;
 	if (block < sb->sv_ind_per_block) {
-		i = inode_bmap(sb,inode,10);
+		i = inode_bmap(sb, inode, 10);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		return block_bmap(sb, bh, block, convert);
+			goto out;
+		ret = block_bmap(sb,
+				 bread(inode->i_dev, i, sb->sv_block_size),
+				 block, convert);
+		goto out;
 	}
 	block -= sb->sv_ind_per_block;
 	if (block < sb->sv_ind_per_block_2) {
-		i = inode_bmap(sb,inode,11);
+		i = inode_bmap(sb, inode, 11);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_bits, convert);
+			goto out;
+		i = block_bmap(sb,
+			       bread(inode->i_dev, i, sb->sv_block_size),
+			       (block >> sb->sv_ind_per_block_bits), convert);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert);
+			goto out;
+		ret = block_bmap(sb,
+				 bread(inode->i_dev, i, sb->sv_block_size),
+				 (block & sb->sv_ind_per_block_1), convert);
+		goto out;
 	}
 	block -= sb->sv_ind_per_block_2;
 	if (block < sb->sv_ind_per_block_3) {
-		i = inode_bmap(sb,inode,12);
+		i = inode_bmap(sb, inode, 12);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		i = block_bmap(sb, bh, block >> sb->sv_ind_per_block_2_bits, convert);
+			goto out;
+		i = block_bmap(sb,
+			       bread(inode->i_dev, i, sb->sv_block_size),
+			       (block >> sb->sv_ind_per_block_2_bits), convert);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		i = block_bmap(sb, bh, (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,convert);
+			goto out;
+		ret = block_bmap(sb,
+				 bread(inode->i_dev, i, sb->sv_block_size),
+				 ((block >> sb->sv_ind_per_block_bits) &
+				  sb->sv_ind_per_block_1), convert);
 		if (!i)
-			return 0;
-		bh = bread(inode->i_dev,i,sb->sv_block_size);
-		return block_bmap(sb, bh, block & sb->sv_ind_per_block_1, convert);
-	}
-	if ((int)block<0) {
-		printk("sysv_bmap: block<0");
-		return 0;
+			goto out;
+		ret = block_bmap(sb,
+				 bread(inode->i_dev, i, sb->sv_block_size),
+				 (block & sb->sv_ind_per_block_1), convert);
+		goto out;
 	}
-	printk("sysv_bmap: block>big");
-	return 0;
+	if ((int)block < 0)
+		printk("sysv_block_map: block < 0\n");
+	else
+		printk("sysv_block_map: block > big\n");
+out:
+	unlock_kernel();
+	return ret;
 }
 
 /* End of bmap support. */
@@ -657,8 +676,8 @@
 
 /* Access selected blocks of regular files (or directories) */
 
-static struct buffer_head * inode_getblk(struct inode * inode, int nr, int create,
-					 int metadata, int *phys_block, int *created)
+static struct buffer_head *inode_getblk(struct inode *inode, int nr, int new_block,
+	int *err, int metadata, long *phys, int *new)
 {
 	struct super_block *sb;
 	u32 tmp;
@@ -677,15 +696,30 @@
 			brelse(result);
 			goto repeat;
 		} else {
-			*phys_block = tmp;
+			*phys = tmp;
 			return NULL;
 		}
 	}
-	if (!create)
-		return NULL;
+	*err = -EFBIG;
+
+	/* Check file limits.. */
+	{
+		unsigned long limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+		if (limit < RLIM_INFINITY) {
+			limit >>= sb->sv_block_size_bits;
+			if (new_block >= limit) {
+				send_sig(SIGXFSZ, current, 0);
+				*err = -EFBIG;
+				return NULL;
+			}
+		}
+	}
+
 	tmp = sysv_new_block(sb);
-	if (!tmp)
+	if (!tmp) {
+		*err = -ENOSPC;
 		return NULL;
+	}
 	if (metadata) {
 		result = sv_getblk(sb, inode->i_dev, tmp);
 		if (*p) {
@@ -695,12 +729,18 @@
 		}
 	} else {
 		if (*p) {
+			/*
+			 * Nobody is allowed to change block allocation
+			 * state from under us:
+			 */
+			BUG();
 			sysv_free_block(sb, tmp);
 			goto repeat;
 		}
-		*phys_block = tmp;
+		*phys = tmp;
 		result = NULL;
-		*created = 1;
+		*err = 0;
+		*new = 1;
 	}
 	*p = tmp;
 
@@ -709,24 +749,24 @@
 	return result;
 }
 
-static struct buffer_head * block_getblk(struct inode * inode,
-	struct buffer_head * bh, int nr, int create,
-	int metadata, int *phys_block, int *created)
+static struct buffer_head *block_getblk(struct inode *inode,
+	struct buffer_head *bh, int nr, int new_block, int *err,
+	int metadata, long *phys, int *new)
 {
 	struct super_block *sb;
 	u32 tmp, block;
 	sysv_zone_t *p;
 	struct buffer_head * result;
+	unsigned long limit;
 
+	result = NULL;
 	if (!bh)
-		return NULL;
+		goto out;
 	if (!buffer_uptodate(bh)) {
 		ll_rw_block(READ, 1, &bh);
 		wait_on_buffer(bh);
-		if (!buffer_uptodate(bh)) {
-			brelse(bh);
-			return NULL;
-		}
+		if (!buffer_uptodate(bh))
+			goto out;
 	}
 	sb = inode->i_sb;
 	p = nr + (sysv_zone_t *) bh->b_data;
@@ -737,120 +777,175 @@
 	if (tmp) {
 		if (metadata) {
 			result = sv_getblk(sb, bh->b_dev, block);
-			if (tmp == *p) {
-				brelse(bh);
-				return result;
-			}
+			if (tmp == *p)
+				goto out;
 			brelse(result);
 			goto repeat;
 		} else {
-			*phys_block = tmp;
-			brelse(bh);
-			return NULL;
+			*phys = tmp;
+			goto out;
 		}
 	}
-	if (!create) {
-		brelse(bh);
-		return NULL;
+	*err = -EFBIG;
+
+	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	if (limit < RLIM_INFINITY) {
+		limit >>= sb->sv_block_size_bits;
+		if (new_block >= limit) {
+			send_sig(SIGXFSZ, current, 0);
+			goto out;
+		}
 	}
+
 	block = sysv_new_block(sb);
-	if (!block) {
-		brelse(bh);
-		return NULL;
-	}
+	if (!block)
+		goto out;
 	if (metadata) {
 		result = sv_getblk(sb, bh->b_dev, block);
 		if (*p) {
-			sysv_free_block(sb,block);
+			sysv_free_block(sb, block);
 			brelse(result);
 			goto repeat;
 		}
+		memset(result->b_data, 0, sb->sv_block_size);
+		mark_buffer_uptodate(result, 1);
+		mark_buffer_dirty(result, 1);
 	} else {
-		*phys_block = tmp;
-		result = NULL;
-		*created = 1;
+		*phys = tmp;
+		*new = 1;
+	}
+	if (*p) {
+		sysv_free_block(sb, block);
+		brelse(result);
+		goto repeat;
 	}
 	*p = (sb->sv_convert ? to_coh_ulong(block) : block);
 	mark_buffer_dirty(bh, 1);
+	*err = 0;
+out:
 	brelse(bh);
 	return result;
 }
 
-int sysv_getblk_block(struct inode *inode, long block, int create,
-		      int *err, int *created)
+int sysv_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
 {
-	struct super_block *sb = inode->i_sb;
-	struct buffer_head *bh, *tmp;
-	int phys_block;
-
-	*err = -EIO;
-	if (block < 0) {
-		printk("sysv_getblk: block<0");
-		return 0;
-	}
-	if (block > sb->sv_ind_per_block_3) {
-		printk("sysv_getblk: block>big");
+	struct super_block *sb;
+	int ret, err, new;
+	struct buffer_head *bh;
+	unsigned long ptr, phys;
+
+	if (!create) {
+		phys = sysv_block_map(inode, iblock);
+		if (phys) {
+			bh_result->b_dev = inode->i_dev;
+			bh_result->b_blocknr = phys;
+			bh_result->b_state |= (1UL << BH_Mapped);
+		}
 		return 0;
 	}
-	if (block < 10) {
-		tmp = inode_getblk(inode, block, create,
-				   0, &phys_block, created);
-		goto out;
-	}
-	block -= 10;
-	if (block < sb->sv_ind_per_block) {
-		bh = inode_getblk(inode, 10, create, 1, NULL, NULL);
-		tmp = block_getblk(inode, bh, block, create,
-				   0, &phys_block, created);
-		goto out;
-	}
-	block -= sb->sv_ind_per_block;
-	if (block < sb->sv_ind_per_block_2) {
-		bh = inode_getblk(inode, 11, create, 1, NULL, NULL);
-		bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_bits, create,
-				  1, NULL, NULL);
-		tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create,
-				   0, &phys_block, created);
+
+	err = -EIO;
+	new = 0;
+	ret = 0;
+	bh = NULL;
+
+	lock_kernel();
+	sb = inode->i_sb;
+	if (iblock < 0)
+		goto abort_negative;
+	if (iblock > sb->sv_ind_per_block_3)
+		goto abort_too_big;
+
+	err = 0;
+	ptr = iblock;
+
+	/*
+	 * ok, these macros clean the logic up a bit and make
+	 * it much more readable:
+	 */
+#define GET_INODE_DATABLOCK(x) \
+		inode_getblk(inode, x, iblock, &err, 0, &phys, &new)
+#define GET_INODE_PTR(x) \
+		inode_getblk(inode, x, iblock, &err, 1, NULL, NULL)
+#define GET_INDIRECT_DATABLOCK(x) \
+		block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new);
+#define GET_INDIRECT_PTR(x) \
+		block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL);
+
+	if (ptr < 10) {
+		bh = GET_INODE_DATABLOCK(ptr);
 		goto out;
 	}
-	block -= sb->sv_ind_per_block_2;
-	bh = inode_getblk(inode, 12, create, 1, NULL, NULL);
-	bh = block_getblk(inode, bh, block >> sb->sv_ind_per_block_2_bits, create,
-			  1, NULL, NULL);
-	bh = block_getblk(inode, bh,
-			  (block >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1,
-			  create, 1, NULL, NULL);
-	tmp = block_getblk(inode, bh, block & sb->sv_ind_per_block_1, create,
-			   0, &phys_block, created);
+	ptr -= 10;
+	if (ptr < sb->sv_ind_per_block) {
+		bh = GET_INODE_PTR(10);
+		goto get_indirect;
+	}
+	ptr -= sb->sv_ind_per_block;
+	if (ptr < sb->sv_ind_per_block_2) {
+		bh = GET_INODE_PTR(11);
+		goto get_double;
+	}
+	ptr -= sb->sv_ind_per_block_2;
+	bh = GET_INODE_PTR(12);
+	bh = GET_INDIRECT_PTR(ptr >> sb->sv_ind_per_block_2_bits);
+get_double:
+	bh = GET_INDIRECT_PTR((ptr >> sb->sv_ind_per_block_bits) & sb->sv_ind_per_block_1);
+get_indirect:
+	bh = GET_INDIRECT_DATABLOCK(ptr & sb->sv_ind_per_block_1);
+
+#undef GET_INODE_DATABLOCK
+#undef GET_INODE_PTR
+#undef GET_INDIRECT_DATABLOCK
+#undef GET_INDIRECT_PTR
 
 out:
-	*err = 0;
-	return phys_block;
-}
-
-struct buffer_head *sysv_getblk (struct inode *inode, unsigned int block, int create)
-{
-	struct buffer_head *tmp = NULL;
-	int phys_block;
-	int err, created;
-
-	phys_block = sysv_getblk_block(inode, block, create, &err, &created);
-	if (phys_block) {
-		tmp = getblk(inode->i_dev, phys_block, BLOCK_SIZE);
-		if (created) {
-			memset(tmp->b_data, 0, BLOCK_SIZE);
-			mark_buffer_uptodate(tmp, 1);
-			mark_buffer_dirty(tmp, 1);
+	if (err)
+		goto abort;
+	bh_result->b_dev = inode->i_dev;
+	bh_result->b_blocknr = phys;
+	bh_result->b_state |= (1UL << BH_Mapped);
+	if (new)
+		bh_result->b_state |= (1UL << BH_New);
+abort:
+	unlock_kernel();
+	return err;
+
+abort_negative:
+	printk("sysv_getblk: block < 0\n");
+	goto abort;
+
+abort_too_big:
+	printk("sysv_getblk: block > big\n");
+	goto abort;
+}
+
+struct buffer_head *sysv_getblk(struct inode *inode, unsigned int block, int create)
+{
+	struct buffer_head dummy;
+	int error;
+
+	dummy.b_state = 0;
+	dummy.b_blocknr = -1000;
+	error = sysv_get_block(inode, block, &dummy, create);
+	if (!error && buffer_mapped(&dummy)) {
+		struct buffer_head *bh;
+		bh = getblk(dummy.b_dev, dummy.b_blocknr, BLOCK_SIZE);
+		if (buffer_new(&dummy)) {
+			memset(bh->b_data, 0, BLOCK_SIZE);
+			mark_buffer_uptodate(bh, 1);
+			mark_buffer_dirty(bh, 1);
 		}
+		return bh;
 	}
-	return tmp;
+	return NULL;
 }
 
-struct buffer_head * sysv_file_bread(struct inode * inode, int block, int create)
+struct buffer_head *sysv_file_bread(struct inode *inode, int block, int create)
 {
-	struct buffer_head * bh;
+	struct buffer_head *bh;
 
-	bh = sysv_getblk(inode,block,create);
+	bh = sysv_getblk(inode, block, create);
 	if (!bh || buffer_uptodate(bh))
 		return bh;
 	ll_rw_block(READ, 1, &bh);
@@ -903,7 +998,7 @@
 	*(unsigned short *)(p+1) = (unsigned short) val;
 }
 
-void sysv_read_inode(struct inode * inode)
+static void sysv_read_inode(struct inode *inode)
 {
 	struct super_block * sb = inode->i_sb;
 	struct buffer_head * bh;
@@ -971,7 +1066,7 @@
 }
 
 /* To avoid inconsistencies between inodes in memory and inodes on disk. */
-int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
+static int sysv_notify_change(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
 	int error;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)