2 * punch.c --- deallocate blocks allocated to an inode
4 * Copyright (C) 2010 Theodore Ts'o.
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
26 * This function returns 1 if the specified block is all zeros
28 static int check_zero_block(char *buf, int blocksize)
42 * This clever recursive function handles i_blocks[] as well as
43 * indirect, double indirect, and triple indirect blocks. It iterates
44 * over the entries in the i_blocks array or indirect blocks, and for
45 * each one, will recursively handle any indirect blocks and then
46 * frees and deallocates the blocks.
48 static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
49 char *block_buf, blk_t *p, int level,
50 blk_t start, blk_t count, int max)
58 printf("Entering ind_punch, level %d, start %u, count %u, "
59 "max %d\n", level, start, count, max);
61 incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
62 for (i=0, offset=0; i < max; i++, p++, offset += incr) {
63 if (offset >= start + count)
65 if (*p == 0 || (offset+incr) <= start)
71 printf("Reading indirect block %u\n", b);
73 retval = ext2fs_read_ind_block(fs, b, block_buf);
76 start2 = (start > offset) ? start - offset : 0;
77 retval = ind_punch(fs, inode, block_buf + fs->blocksize,
78 (blk_t *) block_buf, level - 1,
79 start2, count - offset,
83 retval = ext2fs_write_ind_block(fs, b, block_buf);
86 if (!check_zero_block(block_buf, fs->blocksize))
90 printf("Freeing block %u (offset %d)\n", b, offset);
92 ext2fs_block_alloc_stats(fs, b, -1);
97 printf("Freed %d blocks\n", freed);
99 return ext2fs_iblk_sub_blocks(fs, inode, freed);
102 static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
103 char *block_buf, blk_t start, blk_t count)
108 int num = EXT2_NDIR_BLOCKS;
109 blk_t *bp = inode->i_block;
110 blk_t addr_per_block;
111 blk_t max = EXT2_NDIR_BLOCKS;
114 retval = ext2fs_get_array(3, fs->blocksize, &buf);
120 addr_per_block = (blk_t) fs->blocksize >> 2;
122 for (level=0; level < 4; level++, max *= addr_per_block) {
124 printf("Main loop level %d, start %u count %u "
125 "max %d num %d\n", level, start, count, max, num);
128 retval = ind_punch(fs, inode, block_buf, bp, level,
133 count -= max - start;
148 ext2fs_free_mem(&buf);
154 #define dbg_printf(f, a...) printf(f, ## a)
156 static void dbg_print_extent(char *desc, struct ext2fs_extent *extent)
159 printf("%s: ", desc);
160 printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
161 extent->e_lblk, extent->e_lblk + extent->e_len - 1,
162 extent->e_len, extent->e_pblk);
163 if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF)
164 fputs("LEAF ", stdout);
165 if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT)
166 fputs("UNINIT ", stdout);
167 if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
168 fputs("2ND_VISIT ", stdout);
169 if (!extent->e_flags)
170 fputs("(none)", stdout);
175 #define dbg_print_extent(desc, ex) do { } while (0)
176 #define dbg_printf(f, a...) do { } while (0)
179 static errcode_t ext2fs_punch_extent(ext2_filsys fs, ext2_ino_t ino,
180 struct ext2_inode *inode,
181 blk64_t start, blk64_t end)
183 ext2_extent_handle_t handle = 0;
184 struct ext2fs_extent extent;
186 blk64_t free_start, next;
187 __u32 free_count, newlen;
191 retval = ext2fs_extent_open2(fs, ino, inode, &handle);
194 ext2fs_extent_goto(handle, start);
195 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
199 op = EXT2_EXTENT_NEXT_LEAF;
200 dbg_print_extent("main loop", &extent);
201 next = extent.e_lblk + extent.e_len;
202 dbg_printf("start %llu, end %llu, next %llu\n",
203 (unsigned long long) start,
204 (unsigned long long) end,
205 (unsigned long long) next);
206 if (start <= extent.e_lblk) {
207 if (end < extent.e_lblk)
209 dbg_printf("Case #%d\n", 1);
210 /* Start of deleted region before extent;
211 adjust beginning of extent */
212 free_start = extent.e_pblk;
214 free_count = end - extent.e_lblk + 1;
216 free_count = extent.e_len;
217 extent.e_len -= free_count;
218 extent.e_lblk += free_count;
219 extent.e_pblk += free_count;
220 } else if (end >= next-1) {
223 /* End of deleted region after extent;
224 adjust end of extent */
225 dbg_printf("Case #%d\n", 2);
226 newlen = start - extent.e_lblk;
227 free_start = extent.e_pblk + newlen;
228 free_count = extent.e_len - newlen;
229 extent.e_len = newlen;
231 struct ext2fs_extent newex;
233 dbg_printf("Case #%d\n", 3);
234 /* The hard case; we need to split the extent */
235 newex.e_pblk = extent.e_pblk +
236 (end + 1 - extent.e_lblk);
237 newex.e_lblk = end + 1;
238 newex.e_len = next - end - 1;
239 newex.e_flags = extent.e_flags;
241 extent.e_len = start - extent.e_lblk;
242 free_start = extent.e_pblk + extent.e_len;
243 free_count = end - start + 1;
245 dbg_print_extent("inserting", &newex);
246 retval = ext2fs_extent_insert(handle,
247 EXT2_EXTENT_INSERT_AFTER, &newex);
250 /* Now pointing at inserted extent; so go back */
251 retval = ext2fs_extent_get(handle,
252 EXT2_EXTENT_PREV_LEAF,
258 dbg_print_extent("replacing", &extent);
259 retval = ext2fs_extent_replace(handle, 0, &extent);
261 struct ext2fs_extent newex;
262 dbg_printf("deleting current extent%s\n", "");
263 retval = ext2fs_extent_delete(handle, 0);
267 * We just moved the next extent into the current
268 * extent's position, so re-read the extent next time.
270 retval = ext2fs_extent_get(handle,
271 EXT2_EXTENT_PREV_LEAF,
273 /* Can't go back? Just reread current. */
274 if (retval == EXT2_ET_EXTENT_NO_PREV) {
276 op = EXT2_EXTENT_CURRENT;
281 dbg_printf("Free start %llu, free count = %u\n",
282 free_start, free_count);
283 while (free_count-- > 0) {
284 ext2fs_block_alloc_stats2(fs, free_start++, -1);
288 retval = ext2fs_extent_get(handle, op,
290 if (retval == EXT2_ET_EXTENT_NO_NEXT ||
291 retval == EXT2_ET_NO_CURRENT_NODE)
296 dbg_printf("Freed %d blocks\n", freed);
297 retval = ext2fs_iblk_sub_blocks(fs, inode, freed);
299 ext2fs_extent_free(handle);
304 * Deallocate all logical blocks starting at start to end, inclusive.
305 * If end is ~0, then this is effectively truncate.
307 extern errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
308 struct ext2_inode *inode,
309 char *block_buf, blk64_t start,
313 struct ext2_inode inode_buf;
318 /* Read inode structure if necessary */
320 retval = ext2fs_read_inode(fs, ino, &inode_buf);
325 if (inode->i_flags & EXT4_EXTENTS_FL)
326 retval = ext2fs_punch_extent(fs, ino, inode, start, end);
332 count = ((end - start + 1) < ~0U) ? (end - start + 1) : ~0U;
333 retval = ext2fs_punch_ind(fs, inode, block_buf,
334 (blk_t) start, count);
339 return ext2fs_write_inode(fs, ino, inode);