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