diff --git a/fs/udf/file.c b/fs/udf/file.c index b80cbd78833..f311d509b6a 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -196,6 +196,7 @@ static int udf_release_file(struct inode *inode, struct file *filp) mutex_lock(&inode->i_mutex); lock_kernel(); udf_discard_prealloc(inode); + udf_truncate_tail_extent(inode); unlock_kernel(); mutex_unlock(&inode->i_mutex); } diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 6d24c2c63f9..f90231eb291 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -97,15 +97,17 @@ no_delete: */ void udf_clear_inode(struct inode *inode) { - struct udf_inode_info *iinfo; - if (!(inode->i_sb->s_flags & MS_RDONLY)) { - lock_kernel(); - udf_truncate_tail_extent(inode); - unlock_kernel(); - write_inode_now(inode, 0); - invalidate_inode_buffers(inode); + struct udf_inode_info *iinfo = UDF_I(inode); + + if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && + inode->i_size != iinfo->i_lenExtents) { + printk(KERN_WARNING "UDF-fs (%s): Inode %lu (mode %o) has " + "inode size %llu different from extent lenght %llu. " + "Filesystem need not be standards compliant.\n", + inode->i_sb->s_id, inode->i_ino, inode->i_mode, + (unsigned long long)inode->i_size, + (unsigned long long)iinfo->i_lenExtents); } - iinfo = UDF_I(inode); kfree(iinfo->i_ext.i_data); iinfo->i_ext.i_data = NULL; } @@ -198,7 +200,6 @@ struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block, int newblock; struct buffer_head *dbh = NULL; struct kernel_lb_addr eloc; - uint32_t elen; uint8_t alloctype; struct extent_position epos; @@ -273,12 +274,11 @@ struct buffer_head *udf_expand_dir_adinicb(struct inode *inode, int *block, eloc.logicalBlockNum = *block; eloc.partitionReferenceNum = iinfo->i_location.partitionReferenceNum; - elen = inode->i_sb->s_blocksize; - iinfo->i_lenExtents = elen; + iinfo->i_lenExtents = inode->i_size; epos.bh = NULL; epos.block = iinfo->i_location; epos.offset = udf_file_entry_alloc_offset(inode); - udf_add_aext(inode, &epos, &eloc, elen, 0); + udf_add_aext(inode, &epos, &eloc, inode->i_size, 0); /* UniqueID stuff */ brelse(epos.bh); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 21dad8c608f..cd2115060fd 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -408,15 +408,6 @@ static struct fileIdentDesc *udf_add_entry(struct inode *dir, } add: - /* Is there any extent whose size we need to round up? */ - if (dinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && elen) { - elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); - if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) - epos.offset -= sizeof(struct short_ad); - else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) - epos.offset -= sizeof(struct long_ad); - udf_write_aext(dir, &epos, &eloc, elen, 1); - } f_pos += nfidlen; if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && @@ -439,6 +430,7 @@ add: udf_current_aext(dir, &epos, &eloc, &elen, 1); } + /* Entry fits into current block? */ if (sb->s_blocksize - fibh->eoffset >= nfidlen) { fibh->soffset = fibh->eoffset; fibh->eoffset += nfidlen; @@ -462,6 +454,16 @@ add: (fibh->sbh->b_data + fibh->soffset); } } else { + /* Round up last extent in the file */ + elen = (elen + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + udf_write_aext(dir, &epos, &eloc, elen, 1); + dinfo->i_lenExtents = (dinfo->i_lenExtents + sb->s_blocksize + - 1) & ~(sb->s_blocksize - 1); + fibh->soffset = fibh->eoffset - sb->s_blocksize; fibh->eoffset += nfidlen - sb->s_blocksize; if (fibh->sbh != fibh->ebh) { @@ -508,6 +510,20 @@ add: dir->i_size += nfidlen; if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) dinfo->i_lenAlloc += nfidlen; + else { + /* Find the last extent and truncate it to proper size */ + while (udf_next_aext(dir, &epos, &eloc, &elen, 1) == + (EXT_RECORDED_ALLOCATED >> 30)) + ; + elen -= dinfo->i_lenExtents - dir->i_size; + if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) + epos.offset -= sizeof(struct short_ad); + else if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) + epos.offset -= sizeof(struct long_ad); + udf_write_aext(dir, &epos, &eloc, elen, 1); + dinfo->i_lenExtents = dir->i_size; + } + mark_inode_dirty(dir); goto out_ok; } else { @@ -922,7 +938,7 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, block = udf_get_pblock(inode->i_sb, block, iinfo->i_location.partitionReferenceNum, 0); - epos.bh = udf_tread(inode->i_sb, block); + epos.bh = udf_tgetblk(inode->i_sb, block); lock_buffer(epos.bh); memset(epos.bh->b_data, 0x00, inode->i_sb->s_blocksize); set_buffer_uptodate(epos.bh); @@ -999,6 +1015,8 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry, inode->i_size = elen; if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) iinfo->i_lenAlloc = inode->i_size; + else + udf_truncate_tail_extent(inode); mark_inode_dirty(inode); fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err);