11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * truncate.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * PURPOSE 51da177e4SLinus Torvalds * Truncate handling routines for the OSTA-UDF(tm) filesystem. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * COPYRIGHT 81da177e4SLinus Torvalds * This file is distributed under the terms of the GNU General Public 91da177e4SLinus Torvalds * License (GPL). Copies of the GPL can be obtained from: 101da177e4SLinus Torvalds * ftp://prep.ai.mit.edu/pub/gnu/GPL 111da177e4SLinus Torvalds * Each contributing author retains all rights to their own work. 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * (C) 1999-2004 Ben Fennema 141da177e4SLinus Torvalds * (C) 1999 Stelias Computing Inc 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * HISTORY 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * 02/24/99 blf Created. 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include "udfdecl.h" 231da177e4SLinus Torvalds #include <linux/fs.h> 241da177e4SLinus Torvalds #include <linux/mm.h> 251da177e4SLinus Torvalds #include <linux/udf_fs.h> 261da177e4SLinus Torvalds #include <linux/buffer_head.h> 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds #include "udf_i.h" 291da177e4SLinus Torvalds #include "udf_sb.h" 301da177e4SLinus Torvalds 31ff116fc8SJan Kara static void extent_trunc(struct inode * inode, struct extent_position *epos, 32ff116fc8SJan Kara kernel_lb_addr eloc, int8_t etype, uint32_t elen, uint32_t nelen) 331da177e4SLinus Torvalds { 341da177e4SLinus Torvalds kernel_lb_addr neloc = { 0, 0 }; 351da177e4SLinus Torvalds int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; 361da177e4SLinus Torvalds int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits; 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds if (nelen) 391da177e4SLinus Torvalds { 401da177e4SLinus Torvalds if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) 411da177e4SLinus Torvalds { 421da177e4SLinus Torvalds udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block); 431da177e4SLinus Torvalds etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30); 441da177e4SLinus Torvalds } 451da177e4SLinus Torvalds else 461da177e4SLinus Torvalds neloc = eloc; 471da177e4SLinus Torvalds nelen = (etype << 30) | nelen; 481da177e4SLinus Torvalds } 491da177e4SLinus Torvalds 501da177e4SLinus Torvalds if (elen != nelen) 511da177e4SLinus Torvalds { 52ff116fc8SJan Kara udf_write_aext(inode, epos, neloc, nelen, 0); 531da177e4SLinus Torvalds if (last_block - first_block > 0) 541da177e4SLinus Torvalds { 551da177e4SLinus Torvalds if (etype == (EXT_RECORDED_ALLOCATED >> 30)) 561da177e4SLinus Torvalds mark_inode_dirty(inode); 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) 591da177e4SLinus Torvalds udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block); 601da177e4SLinus Torvalds } 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 6474584ae5SJan Kara /* 6574584ae5SJan Kara * Truncate the last extent to match i_size. This function assumes 6674584ae5SJan Kara * that preallocation extent is already truncated. 6774584ae5SJan Kara */ 6874584ae5SJan Kara void udf_truncate_tail_extent(struct inode *inode) 691da177e4SLinus Torvalds { 70ff116fc8SJan Kara struct extent_position epos = { NULL, 0, {0, 0}}; 71ff116fc8SJan Kara kernel_lb_addr eloc; 72ff116fc8SJan Kara uint32_t elen, nelen; 731da177e4SLinus Torvalds uint64_t lbcount = 0; 741da177e4SLinus Torvalds int8_t etype = -1, netype; 751da177e4SLinus Torvalds int adsize; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || 781da177e4SLinus Torvalds inode->i_size == UDF_I_LENEXTENTS(inode)) 791da177e4SLinus Torvalds return; 8074584ae5SJan Kara /* Are we going to delete the file anyway? */ 8174584ae5SJan Kara if (inode->i_nlink == 0) 8274584ae5SJan Kara return; 8374584ae5SJan Kara 8474584ae5SJan Kara if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 8574584ae5SJan Kara adsize = sizeof(short_ad); 8674584ae5SJan Kara else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 8774584ae5SJan Kara adsize = sizeof(long_ad); 8874584ae5SJan Kara else 8974584ae5SJan Kara BUG(); 9074584ae5SJan Kara 9174584ae5SJan Kara /* Find the last extent in the file */ 9274584ae5SJan Kara while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) 9374584ae5SJan Kara { 9474584ae5SJan Kara etype = netype; 9574584ae5SJan Kara lbcount += elen; 9674584ae5SJan Kara if (lbcount > inode->i_size) { 9774584ae5SJan Kara if (lbcount - inode->i_size >= inode->i_sb->s_blocksize) 9874584ae5SJan Kara printk(KERN_WARNING 9974584ae5SJan Kara "udf_truncate_tail_extent(): Too long " 10074584ae5SJan Kara "extent after EOF in inode %u: i_size: " 10174584ae5SJan Kara "%Ld lbcount: %Ld extent %u+%u\n", 10274584ae5SJan Kara (unsigned)inode->i_ino, 10374584ae5SJan Kara (long long)inode->i_size, 10474584ae5SJan Kara (long long)lbcount, 10574584ae5SJan Kara (unsigned)eloc.logicalBlockNum, 10674584ae5SJan Kara (unsigned)elen); 10774584ae5SJan Kara nelen = elen - (lbcount - inode->i_size); 10874584ae5SJan Kara epos.offset -= adsize; 10974584ae5SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, nelen); 11074584ae5SJan Kara epos.offset += adsize; 11174584ae5SJan Kara if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1) 11274584ae5SJan Kara printk(KERN_ERR "udf_truncate_tail_extent(): " 11374584ae5SJan Kara "Extent after EOF in inode %u.\n", 11474584ae5SJan Kara (unsigned)inode->i_ino); 11574584ae5SJan Kara break; 11674584ae5SJan Kara } 11774584ae5SJan Kara } 11874584ae5SJan Kara /* This inode entry is in-memory only and thus we don't have to mark 11974584ae5SJan Kara * the inode dirty */ 12074584ae5SJan Kara UDF_I_LENEXTENTS(inode) = inode->i_size; 12174584ae5SJan Kara brelse(epos.bh); 12274584ae5SJan Kara } 12374584ae5SJan Kara 12474584ae5SJan Kara void udf_discard_prealloc(struct inode *inode) 12574584ae5SJan Kara { 12674584ae5SJan Kara struct extent_position epos = { NULL, 0, {0, 0}}; 12774584ae5SJan Kara kernel_lb_addr eloc; 12874584ae5SJan Kara uint32_t elen; 12974584ae5SJan Kara uint64_t lbcount = 0; 13074584ae5SJan Kara int8_t etype = -1, netype; 13174584ae5SJan Kara int adsize; 13274584ae5SJan Kara 13374584ae5SJan Kara if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || 13474584ae5SJan Kara inode->i_size == UDF_I_LENEXTENTS(inode)) 13574584ae5SJan Kara return; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 1381da177e4SLinus Torvalds adsize = sizeof(short_ad); 1391da177e4SLinus Torvalds else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 1401da177e4SLinus Torvalds adsize = sizeof(long_ad); 1411da177e4SLinus Torvalds else 1421da177e4SLinus Torvalds adsize = 0; 1431da177e4SLinus Torvalds 144ff116fc8SJan Kara epos.block = UDF_I_LOCATION(inode); 1451da177e4SLinus Torvalds 146ff116fc8SJan Kara /* Find the last extent in the file */ 14774584ae5SJan Kara while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { 1481da177e4SLinus Torvalds etype = netype; 1491da177e4SLinus Torvalds lbcount += elen; 1501da177e4SLinus Torvalds } 151ff116fc8SJan Kara if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { 152ff116fc8SJan Kara epos.offset -= adsize; 1531da177e4SLinus Torvalds lbcount -= elen; 154ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, 0); 15574584ae5SJan Kara if (!epos.bh) { 156ff116fc8SJan Kara UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode); 1571da177e4SLinus Torvalds mark_inode_dirty(inode); 15874584ae5SJan Kara } else { 159ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 160ff116fc8SJan Kara aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc)); 1611da177e4SLinus Torvalds if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) 162ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, epos.offset); 1631da177e4SLinus Torvalds else 164ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 165ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds } 16874584ae5SJan Kara /* This inode entry is in-memory only and thus we don't have to mark 16974584ae5SJan Kara * the inode dirty */ 1701da177e4SLinus Torvalds UDF_I_LENEXTENTS(inode) = lbcount; 1713bf25cb4SJan Kara brelse(epos.bh); 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds void udf_truncate_extents(struct inode * inode) 1751da177e4SLinus Torvalds { 176ff116fc8SJan Kara struct extent_position epos; 177ff116fc8SJan Kara kernel_lb_addr eloc, neloc = { 0, 0 }; 178ff116fc8SJan Kara uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc; 1791da177e4SLinus Torvalds int8_t etype; 18031170b6aSJan Kara struct super_block *sb = inode->i_sb; 18131170b6aSJan Kara sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset; 18260448b1dSJan Kara loff_t byte_offset; 1831da177e4SLinus Torvalds int adsize; 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 1861da177e4SLinus Torvalds adsize = sizeof(short_ad); 1871da177e4SLinus Torvalds else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 1881da177e4SLinus Torvalds adsize = sizeof(long_ad); 1891da177e4SLinus Torvalds else 190ff116fc8SJan Kara BUG(); 1911da177e4SLinus Torvalds 192ff116fc8SJan Kara etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); 19331170b6aSJan Kara byte_offset = (offset << sb->s_blocksize_bits) + (inode->i_size & (sb->s_blocksize-1)); 1941da177e4SLinus Torvalds if (etype != -1) 1951da177e4SLinus Torvalds { 196ff116fc8SJan Kara epos.offset -= adsize; 197ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, byte_offset); 198ff116fc8SJan Kara epos.offset += adsize; 19960448b1dSJan Kara if (byte_offset) 200ff116fc8SJan Kara lenalloc = epos.offset; 2011da177e4SLinus Torvalds else 202ff116fc8SJan Kara lenalloc = epos.offset - adsize; 2031da177e4SLinus Torvalds 204ff116fc8SJan Kara if (!epos.bh) 2051da177e4SLinus Torvalds lenalloc -= udf_file_entry_alloc_offset(inode); 2061da177e4SLinus Torvalds else 2071da177e4SLinus Torvalds lenalloc -= sizeof(struct allocExtDesc); 2081da177e4SLinus Torvalds 209ff116fc8SJan Kara while ((etype = udf_current_aext(inode, &epos, &eloc, &elen, 0)) != -1) 2101da177e4SLinus Torvalds { 2111da177e4SLinus Torvalds if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) 2121da177e4SLinus Torvalds { 213ff116fc8SJan Kara udf_write_aext(inode, &epos, neloc, nelen, 0); 214ff116fc8SJan Kara if (indirect_ext_len) 2151da177e4SLinus Torvalds { 216ff116fc8SJan Kara /* We managed to free all extents in the 217ff116fc8SJan Kara * indirect extent - free it too */ 218ff116fc8SJan Kara if (!epos.bh) 2191da177e4SLinus Torvalds BUG(); 22031170b6aSJan Kara udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len); 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds else 2231da177e4SLinus Torvalds { 224ff116fc8SJan Kara if (!epos.bh) 2251da177e4SLinus Torvalds { 2261da177e4SLinus Torvalds UDF_I_LENALLOC(inode) = lenalloc; 2271da177e4SLinus Torvalds mark_inode_dirty(inode); 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds else 2301da177e4SLinus Torvalds { 231ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 2321da177e4SLinus Torvalds aed->lengthAllocDescs = cpu_to_le32(lenalloc); 23331170b6aSJan Kara if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201) 234ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, lenalloc + 2351da177e4SLinus Torvalds sizeof(struct allocExtDesc)); 2361da177e4SLinus Torvalds else 237ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 238ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds } 241ff116fc8SJan Kara brelse(epos.bh); 242ff116fc8SJan Kara epos.offset = sizeof(struct allocExtDesc); 243ff116fc8SJan Kara epos.block = eloc; 24431170b6aSJan Kara epos.bh = udf_tread(sb, udf_get_lb_pblock(sb, eloc, 0)); 2451da177e4SLinus Torvalds if (elen) 246ff116fc8SJan Kara indirect_ext_len = (elen + 24731170b6aSJan Kara sb->s_blocksize - 1) >> 24831170b6aSJan Kara sb->s_blocksize_bits; 2491da177e4SLinus Torvalds else 250ff116fc8SJan Kara indirect_ext_len = 1; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds else 2531da177e4SLinus Torvalds { 254ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, 0); 255ff116fc8SJan Kara epos.offset += adsize; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds 259ff116fc8SJan Kara if (indirect_ext_len) 2601da177e4SLinus Torvalds { 261ff116fc8SJan Kara if (!epos.bh) 2621da177e4SLinus Torvalds BUG(); 26331170b6aSJan Kara udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len); 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds else 2661da177e4SLinus Torvalds { 267ff116fc8SJan Kara if (!epos.bh) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds UDF_I_LENALLOC(inode) = lenalloc; 2701da177e4SLinus Torvalds mark_inode_dirty(inode); 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds else 2731da177e4SLinus Torvalds { 274ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 2751da177e4SLinus Torvalds aed->lengthAllocDescs = cpu_to_le32(lenalloc); 27631170b6aSJan Kara if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201) 277ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, lenalloc + 2781da177e4SLinus Torvalds sizeof(struct allocExtDesc)); 2791da177e4SLinus Torvalds else 280ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 281ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds else if (inode->i_size) 2861da177e4SLinus Torvalds { 28760448b1dSJan Kara if (byte_offset) 2881da177e4SLinus Torvalds { 28931170b6aSJan Kara kernel_long_ad extent; 29031170b6aSJan Kara 29100a2b0f6SJan Kara /* 29200a2b0f6SJan Kara * OK, there is not extent covering inode->i_size and 29300a2b0f6SJan Kara * no extent above inode->i_size => truncate is 29431170b6aSJan Kara * extending the file by 'offset' blocks. 29500a2b0f6SJan Kara */ 296ff116fc8SJan Kara if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || 297ff116fc8SJan Kara (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { 29831170b6aSJan Kara /* File has no extents at all or has empty last 29931170b6aSJan Kara * indirect extent! Create a fake extent... */ 30031170b6aSJan Kara extent.extLocation.logicalBlockNum = 0; 30131170b6aSJan Kara extent.extLocation.partitionReferenceNum = 0; 30231170b6aSJan Kara extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; 30300a2b0f6SJan Kara } 30400a2b0f6SJan Kara else { 305ff116fc8SJan Kara epos.offset -= adsize; 30631170b6aSJan Kara etype = udf_next_aext(inode, &epos, 30731170b6aSJan Kara &extent.extLocation, &extent.extLength, 0); 30831170b6aSJan Kara extent.extLength |= etype << 30; 3091da177e4SLinus Torvalds } 31031170b6aSJan Kara udf_extend_file(inode, &epos, &extent, offset+((inode->i_size & (sb->s_blocksize-1)) != 0)); 3111da177e4SLinus Torvalds } 31200a2b0f6SJan Kara } 3131da177e4SLinus Torvalds UDF_I_LENEXTENTS(inode) = inode->i_size; 3141da177e4SLinus Torvalds 3153bf25cb4SJan Kara brelse(epos.bh); 3161da177e4SLinus Torvalds } 317