blob: 27ca98b85d807d1f4d219985fa4f8b1b37e341f2 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
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 *
Theodore Ts'o21c84b71997-04-29 16:15:03 +000021 * 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%
Theodore Ts'o3839e651997-04-26 13:21:57 +000027 *
28 */
29
30#include <time.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000031#ifdef HAVE_ERRNO_H
32#include <errno.h>
33#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000034
35#include <et/com_err.h>
36#include "e2fsck.h"
37
Theodore Ts'o21c84b71997-04-29 16:15:03 +000038#include "problem.h"
39
Theodore Ts'o3839e651997-04-26 13:21:57 +000040/*
41 * This is structure is allocated for each time that a block is
42 * claimed by more than one file. So if a particular block is claimed
43 * by 3 files, then three copies of this structure will be allocated,
44 * one for each conflict.
45 *
46 * The linked list structure is as follows:
47 *
48 * dup_blk --> block #34 --> block #35 --> block #47
49 * inode #12 inode #14 inode #17
50 * num_bad = 3 num_bad = 2 num_bad = 2
51 * | | |
52 * V V V
53 * block #34 block #35 block #47
54 * inode #14 inode #15 inode #23
55 * |
56 * V
57 * block #34
58 * inode #15
59 *
60 * The num_bad field indicates how many inodes are sharing a
61 * particular block, and is only stored in the first element of the
62 * linked list for a particular block. As the block conflicts are
63 * resolved, num_bad is decremented; when it reaches 1, then we no
64 * longer need to worry about that block.
65 */
66struct dup_block {
67 blk_t block; /* Block number */
68 ino_t ino; /* Inode number */
69 int num_bad;
70 /* Pointer to next dup record with different block */
71 struct dup_block *next_block;
72 /* Pointer to next dup record with different inode */
73 struct dup_block *next_inode;
74};
75
76/*
77 * This structure stores information about a particular inode which
78 * is sharing blocks with other inodes. This information is collected
79 * to display to the user, so that the user knows what files he or she
80 * is dealing with, when trying to decide how to resolve the conflict
81 * of multiply-claimed blocks.
82 */
83struct dup_inode {
Theodore Ts'o21c84b71997-04-29 16:15:03 +000084 ino_t ino, dir;
85 int num_dupblocks;
86 struct ext2_inode inode;
Theodore Ts'o3839e651997-04-26 13:21:57 +000087 struct dup_inode *next;
88};
89
Theodore Ts'o3839e651997-04-26 13:21:57 +000090static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
91 int blockcnt, void *private);
92static void delete_file(ext2_filsys fs, struct dup_inode *dp,
93 char *block_buf);
94static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf);
95static void pass1b(ext2_filsys fs, char *block_buf);
96static void pass1c(ext2_filsys fs, char *block_buf);
97static void pass1d(ext2_filsys fs, char *block_buf);
98
99static struct dup_block *dup_blk = 0;
100static struct dup_inode *dup_ino = 0;
101static int dup_inode_count = 0;
102
Theodore Ts'of3db3561997-04-26 13:34:30 +0000103static ext2fs_inode_bitmap inode_dup_map;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000104
105/*
106 * Main procedure for handling duplicate blocks
107 */
108void pass1_dupblocks(ext2_filsys fs, char *block_buf)
109{
110 errcode_t retval;
111 struct dup_block *p, *q, *next_p, *next_q;
112 struct dup_inode *r, *next_r;
113
Theodore Ts'of3db3561997-04-26 13:34:30 +0000114 retval = ext2fs_allocate_inode_bitmap(fs,
115 "multiply claimed inode map", &inode_dup_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000116 if (retval) {
117 com_err("ext2fs_allocate_inode_bitmap", retval,
118 "while allocating inode_dup_map");
119 fatal_error(0);
120 }
121
122 pass1b(fs, block_buf);
123 pass1c(fs, block_buf);
124 pass1d(fs, block_buf);
125
126 /*
127 * Time to free all of the accumulated data structures that we
128 * don't need anymore.
129 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000130 ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000131 ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000132 for (p = dup_blk; p; p = next_p) {
133 next_p = p->next_block;
134 for (q = p; q; q = next_q) {
135 next_q = q->next_inode;
136 free(q);
137 }
138 }
139 for (r = dup_ino; r; r = next_r) {
140 next_r = r->next;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000141 free(r);
142 }
143}
144
145/*
146 * Scan the inodes looking for inodes that contain duplicate blocks.
147 */
148struct process_block_struct {
149 ino_t ino;
150 int dup_blocks;
151};
152
153void pass1b(ext2_filsys fs, char *block_buf)
154{
155 ino_t ino;
156 struct ext2_inode inode;
157 ext2_inode_scan scan;
158 errcode_t retval;
159 struct process_block_struct pb;
160 struct dup_inode *dp;
161
162 printf("Duplicate blocks found... invoking duplicate block passes.\n");
163 printf("Pass 1B: Rescan for duplicate/bad blocks\n");
164 retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
165 if (retval) {
166 com_err(program_name, retval, "while opening inode scan");
167 fatal_error(0);
168 }
169 retval = ext2fs_get_next_inode(scan, &ino, &inode);
170 if (retval) {
171 com_err(program_name, retval, "while starting inode scan");
172 fatal_error(0);
173 }
174 stashed_inode = &inode;
175 while (ino) {
176 stashed_ino = ino;
177 if ((ino != EXT2_BAD_INO) &&
Theodore Ts'of3db3561997-04-26 13:34:30 +0000178 (!ext2fs_test_inode_bitmap(inode_used_map, ino) ||
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000179 !ext2fs_inode_has_valid_blocks(&inode)))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180 goto next;
181
182 pb.ino = ino;
183 pb.dup_blocks = 0;
184 retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
185 process_pass1b_block, &pb);
186 if (pb.dup_blocks) {
187 if (ino != EXT2_BAD_INO)
188 printf("\n");
189 dp = allocate_memory(sizeof(struct dup_inode),
190 "duplicate inode record");
191 dp->ino = ino;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000192 dp->dir = 0;
193 dp->inode = inode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000194 dp->num_dupblocks = pb.dup_blocks;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000195 dp->next = dup_ino;
196 dup_ino = dp;
197 if (ino != EXT2_BAD_INO)
198 dup_inode_count++;
199 }
200 if (retval)
201 com_err(program_name, retval,
202 "while calling ext2fs_block_iterate in pass1b");
203
204 next:
205 retval = ext2fs_get_next_inode(scan, &ino, &inode);
206 if (retval) {
207 com_err(program_name, retval,
208 "while doing inode scan");
209 fatal_error(0);
210 }
211 }
212 ext2fs_close_inode_scan(scan);
213 fs->get_blocks = 0;
214 fs->check_directory = 0;
215}
216
217int process_pass1b_block(ext2_filsys fs,
218 blk_t *block_nr,
219 int blockcnt,
220 void *private)
221{
222 struct process_block_struct *p;
223 struct dup_block *dp, *q, *r;
224 int i;
225
226 if (!*block_nr)
227 return 0;
228 p = (struct process_block_struct *) private;
229
Theodore Ts'of3db3561997-04-26 13:34:30 +0000230 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000231 /* OK, this is a duplicate block */
232 if (p->ino != EXT2_BAD_INO) {
233 if (!p->dup_blocks)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000234 printf("Duplicate/bad block(s) in inode %lu:",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000235 p->ino);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000236 printf(" %u", *block_nr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000237 }
238 p->dup_blocks++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000239 ext2fs_mark_block_bitmap(block_dup_map, *block_nr);
240 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000241 dp = allocate_memory(sizeof(struct dup_block),
242 "duplicate block record");
243 dp->block = *block_nr;
244 dp->ino = p->ino;
245 dp->num_bad = 0;
246 q = dup_blk;
247 while (q) {
248 if (q->block == *block_nr)
249 break;
250 q = q->next_block;
251 }
252 if (q) {
253 dp->next_inode = q->next_inode;
254 q->next_inode = dp;
255 } else {
256 dp->next_block = dup_blk;
257 dup_blk = dp;
258 }
259 }
260 /*
261 * Set the num_bad field
262 */
263 for (q = dup_blk; q; q = q->next_block) {
264 i = 0;
265 for (r = q; r; r = r->next_inode)
266 i++;
267 q->num_bad = i;
268 }
269 return 0;
270}
271
272/*
Theodore Ts'o3839e651997-04-26 13:21:57 +0000273 * Pass 1c: Scan directories for inodes with duplicate blocks. This
274 * is used so that we can print pathnames when prompting the user for
275 * what to do.
276 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000277struct search_dir_struct {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000278 int count;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000279 ino_t first_inode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000280};
281
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000282static int search_dirent_proc(ino_t dir, int entry,
283 struct ext2_dir_entry *dirent,
284 int offset, int blocksize,
285 char *buf, void *private)
286{
287 struct search_dir_struct *sd = private;
288 struct dup_inode *p;
289
290 if (!dirent->inode || (entry < DIRENT_OTHER_FILE) ||
291 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
292 return 0;
293
294 for (p = dup_ino; p; p = p->next) {
295 if ((p->ino >= sd->first_inode) &&
296 (p->ino == dirent->inode))
297 break;
298 }
299
300 if (!p || p->dir)
301 return 0;
302
303 p->dir = dir;
304 sd->count--;
305
306 return(sd->count ? 0 : DIRENT_ABORT);
307}
308
309
Theodore Ts'o3839e651997-04-26 13:21:57 +0000310void pass1c(ext2_filsys fs, char *block_buf)
311{
Theodore Ts'o3839e651997-04-26 13:21:57 +0000312 struct dup_inode *p;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000313 int inodes_left = dup_inode_count;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000314 struct search_dir_struct sd;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000315
316 printf("Pass 1C: Scan directories for inodes with dup blocks.\n");
317
318 /*
319 * First check to see if any of the inodes with dup blocks is
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000320 * a special inode. (Note that the bad block inode isn't
321 * counted.)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000322 */
323 for (p = dup_ino; p; p = p->next) {
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000324 if ((p->ino < EXT2_FIRST_INODE(fs->super)) &&
325 (p->ino != EXT2_BAD_INO))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000326 inodes_left--;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000327 }
328
329 /*
330 * Search through all directories to translate inodes to names
331 * (by searching for the containing directory for that inode.)
332 */
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000333 sd.count = inodes_left;
334 sd.first_inode = EXT2_FIRST_INODE(fs->super);
335 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
336 search_dirent_proc, &sd);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000337}
338
339static void pass1d(ext2_filsys fs, char *block_buf)
340{
341 struct dup_inode *p, *s;
342 struct dup_block *q, *r;
343 ino_t *shared;
344 int shared_len;
345 int i;
346 errcode_t retval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000347 int file_ok;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000348 struct problem_context pctx;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000349
350 printf("Pass 1D: Reconciling duplicate blocks\n");
351 read_bitmaps(fs);
352
353 printf("(There are %d inodes containing duplicate/bad blocks.)\n\n",
354 dup_inode_count);
355 shared = allocate_memory(sizeof(ino_t) * dup_inode_count,
356 "Shared inode list");
357 for (p = dup_ino; p; p = p->next) {
358 shared_len = 0;
359 file_ok = 1;
360 if (p->ino == EXT2_BAD_INO)
361 continue;
362
363 /*
364 * Search through the duplicate records to see which
365 * inodes share blocks with this one
366 */
367 for (q = dup_blk; q; q = q->next_block) {
368 /*
369 * See if this block is used by this inode.
370 * If it isn't, continue.
371 */
372 for (r = q; r; r = r->next_inode)
373 if (r->ino == p->ino)
374 break;
375 if (!r)
376 continue;
377 if (q->num_bad > 1)
378 file_ok = 0;
379 /*
380 * Add all inodes used by this block to the
381 * shared[] --- which is a unique list, so
382 * if an inode is already in shared[], don't
383 * add it again.
384 */
385 for (r = q; r; r = r->next_inode) {
386 if (r->ino == p->ino)
387 continue;
388 for (i = 0; i < shared_len; i++)
389 if (shared[i] == r->ino)
390 break;
391 if (i == shared_len) {
392 shared[shared_len++] = r->ino;
393 }
394 }
395 }
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000396
397 /*
398 * Report the inode that we are working on
399 */
400 clear_problem_context(&pctx);
401 pctx.inode = &p->inode;
402 pctx.ino = p->ino;
403 pctx.dir = p->dir;
404 pctx.blkcount = p->num_dupblocks;
405 pctx.num = shared_len;
406 fix_problem(fs, PR_1B_DUP_FILE, &pctx);
407 pctx.blkcount = 0;
408 pctx.num = 0;
409
Theodore Ts'o3839e651997-04-26 13:21:57 +0000410 for (i = 0; i < shared_len; i++) {
411 for (s = dup_ino; s; s = s->next)
412 if (s->ino == shared[i])
413 break;
414 if (!s)
415 continue;
Theodore Ts'o21c84b71997-04-29 16:15:03 +0000416 /*
417 * Report the inode that we are sharing with
418 */
419 pctx.inode = &s->inode;
420 pctx.ino = s->ino;
421 pctx.dir = s->dir;
422 fix_problem(fs, PR_1B_DUP_FILE_LIST, &pctx);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000423 }
424 if (file_ok) {
425 printf("Duplicated blocks already reassigned or cloned.\n\n");
426 continue;
427 }
428
429 if (ask("Clone duplicate/bad blocks", 1)) {
430 retval = clone_file(fs, p, block_buf);
431 if (retval)
432 printf("Couldn't clone file: %s\n",
433 error_message(retval));
434 else {
435 printf("\n");
436 continue;
437 }
438 }
439 if (ask("Delete file", 1))
440 delete_file(fs, p, block_buf);
441 else
442 ext2fs_unmark_valid(fs);
443 printf("\n");
444 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000445 free(shared);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000446}
447
448static int delete_file_block(ext2_filsys fs,
449 blk_t *block_nr,
450 int blockcnt,
451 void *private)
452{
453 struct dup_block *p;
454
455 if (!*block_nr)
456 return 0;
457
Theodore Ts'of3db3561997-04-26 13:34:30 +0000458 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000459 for (p = dup_blk; p; p = p->next_block)
460 if (p->block == *block_nr)
461 break;
462 if (p) {
463 p->num_bad--;
464 if (p->num_bad == 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000465 ext2fs_unmark_block_bitmap(block_dup_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000466 *block_nr);
467 } else
468 com_err("delete_file_block", 0,
469 "internal error; can't find dup_blk for %d\n",
470 *block_nr);
471 } else {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000472 ext2fs_unmark_block_bitmap(block_found_map, *block_nr);
473 ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000474 }
475
476 return 0;
477}
478
479static void delete_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
480{
481 errcode_t retval;
482 struct process_block_struct pb;
483 struct ext2_inode inode;
484
485 pb.ino = dp->ino;
486 pb.dup_blocks = dp->num_dupblocks;
487
488 retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
489 delete_file_block, &pb);
490 if (retval)
491 com_err("delete_file", retval,
492 "while calling ext2fs_block_iterate for inode %d",
493 dp->ino);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000494 ext2fs_unmark_inode_bitmap(inode_used_map, dp->ino);
495 ext2fs_unmark_inode_bitmap(inode_dir_map, dp->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000496 if (inode_bad_map)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000497 ext2fs_unmark_inode_bitmap(inode_bad_map, dp->ino);
498 ext2fs_unmark_inode_bitmap(fs->inode_map, dp->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000499 ext2fs_mark_ib_dirty(fs);
500 ext2fs_mark_bb_dirty(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000501 e2fsck_read_inode(fs, dp->ino, &inode, "delete_file");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000502 inode.i_links_count = 0;
503 inode.i_dtime = time(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000504 e2fsck_write_inode(fs, dp->ino, &inode, "delete_file");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000505}
506
507struct clone_struct {
508 errcode_t errcode;
509 char *buf;
510};
511
512static int clone_file_block(ext2_filsys fs,
513 blk_t *block_nr,
514 int blockcnt,
515 void *private)
516{
517 struct dup_block *p;
518 blk_t new_block;
519 errcode_t retval;
520 struct clone_struct *cs = (struct clone_struct *) private;
521
522 if (!*block_nr)
523 return 0;
524
Theodore Ts'of3db3561997-04-26 13:34:30 +0000525 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000526 for (p = dup_blk; p; p = p->next_block)
527 if (p->block == *block_nr)
528 break;
529 if (p) {
530 retval = ext2fs_new_block(fs, 0, block_found_map,
531 &new_block);
532 if (retval) {
533 cs->errcode = retval;
534 return BLOCK_ABORT;
535 }
536 retval = io_channel_read_blk(fs->io, *block_nr, 1,
537 cs->buf);
538 if (retval) {
539 cs->errcode = retval;
540 return BLOCK_ABORT;
541 }
542 retval = io_channel_write_blk(fs->io, new_block, 1,
543 cs->buf);
544 if (retval) {
545 cs->errcode = retval;
546 return BLOCK_ABORT;
547 }
548 p->num_bad--;
549 if (p->num_bad == 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000550 ext2fs_unmark_block_bitmap(block_dup_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000551 *block_nr);
552 *block_nr = new_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000553 ext2fs_mark_block_bitmap(block_found_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000554 new_block);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000555 ext2fs_mark_block_bitmap(fs->block_map, new_block);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000556 return BLOCK_CHANGED;
557 } else
558 com_err("clone_file_block", 0,
559 "internal error; can't find dup_blk for %d\n",
560 *block_nr);
561 }
562 return 0;
563}
564
565static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
566{
567 errcode_t retval;
568 struct clone_struct cs;
569
570 cs.errcode = 0;
571 cs.buf = malloc(fs->blocksize);
572 if (!cs.buf)
573 return ENOMEM;
574
575 retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
576 clone_file_block, &cs);
577 ext2fs_mark_bb_dirty(fs);
578 free(cs.buf);
579 if (retval) {
580 com_err("clone_file", retval,
581 "while calling ext2fs_block_iterate for inode %d",
582 dp->ino);
583 return retval;
584 }
585 if (cs.errcode) {
586 com_err("clone_file", retval,
587 "returned from clone_file_block");
588 return retval;
589 }
590 return 0;
591}
592
593
594
595
596