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 641da177e4SLinus Torvalds void udf_discard_prealloc(struct inode * inode) 651da177e4SLinus Torvalds { 66ff116fc8SJan Kara struct extent_position epos = { NULL, 0, {0, 0}}; 67ff116fc8SJan Kara kernel_lb_addr eloc; 68ff116fc8SJan Kara uint32_t elen, nelen; 691da177e4SLinus Torvalds uint64_t lbcount = 0; 701da177e4SLinus Torvalds int8_t etype = -1, netype; 711da177e4SLinus Torvalds int adsize; 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || 741da177e4SLinus Torvalds inode->i_size == UDF_I_LENEXTENTS(inode)) 751da177e4SLinus Torvalds return; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 781da177e4SLinus Torvalds adsize = sizeof(short_ad); 791da177e4SLinus Torvalds else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 801da177e4SLinus Torvalds adsize = sizeof(long_ad); 811da177e4SLinus Torvalds else 821da177e4SLinus Torvalds adsize = 0; 831da177e4SLinus Torvalds 84ff116fc8SJan Kara epos.block = UDF_I_LOCATION(inode); 851da177e4SLinus Torvalds 86ff116fc8SJan Kara /* Find the last extent in the file */ 87ff116fc8SJan Kara while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) 881da177e4SLinus Torvalds { 891da177e4SLinus Torvalds etype = netype; 901da177e4SLinus Torvalds lbcount += elen; 911da177e4SLinus Torvalds if (lbcount > inode->i_size && lbcount - inode->i_size < inode->i_sb->s_blocksize) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds nelen = elen - (lbcount - inode->i_size); 94ff116fc8SJan Kara epos.offset -= adsize; 95ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, nelen); 96ff116fc8SJan Kara epos.offset += adsize; 971da177e4SLinus Torvalds lbcount = inode->i_size; 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds } 100ff116fc8SJan Kara if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { 101ff116fc8SJan Kara epos.offset -= adsize; 1021da177e4SLinus Torvalds lbcount -= elen; 103ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, 0); 104ff116fc8SJan Kara if (!epos.bh) 1051da177e4SLinus Torvalds { 106ff116fc8SJan Kara UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode); 1071da177e4SLinus Torvalds mark_inode_dirty(inode); 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds else 1101da177e4SLinus Torvalds { 111ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 112ff116fc8SJan Kara aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc)); 1131da177e4SLinus Torvalds if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) 114ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, epos.offset); 1151da177e4SLinus Torvalds else 116ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 117ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds UDF_I_LENEXTENTS(inode) = lbcount; 1211da177e4SLinus Torvalds 122ff116fc8SJan Kara udf_release_data(epos.bh); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds void udf_truncate_extents(struct inode * inode) 1261da177e4SLinus Torvalds { 127ff116fc8SJan Kara struct extent_position epos; 128ff116fc8SJan Kara kernel_lb_addr eloc, neloc = { 0, 0 }; 129ff116fc8SJan Kara uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc; 1301da177e4SLinus Torvalds int8_t etype; 13160448b1dSJan Kara sector_t first_block = inode->i_size >> inode->i_sb->s_blocksize_bits, offset; 13260448b1dSJan Kara loff_t byte_offset; 1331da177e4SLinus Torvalds int adsize; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 1361da177e4SLinus Torvalds adsize = sizeof(short_ad); 1371da177e4SLinus Torvalds else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 1381da177e4SLinus Torvalds adsize = sizeof(long_ad); 1391da177e4SLinus Torvalds else 140ff116fc8SJan Kara BUG(); 1411da177e4SLinus Torvalds 142ff116fc8SJan Kara etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); 14360448b1dSJan Kara byte_offset = (offset << inode->i_sb->s_blocksize_bits) + (inode->i_size & (inode->i_sb->s_blocksize-1)); 1441da177e4SLinus Torvalds if (etype != -1) 1451da177e4SLinus Torvalds { 146ff116fc8SJan Kara epos.offset -= adsize; 147ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, byte_offset); 148ff116fc8SJan Kara epos.offset += adsize; 14960448b1dSJan Kara if (byte_offset) 150ff116fc8SJan Kara lenalloc = epos.offset; 1511da177e4SLinus Torvalds else 152ff116fc8SJan Kara lenalloc = epos.offset - adsize; 1531da177e4SLinus Torvalds 154ff116fc8SJan Kara if (!epos.bh) 1551da177e4SLinus Torvalds lenalloc -= udf_file_entry_alloc_offset(inode); 1561da177e4SLinus Torvalds else 1571da177e4SLinus Torvalds lenalloc -= sizeof(struct allocExtDesc); 1581da177e4SLinus Torvalds 159ff116fc8SJan Kara while ((etype = udf_current_aext(inode, &epos, &eloc, &elen, 0)) != -1) 1601da177e4SLinus Torvalds { 1611da177e4SLinus Torvalds if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) 1621da177e4SLinus Torvalds { 163ff116fc8SJan Kara udf_write_aext(inode, &epos, neloc, nelen, 0); 164ff116fc8SJan Kara if (indirect_ext_len) 1651da177e4SLinus Torvalds { 166ff116fc8SJan Kara /* We managed to free all extents in the 167ff116fc8SJan Kara * indirect extent - free it too */ 168ff116fc8SJan Kara if (!epos.bh) 1691da177e4SLinus Torvalds BUG(); 170ff116fc8SJan Kara udf_free_blocks(inode->i_sb, inode, epos.block, 0, indirect_ext_len); 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds else 1731da177e4SLinus Torvalds { 174ff116fc8SJan Kara if (!epos.bh) 1751da177e4SLinus Torvalds { 1761da177e4SLinus Torvalds UDF_I_LENALLOC(inode) = lenalloc; 1771da177e4SLinus Torvalds mark_inode_dirty(inode); 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds else 1801da177e4SLinus Torvalds { 181ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 1821da177e4SLinus Torvalds aed->lengthAllocDescs = cpu_to_le32(lenalloc); 1831da177e4SLinus Torvalds if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) 184ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, lenalloc + 1851da177e4SLinus Torvalds sizeof(struct allocExtDesc)); 1861da177e4SLinus Torvalds else 187ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 188ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds } 191ff116fc8SJan Kara brelse(epos.bh); 192ff116fc8SJan Kara epos.offset = sizeof(struct allocExtDesc); 193ff116fc8SJan Kara epos.block = eloc; 194ff116fc8SJan Kara epos.bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, eloc, 0)); 1951da177e4SLinus Torvalds if (elen) 196ff116fc8SJan Kara indirect_ext_len = (elen + 197ff116fc8SJan Kara inode->i_sb->s_blocksize - 1) >> 1981da177e4SLinus Torvalds inode->i_sb->s_blocksize_bits; 1991da177e4SLinus Torvalds else 200ff116fc8SJan Kara indirect_ext_len = 1; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds else 2031da177e4SLinus Torvalds { 204ff116fc8SJan Kara extent_trunc(inode, &epos, eloc, etype, elen, 0); 205ff116fc8SJan Kara epos.offset += adsize; 2061da177e4SLinus Torvalds } 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 209ff116fc8SJan Kara if (indirect_ext_len) 2101da177e4SLinus Torvalds { 211ff116fc8SJan Kara if (!epos.bh) 2121da177e4SLinus Torvalds BUG(); 213ff116fc8SJan Kara udf_free_blocks(inode->i_sb, inode, epos.block, 0, indirect_ext_len); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds else 2161da177e4SLinus Torvalds { 217ff116fc8SJan Kara if (!epos.bh) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds UDF_I_LENALLOC(inode) = lenalloc; 2201da177e4SLinus Torvalds mark_inode_dirty(inode); 2211da177e4SLinus Torvalds } 2221da177e4SLinus Torvalds else 2231da177e4SLinus Torvalds { 224ff116fc8SJan Kara struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 2251da177e4SLinus Torvalds aed->lengthAllocDescs = cpu_to_le32(lenalloc); 2261da177e4SLinus Torvalds if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) 227ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, lenalloc + 2281da177e4SLinus Torvalds sizeof(struct allocExtDesc)); 2291da177e4SLinus Torvalds else 230ff116fc8SJan Kara udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc)); 231ff116fc8SJan Kara mark_buffer_dirty_inode(epos.bh, inode); 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds } 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds else if (inode->i_size) 2361da177e4SLinus Torvalds { 23760448b1dSJan Kara if (byte_offset) 2381da177e4SLinus Torvalds { 23900a2b0f6SJan Kara /* 24000a2b0f6SJan Kara * OK, there is not extent covering inode->i_size and 24100a2b0f6SJan Kara * no extent above inode->i_size => truncate is 24200a2b0f6SJan Kara * extending the file by 'offset'. 24300a2b0f6SJan Kara */ 244ff116fc8SJan Kara if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || 245ff116fc8SJan Kara (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { 24600a2b0f6SJan Kara /* File has no extents at all! */ 24700a2b0f6SJan Kara memset(&eloc, 0x00, sizeof(kernel_lb_addr)); 24860448b1dSJan Kara elen = EXT_NOT_RECORDED_NOT_ALLOCATED | byte_offset; 249ff116fc8SJan Kara udf_add_aext(inode, &epos, eloc, elen, 1); 25000a2b0f6SJan Kara } 25100a2b0f6SJan Kara else { 252ff116fc8SJan Kara epos.offset -= adsize; 253ff116fc8SJan Kara etype = udf_next_aext(inode, &epos, &eloc, &elen, 1); 254ff116fc8SJan Kara 2551da177e4SLinus Torvalds if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) 2561da177e4SLinus Torvalds { 257ff116fc8SJan Kara epos.offset -= adsize; 25860448b1dSJan Kara elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + byte_offset); 259ff116fc8SJan Kara udf_write_aext(inode, &epos, eloc, elen, 0); 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) 2621da177e4SLinus Torvalds { 2631da177e4SLinus Torvalds kernel_lb_addr neloc = { 0, 0 }; 264ff116fc8SJan Kara epos.offset -= adsize; 2651da177e4SLinus Torvalds nelen = EXT_NOT_RECORDED_NOT_ALLOCATED | 26660448b1dSJan Kara ((elen + byte_offset + inode->i_sb->s_blocksize - 1) & 2671da177e4SLinus Torvalds ~(inode->i_sb->s_blocksize - 1)); 268ff116fc8SJan Kara udf_write_aext(inode, &epos, neloc, nelen, 1); 269ff116fc8SJan Kara udf_add_aext(inode, &epos, eloc, (etype << 30) | elen, 1); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds else 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds if (elen & (inode->i_sb->s_blocksize - 1)) 2741da177e4SLinus Torvalds { 275ff116fc8SJan Kara epos.offset -= adsize; 2761da177e4SLinus Torvalds elen = EXT_RECORDED_ALLOCATED | 2771da177e4SLinus Torvalds ((elen + inode->i_sb->s_blocksize - 1) & 2781da177e4SLinus Torvalds ~(inode->i_sb->s_blocksize - 1)); 279ff116fc8SJan Kara udf_write_aext(inode, &epos, eloc, elen, 1); 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds memset(&eloc, 0x00, sizeof(kernel_lb_addr)); 28260448b1dSJan Kara elen = EXT_NOT_RECORDED_NOT_ALLOCATED | byte_offset; 283ff116fc8SJan Kara udf_add_aext(inode, &epos, eloc, elen, 1); 2841da177e4SLinus Torvalds } 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds } 28700a2b0f6SJan Kara } 2881da177e4SLinus Torvalds UDF_I_LENEXTENTS(inode) = inode->i_size; 2891da177e4SLinus Torvalds 290ff116fc8SJan Kara udf_release_data(epos.bh); 2911da177e4SLinus Torvalds } 292