diff options
Diffstat (limited to 'e2fsprogs/e2fsck/pass1b.c')
-rw-r--r-- | e2fsprogs/e2fsck/pass1b.c | 805 |
1 files changed, 0 insertions, 805 deletions
diff --git a/e2fsprogs/e2fsck/pass1b.c b/e2fsprogs/e2fsck/pass1b.c deleted file mode 100644 index ff63807b7..000000000 --- a/e2fsprogs/e2fsck/pass1b.c +++ /dev/null | |||
@@ -1,805 +0,0 @@ | |||
1 | /* | ||
2 | * pass1b.c --- Pass #1b of e2fsck | ||
3 | * | ||
4 | * This file contains pass1B, pass1C, and pass1D of e2fsck. They are | ||
5 | * only invoked if pass 1 discovered blocks which are in use by more | ||
6 | * than one inode. | ||
7 | * | ||
8 | * Pass1B scans the data blocks of all the inodes again, generating a | ||
9 | * complete list of duplicate blocks and which inodes have claimed | ||
10 | * them. | ||
11 | * | ||
12 | * Pass1C does a tree-traversal of the filesystem, to determine the | ||
13 | * parent directories of these inodes. This step is necessary so that | ||
14 | * e2fsck can print out the pathnames of affected inodes. | ||
15 | * | ||
16 | * Pass1D is a reconciliation pass. For each inode with duplicate | ||
17 | * blocks, the user is prompted if s/he would like to clone the file | ||
18 | * (so that the file gets a fresh copy of the duplicated blocks) or | ||
19 | * simply to delete the file. | ||
20 | * | ||
21 | * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. | ||
22 | * | ||
23 | * %Begin-Header% | ||
24 | * This file may be redistributed under the terms of the GNU Public | ||
25 | * License. | ||
26 | * %End-Header% | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | #include <time.h> | ||
31 | #ifdef HAVE_ERRNO_H | ||
32 | #include <errno.h> | ||
33 | #endif | ||
34 | |||
35 | #ifdef HAVE_INTTYPES_H | ||
36 | #include <inttypes.h> | ||
37 | #endif | ||
38 | |||
39 | /* Needed for architectures where sizeof(int) != sizeof(void *) */ | ||
40 | #define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val)) | ||
41 | #define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr)) | ||
42 | |||
43 | #include "e2fsck.h" | ||
44 | |||
45 | #include "problem.h" | ||
46 | #include "dict.h" | ||
47 | |||
48 | /* Define an extension to the ext2 library's block count information */ | ||
49 | #define BLOCK_COUNT_EXTATTR (-5) | ||
50 | |||
51 | struct block_el { | ||
52 | blk_t block; | ||
53 | struct block_el *next; | ||
54 | }; | ||
55 | |||
56 | struct inode_el { | ||
57 | ext2_ino_t inode; | ||
58 | struct inode_el *next; | ||
59 | }; | ||
60 | |||
61 | struct dup_block { | ||
62 | int num_bad; | ||
63 | struct inode_el *inode_list; | ||
64 | }; | ||
65 | |||
66 | /* | ||
67 | * This structure stores information about a particular inode which | ||
68 | * is sharing blocks with other inodes. This information is collected | ||
69 | * to display to the user, so that the user knows what files he or she | ||
70 | * is dealing with, when trying to decide how to resolve the conflict | ||
71 | * of multiply-claimed blocks. | ||
72 | */ | ||
73 | struct dup_inode { | ||
74 | ext2_ino_t dir; | ||
75 | int num_dupblocks; | ||
76 | struct ext2_inode inode; | ||
77 | struct block_el *block_list; | ||
78 | }; | ||
79 | |||
80 | static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr, | ||
81 | e2_blkcnt_t blockcnt, blk_t ref_blk, | ||
82 | int ref_offset, void *priv_data); | ||
83 | static void delete_file(e2fsck_t ctx, ext2_ino_t ino, | ||
84 | struct dup_inode *dp, char *block_buf); | ||
85 | static int clone_file(e2fsck_t ctx, ext2_ino_t ino, | ||
86 | struct dup_inode *dp, char* block_buf); | ||
87 | static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk); | ||
88 | |||
89 | static void pass1b(e2fsck_t ctx, char *block_buf); | ||
90 | static void pass1c(e2fsck_t ctx, char *block_buf); | ||
91 | static void pass1d(e2fsck_t ctx, char *block_buf); | ||
92 | |||
93 | static int dup_inode_count = 0; | ||
94 | |||
95 | static dict_t blk_dict, ino_dict; | ||
96 | |||
97 | static ext2fs_inode_bitmap inode_dup_map; | ||
98 | |||
99 | static int dict_int_cmp(const void *a, const void *b) | ||
100 | { | ||
101 | intptr_t ia, ib; | ||
102 | |||
103 | ia = (intptr_t)a; | ||
104 | ib = (intptr_t)b; | ||
105 | |||
106 | return (ia-ib); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * Add a duplicate block record | ||
111 | */ | ||
112 | static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk, | ||
113 | struct ext2_inode *inode) | ||
114 | { | ||
115 | dnode_t *n; | ||
116 | struct dup_block *db; | ||
117 | struct dup_inode *di; | ||
118 | struct block_el *blk_el; | ||
119 | struct inode_el *ino_el; | ||
120 | |||
121 | n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); | ||
122 | if (n) | ||
123 | db = (struct dup_block *) dnode_get(n); | ||
124 | else { | ||
125 | db = (struct dup_block *) e2fsck_allocate_memory(ctx, | ||
126 | sizeof(struct dup_block), "duplicate block header"); | ||
127 | db->num_bad = 0; | ||
128 | db->inode_list = 0; | ||
129 | dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db); | ||
130 | } | ||
131 | ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx, | ||
132 | sizeof(struct inode_el), "inode element"); | ||
133 | ino_el->inode = ino; | ||
134 | ino_el->next = db->inode_list; | ||
135 | db->inode_list = ino_el; | ||
136 | db->num_bad++; | ||
137 | |||
138 | n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino)); | ||
139 | if (n) | ||
140 | di = (struct dup_inode *) dnode_get(n); | ||
141 | else { | ||
142 | di = (struct dup_inode *) e2fsck_allocate_memory(ctx, | ||
143 | sizeof(struct dup_inode), "duplicate inode header"); | ||
144 | di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ; | ||
145 | di->num_dupblocks = 0; | ||
146 | di->block_list = 0; | ||
147 | di->inode = *inode; | ||
148 | dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di); | ||
149 | } | ||
150 | blk_el = (struct block_el *) e2fsck_allocate_memory(ctx, | ||
151 | sizeof(struct block_el), "block element"); | ||
152 | blk_el->block = blk; | ||
153 | blk_el->next = di->block_list; | ||
154 | di->block_list = blk_el; | ||
155 | di->num_dupblocks++; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Free a duplicate inode record | ||
160 | */ | ||
161 | static void inode_dnode_free(dnode_t *node, | ||
162 | void *context EXT2FS_ATTR((unused))) | ||
163 | { | ||
164 | struct dup_inode *di; | ||
165 | struct block_el *p, *next; | ||
166 | |||
167 | di = (struct dup_inode *) dnode_get(node); | ||
168 | for (p = di->block_list; p; p = next) { | ||
169 | next = p->next; | ||
170 | free(p); | ||
171 | } | ||
172 | free(node); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Free a duplicate block record | ||
177 | */ | ||
178 | static void block_dnode_free(dnode_t *node, | ||
179 | void *context EXT2FS_ATTR((unused))) | ||
180 | { | ||
181 | struct dup_block *db; | ||
182 | struct inode_el *p, *next; | ||
183 | |||
184 | db = (struct dup_block *) dnode_get(node); | ||
185 | for (p = db->inode_list; p; p = next) { | ||
186 | next = p->next; | ||
187 | free(p); | ||
188 | } | ||
189 | free(node); | ||
190 | } | ||
191 | |||
192 | |||
193 | /* | ||
194 | * Main procedure for handling duplicate blocks | ||
195 | */ | ||
196 | void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf) | ||
197 | { | ||
198 | ext2_filsys fs = ctx->fs; | ||
199 | struct problem_context pctx; | ||
200 | |||
201 | clear_problem_context(&pctx); | ||
202 | |||
203 | pctx.errcode = ext2fs_allocate_inode_bitmap(fs, | ||
204 | _("multiply claimed inode map"), &inode_dup_map); | ||
205 | if (pctx.errcode) { | ||
206 | fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx); | ||
207 | ctx->flags |= E2F_FLAG_ABORT; | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp); | ||
212 | dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp); | ||
213 | dict_set_allocator(&ino_dict, NULL, inode_dnode_free, NULL); | ||
214 | dict_set_allocator(&blk_dict, NULL, block_dnode_free, NULL); | ||
215 | |||
216 | pass1b(ctx, block_buf); | ||
217 | pass1c(ctx, block_buf); | ||
218 | pass1d(ctx, block_buf); | ||
219 | |||
220 | /* | ||
221 | * Time to free all of the accumulated data structures that we | ||
222 | * don't need anymore. | ||
223 | */ | ||
224 | dict_free_nodes(&ino_dict); | ||
225 | dict_free_nodes(&blk_dict); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Scan the inodes looking for inodes that contain duplicate blocks. | ||
230 | */ | ||
231 | struct process_block_struct { | ||
232 | e2fsck_t ctx; | ||
233 | ext2_ino_t ino; | ||
234 | int dup_blocks; | ||
235 | struct ext2_inode *inode; | ||
236 | struct problem_context *pctx; | ||
237 | }; | ||
238 | |||
239 | static void pass1b(e2fsck_t ctx, char *block_buf) | ||
240 | { | ||
241 | ext2_filsys fs = ctx->fs; | ||
242 | ext2_ino_t ino; | ||
243 | struct ext2_inode inode; | ||
244 | ext2_inode_scan scan; | ||
245 | struct process_block_struct pb; | ||
246 | struct problem_context pctx; | ||
247 | |||
248 | clear_problem_context(&pctx); | ||
249 | |||
250 | if (!(ctx->options & E2F_OPT_PREEN)) | ||
251 | fix_problem(ctx, PR_1B_PASS_HEADER, &pctx); | ||
252 | pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, | ||
253 | &scan); | ||
254 | if (pctx.errcode) { | ||
255 | fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); | ||
256 | ctx->flags |= E2F_FLAG_ABORT; | ||
257 | return; | ||
258 | } | ||
259 | ctx->stashed_inode = &inode; | ||
260 | pb.ctx = ctx; | ||
261 | pb.pctx = &pctx; | ||
262 | pctx.str = "pass1b"; | ||
263 | while (1) { | ||
264 | pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode); | ||
265 | if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) | ||
266 | continue; | ||
267 | if (pctx.errcode) { | ||
268 | fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx); | ||
269 | ctx->flags |= E2F_FLAG_ABORT; | ||
270 | return; | ||
271 | } | ||
272 | if (!ino) | ||
273 | break; | ||
274 | pctx.ino = ctx->stashed_ino = ino; | ||
275 | if ((ino != EXT2_BAD_INO) && | ||
276 | !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)) | ||
277 | continue; | ||
278 | |||
279 | pb.ino = ino; | ||
280 | pb.dup_blocks = 0; | ||
281 | pb.inode = &inode; | ||
282 | |||
283 | if (ext2fs_inode_has_valid_blocks(&inode) || | ||
284 | (ino == EXT2_BAD_INO)) | ||
285 | pctx.errcode = ext2fs_block_iterate2(fs, ino, | ||
286 | 0, block_buf, process_pass1b_block, &pb); | ||
287 | if (inode.i_file_acl) | ||
288 | process_pass1b_block(fs, &inode.i_file_acl, | ||
289 | BLOCK_COUNT_EXTATTR, 0, 0, &pb); | ||
290 | if (pb.dup_blocks) { | ||
291 | end_problem_latch(ctx, PR_LATCH_DBLOCK); | ||
292 | if (ino >= EXT2_FIRST_INODE(fs->super) || | ||
293 | ino == EXT2_ROOT_INO) | ||
294 | dup_inode_count++; | ||
295 | } | ||
296 | if (pctx.errcode) | ||
297 | fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); | ||
298 | } | ||
299 | ext2fs_close_inode_scan(scan); | ||
300 | e2fsck_use_inode_shortcuts(ctx, 0); | ||
301 | } | ||
302 | |||
303 | static int process_pass1b_block(ext2_filsys fs EXT2FS_ATTR((unused)), | ||
304 | blk_t *block_nr, | ||
305 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), | ||
306 | blk_t ref_blk EXT2FS_ATTR((unused)), | ||
307 | int ref_offset EXT2FS_ATTR((unused)), | ||
308 | void *priv_data) | ||
309 | { | ||
310 | struct process_block_struct *p; | ||
311 | e2fsck_t ctx; | ||
312 | |||
313 | if (HOLE_BLKADDR(*block_nr)) | ||
314 | return 0; | ||
315 | p = (struct process_block_struct *) priv_data; | ||
316 | ctx = p->ctx; | ||
317 | |||
318 | if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) | ||
319 | return 0; | ||
320 | |||
321 | /* OK, this is a duplicate block */ | ||
322 | if (p->ino != EXT2_BAD_INO) { | ||
323 | p->pctx->blk = *block_nr; | ||
324 | fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx); | ||
325 | } | ||
326 | p->dup_blocks++; | ||
327 | ext2fs_mark_inode_bitmap(inode_dup_map, p->ino); | ||
328 | |||
329 | add_dupe(ctx, p->ino, *block_nr, p->inode); | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | /* | ||
335 | * Pass 1c: Scan directories for inodes with duplicate blocks. This | ||
336 | * is used so that we can print pathnames when prompting the user for | ||
337 | * what to do. | ||
338 | */ | ||
339 | struct search_dir_struct { | ||
340 | int count; | ||
341 | ext2_ino_t first_inode; | ||
342 | ext2_ino_t max_inode; | ||
343 | }; | ||
344 | |||
345 | static int search_dirent_proc(ext2_ino_t dir, int entry, | ||
346 | struct ext2_dir_entry *dirent, | ||
347 | int offset EXT2FS_ATTR((unused)), | ||
348 | int blocksize EXT2FS_ATTR((unused)), | ||
349 | char *buf EXT2FS_ATTR((unused)), | ||
350 | void *priv_data) | ||
351 | { | ||
352 | struct search_dir_struct *sd; | ||
353 | struct dup_inode *p; | ||
354 | dnode_t *n; | ||
355 | |||
356 | sd = (struct search_dir_struct *) priv_data; | ||
357 | |||
358 | if (dirent->inode > sd->max_inode) | ||
359 | /* Should abort this inode, but not everything */ | ||
360 | return 0; | ||
361 | |||
362 | if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) || | ||
363 | !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode)) | ||
364 | return 0; | ||
365 | |||
366 | n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode)); | ||
367 | if (!n) | ||
368 | return 0; | ||
369 | p = (struct dup_inode *) dnode_get(n); | ||
370 | p->dir = dir; | ||
371 | sd->count--; | ||
372 | |||
373 | return(sd->count ? 0 : DIRENT_ABORT); | ||
374 | } | ||
375 | |||
376 | |||
377 | static void pass1c(e2fsck_t ctx, char *block_buf) | ||
378 | { | ||
379 | ext2_filsys fs = ctx->fs; | ||
380 | struct search_dir_struct sd; | ||
381 | struct problem_context pctx; | ||
382 | |||
383 | clear_problem_context(&pctx); | ||
384 | |||
385 | if (!(ctx->options & E2F_OPT_PREEN)) | ||
386 | fix_problem(ctx, PR_1C_PASS_HEADER, &pctx); | ||
387 | |||
388 | /* | ||
389 | * Search through all directories to translate inodes to names | ||
390 | * (by searching for the containing directory for that inode.) | ||
391 | */ | ||
392 | sd.count = dup_inode_count; | ||
393 | sd.first_inode = EXT2_FIRST_INODE(fs->super); | ||
394 | sd.max_inode = fs->super->s_inodes_count; | ||
395 | ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf, | ||
396 | search_dirent_proc, &sd); | ||
397 | } | ||
398 | |||
399 | static void pass1d(e2fsck_t ctx, char *block_buf) | ||
400 | { | ||
401 | ext2_filsys fs = ctx->fs; | ||
402 | struct dup_inode *p, *t; | ||
403 | struct dup_block *q; | ||
404 | ext2_ino_t *shared, ino; | ||
405 | int shared_len; | ||
406 | int i; | ||
407 | int file_ok; | ||
408 | int meta_data = 0; | ||
409 | struct problem_context pctx; | ||
410 | dnode_t *n, *m; | ||
411 | struct block_el *s; | ||
412 | struct inode_el *r; | ||
413 | |||
414 | clear_problem_context(&pctx); | ||
415 | |||
416 | if (!(ctx->options & E2F_OPT_PREEN)) | ||
417 | fix_problem(ctx, PR_1D_PASS_HEADER, &pctx); | ||
418 | e2fsck_read_bitmaps(ctx); | ||
419 | |||
420 | pctx.num = dup_inode_count; /* dict_count(&ino_dict); */ | ||
421 | fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx); | ||
422 | shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx, | ||
423 | sizeof(ext2_ino_t) * dict_count(&ino_dict), | ||
424 | "Shared inode list"); | ||
425 | for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) { | ||
426 | p = (struct dup_inode *) dnode_get(n); | ||
427 | shared_len = 0; | ||
428 | file_ok = 1; | ||
429 | ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n)); | ||
430 | if (ino == EXT2_BAD_INO) | ||
431 | continue; | ||
432 | |||
433 | /* | ||
434 | * Find all of the inodes which share blocks with this | ||
435 | * one. First we find all of the duplicate blocks | ||
436 | * belonging to this inode, and then search each block | ||
437 | * get the list of inodes, and merge them together. | ||
438 | */ | ||
439 | for (s = p->block_list; s; s = s->next) { | ||
440 | m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block)); | ||
441 | if (!m) | ||
442 | continue; /* Should never happen... */ | ||
443 | q = (struct dup_block *) dnode_get(m); | ||
444 | if (q->num_bad > 1) | ||
445 | file_ok = 0; | ||
446 | if (check_if_fs_block(ctx, s->block)) { | ||
447 | file_ok = 0; | ||
448 | meta_data = 1; | ||
449 | } | ||
450 | |||
451 | /* | ||
452 | * Add all inodes used by this block to the | ||
453 | * shared[] --- which is a unique list, so | ||
454 | * if an inode is already in shared[], don't | ||
455 | * add it again. | ||
456 | */ | ||
457 | for (r = q->inode_list; r; r = r->next) { | ||
458 | if (r->inode == ino) | ||
459 | continue; | ||
460 | for (i = 0; i < shared_len; i++) | ||
461 | if (shared[i] == r->inode) | ||
462 | break; | ||
463 | if (i == shared_len) { | ||
464 | shared[shared_len++] = r->inode; | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * Report the inode that we are working on | ||
471 | */ | ||
472 | pctx.inode = &p->inode; | ||
473 | pctx.ino = ino; | ||
474 | pctx.dir = p->dir; | ||
475 | pctx.blkcount = p->num_dupblocks; | ||
476 | pctx.num = meta_data ? shared_len+1 : shared_len; | ||
477 | fix_problem(ctx, PR_1D_DUP_FILE, &pctx); | ||
478 | pctx.blkcount = 0; | ||
479 | pctx.num = 0; | ||
480 | |||
481 | if (meta_data) | ||
482 | fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx); | ||
483 | |||
484 | for (i = 0; i < shared_len; i++) { | ||
485 | m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i])); | ||
486 | if (!m) | ||
487 | continue; /* should never happen */ | ||
488 | t = (struct dup_inode *) dnode_get(m); | ||
489 | /* | ||
490 | * Report the inode that we are sharing with | ||
491 | */ | ||
492 | pctx.inode = &t->inode; | ||
493 | pctx.ino = shared[i]; | ||
494 | pctx.dir = t->dir; | ||
495 | fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx); | ||
496 | } | ||
497 | if (file_ok) { | ||
498 | fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx); | ||
499 | continue; | ||
500 | } | ||
501 | if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) { | ||
502 | pctx.errcode = clone_file(ctx, ino, p, block_buf); | ||
503 | if (pctx.errcode) | ||
504 | fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx); | ||
505 | else | ||
506 | continue; | ||
507 | } | ||
508 | if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx)) | ||
509 | delete_file(ctx, ino, p, block_buf); | ||
510 | else | ||
511 | ext2fs_unmark_valid(fs); | ||
512 | } | ||
513 | ext2fs_free_mem(&shared); | ||
514 | } | ||
515 | |||
516 | /* | ||
517 | * Drop the refcount on the dup_block structure, and clear the entry | ||
518 | * in the block_dup_map if appropriate. | ||
519 | */ | ||
520 | static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p) | ||
521 | { | ||
522 | p->num_bad--; | ||
523 | if (p->num_bad <= 0 || | ||
524 | (p->num_bad == 1 && !check_if_fs_block(ctx, block))) | ||
525 | ext2fs_unmark_block_bitmap(ctx->block_dup_map, block); | ||
526 | } | ||
527 | |||
528 | static int delete_file_block(ext2_filsys fs, | ||
529 | blk_t *block_nr, | ||
530 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), | ||
531 | blk_t ref_block EXT2FS_ATTR((unused)), | ||
532 | int ref_offset EXT2FS_ATTR((unused)), | ||
533 | void *priv_data) | ||
534 | { | ||
535 | struct process_block_struct *pb; | ||
536 | struct dup_block *p; | ||
537 | dnode_t *n; | ||
538 | e2fsck_t ctx; | ||
539 | |||
540 | pb = (struct process_block_struct *) priv_data; | ||
541 | ctx = pb->ctx; | ||
542 | |||
543 | if (HOLE_BLKADDR(*block_nr)) | ||
544 | return 0; | ||
545 | |||
546 | if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { | ||
547 | n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); | ||
548 | if (n) { | ||
549 | p = (struct dup_block *) dnode_get(n); | ||
550 | decrement_badcount(ctx, *block_nr, p); | ||
551 | } else | ||
552 | com_err("delete_file_block", 0, | ||
553 | _("internal error; can't find dup_blk for %d\n"), | ||
554 | *block_nr); | ||
555 | } else { | ||
556 | ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr); | ||
557 | ext2fs_block_alloc_stats(fs, *block_nr, -1); | ||
558 | } | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | static void delete_file(e2fsck_t ctx, ext2_ino_t ino, | ||
564 | struct dup_inode *dp, char* block_buf) | ||
565 | { | ||
566 | ext2_filsys fs = ctx->fs; | ||
567 | struct process_block_struct pb; | ||
568 | struct ext2_inode inode; | ||
569 | struct problem_context pctx; | ||
570 | unsigned int count; | ||
571 | |||
572 | clear_problem_context(&pctx); | ||
573 | pctx.ino = pb.ino = ino; | ||
574 | pb.dup_blocks = dp->num_dupblocks; | ||
575 | pb.ctx = ctx; | ||
576 | pctx.str = "delete_file"; | ||
577 | |||
578 | e2fsck_read_inode(ctx, ino, &inode, "delete_file"); | ||
579 | if (ext2fs_inode_has_valid_blocks(&inode)) | ||
580 | pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, | ||
581 | delete_file_block, &pb); | ||
582 | if (pctx.errcode) | ||
583 | fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); | ||
584 | ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino); | ||
585 | ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino); | ||
586 | if (ctx->inode_bad_map) | ||
587 | ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino); | ||
588 | ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode)); | ||
589 | |||
590 | /* Inode may have changed by block_iterate, so reread it */ | ||
591 | e2fsck_read_inode(ctx, ino, &inode, "delete_file"); | ||
592 | inode.i_links_count = 0; | ||
593 | inode.i_dtime = time(0); | ||
594 | if (inode.i_file_acl && | ||
595 | (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) { | ||
596 | count = 1; | ||
597 | pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl, | ||
598 | block_buf, -1, &count); | ||
599 | if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) { | ||
600 | pctx.errcode = 0; | ||
601 | count = 1; | ||
602 | } | ||
603 | if (pctx.errcode) { | ||
604 | pctx.blk = inode.i_file_acl; | ||
605 | fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx); | ||
606 | } | ||
607 | /* | ||
608 | * If the count is zero, then arrange to have the | ||
609 | * block deleted. If the block is in the block_dup_map, | ||
610 | * also call delete_file_block since it will take care | ||
611 | * of keeping the accounting straight. | ||
612 | */ | ||
613 | if ((count == 0) || | ||
614 | ext2fs_test_block_bitmap(ctx->block_dup_map, | ||
615 | inode.i_file_acl)) | ||
616 | delete_file_block(fs, &inode.i_file_acl, | ||
617 | BLOCK_COUNT_EXTATTR, 0, 0, &pb); | ||
618 | } | ||
619 | e2fsck_write_inode(ctx, ino, &inode, "delete_file"); | ||
620 | } | ||
621 | |||
622 | struct clone_struct { | ||
623 | errcode_t errcode; | ||
624 | ext2_ino_t dir; | ||
625 | char *buf; | ||
626 | e2fsck_t ctx; | ||
627 | }; | ||
628 | |||
629 | static int clone_file_block(ext2_filsys fs, | ||
630 | blk_t *block_nr, | ||
631 | e2_blkcnt_t blockcnt, | ||
632 | blk_t ref_block EXT2FS_ATTR((unused)), | ||
633 | int ref_offset EXT2FS_ATTR((unused)), | ||
634 | void *priv_data) | ||
635 | { | ||
636 | struct dup_block *p; | ||
637 | blk_t new_block; | ||
638 | errcode_t retval; | ||
639 | struct clone_struct *cs = (struct clone_struct *) priv_data; | ||
640 | dnode_t *n; | ||
641 | e2fsck_t ctx; | ||
642 | |||
643 | ctx = cs->ctx; | ||
644 | |||
645 | if (HOLE_BLKADDR(*block_nr)) | ||
646 | return 0; | ||
647 | |||
648 | if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) { | ||
649 | n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr)); | ||
650 | if (n) { | ||
651 | p = (struct dup_block *) dnode_get(n); | ||
652 | retval = ext2fs_new_block(fs, 0, ctx->block_found_map, | ||
653 | &new_block); | ||
654 | if (retval) { | ||
655 | cs->errcode = retval; | ||
656 | return BLOCK_ABORT; | ||
657 | } | ||
658 | if (cs->dir && (blockcnt >= 0)) { | ||
659 | retval = ext2fs_set_dir_block(fs->dblist, | ||
660 | cs->dir, new_block, blockcnt); | ||
661 | if (retval) { | ||
662 | cs->errcode = retval; | ||
663 | return BLOCK_ABORT; | ||
664 | } | ||
665 | } | ||
666 | #if 0 | ||
667 | printf("Cloning block %u to %u\n", *block_nr, | ||
668 | new_block); | ||
669 | #endif | ||
670 | retval = io_channel_read_blk(fs->io, *block_nr, 1, | ||
671 | cs->buf); | ||
672 | if (retval) { | ||
673 | cs->errcode = retval; | ||
674 | return BLOCK_ABORT; | ||
675 | } | ||
676 | retval = io_channel_write_blk(fs->io, new_block, 1, | ||
677 | cs->buf); | ||
678 | if (retval) { | ||
679 | cs->errcode = retval; | ||
680 | return BLOCK_ABORT; | ||
681 | } | ||
682 | decrement_badcount(ctx, *block_nr, p); | ||
683 | *block_nr = new_block; | ||
684 | ext2fs_mark_block_bitmap(ctx->block_found_map, | ||
685 | new_block); | ||
686 | ext2fs_mark_block_bitmap(fs->block_map, new_block); | ||
687 | return BLOCK_CHANGED; | ||
688 | } else | ||
689 | com_err("clone_file_block", 0, | ||
690 | _("internal error; can't find dup_blk for %d\n"), | ||
691 | *block_nr); | ||
692 | } | ||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | static int clone_file(e2fsck_t ctx, ext2_ino_t ino, | ||
697 | struct dup_inode *dp, char* block_buf) | ||
698 | { | ||
699 | ext2_filsys fs = ctx->fs; | ||
700 | errcode_t retval; | ||
701 | struct clone_struct cs; | ||
702 | struct problem_context pctx; | ||
703 | blk_t blk; | ||
704 | dnode_t *n; | ||
705 | struct inode_el *ino_el; | ||
706 | struct dup_block *db; | ||
707 | struct dup_inode *di; | ||
708 | |||
709 | clear_problem_context(&pctx); | ||
710 | cs.errcode = 0; | ||
711 | cs.dir = 0; | ||
712 | cs.ctx = ctx; | ||
713 | retval = ext2fs_get_mem(fs->blocksize, &cs.buf); | ||
714 | if (retval) | ||
715 | return retval; | ||
716 | |||
717 | if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) | ||
718 | cs.dir = ino; | ||
719 | |||
720 | pctx.ino = ino; | ||
721 | pctx.str = "clone_file"; | ||
722 | if (ext2fs_inode_has_valid_blocks(&dp->inode)) | ||
723 | pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf, | ||
724 | clone_file_block, &cs); | ||
725 | ext2fs_mark_bb_dirty(fs); | ||
726 | if (pctx.errcode) { | ||
727 | fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx); | ||
728 | retval = pctx.errcode; | ||
729 | goto errout; | ||
730 | } | ||
731 | if (cs.errcode) { | ||
732 | com_err("clone_file", cs.errcode, | ||
733 | _("returned from clone_file_block")); | ||
734 | retval = cs.errcode; | ||
735 | goto errout; | ||
736 | } | ||
737 | /* The inode may have changed on disk, so we have to re-read it */ | ||
738 | e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA"); | ||
739 | blk = dp->inode.i_file_acl; | ||
740 | if (blk && (clone_file_block(fs, &dp->inode.i_file_acl, | ||
741 | BLOCK_COUNT_EXTATTR, 0, 0, &cs) == | ||
742 | BLOCK_CHANGED)) { | ||
743 | e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA"); | ||
744 | /* | ||
745 | * If we cloned the EA block, find all other inodes | ||
746 | * which refered to that EA block, and modify | ||
747 | * them to point to the new EA block. | ||
748 | */ | ||
749 | n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk)); | ||
750 | db = (struct dup_block *) dnode_get(n); | ||
751 | for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) { | ||
752 | if (ino_el->inode == ino) | ||
753 | continue; | ||
754 | n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode)); | ||
755 | di = (struct dup_inode *) dnode_get(n); | ||
756 | if (di->inode.i_file_acl == blk) { | ||
757 | di->inode.i_file_acl = dp->inode.i_file_acl; | ||
758 | e2fsck_write_inode(ctx, ino_el->inode, | ||
759 | &di->inode, "clone file EA"); | ||
760 | decrement_badcount(ctx, blk, db); | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | retval = 0; | ||
765 | errout: | ||
766 | ext2fs_free_mem(&cs.buf); | ||
767 | return retval; | ||
768 | } | ||
769 | |||
770 | /* | ||
771 | * This routine returns 1 if a block overlaps with one of the superblocks, | ||
772 | * group descriptors, inode bitmaps, or block bitmaps. | ||
773 | */ | ||
774 | static int check_if_fs_block(e2fsck_t ctx, blk_t test_block) | ||
775 | { | ||
776 | ext2_filsys fs = ctx->fs; | ||
777 | blk_t block; | ||
778 | dgrp_t i; | ||
779 | |||
780 | block = fs->super->s_first_data_block; | ||
781 | for (i = 0; i < fs->group_desc_count; i++) { | ||
782 | |||
783 | /* Check superblocks/block group descriptros */ | ||
784 | if (ext2fs_bg_has_super(fs, i)) { | ||
785 | if (test_block >= block && | ||
786 | (test_block <= block + fs->desc_blocks)) | ||
787 | return 1; | ||
788 | } | ||
789 | |||
790 | /* Check the inode table */ | ||
791 | if ((fs->group_desc[i].bg_inode_table) && | ||
792 | (test_block >= fs->group_desc[i].bg_inode_table) && | ||
793 | (test_block < (fs->group_desc[i].bg_inode_table + | ||
794 | fs->inode_blocks_per_group))) | ||
795 | return 1; | ||
796 | |||
797 | /* Check the bitmap blocks */ | ||
798 | if ((test_block == fs->group_desc[i].bg_block_bitmap) || | ||
799 | (test_block == fs->group_desc[i].bg_inode_bitmap)) | ||
800 | return 1; | ||
801 | |||
802 | block += fs->super->s_blocks_per_group; | ||
803 | } | ||
804 | return 0; | ||
805 | } | ||