Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31170b6a authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds
Browse files

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: default avatarJan Kara <jack@suse.cz>
Acked-by: default avatarChristoph Hellwig <hch@infradead.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 948b9b2c
Loading
Loading
Loading
Loading
+157 −35
Original line number Diff line number Diff line
@@ -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,29 +543,54 @@ 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 = 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 |
			((offset + 1) << inode->i_sb->s_blocksize_bits);
				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 */
@@ -478,9 +602,7 @@ static struct buffer_head * inode_getblk(struct inode * inode, sector_t block,
			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 */
@@ -991,6 +1112,7 @@ __udf_read_inode(struct inode *inode)
		return;
	}
	udf_fill_inode(inode, bh);

	brelse(bh);
}

+1 −1
Original line number Diff line number Diff line
@@ -1661,7 +1661,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:
+22 −46
Original line number Diff line number Diff line
@@ -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;