Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1197

kernel-2.6.18-238.el5.src.rpm

From: Eric Sandeen <sandeen@redhat.com>
Date: Mon, 8 Oct 2007 15:50:34 -0500
Subject: [fs] udf: support files larger than 1G
Message-id: 470A981A.60203@redhat.com
O-Subject: [RHEL5 PATCH 5/7] udf: support files larger than 1G
Bugzilla: 221282

From: Jan Kara <jack@suse.cz>
Date: Tue, 8 May 2007 07:35:21 +0000 (-0700)
Subject: udf: support files larger than 1G
X-Git-Tag: v2.6.22-rc1~520
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=31170b6ad4ebe6c43c1cc3b8112274cf59474de0

udf: support files larger than 1G

Make UDF work correctly for files larger than 1GB.  As no extent can be
longer than (1<<30)-blocksize bytes, we have to create several extents if a
big hole is being created.  As a side-effect, we now don't discard
preallocated blocks when creating a hole.

Signed-off-by: Jan Kara <jack@suse.cz>
Acked-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Acked-by: "Stephen C. Tweedie" <sct@redhat.com>
Acked-by: Josef Bacik <jbacik@redhat.com>

diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index f02a301..eeca953 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -356,9 +356,106 @@ udf_getblk(struct inode *inode, long block, int create, int *err)
 	return NULL;
 }
 
+/* Extend the file by 'blocks' blocks, return the number of extents added */
+int udf_extend_file(struct inode *inode, struct extent_position *last_pos,
+	kernel_long_ad *last_ext, sector_t blocks)
+{
+	sector_t add;
+	int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+	struct super_block *sb = inode->i_sb;
+	kernel_lb_addr prealloc_loc = {0, 0};
+	int prealloc_len = 0;
+
+	/* The previous extent is fake and we should not extend by anything
+	 * - there's nothing to do... */
+	if (!blocks && fake)
+		return 0;
+	/* Round the last extent up to a multiple of block size */
+	if (last_ext->extLength & (sb->s_blocksize - 1)) {
+		last_ext->extLength =
+			(last_ext->extLength & UDF_EXTENT_FLAG_MASK) |
+			(((last_ext->extLength & UDF_EXTENT_LENGTH_MASK) +
+				sb->s_blocksize - 1) & ~(sb->s_blocksize - 1));
+		UDF_I_LENEXTENTS(inode) =
+			(UDF_I_LENEXTENTS(inode) + sb->s_blocksize - 1) &
+				~(sb->s_blocksize - 1);
+	}
+	/* Last extent are just preallocated blocks? */
+	if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_ALLOCATED) {
+		/* Save the extent so that we can reattach it to the end */
+		prealloc_loc = last_ext->extLocation;
+		prealloc_len = last_ext->extLength;
+		/* Mark the extent as a hole */
+		last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+			(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+		last_ext->extLocation.logicalBlockNum = 0;
+       		last_ext->extLocation.partitionReferenceNum = 0;
+	}
+	/* Can we merge with the previous extent? */
+	if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED) {
+		add = ((1<<30) - sb->s_blocksize - (last_ext->extLength &
+			UDF_EXTENT_LENGTH_MASK)) >> sb->s_blocksize_bits;
+		if (add > blocks)
+			add = blocks;
+		blocks -= add;
+		last_ext->extLength += add << sb->s_blocksize_bits;
+	}
+
+	if (fake) {
+		udf_add_aext(inode, last_pos, last_ext->extLocation,
+			last_ext->extLength, 1);
+		count++;
+	}
+	else
+		udf_write_aext(inode, last_pos, last_ext->extLocation, last_ext->extLength, 1);
+	/* Managed to do everything necessary? */
+	if (!blocks)
+		goto out;
+
+	/* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
+	last_ext->extLocation.logicalBlockNum = 0;
+       	last_ext->extLocation.partitionReferenceNum = 0;
+	add = (1 << (30-sb->s_blocksize_bits)) - 1;
+	last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | (add << sb->s_blocksize_bits);
+	/* Create enough extents to cover the whole hole */
+	while (blocks > add) {
+		blocks -= add;
+		if (udf_add_aext(inode, last_pos, last_ext->extLocation,
+			last_ext->extLength, 1) == -1)
+			return -1;
+		count++;
+	}
+	if (blocks) {
+		last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+			(blocks << sb->s_blocksize_bits);
+		if (udf_add_aext(inode, last_pos, last_ext->extLocation,
+			last_ext->extLength, 1) == -1)
+			return -1;
+		count++;
+	}
+out:
+	/* Do we have some preallocated blocks saved? */
+	if (prealloc_len) {
+		if (udf_add_aext(inode, last_pos, prealloc_loc, prealloc_len, 1) == -1)
+			return -1;
+		last_ext->extLocation = prealloc_loc;
+		last_ext->extLength = prealloc_len;
+		count++;
+	}
+	/* last_pos should point to the last written extent... */
+	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+		last_pos->offset -= sizeof(short_ad);
+	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+		last_pos->offset -= sizeof(long_ad);
+	else
+		return -1;
+	return count;
+}
+
 static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 	int *err, long *phys, int *new)
 {
+	static sector_t last_block;
 	struct buffer_head *result = NULL;
 	kernel_long_ad laarr[EXTENT_MERGE_SIZE];
 	struct extent_position prev_epos, cur_epos, next_epos;
@@ -371,7 +468,7 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 	sector_t offset = 0;
 	int8_t etype;
 	int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum;
-	char lastblock = 0;
+	int lastblock = 0;
 
 	prev_epos.offset = udf_file_entry_alloc_offset(inode);
 	prev_epos.block = UDF_I_LOCATION(inode);
@@ -423,6 +520,8 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 
 	b_off -= lbcount;
 	offset = b_off >> inode->i_sb->s_blocksize_bits;
+	/* Move into indirect extent if we are at a pointer to it */
+	udf_next_aext(inode, &prev_epos, &eloc, &elen, 0);
 
 	/* if the extent is allocated and recorded, return the block
        if the extent is not a multiple of the blocksize, round up */
@@ -444,43 +543,66 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 		return NULL;
 	}
 
+	last_block = block;
+	/* Are we beyond EOF? */
 	if (etype == -1)
 	{
-		endnum = startnum = ((count > 1) ? 1 : count);
-		if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1))
-		{
-			laarr[c].extLength =
-				(laarr[c].extLength & UDF_EXTENT_FLAG_MASK) |
-				(((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) +
-					inode->i_sb->s_blocksize - 1) &
-				~(inode->i_sb->s_blocksize - 1));
-			UDF_I_LENEXTENTS(inode) =
-				(UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) &
-					~(inode->i_sb->s_blocksize - 1);
+		int ret;
+
+		if (count) {
+			if (c)
+				laarr[0] = laarr[1];
+			startnum = 1;
+		}
+		else {
+			/* Create a fake extent when there's not one */
+			memset(&laarr[0].extLocation, 0x00, sizeof(kernel_lb_addr));
+			laarr[0].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
+			/* Will udf_extend_file() create real extent from a fake one? */
+			startnum = (offset > 0);
+		}
+		/* Create extents for the hole between EOF and offset */
+		ret = udf_extend_file(inode, &prev_epos, laarr, offset);
+		if (ret == -1) {
+			brelse(prev_epos.bh);
+			brelse(cur_epos.bh);
+			brelse(next_epos.bh);
+			/* We don't really know the error here so we just make
+			 * something up */
+			*err = -ENOSPC;
+			return NULL;
 		}
-		c = !c;
-		laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-			((offset + 1) << inode->i_sb->s_blocksize_bits);
-		memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr));
-		count ++;
-		endnum ++;
+		c = 0;
+		offset = 0;
+		count += ret;
+		/* We are not covered by a preallocated extent? */
+		if ((laarr[0].extLength & UDF_EXTENT_FLAG_MASK) != EXT_NOT_RECORDED_ALLOCATED) {
+			/* Is there any real extent? - otherwise we overwrite
+			 * the fake one... */
+			if (count)
+				c = !c;
+			laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+				inode->i_sb->s_blocksize;
+			memset(&laarr[c].extLocation, 0x00, sizeof(kernel_lb_addr));
+			count ++;
+			endnum ++;
+		}
+		endnum = c+1;
 		lastblock = 1;
 	}
-	else
+	else {
 		endnum = startnum = ((count > 2) ? 2 : count);
 
-	/* if the current extent is in position 0, swap it with the previous */
-	if (!c && count != 1)
-	{
-		laarr[2] = laarr[0];
-		laarr[0] = laarr[1];
-		laarr[1] = laarr[2];
-		c = 1;
-	}
+		/* if the current extent is in position 0, swap it with the previous */
+		if (!c && count != 1)
+		{
+			laarr[2] = laarr[0];
+			laarr[0] = laarr[1];
+			laarr[1] = laarr[2];
+			c = 1;
+		}
 
-	/* if the current block is located in a extent, read the next extent */
-	if (etype != -1)
-	{
+		/* if the current block is located in an extent, read the next extent */
 		if ((etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 0)) != -1)
 		{
 			laarr[c+1].extLength = (etype << 30) | elen;
@@ -489,11 +611,10 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 			startnum ++;
 			endnum ++;
 		}
-		else
+		else {
 			lastblock = 1;
+		}
 	}
-	brelse(cur_epos.bh);
-	brelse(next_epos.bh);
 
 	/* if the current extent is not recorded but allocated, get the
 		block in the extent corresponding to the requested block */
@@ -534,8 +655,8 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
 	udf_merge_extents(inode, laarr, &endnum);
 
 	/* write back the new extents, inserting new extents if the new number
-       of extents is greater than the old number, and deleting extents if
-       the new number of extents is less than the old number */
+	of extents is greater than the old number, and deleting extents if
+	the new number of extents is less than the old number */
 	udf_update_extents(inode, laarr, startnum, endnum, &prev_epos);
 
 	brelse(prev_epos.bh);
@@ -991,6 +1112,7 @@ __udf_read_inode(struct inode *inode)
 		return;
 	}
 	udf_fill_inode(inode, bh);
+
 	brelse(bh);
 }
 
diff --git a/fs/udf/super.c b/fs/udf/super.c
index eddd9c9..6f84480 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1659,7 +1659,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
 		iput(inode);
 		goto error_out;
 	}
-	sb->s_maxbytes = 1<<30;
+	sb->s_maxbytes = MAX_LFS_FILESIZE;
 	return 0;
 
 error_out:
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
index 0e328cd..77975ae 100644
--- a/fs/udf/truncate.c
+++ b/fs/udf/truncate.c
@@ -130,7 +130,8 @@ void udf_truncate_extents(struct inode * inode)
 	kernel_lb_addr eloc, neloc = { 0, 0 };
 	uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
 	int8_t etype;
-	sector_t first_block = inode->i_size >> inode->i_sb->s_blocksize_bits, offset;
+	struct super_block *sb = inode->i_sb;
+	sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
 	loff_t byte_offset;
 	int adsize;
 
@@ -142,7 +143,7 @@ void udf_truncate_extents(struct inode * inode)
 		BUG();
 
 	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
-	byte_offset = (offset << inode->i_sb->s_blocksize_bits) + (inode->i_size & (inode->i_sb->s_blocksize-1));
+	byte_offset = (offset << sb->s_blocksize_bits) + (inode->i_size & (sb->s_blocksize-1));
 	if (etype != -1)
 	{
 		epos.offset -= adsize;
@@ -169,7 +170,7 @@ void udf_truncate_extents(struct inode * inode)
 					 * indirect extent - free it too */
 					if (!epos.bh)
 						BUG();
-					udf_free_blocks(inode->i_sb, inode, epos.block, 0, indirect_ext_len);
+					udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len);
 				}
 				else
 				{
@@ -182,7 +183,7 @@ void udf_truncate_extents(struct inode * inode)
 					{
 						struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
 						aed->lengthAllocDescs = cpu_to_le32(lenalloc);
-						if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+						if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201)
 							udf_update_tag(epos.bh->b_data, lenalloc +
 								sizeof(struct allocExtDesc));
 						else
@@ -193,11 +194,11 @@ void udf_truncate_extents(struct inode * inode)
 				brelse(epos.bh);
 				epos.offset = sizeof(struct allocExtDesc);
 				epos.block = eloc;
-				epos.bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, eloc, 0));
+				epos.bh = udf_tread(sb, udf_get_lb_pblock(sb, eloc, 0));
 				if (elen)
 					indirect_ext_len = (elen +
-						inode->i_sb->s_blocksize - 1) >>
-						inode->i_sb->s_blocksize_bits;
+						sb->s_blocksize - 1) >>
+						sb->s_blocksize_bits;
 				else
 					indirect_ext_len = 1;
 			}
@@ -212,7 +213,7 @@ void udf_truncate_extents(struct inode * inode)
 		{
 			if (!epos.bh)
 				BUG();
-			udf_free_blocks(inode->i_sb, inode, epos.block, 0, indirect_ext_len);
+			udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len);
 		}
 		else
 		{
@@ -225,7 +226,7 @@ void udf_truncate_extents(struct inode * inode)
 			{
 				struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
 				aed->lengthAllocDescs = cpu_to_le32(lenalloc);
-				if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+				if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201)
 					udf_update_tag(epos.bh->b_data, lenalloc +
 						sizeof(struct allocExtDesc));
 				else
@@ -238,53 +239,28 @@ void udf_truncate_extents(struct inode * inode)
 	{
 		if (byte_offset)
 		{
+			kernel_long_ad extent;
+
 			/*
 			 *  OK, there is not extent covering inode->i_size and
 			 *  no extent above inode->i_size => truncate is
-			 *  extending the file by 'offset'.
+			 *  extending the file by 'offset' blocks.
 			 */
 			if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
 			    (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
-				/* File has no extents at all! */
-				memset(&eloc, 0x00, sizeof(kernel_lb_addr));
-				elen = EXT_NOT_RECORDED_NOT_ALLOCATED | byte_offset;
-				udf_add_aext(inode, &epos, eloc, elen, 1);
+				/* File has no extents at all or has empty last
+				 * indirect extent! Create a fake extent... */
+				extent.extLocation.logicalBlockNum = 0;
+				extent.extLocation.partitionReferenceNum = 0;
+				extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
 			}
 			else {
 				epos.offset -= adsize;
-				etype = udf_next_aext(inode, &epos, &eloc, &elen, 1);
-
-				if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
-				{
-					epos.offset -= adsize;
-					elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + byte_offset);
-					udf_write_aext(inode, &epos, eloc, elen, 0);
-				}
-				else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
-				{
-					kernel_lb_addr neloc = { 0, 0 };
-					epos.offset -= adsize;
-					nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
-						((elen + byte_offset + inode->i_sb->s_blocksize - 1) &
-						~(inode->i_sb->s_blocksize - 1));
-					udf_write_aext(inode, &epos, neloc, nelen, 1);
-					udf_add_aext(inode, &epos, eloc, (etype << 30) | elen, 1);
-				}
-				else
-				{
-					if (elen & (inode->i_sb->s_blocksize - 1))
-					{
-						epos.offset -= adsize;
-						elen = EXT_RECORDED_ALLOCATED |
-							((elen + inode->i_sb->s_blocksize - 1) &
-							~(inode->i_sb->s_blocksize - 1));
-						udf_write_aext(inode, &epos, eloc, elen, 1);
-					}
-					memset(&eloc, 0x00, sizeof(kernel_lb_addr));
-					elen = EXT_NOT_RECORDED_NOT_ALLOCATED | byte_offset;
-					udf_add_aext(inode, &epos, eloc, elen, 1);
-				}
+				etype = udf_next_aext(inode, &epos,
+					&extent.extLocation, &extent.extLength, 0);
+				extent.extLength |= etype << 30;
 			}
+			udf_extend_file(inode, &epos, &extent, offset+((inode->i_size & (sb->s_blocksize-1)) != 0));
 		}
 	}
 	UDF_I_LENEXTENTS(inode) = inode->i_size;