From 31170b6ad4ebe6c43c1cc3b8112274cf59474de0 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 8 May 2007 00:35:21 -0700 Subject: [PATCH] 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 Acked-by: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/udf/inode.c | 192 ++++++++++++++++++++++++++++++++++++++++++++---------- fs/udf/super.c | 2 +- fs/udf/truncate.c | 68 +++++++------------ 3 files changed, 180 insertions(+), 82 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 2171bcfeec20..c8461551e108 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 9f516d4baf9d..a92daaba6c4c 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -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: diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 0e328cdc9a45..77975ae291a5 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; -- 2.11.4.GIT