]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - e2fsck/extents.c
e2fsck: consistently use ext2fs_get_mem()
[thirdparty/e2fsprogs.git] / e2fsck / extents.c
CommitLineData
e228d700
DW
1/*
2 * extents.c --- rebuild extent tree
3 *
4 * Copyright (C) 2014 Oracle.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Public
8 * License, version 2.
9 * %End-Header%
10 */
11
12#include "config.h"
13#include <string.h>
14#include <ctype.h>
15#include <errno.h>
16#include "e2fsck.h"
17#include "problem.h"
18
19#undef DEBUG
20#undef DEBUG_SUMMARY
21#undef DEBUG_FREE
22
23#define NUM_EXTENTS 341 /* about one ETB' worth of extents */
24
25static errcode_t e2fsck_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino);
26
27/* Schedule an inode to have its extent tree rebuilt during pass 1E. */
28errcode_t e2fsck_rebuild_extents_later(e2fsck_t ctx, ext2_ino_t ino)
29{
63cd76d6
DW
30 errcode_t retval = 0;
31
86f3b6cf 32 if (!ext2fs_has_feature_extents(ctx->fs->super) ||
e228d700
DW
33 (ctx->options & E2F_OPT_NO) ||
34 (ino != EXT2_ROOT_INO && ino < ctx->fs->super->s_first_ino))
35 return 0;
36
37 if (ctx->flags & E2F_FLAG_ALLOC_OK)
38 return e2fsck_rebuild_extents(ctx, ino);
39
40 if (!ctx->inodes_to_rebuild)
63cd76d6 41 retval = e2fsck_allocate_inode_bitmap(ctx->fs,
e228d700
DW
42 _("extent rebuild inode map"),
43 EXT2FS_BMAP64_RBTREE,
44 "inodes_to_rebuild",
45 &ctx->inodes_to_rebuild);
63cd76d6
DW
46 if (retval)
47 return retval;
48
49 ext2fs_mark_inode_bitmap2(ctx->inodes_to_rebuild, ino);
e228d700
DW
50 return 0;
51}
52
53/* Ask if an inode will have its extents rebuilt during pass 1E. */
54int e2fsck_ino_will_be_rebuilt(e2fsck_t ctx, ext2_ino_t ino)
55{
56 if (!ctx->inodes_to_rebuild)
57 return 0;
58 return ext2fs_test_inode_bitmap2(ctx->inodes_to_rebuild, ino);
59}
60
61struct extent_list {
62 blk64_t blocks_freed;
63 struct ext2fs_extent *extents;
64 unsigned int count;
65 unsigned int size;
66 unsigned int ext_read;
67 errcode_t retval;
68 ext2_ino_t ino;
69};
70
71static errcode_t load_extents(e2fsck_t ctx, struct extent_list *list)
72{
73 ext2_filsys fs = ctx->fs;
74 ext2_extent_handle_t handle;
75 struct ext2fs_extent extent;
76 errcode_t retval;
77
78 retval = ext2fs_extent_open(fs, list->ino, &handle);
79 if (retval)
80 return retval;
81
82 retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
83 if (retval)
84 goto out;
85
86 do {
87 if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)
88 goto next;
89
90 /* Internal node; free it and we'll re-allocate it later */
91 if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) {
92#if defined(DEBUG) || defined(DEBUG_FREE)
93 printf("ino=%d free=%llu bf=%llu\n", list->ino,
94 extent.e_pblk, list->blocks_freed + 1);
95#endif
96 list->blocks_freed++;
97 ext2fs_block_alloc_stats2(fs, extent.e_pblk, -1);
98 goto next;
99 }
100
101 list->ext_read++;
102 /* Can we attach it to the previous extent? */
103 if (list->count) {
104 struct ext2fs_extent *last = list->extents +
105 list->count - 1;
106 blk64_t end = last->e_len + extent.e_len;
107
108 if (last->e_pblk + last->e_len == extent.e_pblk &&
109 last->e_lblk + last->e_len == extent.e_lblk &&
110 (last->e_flags & EXT2_EXTENT_FLAGS_UNINIT) ==
111 (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
112 end < (1ULL << 32)) {
113 last->e_len += extent.e_len;
114#ifdef DEBUG
115 printf("R: ino=%d len=%u\n", list->ino,
116 last->e_len);
117#endif
118 goto next;
119 }
120 }
121
122 /* Do we need to expand? */
123 if (list->count == list->size) {
124 unsigned int new_size = (list->size + NUM_EXTENTS) *
125 sizeof(struct ext2fs_extent);
126 retval = ext2fs_resize_mem(0, new_size, &list->extents);
127 if (retval)
128 goto out;
129 list->size += NUM_EXTENTS;
130 }
131
132 /* Add a new extent */
133 memcpy(list->extents + list->count, &extent, sizeof(extent));
134#ifdef DEBUG
135 printf("R: ino=%d pblk=%llu lblk=%llu len=%u\n", list->ino,
136 extent.e_pblk, extent.e_lblk, extent.e_len);
137#endif
138 list->count++;
139next:
140 retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
141 } while (retval == 0);
142
143out:
144 /* Ok if we run off the end */
145 if (retval == EXT2_ET_EXTENT_NO_NEXT)
146 retval = 0;
147 ext2fs_extent_free(handle);
148 return retval;
149}
150
151static int find_blocks(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt,
152 blk64_t ref_blk EXT2FS_ATTR((unused)),
153 int ref_offset EXT2FS_ATTR((unused)), void *priv_data)
154{
155 struct extent_list *list = priv_data;
156
157 /* Internal node? */
158 if (blockcnt < 0) {
159#if defined(DEBUG) || defined(DEBUG_FREE)
160 printf("ino=%d free=%llu bf=%llu\n", list->ino, *blocknr,
161 list->blocks_freed + 1);
162#endif
163 list->blocks_freed++;
164 ext2fs_block_alloc_stats2(fs, *blocknr, -1);
165 return 0;
166 }
167
168 /* Can we attach it to the previous extent? */
169 if (list->count) {
170 struct ext2fs_extent *last = list->extents +
171 list->count - 1;
172 blk64_t end = last->e_len + 1;
173
ac3256fd 174 if (last->e_lblk + last->e_len == (__u64) blockcnt &&
855c2ecb 175 last->e_pblk + last->e_len == *blocknr &&
e228d700
DW
176 end < (1ULL << 32)) {
177 last->e_len++;
178#ifdef DEBUG
179 printf("R: ino=%d len=%u\n", list->ino, last->e_len);
180#endif
181 return 0;
182 }
183 }
184
185 /* Do we need to expand? */
186 if (list->count == list->size) {
187 unsigned int new_size = (list->size + NUM_EXTENTS) *
188 sizeof(struct ext2fs_extent);
189 list->retval = ext2fs_resize_mem(0, new_size, &list->extents);
190 if (list->retval)
191 return BLOCK_ABORT;
192 list->size += NUM_EXTENTS;
193 }
194
195 /* Add a new extent */
196 list->extents[list->count].e_pblk = *blocknr;
197 list->extents[list->count].e_lblk = blockcnt;
198 list->extents[list->count].e_len = 1;
199 list->extents[list->count].e_flags = 0;
200#ifdef DEBUG
201 printf("R: ino=%d pblk=%llu lblk=%llu len=%u\n", list->ino, *blocknr,
202 blockcnt, 1);
203#endif
204 list->count++;
205
206 return 0;
207}
208
209static errcode_t rebuild_extent_tree(e2fsck_t ctx, struct extent_list *list,
210 ext2_ino_t ino)
211{
bc177d42 212 struct ext2_inode_large inode;
e228d700
DW
213 errcode_t retval;
214 ext2_extent_handle_t handle;
215 unsigned int i, ext_written;
216 struct ext2fs_extent *ex, extent;
bc177d42 217 blk64_t start_val, delta;
e228d700
DW
218
219 list->count = 0;
220 list->blocks_freed = 0;
221 list->ino = ino;
222 list->ext_read = 0;
bc177d42
TT
223 e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode), sizeof(inode),
224 "rebuild_extents");
e228d700
DW
225
226 /* Skip deleted inodes and inline data files */
227 if (inode.i_links_count == 0 ||
228 inode.i_flags & EXT4_INLINE_DATA_FL)
229 return 0;
230
231 /* Collect lblk->pblk mappings */
232 if (inode.i_flags & EXT4_EXTENTS_FL) {
233 retval = load_extents(ctx, list);
63cd76d6
DW
234 if (retval)
235 goto err;
e228d700
DW
236 goto extents_loaded;
237 }
238
239 retval = ext2fs_block_iterate3(ctx->fs, ino, BLOCK_FLAG_READ_ONLY, 0,
240 find_blocks, list);
241 if (retval)
242 goto err;
243 if (list->retval) {
244 retval = list->retval;
245 goto err;
246 }
247
248extents_loaded:
249 /* Reset extent tree */
250 inode.i_flags &= ~EXT4_EXTENTS_FL;
251 memset(inode.i_block, 0, sizeof(inode.i_block));
252
253 /* Make a note of freed blocks */
bc177d42
TT
254 quota_data_sub(ctx->qctx, &inode, ino,
255 list->blocks_freed * ctx->fs->blocksize);
256 retval = ext2fs_iblk_sub_blocks(ctx->fs, EXT2_INODE(&inode),
257 list->blocks_freed);
e228d700
DW
258 if (retval)
259 goto err;
260
261 /* Now stuff extents into the file */
bc177d42 262 retval = ext2fs_extent_open2(ctx->fs, ino, EXT2_INODE(&inode), &handle);
e228d700
DW
263 if (retval)
264 goto err;
265
266 ext_written = 0;
3a15d85b 267 start_val = ext2fs_get_stat_i_blocks(ctx->fs, EXT2_INODE(&inode));
e228d700
DW
268 for (i = 0, ex = list->extents; i < list->count; i++, ex++) {
269 memcpy(&extent, ex, sizeof(struct ext2fs_extent));
270 extent.e_flags &= EXT2_EXTENT_FLAGS_UNINIT;
271 if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
272 if (extent.e_len > EXT_UNINIT_MAX_LEN) {
273 extent.e_len = EXT_UNINIT_MAX_LEN;
274 ex->e_pblk += EXT_UNINIT_MAX_LEN;
275 ex->e_lblk += EXT_UNINIT_MAX_LEN;
276 ex->e_len -= EXT_UNINIT_MAX_LEN;
277 ex--;
278 i--;
279 }
280 } else {
281 if (extent.e_len > EXT_INIT_MAX_LEN) {
282 extent.e_len = EXT_INIT_MAX_LEN;
283 ex->e_pblk += EXT_INIT_MAX_LEN;
284 ex->e_lblk += EXT_INIT_MAX_LEN;
285 ex->e_len -= EXT_INIT_MAX_LEN;
286 ex--;
287 i--;
288 }
289 }
290
291#ifdef DEBUG
292 printf("W: ino=%d pblk=%llu lblk=%llu len=%u\n", ino,
293 extent.e_pblk, extent.e_lblk, extent.e_len);
294#endif
295 retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER,
296 &extent);
297 if (retval)
298 goto err2;
299 retval = ext2fs_extent_fix_parents(handle);
300 if (retval)
301 goto err2;
302 ext_written++;
303 }
304
3a15d85b
TT
305 delta = ext2fs_get_stat_i_blocks(ctx->fs, EXT2_INODE(&inode)) -
306 start_val;
307 if (delta)
308 quota_data_add(ctx->qctx, &inode, ino, delta << 9);
bc177d42 309
e228d700
DW
310#if defined(DEBUG) || defined(DEBUG_SUMMARY)
311 printf("rebuild: ino=%d extents=%d->%d\n", ino, list->ext_read,
312 ext_written);
313#endif
bc177d42 314 e2fsck_write_inode(ctx, ino, EXT2_INODE(&inode), "rebuild_extents");
e228d700
DW
315
316err2:
317 ext2fs_extent_free(handle);
318err:
319 return retval;
320}
321
322/* Rebuild the extents immediately */
323static errcode_t e2fsck_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino)
324{
70303df1 325 struct extent_list list = { 0 };
e228d700
DW
326 errcode_t err;
327
86f3b6cf 328 if (!ext2fs_has_feature_extents(ctx->fs->super) ||
e228d700
DW
329 (ctx->options & E2F_OPT_NO) ||
330 (ino != EXT2_ROOT_INO && ino < ctx->fs->super->s_first_ino))
331 return 0;
332
333 e2fsck_read_bitmaps(ctx);
70303df1
AD
334 err = ext2fs_get_array(NUM_EXTENTS, sizeof(struct ext2fs_extent),
335 &list.extents);
e228d700
DW
336 if (err)
337 return err;
338 list.size = NUM_EXTENTS;
339 err = rebuild_extent_tree(ctx, &list, ino);
340 ext2fs_free_mem(&list.extents);
341
342 return err;
343}
344
345static void rebuild_extents(e2fsck_t ctx, const char *pass_name, int pr_header)
346{
347 struct problem_context pctx;
348#ifdef RESOURCE_TRACK
349 struct resource_track rtrack;
350#endif
70303df1 351 struct extent_list list = { 0 };
e228d700
DW
352 int first = 1;
353 ext2_ino_t ino = 0;
354 errcode_t retval;
355
86f3b6cf 356 if (!ext2fs_has_feature_extents(ctx->fs->super) ||
e228d700
DW
357 !ext2fs_test_valid(ctx->fs) ||
358 ctx->invalid_bitmaps) {
359 if (ctx->inodes_to_rebuild)
360 ext2fs_free_inode_bitmap(ctx->inodes_to_rebuild);
361 ctx->inodes_to_rebuild = NULL;
362 }
363
364 if (ctx->inodes_to_rebuild == NULL)
365 return;
366
367 init_resource_track(&rtrack, ctx->fs->io);
368 clear_problem_context(&pctx);
369 e2fsck_read_bitmaps(ctx);
370
e228d700 371 list.size = NUM_EXTENTS;
70303df1
AD
372 retval = ext2fs_get_array(sizeof(struct ext2fs_extent),
373 list.size, &list.extents);
374 if (retval)
375 return;
e228d700
DW
376 while (1) {
377 retval = ext2fs_find_first_set_inode_bitmap2(
378 ctx->inodes_to_rebuild, ino + 1,
379 ctx->fs->super->s_inodes_count, &ino);
380 if (retval)
381 break;
382 pctx.ino = ino;
383 if (first) {
384 fix_problem(ctx, pr_header, &pctx);
385 first = 0;
386 }
387 pctx.errcode = rebuild_extent_tree(ctx, &list, ino);
388 if (pctx.errcode) {
389 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_EXT);
390 fix_problem(ctx, PR_1E_OPTIMIZE_EXT_ERR, &pctx);
391 }
392 if (ctx->progress && !ctx->progress_fd)
393 e2fsck_simple_progress(ctx, "Rebuilding extents",
394 100.0 * (float) ino /
395 (float) ctx->fs->super->s_inodes_count,
396 ino);
397 }
398 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_EXT);
399
400 ext2fs_free_inode_bitmap(ctx->inodes_to_rebuild);
401 ctx->inodes_to_rebuild = NULL;
402 ext2fs_free_mem(&list.extents);
403
404 print_resource_track(ctx, pass_name, &rtrack, ctx->fs->io);
405}
406
407/* Scan a file to see if we should rebuild its extent tree */
408errcode_t e2fsck_check_rebuild_extents(e2fsck_t ctx, ext2_ino_t ino,
409 struct ext2_inode *inode,
410 struct problem_context *pctx)
411{
412 struct extent_tree_info eti;
413 struct ext2_extent_info info, top_info;
414 struct ext2fs_extent extent;
415 ext2_extent_handle_t ehandle;
416 ext2_filsys fs = ctx->fs;
417 errcode_t retval;
418
419 /* block map file and we want extent conversion */
420 if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
421 !(inode->i_flags & EXT4_INLINE_DATA_FL) &&
422 (ctx->options & E2F_OPT_CONVERT_BMAP)) {
423 return e2fsck_rebuild_extents_later(ctx, ino);
424 }
425
426 if (!(inode->i_flags & EXT4_EXTENTS_FL))
427 return 0;
428 memset(&eti, 0, sizeof(eti));
429 eti.ino = ino;
430
431 /* Otherwise, go scan the extent tree... */
432 retval = ext2fs_extent_open2(fs, ino, inode, &ehandle);
433 if (retval)
434 return 0;
435
436 retval = ext2fs_extent_get_info(ehandle, &top_info);
437 if (retval)
438 goto out;
439
440 /* Check maximum extent depth */
441 pctx->ino = ino;
442 pctx->blk = top_info.max_depth;
443 pctx->blk2 = ext2fs_max_extent_depth(ehandle);
444 if (pctx->blk2 < pctx->blk &&
445 fix_problem(ctx, PR_1_EXTENT_BAD_MAX_DEPTH, pctx))
446 eti.force_rebuild = 1;
447
448 /* Can we collect extent tree level stats? */
449 pctx->blk = MAX_EXTENT_DEPTH_COUNT;
450 if (pctx->blk2 > pctx->blk)
451 fix_problem(ctx, PR_1E_MAX_EXTENT_TREE_DEPTH, pctx);
452
453 /* We need to fix tree depth problems, but the scan isn't a fix. */
454 if (ctx->options & E2F_OPT_FIXES_ONLY)
455 goto out;
456
457 retval = ext2fs_extent_get(ehandle, EXT2_EXTENT_ROOT, &extent);
458 if (retval)
459 goto out;
460
461 do {
462 retval = ext2fs_extent_get_info(ehandle, &info);
463 if (retval)
464 break;
465
466 /*
467 * If this is the first extent in an extent block that we
468 * haven't visited, collect stats on the block.
469 */
470 if (info.curr_entry == 1 &&
471 !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) &&
472 !eti.force_rebuild) {
473 struct extent_tree_level *etl;
474
475 etl = eti.ext_info + info.curr_level;
476 etl->num_extents += info.num_entries;
477 etl->max_extents += info.max_entries;
478 /*
479 * Implementation wart: Splitting extent blocks when
480 * appending will leave the old block with one free
481 * entry. Therefore unless the node is totally full,
482 * pretend that a non-root extent block can hold one
483 * fewer entry than it actually does, so that we don't
484 * repeatedly rebuild the extent tree.
485 */
486 if (info.curr_level &&
487 info.num_entries < info.max_entries)
488 etl->max_extents--;
489 }
490
491 /* Skip to the end of a block of leaf nodes */
492 if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
493 retval = ext2fs_extent_get(ehandle,
494 EXT2_EXTENT_LAST_SIB,
495 &extent);
496 if (retval)
497 break;
498 }
499
500 retval = ext2fs_extent_get(ehandle, EXT2_EXTENT_NEXT, &extent);
501 } while (retval == 0);
502out:
503 ext2fs_extent_free(ehandle);
504 return e2fsck_should_rebuild_extents(ctx, pctx, &eti, &top_info);
505}
506
507/* Having scanned a file's extent tree, decide if we should rebuild it */
508errcode_t e2fsck_should_rebuild_extents(e2fsck_t ctx,
509 struct problem_context *pctx,
510 struct extent_tree_info *eti,
511 struct ext2_extent_info *info)
512{
513 struct extent_tree_level *ei;
514 int i, j, op;
515 unsigned int extents_per_block;
516
517 if (eti->force_rebuild)
518 goto rebuild;
519
0b76d709
TT
520 if (ctx->options & E2F_OPT_NOOPT_EXTENTS)
521 return 0;
522
e228d700
DW
523 extents_per_block = (ctx->fs->blocksize -
524 sizeof(struct ext3_extent_header)) /
525 sizeof(struct ext3_extent);
526 /*
527 * If we can consolidate a level or shorten the tree, schedule the
528 * extent tree to be rebuilt.
529 */
530 for (i = 0, ei = eti->ext_info; i < info->max_depth + 1; i++, ei++) {
531 if (ei->max_extents - ei->num_extents > extents_per_block) {
532 pctx->blk = i;
533 op = PR_1E_CAN_NARROW_EXTENT_TREE;
534 goto rebuild;
535 }
536 for (j = 0; j < i; j++) {
537 if (ei->num_extents < eti->ext_info[j].max_extents) {
538 pctx->blk = i;
539 op = PR_1E_CAN_COLLAPSE_EXTENT_TREE;
540 goto rebuild;
541 }
542 }
543 }
544 return 0;
545
546rebuild:
547 if (eti->force_rebuild || fix_problem(ctx, op, pctx))
548 return e2fsck_rebuild_extents_later(ctx, eti->ino);
549 return 0;
550}
551
552void e2fsck_pass1e(e2fsck_t ctx)
553{
554 rebuild_extents(ctx, "Pass 1E", PR_1E_PASS_HEADER);
555}