|
@@ -539,9 +539,18 @@ static int udf_do_extend_file(struct inode *inode,
|
|
|
udf_add_aext(inode, last_pos, &last_ext->extLocation,
|
|
|
last_ext->extLength, 1);
|
|
|
count++;
|
|
|
- } else
|
|
|
+ } else {
|
|
|
+ struct kernel_lb_addr tmploc;
|
|
|
+ uint32_t tmplen;
|
|
|
+
|
|
|
udf_write_aext(inode, last_pos, &last_ext->extLocation,
|
|
|
last_ext->extLength, 1);
|
|
|
+ /*
|
|
|
+ * We've rewritten the last extent but there may be empty
|
|
|
+ * indirect extent after it - enter it.
|
|
|
+ */
|
|
|
+ udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
|
|
|
+ }
|
|
|
|
|
|
/* Managed to do everything necessary? */
|
|
|
if (!blocks)
|
|
@@ -1867,22 +1876,90 @@ struct inode *__udf_iget(struct super_block *sb, struct kernel_lb_addr *ino,
|
|
|
return inode;
|
|
|
}
|
|
|
|
|
|
-int udf_add_aext(struct inode *inode, struct extent_position *epos,
|
|
|
- struct kernel_lb_addr *eloc, uint32_t elen, int inc)
|
|
|
+int udf_setup_indirect_aext(struct inode *inode, int block,
|
|
|
+ struct extent_position *epos)
|
|
|
{
|
|
|
- int adsize;
|
|
|
- struct short_ad *sad = NULL;
|
|
|
- struct long_ad *lad = NULL;
|
|
|
+ struct super_block *sb = inode->i_sb;
|
|
|
+ struct buffer_head *bh;
|
|
|
struct allocExtDesc *aed;
|
|
|
- uint8_t *ptr;
|
|
|
- struct udf_inode_info *iinfo = UDF_I(inode);
|
|
|
+ struct extent_position nepos;
|
|
|
+ struct kernel_lb_addr neloc;
|
|
|
+ int ver, adsize;
|
|
|
|
|
|
- if (!epos->bh)
|
|
|
- ptr = iinfo->i_ext.i_data + epos->offset -
|
|
|
- udf_file_entry_alloc_offset(inode) +
|
|
|
- iinfo->i_lenEAttr;
|
|
|
+ if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
|
|
+ adsize = sizeof(struct short_ad);
|
|
|
+ else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
|
|
|
+ adsize = sizeof(struct long_ad);
|
|
|
else
|
|
|
- ptr = epos->bh->b_data + epos->offset;
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ neloc.logicalBlockNum = block;
|
|
|
+ neloc.partitionReferenceNum = epos->block.partitionReferenceNum;
|
|
|
+
|
|
|
+ bh = udf_tgetblk(sb, udf_get_lb_pblock(sb, &neloc, 0));
|
|
|
+ if (!bh)
|
|
|
+ return -EIO;
|
|
|
+ lock_buffer(bh);
|
|
|
+ memset(bh->b_data, 0x00, sb->s_blocksize);
|
|
|
+ set_buffer_uptodate(bh);
|
|
|
+ unlock_buffer(bh);
|
|
|
+ mark_buffer_dirty_inode(bh, inode);
|
|
|
+
|
|
|
+ aed = (struct allocExtDesc *)(bh->b_data);
|
|
|
+ if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT)) {
|
|
|
+ aed->previousAllocExtLocation =
|
|
|
+ cpu_to_le32(epos->block.logicalBlockNum);
|
|
|
+ }
|
|
|
+ aed->lengthAllocDescs = cpu_to_le32(0);
|
|
|
+ if (UDF_SB(sb)->s_udfrev >= 0x0200)
|
|
|
+ ver = 3;
|
|
|
+ else
|
|
|
+ ver = 2;
|
|
|
+ udf_new_tag(bh->b_data, TAG_IDENT_AED, ver, 1, block,
|
|
|
+ sizeof(struct tag));
|
|
|
+
|
|
|
+ nepos.block = neloc;
|
|
|
+ nepos.offset = sizeof(struct allocExtDesc);
|
|
|
+ nepos.bh = bh;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do we have to copy current last extent to make space for indirect
|
|
|
+ * one?
|
|
|
+ */
|
|
|
+ if (epos->offset + adsize > sb->s_blocksize) {
|
|
|
+ struct kernel_lb_addr cp_loc;
|
|
|
+ uint32_t cp_len;
|
|
|
+ int cp_type;
|
|
|
+
|
|
|
+ epos->offset -= adsize;
|
|
|
+ cp_type = udf_current_aext(inode, epos, &cp_loc, &cp_len, 0);
|
|
|
+ cp_len |= ((uint32_t)cp_type) << 30;
|
|
|
+
|
|
|
+ __udf_add_aext(inode, &nepos, &cp_loc, cp_len, 1);
|
|
|
+ udf_write_aext(inode, epos, &nepos.block,
|
|
|
+ sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
|
|
|
+ } else {
|
|
|
+ __udf_add_aext(inode, epos, &nepos.block,
|
|
|
+ sb->s_blocksize | EXT_NEXT_EXTENT_ALLOCDECS, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ brelse(epos->bh);
|
|
|
+ *epos = nepos;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Append extent at the given position - should be the first free one in inode
|
|
|
+ * / indirect extent. This function assumes there is enough space in the inode
|
|
|
+ * or indirect extent. Use udf_add_aext() if you didn't check for this before.
|
|
|
+ */
|
|
|
+int __udf_add_aext(struct inode *inode, struct extent_position *epos,
|
|
|
+ struct kernel_lb_addr *eloc, uint32_t elen, int inc)
|
|
|
+{
|
|
|
+ struct udf_inode_info *iinfo = UDF_I(inode);
|
|
|
+ struct allocExtDesc *aed;
|
|
|
+ int adsize;
|
|
|
|
|
|
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
|
|
adsize = sizeof(struct short_ad);
|
|
@@ -1891,88 +1968,14 @@ int udf_add_aext(struct inode *inode, struct extent_position *epos,
|
|
|
else
|
|
|
return -EIO;
|
|
|
|
|
|
- if (epos->offset + (2 * adsize) > inode->i_sb->s_blocksize) {
|
|
|
- unsigned char *sptr, *dptr;
|
|
|
- struct buffer_head *nbh;
|
|
|
- int err, loffset;
|
|
|
- struct kernel_lb_addr obloc = epos->block;
|
|
|
-
|
|
|
- epos->block.logicalBlockNum = udf_new_block(inode->i_sb, NULL,
|
|
|
- obloc.partitionReferenceNum,
|
|
|
- obloc.logicalBlockNum, &err);
|
|
|
- if (!epos->block.logicalBlockNum)
|
|
|
- return -ENOSPC;
|
|
|
- nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb,
|
|
|
- &epos->block,
|
|
|
- 0));
|
|
|
- if (!nbh)
|
|
|
- return -EIO;
|
|
|
- lock_buffer(nbh);
|
|
|
- memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize);
|
|
|
- set_buffer_uptodate(nbh);
|
|
|
- unlock_buffer(nbh);
|
|
|
- mark_buffer_dirty_inode(nbh, inode);
|
|
|
-
|
|
|
- aed = (struct allocExtDesc *)(nbh->b_data);
|
|
|
- if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
|
|
|
- aed->previousAllocExtLocation =
|
|
|
- cpu_to_le32(obloc.logicalBlockNum);
|
|
|
- if (epos->offset + adsize > inode->i_sb->s_blocksize) {
|
|
|
- loffset = epos->offset;
|
|
|
- aed->lengthAllocDescs = cpu_to_le32(adsize);
|
|
|
- sptr = ptr - adsize;
|
|
|
- dptr = nbh->b_data + sizeof(struct allocExtDesc);
|
|
|
- memcpy(dptr, sptr, adsize);
|
|
|
- epos->offset = sizeof(struct allocExtDesc) + adsize;
|
|
|
- } else {
|
|
|
- loffset = epos->offset + adsize;
|
|
|
- aed->lengthAllocDescs = cpu_to_le32(0);
|
|
|
- sptr = ptr;
|
|
|
- epos->offset = sizeof(struct allocExtDesc);
|
|
|
-
|
|
|
- if (epos->bh) {
|
|
|
- aed = (struct allocExtDesc *)epos->bh->b_data;
|
|
|
- le32_add_cpu(&aed->lengthAllocDescs, adsize);
|
|
|
- } else {
|
|
|
- iinfo->i_lenAlloc += adsize;
|
|
|
- mark_inode_dirty(inode);
|
|
|
- }
|
|
|
- }
|
|
|
- if (UDF_SB(inode->i_sb)->s_udfrev >= 0x0200)
|
|
|
- udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
|
|
|
- epos->block.logicalBlockNum, sizeof(struct tag));
|
|
|
- else
|
|
|
- udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
|
|
|
- epos->block.logicalBlockNum, sizeof(struct tag));
|
|
|
- switch (iinfo->i_alloc_type) {
|
|
|
- case ICBTAG_FLAG_AD_SHORT:
|
|
|
- sad = (struct short_ad *)sptr;
|
|
|
- sad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS |
|
|
|
- inode->i_sb->s_blocksize);
|
|
|
- sad->extPosition =
|
|
|
- cpu_to_le32(epos->block.logicalBlockNum);
|
|
|
- break;
|
|
|
- case ICBTAG_FLAG_AD_LONG:
|
|
|
- lad = (struct long_ad *)sptr;
|
|
|
- lad->extLength = cpu_to_le32(EXT_NEXT_EXTENT_ALLOCDECS |
|
|
|
- inode->i_sb->s_blocksize);
|
|
|
- lad->extLocation = cpu_to_lelb(epos->block);
|
|
|
- memset(lad->impUse, 0x00, sizeof(lad->impUse));
|
|
|
- break;
|
|
|
- }
|
|
|
- if (epos->bh) {
|
|
|
- if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
|
|
|
- UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
|
|
|
- udf_update_tag(epos->bh->b_data, loffset);
|
|
|
- else
|
|
|
- udf_update_tag(epos->bh->b_data,
|
|
|
- sizeof(struct allocExtDesc));
|
|
|
- mark_buffer_dirty_inode(epos->bh, inode);
|
|
|
- brelse(epos->bh);
|
|
|
- } else {
|
|
|
- mark_inode_dirty(inode);
|
|
|
- }
|
|
|
- epos->bh = nbh;
|
|
|
+ if (!epos->bh) {
|
|
|
+ WARN_ON(iinfo->i_lenAlloc !=
|
|
|
+ epos->offset - udf_file_entry_alloc_offset(inode));
|
|
|
+ } else {
|
|
|
+ aed = (struct allocExtDesc *)epos->bh->b_data;
|
|
|
+ WARN_ON(le32_to_cpu(aed->lengthAllocDescs) !=
|
|
|
+ epos->offset - sizeof(struct allocExtDesc));
|
|
|
+ WARN_ON(epos->offset + adsize > inode->i_sb->s_blocksize);
|
|
|
}
|
|
|
|
|
|
udf_write_aext(inode, epos, eloc, elen, inc);
|
|
@@ -1996,6 +1999,41 @@ int udf_add_aext(struct inode *inode, struct extent_position *epos,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Append extent at given position - should be the first free one in inode
|
|
|
+ * / indirect extent. Takes care of allocating and linking indirect blocks.
|
|
|
+ */
|
|
|
+int udf_add_aext(struct inode *inode, struct extent_position *epos,
|
|
|
+ struct kernel_lb_addr *eloc, uint32_t elen, int inc)
|
|
|
+{
|
|
|
+ int adsize;
|
|
|
+ struct super_block *sb = inode->i_sb;
|
|
|
+
|
|
|
+ if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
|
|
|
+ adsize = sizeof(struct short_ad);
|
|
|
+ else if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_LONG)
|
|
|
+ adsize = sizeof(struct long_ad);
|
|
|
+ else
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ if (epos->offset + (2 * adsize) > sb->s_blocksize) {
|
|
|
+ int err;
|
|
|
+ int new_block;
|
|
|
+
|
|
|
+ new_block = udf_new_block(sb, NULL,
|
|
|
+ epos->block.partitionReferenceNum,
|
|
|
+ epos->block.logicalBlockNum, &err);
|
|
|
+ if (!new_block)
|
|
|
+ return -ENOSPC;
|
|
|
+
|
|
|
+ err = udf_setup_indirect_aext(inode, new_block, epos);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __udf_add_aext(inode, epos, eloc, elen, inc);
|
|
|
+}
|
|
|
+
|
|
|
void udf_write_aext(struct inode *inode, struct extent_position *epos,
|
|
|
struct kernel_lb_addr *eloc, uint32_t elen, int inc)
|
|
|
{
|
|
@@ -2048,14 +2086,29 @@ void udf_write_aext(struct inode *inode, struct extent_position *epos,
|
|
|
epos->offset += adsize;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Only 1 indirect extent in a row really makes sense but allow upto 16 in case
|
|
|
+ * someone does some weird stuff.
|
|
|
+ */
|
|
|
+#define UDF_MAX_INDIR_EXTS 16
|
|
|
+
|
|
|
int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
|
|
|
struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
|
|
|
{
|
|
|
int8_t etype;
|
|
|
+ unsigned int indirections = 0;
|
|
|
|
|
|
while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
|
|
|
(EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
|
|
|
int block;
|
|
|
+
|
|
|
+ if (++indirections > UDF_MAX_INDIR_EXTS) {
|
|
|
+ udf_err(inode->i_sb,
|
|
|
+ "too many indirect extents in inode %lu\n",
|
|
|
+ inode->i_ino);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
epos->block = *eloc;
|
|
|
epos->offset = sizeof(struct allocExtDesc);
|
|
|
brelse(epos->bh);
|