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>
This commit is contained in:
Jan Kara 2007-05-08 00:35:21 -07:00 committed by Linus Torvalds
parent 948b9b2c96
commit 31170b6ad4
3 changed files with 180 additions and 82 deletions

View File

@ -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;
}
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 ++;
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 |
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);
}

View File

@ -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:

View File

@ -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;