mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
00a2b0f6dd
UDF code is not really ready to handle extents larger that 1GB. This is the easy way to forbid creating those. Also truncation code did not count with the case when there are no extents in the file and we are extending the file. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
294 lines
8.0 KiB
C
294 lines
8.0 KiB
C
/*
|
|
* truncate.c
|
|
*
|
|
* PURPOSE
|
|
* Truncate handling routines for the OSTA-UDF(tm) filesystem.
|
|
*
|
|
* COPYRIGHT
|
|
* This file is distributed under the terms of the GNU General Public
|
|
* License (GPL). Copies of the GPL can be obtained from:
|
|
* ftp://prep.ai.mit.edu/pub/gnu/GPL
|
|
* Each contributing author retains all rights to their own work.
|
|
*
|
|
* (C) 1999-2004 Ben Fennema
|
|
* (C) 1999 Stelias Computing Inc
|
|
*
|
|
* HISTORY
|
|
*
|
|
* 02/24/99 blf Created.
|
|
*
|
|
*/
|
|
|
|
#include "udfdecl.h"
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/udf_fs.h>
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include "udf_i.h"
|
|
#include "udf_sb.h"
|
|
|
|
static void extent_trunc(struct inode * inode, kernel_lb_addr bloc, int extoffset,
|
|
kernel_lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen)
|
|
{
|
|
kernel_lb_addr neloc = { 0, 0 };
|
|
int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
|
|
int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
|
|
|
|
if (nelen)
|
|
{
|
|
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
|
|
{
|
|
udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
|
|
etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
|
|
}
|
|
else
|
|
neloc = eloc;
|
|
nelen = (etype << 30) | nelen;
|
|
}
|
|
|
|
if (elen != nelen)
|
|
{
|
|
udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
|
|
if (last_block - first_block > 0)
|
|
{
|
|
if (etype == (EXT_RECORDED_ALLOCATED >> 30))
|
|
mark_inode_dirty(inode);
|
|
|
|
if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
|
|
udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
|
|
}
|
|
}
|
|
}
|
|
|
|
void udf_discard_prealloc(struct inode * inode)
|
|
{
|
|
kernel_lb_addr bloc, eloc;
|
|
uint32_t extoffset = 0, elen, nelen;
|
|
uint64_t lbcount = 0;
|
|
int8_t etype = -1, netype;
|
|
struct buffer_head *bh = NULL;
|
|
int adsize;
|
|
|
|
if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ||
|
|
inode->i_size == UDF_I_LENEXTENTS(inode))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
|
|
adsize = sizeof(short_ad);
|
|
else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
|
|
adsize = sizeof(long_ad);
|
|
else
|
|
adsize = 0;
|
|
|
|
bloc = UDF_I_LOCATION(inode);
|
|
|
|
while ((netype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
|
|
{
|
|
etype = netype;
|
|
lbcount += elen;
|
|
if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize)
|
|
{
|
|
nelen = elen - (lbcount - inode->i_size);
|
|
extent_trunc(inode, bloc, extoffset-adsize, eloc, etype, elen, bh, nelen);
|
|
lbcount = inode->i_size;
|
|
}
|
|
}
|
|
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
|
|
{
|
|
extoffset -= adsize;
|
|
lbcount -= elen;
|
|
extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
|
|
if (!bh)
|
|
{
|
|
UDF_I_LENALLOC(inode) = extoffset - udf_file_entry_alloc_offset(inode);
|
|
mark_inode_dirty(inode);
|
|
}
|
|
else
|
|
{
|
|
struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
|
|
aed->lengthAllocDescs = cpu_to_le32(extoffset - sizeof(struct allocExtDesc));
|
|
if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
|
|
udf_update_tag(bh->b_data, extoffset);
|
|
else
|
|
udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
|
|
mark_buffer_dirty_inode(bh, inode);
|
|
}
|
|
}
|
|
UDF_I_LENEXTENTS(inode) = lbcount;
|
|
|
|
udf_release_data(bh);
|
|
}
|
|
|
|
void udf_truncate_extents(struct inode * inode)
|
|
{
|
|
kernel_lb_addr bloc, eloc, neloc = { 0, 0 };
|
|
uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc;
|
|
int8_t etype;
|
|
int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits;
|
|
struct buffer_head *bh = NULL;
|
|
int adsize;
|
|
|
|
if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
|
|
adsize = sizeof(short_ad);
|
|
else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
|
|
adsize = sizeof(long_ad);
|
|
else
|
|
adsize = 0;
|
|
|
|
etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
|
|
offset += (inode->i_size & (inode->i_sb->s_blocksize - 1));
|
|
if (etype != -1)
|
|
{
|
|
extoffset -= adsize;
|
|
extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset);
|
|
extoffset += adsize;
|
|
|
|
if (offset)
|
|
lenalloc = extoffset;
|
|
else
|
|
lenalloc = extoffset - adsize;
|
|
|
|
if (!bh)
|
|
lenalloc -= udf_file_entry_alloc_offset(inode);
|
|
else
|
|
lenalloc -= sizeof(struct allocExtDesc);
|
|
|
|
while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1)
|
|
{
|
|
if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
|
|
{
|
|
udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
|
|
extoffset = 0;
|
|
if (lelen)
|
|
{
|
|
if (!bh)
|
|
BUG();
|
|
else
|
|
memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
|
|
udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
|
|
}
|
|
else
|
|
{
|
|
if (!bh)
|
|
{
|
|
UDF_I_LENALLOC(inode) = lenalloc;
|
|
mark_inode_dirty(inode);
|
|
}
|
|
else
|
|
{
|
|
struct allocExtDesc *aed = (struct allocExtDesc *)(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)
|
|
udf_update_tag(bh->b_data, lenalloc +
|
|
sizeof(struct allocExtDesc));
|
|
else
|
|
udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
|
|
mark_buffer_dirty_inode(bh, inode);
|
|
}
|
|
}
|
|
|
|
udf_release_data(bh);
|
|
extoffset = sizeof(struct allocExtDesc);
|
|
bloc = eloc;
|
|
bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0));
|
|
if (elen)
|
|
lelen = (elen + inode->i_sb->s_blocksize - 1) >>
|
|
inode->i_sb->s_blocksize_bits;
|
|
else
|
|
lelen = 1;
|
|
}
|
|
else
|
|
{
|
|
extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
|
|
extoffset += adsize;
|
|
}
|
|
}
|
|
|
|
if (lelen)
|
|
{
|
|
if (!bh)
|
|
BUG();
|
|
else
|
|
memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
|
|
udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
|
|
}
|
|
else
|
|
{
|
|
if (!bh)
|
|
{
|
|
UDF_I_LENALLOC(inode) = lenalloc;
|
|
mark_inode_dirty(inode);
|
|
}
|
|
else
|
|
{
|
|
struct allocExtDesc *aed = (struct allocExtDesc *)(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)
|
|
udf_update_tag(bh->b_data, lenalloc +
|
|
sizeof(struct allocExtDesc));
|
|
else
|
|
udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
|
|
mark_buffer_dirty_inode(bh, inode);
|
|
}
|
|
}
|
|
}
|
|
else if (inode->i_size)
|
|
{
|
|
if (offset)
|
|
{
|
|
/*
|
|
* OK, there is not extent covering inode->i_size and
|
|
* no extent above inode->i_size => truncate is
|
|
* extending the file by 'offset'.
|
|
*/
|
|
if ((!bh && extoffset == udf_file_entry_alloc_offset(inode)) ||
|
|
(bh && extoffset == sizeof(struct allocExtDesc))) {
|
|
/* File has no extents at all! */
|
|
memset(&eloc, 0x00, sizeof(kernel_lb_addr));
|
|
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
|
|
udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
|
|
}
|
|
else {
|
|
extoffset -= adsize;
|
|
etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
|
|
if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
|
|
{
|
|
extoffset -= adsize;
|
|
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
|
|
udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
|
|
}
|
|
else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
|
|
{
|
|
kernel_lb_addr neloc = { 0, 0 };
|
|
extoffset -= adsize;
|
|
nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
|
|
((elen + offset + inode->i_sb->s_blocksize - 1) &
|
|
~(inode->i_sb->s_blocksize - 1));
|
|
udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
|
|
udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
|
|
}
|
|
else
|
|
{
|
|
if (elen & (inode->i_sb->s_blocksize - 1))
|
|
{
|
|
extoffset -= adsize;
|
|
elen = EXT_RECORDED_ALLOCATED |
|
|
((elen + inode->i_sb->s_blocksize - 1) &
|
|
~(inode->i_sb->s_blocksize - 1));
|
|
udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
|
|
}
|
|
memset(&eloc, 0x00, sizeof(kernel_lb_addr));
|
|
elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
|
|
udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
UDF_I_LENEXTENTS(inode) = inode->i_size;
|
|
|
|
udf_release_data(bh);
|
|
}
|