cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

truncate.c (8121B)


      1/*
      2 * truncate.c
      3 *
      4 * PURPOSE
      5 *	Truncate handling routines for the OSTA-UDF(tm) filesystem.
      6 *
      7 * COPYRIGHT
      8 *	This file is distributed under the terms of the GNU General Public
      9 *	License (GPL). Copies of the GPL can be obtained from:
     10 *		ftp://prep.ai.mit.edu/pub/gnu/GPL
     11 *	Each contributing author retains all rights to their own work.
     12 *
     13 *  (C) 1999-2004 Ben Fennema
     14 *  (C) 1999 Stelias Computing Inc
     15 *
     16 * HISTORY
     17 *
     18 *  02/24/99 blf  Created.
     19 *
     20 */
     21
     22#include "udfdecl.h"
     23#include <linux/fs.h>
     24#include <linux/mm.h>
     25
     26#include "udf_i.h"
     27#include "udf_sb.h"
     28
     29static void extent_trunc(struct inode *inode, struct extent_position *epos,
     30			 struct kernel_lb_addr *eloc, int8_t etype, uint32_t elen,
     31			 uint32_t nelen)
     32{
     33	struct kernel_lb_addr neloc = {};
     34	int last_block = (elen + inode->i_sb->s_blocksize - 1) >>
     35		inode->i_sb->s_blocksize_bits;
     36	int first_block = (nelen + inode->i_sb->s_blocksize - 1) >>
     37		inode->i_sb->s_blocksize_bits;
     38
     39	if (nelen) {
     40		if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
     41			udf_free_blocks(inode->i_sb, inode, eloc, 0,
     42					last_block);
     43			etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
     44		} else
     45			neloc = *eloc;
     46		nelen = (etype << 30) | nelen;
     47	}
     48
     49	if (elen != nelen) {
     50		udf_write_aext(inode, epos, &neloc, nelen, 0);
     51		if (last_block > first_block) {
     52			if (etype == (EXT_RECORDED_ALLOCATED >> 30))
     53				mark_inode_dirty(inode);
     54
     55			if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
     56				udf_free_blocks(inode->i_sb, inode, eloc,
     57						first_block,
     58						last_block - first_block);
     59		}
     60	}
     61}
     62
     63/*
     64 * Truncate the last extent to match i_size. This function assumes
     65 * that preallocation extent is already truncated.
     66 */
     67void udf_truncate_tail_extent(struct inode *inode)
     68{
     69	struct extent_position epos = {};
     70	struct kernel_lb_addr eloc;
     71	uint32_t elen, nelen;
     72	uint64_t lbcount = 0;
     73	int8_t etype = -1, netype;
     74	int adsize;
     75	struct udf_inode_info *iinfo = UDF_I(inode);
     76
     77	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
     78	    inode->i_size == iinfo->i_lenExtents)
     79		return;
     80	/* Are we going to delete the file anyway? */
     81	if (inode->i_nlink == 0)
     82		return;
     83
     84	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
     85		adsize = sizeof(struct short_ad);
     86	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
     87		adsize = sizeof(struct long_ad);
     88	else
     89		BUG();
     90
     91	/* Find the last extent in the file */
     92	while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
     93		etype = netype;
     94		lbcount += elen;
     95		if (lbcount > inode->i_size) {
     96			if (lbcount - inode->i_size >= inode->i_sb->s_blocksize)
     97				udf_warn(inode->i_sb,
     98					 "Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u\n",
     99					 (unsigned)inode->i_ino,
    100					 (long long)inode->i_size,
    101					 (long long)lbcount,
    102					 (unsigned)eloc.logicalBlockNum,
    103					 (unsigned)elen);
    104			nelen = elen - (lbcount - inode->i_size);
    105			epos.offset -= adsize;
    106			extent_trunc(inode, &epos, &eloc, etype, elen, nelen);
    107			epos.offset += adsize;
    108			if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1)
    109				udf_err(inode->i_sb,
    110					"Extent after EOF in inode %u\n",
    111					(unsigned)inode->i_ino);
    112			break;
    113		}
    114	}
    115	/* This inode entry is in-memory only and thus we don't have to mark
    116	 * the inode dirty */
    117	iinfo->i_lenExtents = inode->i_size;
    118	brelse(epos.bh);
    119}
    120
    121void udf_discard_prealloc(struct inode *inode)
    122{
    123	struct extent_position epos = { NULL, 0, {0, 0} };
    124	struct kernel_lb_addr eloc;
    125	uint32_t elen;
    126	uint64_t lbcount = 0;
    127	int8_t etype = -1, netype;
    128	int adsize;
    129	struct udf_inode_info *iinfo = UDF_I(inode);
    130
    131	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
    132	    inode->i_size == iinfo->i_lenExtents)
    133		return;
    134
    135	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
    136		adsize = sizeof(struct short_ad);
    137	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
    138		adsize = sizeof(struct long_ad);
    139	else
    140		adsize = 0;
    141
    142	epos.block = iinfo->i_location;
    143
    144	/* Find the last extent in the file */
    145	while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
    146		etype = netype;
    147		lbcount += elen;
    148	}
    149	if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
    150		epos.offset -= adsize;
    151		lbcount -= elen;
    152		extent_trunc(inode, &epos, &eloc, etype, elen, 0);
    153		if (!epos.bh) {
    154			iinfo->i_lenAlloc =
    155				epos.offset -
    156				udf_file_entry_alloc_offset(inode);
    157			mark_inode_dirty(inode);
    158		} else {
    159			struct allocExtDesc *aed =
    160				(struct allocExtDesc *)(epos.bh->b_data);
    161			aed->lengthAllocDescs =
    162				cpu_to_le32(epos.offset -
    163					    sizeof(struct allocExtDesc));
    164			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
    165			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
    166				udf_update_tag(epos.bh->b_data, epos.offset);
    167			else
    168				udf_update_tag(epos.bh->b_data,
    169					       sizeof(struct allocExtDesc));
    170			mark_buffer_dirty_inode(epos.bh, inode);
    171		}
    172	}
    173	/* This inode entry is in-memory only and thus we don't have to mark
    174	 * the inode dirty */
    175	iinfo->i_lenExtents = lbcount;
    176	brelse(epos.bh);
    177}
    178
    179static void udf_update_alloc_ext_desc(struct inode *inode,
    180				      struct extent_position *epos,
    181				      u32 lenalloc)
    182{
    183	struct super_block *sb = inode->i_sb;
    184	struct udf_sb_info *sbi = UDF_SB(sb);
    185
    186	struct allocExtDesc *aed = (struct allocExtDesc *) (epos->bh->b_data);
    187	int len = sizeof(struct allocExtDesc);
    188
    189	aed->lengthAllocDescs =	cpu_to_le32(lenalloc);
    190	if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || sbi->s_udfrev >= 0x0201)
    191		len += lenalloc;
    192
    193	udf_update_tag(epos->bh->b_data, len);
    194	mark_buffer_dirty_inode(epos->bh, inode);
    195}
    196
    197/*
    198 * Truncate extents of inode to inode->i_size. This function can be used only
    199 * for making file shorter. For making file longer, udf_extend_file() has to
    200 * be used.
    201 */
    202int udf_truncate_extents(struct inode *inode)
    203{
    204	struct extent_position epos;
    205	struct kernel_lb_addr eloc, neloc = {};
    206	uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
    207	int8_t etype;
    208	struct super_block *sb = inode->i_sb;
    209	sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
    210	loff_t byte_offset;
    211	int adsize;
    212	struct udf_inode_info *iinfo = UDF_I(inode);
    213
    214	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
    215		adsize = sizeof(struct short_ad);
    216	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
    217		adsize = sizeof(struct long_ad);
    218	else
    219		BUG();
    220
    221	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
    222	byte_offset = (offset << sb->s_blocksize_bits) +
    223		(inode->i_size & (sb->s_blocksize - 1));
    224	if (etype == -1) {
    225		/* We should extend the file? */
    226		WARN_ON(byte_offset);
    227		return 0;
    228	}
    229	epos.offset -= adsize;
    230	extent_trunc(inode, &epos, &eloc, etype, elen, byte_offset);
    231	epos.offset += adsize;
    232	if (byte_offset)
    233		lenalloc = epos.offset;
    234	else
    235		lenalloc = epos.offset - adsize;
    236
    237	if (!epos.bh)
    238		lenalloc -= udf_file_entry_alloc_offset(inode);
    239	else
    240		lenalloc -= sizeof(struct allocExtDesc);
    241
    242	while ((etype = udf_current_aext(inode, &epos, &eloc,
    243					 &elen, 0)) != -1) {
    244		if (etype == (EXT_NEXT_EXTENT_ALLOCDESCS >> 30)) {
    245			udf_write_aext(inode, &epos, &neloc, nelen, 0);
    246			if (indirect_ext_len) {
    247				/* We managed to free all extents in the
    248				 * indirect extent - free it too */
    249				BUG_ON(!epos.bh);
    250				udf_free_blocks(sb, NULL, &epos.block,
    251						0, indirect_ext_len);
    252			} else if (!epos.bh) {
    253				iinfo->i_lenAlloc = lenalloc;
    254				mark_inode_dirty(inode);
    255			} else
    256				udf_update_alloc_ext_desc(inode,
    257						&epos, lenalloc);
    258			brelse(epos.bh);
    259			epos.offset = sizeof(struct allocExtDesc);
    260			epos.block = eloc;
    261			epos.bh = udf_tread(sb,
    262					udf_get_lb_pblock(sb, &eloc, 0));
    263			/* Error reading indirect block? */
    264			if (!epos.bh)
    265				return -EIO;
    266			if (elen)
    267				indirect_ext_len =
    268					(elen + sb->s_blocksize - 1) >>
    269					sb->s_blocksize_bits;
    270			else
    271				indirect_ext_len = 1;
    272		} else {
    273			extent_trunc(inode, &epos, &eloc, etype, elen, 0);
    274			epos.offset += adsize;
    275		}
    276	}
    277
    278	if (indirect_ext_len) {
    279		BUG_ON(!epos.bh);
    280		udf_free_blocks(sb, NULL, &epos.block, 0, indirect_ext_len);
    281	} else if (!epos.bh) {
    282		iinfo->i_lenAlloc = lenalloc;
    283		mark_inode_dirty(inode);
    284	} else
    285		udf_update_alloc_ext_desc(inode, &epos, lenalloc);
    286	iinfo->i_lenExtents = inode->i_size;
    287
    288	brelse(epos.bh);
    289	return 0;
    290}