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.
25 * This function returns 1 if the specified block is all zeros
27 static int check_zero_block(char *buf
, int blocksize
)
41 * This clever recursive function handles i_blocks[] as well as
42 * indirect, double indirect, and triple indirect blocks. It iterates
43 * over the entries in the i_blocks array or indirect blocks, and for
44 * each one, will recursively handle any indirect blocks and then
45 * frees and deallocates the blocks.
47 static errcode_t
ind_punch(ext2_filsys fs
, struct ext2_inode
*inode
,
48 char *block_buf
, blk_t
*p
, int level
,
49 blk_t start
, blk_t count
, int max
)
57 printf("Entering ind_punch, level %d, start %u, count %u, "
58 "max %d\n", level
, start
, count
, max
);
60 incr
= 1 << ((EXT2_BLOCK_SIZE_BITS(fs
->super
)-2)*level
);
61 for (i
=0, offset
=0; i
< max
; i
++, p
++, offset
+= incr
) {
64 if (*p
== 0 || (offset
+incr
) <= start
)
70 printf("Reading indirect block %u\n", b
);
72 retval
= ext2fs_read_ind_block(fs
, b
, block_buf
);
75 start2
= (start
> offset
) ? start
- offset
: 0;
76 retval
= ind_punch(fs
, inode
, block_buf
+ fs
->blocksize
,
77 (blk_t
*) block_buf
, level
- 1,
78 start2
, count
- offset
,
82 retval
= ext2fs_write_ind_block(fs
, b
, block_buf
);
85 if (!check_zero_block(block_buf
, fs
->blocksize
))
89 printf("Freeing block %u (offset %d)\n", b
, offset
);
91 ext2fs_block_alloc_stats(fs
, b
, -1);
96 printf("Freed %d blocks\n", freed
);
98 return ext2fs_iblk_sub_blocks(fs
, inode
, freed
);
101 static errcode_t
ext2fs_punch_ind(ext2_filsys fs
, struct ext2_inode
*inode
,
102 char *block_buf
, blk_t start
, blk_t count
)
107 int num
= EXT2_NDIR_BLOCKS
;
108 blk_t
*bp
= inode
->i_block
;
109 blk_t addr_per_block
;
110 blk_t max
= EXT2_NDIR_BLOCKS
;
113 retval
= ext2fs_get_array(3, fs
->blocksize
, &buf
);
119 addr_per_block
= (blk_t
) fs
->blocksize
>> 2;
121 for (level
=0; level
< 4; level
++, max
*= addr_per_block
) {
123 printf("Main loop level %d, start %u count %u "
124 "max %d num %d\n", level
, start
, count
, max
, num
);
127 retval
= ind_punch(fs
, inode
, block_buf
, bp
, level
,
132 count
-= max
- start
;
147 ext2fs_free_mem(&buf
);
153 #define dbg_printf(f, a...) printf(f, ## a)
155 static void dbg_print_extent(char *desc
, struct ext2fs_extent
*extent
)
158 printf("%s: ", desc
);
159 printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ",
160 extent
->e_lblk
, extent
->e_lblk
+ extent
->e_len
- 1,
161 extent
->e_len
, extent
->e_pblk
);
162 if (extent
->e_flags
& EXT2_EXTENT_FLAGS_LEAF
)
163 fputs("LEAF ", stdout
);
164 if (extent
->e_flags
& EXT2_EXTENT_FLAGS_UNINIT
)
165 fputs("UNINIT ", stdout
);
166 if (extent
->e_flags
& EXT2_EXTENT_FLAGS_SECOND_VISIT
)
167 fputs("2ND_VISIT ", stdout
);
168 if (!extent
->e_flags
)
169 fputs("(none)", stdout
);
174 #define dbg_print_extent(desc, ex) do { } while (0)
175 #define dbg_printf(f, a...) do { } while (0)
178 static errcode_t
ext2fs_punch_extent(ext2_filsys fs
, ext2_ino_t ino
,
179 struct ext2_inode
*inode
,
180 blk64_t start
, blk64_t end
)
182 ext2_extent_handle_t handle
= 0;
183 struct ext2fs_extent extent
;
185 blk64_t free_start
, next
;
186 __u32 free_count
, newlen
;
189 retval
= ext2fs_extent_open2(fs
, ino
, inode
, &handle
);
192 ext2fs_extent_goto(handle
, start
);
193 retval
= ext2fs_extent_get(handle
, EXT2_EXTENT_CURRENT
, &extent
);
197 dbg_print_extent("main loop", &extent
);
198 next
= extent
.e_lblk
+ extent
.e_len
;
199 dbg_printf("start %llu, end %llu, next %llu\n",
200 (unsigned long long) start
,
201 (unsigned long long) end
,
202 (unsigned long long) next
);
203 if (start
<= extent
.e_lblk
) {
204 if (end
< extent
.e_lblk
)
206 dbg_printf("Case #1\n");
207 /* Start of deleted region before extent;
208 adjust beginning of extent */
209 free_start
= extent
.e_pblk
;
211 free_count
= end
- extent
.e_lblk
+ 1;
213 free_count
= extent
.e_len
;
214 extent
.e_len
-= free_count
;
215 extent
.e_lblk
+= free_count
;
216 extent
.e_pblk
+= free_count
;
217 } else if (end
>= next
-1) {
220 /* End of deleted region after extent;
221 adjust end of extent */
222 dbg_printf("Case #2\n");
223 newlen
= start
- extent
.e_lblk
;
224 free_start
= extent
.e_pblk
+ newlen
;
225 free_count
= extent
.e_len
- newlen
;
226 extent
.e_len
= newlen
;
228 struct ext2fs_extent newex
;
230 dbg_printf("Case #3\n");
231 /* The hard case; we need to split the extent */
232 newex
.e_pblk
= extent
.e_pblk
+
233 (end
+ 1 - extent
.e_lblk
);
234 newex
.e_lblk
= end
+ 1;
235 newex
.e_len
= next
- end
- 1;
236 newex
.e_flags
= extent
.e_flags
;
238 extent
.e_len
= start
- extent
.e_lblk
;
239 free_start
= extent
.e_pblk
+ extent
.e_len
;
240 free_count
= end
- start
+ 1;
242 dbg_print_extent("inserting", &newex
);
243 retval
= ext2fs_extent_insert(handle
,
244 EXT2_EXTENT_INSERT_AFTER
, &newex
);
247 /* Now pointing at inserted extent; so go back */
248 retval
= ext2fs_extent_get(handle
,
249 EXT2_EXTENT_PREV_LEAF
,
255 dbg_print_extent("replacing", &extent
);
256 retval
= ext2fs_extent_replace(handle
, 0, &extent
);
258 dbg_printf("deleting current extent\n");
259 retval
= ext2fs_extent_delete(handle
, 0);
263 dbg_printf("Free start %llu, free count = %u\n",
264 free_start
, free_count
);
265 while (free_count
-- > 0) {
266 ext2fs_block_alloc_stats(fs
, free_start
++, -1);
270 retval
= ext2fs_extent_get(handle
, EXT2_EXTENT_NEXT_LEAF
,
272 if (retval
== EXT2_ET_EXTENT_NO_NEXT
)
277 dbg_printf("Freed %d blocks\n", freed
);
278 retval
= ext2fs_iblk_sub_blocks(fs
, inode
, freed
);
280 ext2fs_extent_free(handle
);
285 * Deallocate all logical blocks starting at start to end, inclusive.
286 * If end is ~0, then this is effectively truncate.
288 extern errcode_t
ext2fs_punch(ext2_filsys fs
, ext2_ino_t ino
,
289 struct ext2_inode
*inode
,
290 char *block_buf
, blk64_t start
,
294 struct ext2_inode inode_buf
;
302 /* Read inode structure if necessary */
304 retval
= ext2fs_read_inode(fs
, ino
, &inode_buf
);
309 if (inode
->i_flags
& EXT4_EXTENTS_FL
)
310 retval
= ext2fs_punch_extent(fs
, ino
, inode
, start
, end
);
316 count
= ((end
- start
) < ~0U) ? (end
- start
) : ~0U;
317 retval
= ext2fs_punch_ind(fs
, inode
, block_buf
,
318 (blk_t
) start
, count
);
323 return ext2fs_write_inode(fs
, ino
, inode
);