blob: c98030fbecc30560614c8805ecce491a2657e7eb [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 *
21 * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be
22 * redistributed under the terms of the GNU Public License.
23 *
24 */
25
26#include <time.h>
27
28#include <et/com_err.h>
29#include "e2fsck.h"
30
31/*
32 * This is structure is allocated for each time that a block is
33 * claimed by more than one file. So if a particular block is claimed
34 * by 3 files, then three copies of this structure will be allocated,
35 * one for each conflict.
36 *
37 * The linked list structure is as follows:
38 *
39 * dup_blk --> block #34 --> block #35 --> block #47
40 * inode #12 inode #14 inode #17
41 * num_bad = 3 num_bad = 2 num_bad = 2
42 * | | |
43 * V V V
44 * block #34 block #35 block #47
45 * inode #14 inode #15 inode #23
46 * |
47 * V
48 * block #34
49 * inode #15
50 *
51 * The num_bad field indicates how many inodes are sharing a
52 * particular block, and is only stored in the first element of the
53 * linked list for a particular block. As the block conflicts are
54 * resolved, num_bad is decremented; when it reaches 1, then we no
55 * longer need to worry about that block.
56 */
57struct dup_block {
58 blk_t block; /* Block number */
59 ino_t ino; /* Inode number */
60 int num_bad;
61 /* Pointer to next dup record with different block */
62 struct dup_block *next_block;
63 /* Pointer to next dup record with different inode */
64 struct dup_block *next_inode;
65};
66
67/*
68 * This structure stores information about a particular inode which
69 * is sharing blocks with other inodes. This information is collected
70 * to display to the user, so that the user knows what files he or she
71 * is dealing with, when trying to decide how to resolve the conflict
72 * of multiply-claimed blocks.
73 */
74struct dup_inode {
75 ino_t ino;
76 time_t mtime;
77 char *pathname;
78 int num_dupblocks;
79 int flags;
80 struct dup_inode *next;
81};
82
83#define DUP_INODE_DONT_FREE_PATHNAME 0x1
84
85static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
86 int blockcnt, void *private);
87static void delete_file(ext2_filsys fs, struct dup_inode *dp,
88 char *block_buf);
89static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf);
90static void pass1b(ext2_filsys fs, char *block_buf);
91static void pass1c(ext2_filsys fs, char *block_buf);
92static void pass1d(ext2_filsys fs, char *block_buf);
93
94static struct dup_block *dup_blk = 0;
95static struct dup_inode *dup_ino = 0;
96static int dup_inode_count = 0;
97
98/*
99 * For pass1_check_directory and pass1_get_blocks
100 */
101extern ino_t stashed_ino;
102extern struct ext2_inode *stashed_inode;
103
Theodore Ts'of3db3561997-04-26 13:34:30 +0000104static ext2fs_inode_bitmap inode_dup_map;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000105
106/*
107 * Main procedure for handling duplicate blocks
108 */
109void pass1_dupblocks(ext2_filsys fs, char *block_buf)
110{
111 errcode_t retval;
112 struct dup_block *p, *q, *next_p, *next_q;
113 struct dup_inode *r, *next_r;
114
Theodore Ts'of3db3561997-04-26 13:34:30 +0000115 retval = ext2fs_allocate_inode_bitmap(fs,
116 "multiply claimed inode map", &inode_dup_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000117 if (retval) {
118 com_err("ext2fs_allocate_inode_bitmap", retval,
119 "while allocating inode_dup_map");
120 fatal_error(0);
121 }
122
123 pass1b(fs, block_buf);
124 pass1c(fs, block_buf);
125 pass1d(fs, block_buf);
126
127 /*
128 * Time to free all of the accumulated data structures that we
129 * don't need anymore.
130 */
Theodore Ts'of3db3561997-04-26 13:34:30 +0000131 ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0;
132 ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000133 for (p = dup_blk; p; p = next_p) {
134 next_p = p->next_block;
135 for (q = p; q; q = next_q) {
136 next_q = q->next_inode;
137 free(q);
138 }
139 }
140 for (r = dup_ino; r; r = next_r) {
141 next_r = r->next;
142 if (r->pathname && !(r->flags & DUP_INODE_DONT_FREE_PATHNAME))
143 free(r->pathname);
144 free(r);
145 }
146}
147
148/*
149 * Scan the inodes looking for inodes that contain duplicate blocks.
150 */
151struct process_block_struct {
152 ino_t ino;
153 int dup_blocks;
154};
155
156void pass1b(ext2_filsys fs, char *block_buf)
157{
158 ino_t ino;
159 struct ext2_inode inode;
160 ext2_inode_scan scan;
161 errcode_t retval;
162 struct process_block_struct pb;
163 struct dup_inode *dp;
164
165 printf("Duplicate blocks found... invoking duplicate block passes.\n");
166 printf("Pass 1B: Rescan for duplicate/bad blocks\n");
167 retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
168 if (retval) {
169 com_err(program_name, retval, "while opening inode scan");
170 fatal_error(0);
171 }
172 retval = ext2fs_get_next_inode(scan, &ino, &inode);
173 if (retval) {
174 com_err(program_name, retval, "while starting inode scan");
175 fatal_error(0);
176 }
177 stashed_inode = &inode;
178 while (ino) {
179 stashed_ino = ino;
180 if ((ino != EXT2_BAD_INO) &&
Theodore Ts'of3db3561997-04-26 13:34:30 +0000181 (!ext2fs_test_inode_bitmap(inode_used_map, ino) ||
Theodore Ts'o3839e651997-04-26 13:21:57 +0000182 !inode_has_valid_blocks(&inode)))
183 goto next;
184
185 pb.ino = ino;
186 pb.dup_blocks = 0;
187 retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
188 process_pass1b_block, &pb);
189 if (pb.dup_blocks) {
190 if (ino != EXT2_BAD_INO)
191 printf("\n");
192 dp = allocate_memory(sizeof(struct dup_inode),
193 "duplicate inode record");
194 dp->ino = ino;
195 dp->mtime = inode.i_mtime;
196 dp->num_dupblocks = pb.dup_blocks;
197 dp->pathname = 0;
198 dp->flags = 0;
199 dp->next = dup_ino;
200 dup_ino = dp;
201 if (ino != EXT2_BAD_INO)
202 dup_inode_count++;
203 }
204 if (retval)
205 com_err(program_name, retval,
206 "while calling ext2fs_block_iterate in pass1b");
207
208 next:
209 retval = ext2fs_get_next_inode(scan, &ino, &inode);
210 if (retval) {
211 com_err(program_name, retval,
212 "while doing inode scan");
213 fatal_error(0);
214 }
215 }
216 ext2fs_close_inode_scan(scan);
217 fs->get_blocks = 0;
218 fs->check_directory = 0;
219}
220
221int process_pass1b_block(ext2_filsys fs,
222 blk_t *block_nr,
223 int blockcnt,
224 void *private)
225{
226 struct process_block_struct *p;
227 struct dup_block *dp, *q, *r;
228 int i;
229
230 if (!*block_nr)
231 return 0;
232 p = (struct process_block_struct *) private;
233
Theodore Ts'of3db3561997-04-26 13:34:30 +0000234 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000235 /* OK, this is a duplicate block */
236 if (p->ino != EXT2_BAD_INO) {
237 if (!p->dup_blocks)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000238 printf("Duplicate/bad block(s) in inode %lu:",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000239 p->ino);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000240 printf(" %lu", *block_nr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000241 }
242 p->dup_blocks++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000243 ext2fs_mark_block_bitmap(block_dup_map, *block_nr);
244 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000245 dp = allocate_memory(sizeof(struct dup_block),
246 "duplicate block record");
247 dp->block = *block_nr;
248 dp->ino = p->ino;
249 dp->num_bad = 0;
250 q = dup_blk;
251 while (q) {
252 if (q->block == *block_nr)
253 break;
254 q = q->next_block;
255 }
256 if (q) {
257 dp->next_inode = q->next_inode;
258 q->next_inode = dp;
259 } else {
260 dp->next_block = dup_blk;
261 dup_blk = dp;
262 }
263 }
264 /*
265 * Set the num_bad field
266 */
267 for (q = dup_blk; q; q = q->next_block) {
268 i = 0;
269 for (r = q; r; r = r->next_inode)
270 i++;
271 q->num_bad = i;
272 }
273 return 0;
274}
275
276/*
277 * Used by pass1c to name the "special" inodes. They are declared as
278 * writeable strings to prevent const problems.
279 */
280#define num_special_inodes 7
281char special_inode_name[num_special_inodes][40] =
282{
283 "<The NULL inode>", /* 0 */
284 "<The bad blocks inode>", /* 1 */
285 "/", /* 2 */
286 "<The ACL index inode>", /* 3 */
287 "<The ACL data inode>", /* 4 */
288 "<The boot loader inode>", /* 5 */
289 "<The undelete directory inode>" /* 6 */
290};
291
292/*
293 * Pass 1c: Scan directories for inodes with duplicate blocks. This
294 * is used so that we can print pathnames when prompting the user for
295 * what to do.
296 */
297struct process_dir_struct {
298 ext2_filsys fs;
299 ino_t dir_ino;
300 int count;
301};
302
303void pass1c(ext2_filsys fs, char *block_buf)
304{
305 int i;
306 struct dup_inode *p;
307 errcode_t retval;
308 char buf[80];
309 int inodes_left = dup_inode_count;
310 int offset, entry;
311 struct ext2_dir_entry *dirent;
312
313 printf("Pass 1C: Scan directories for inodes with dup blocks.\n");
314
315 /*
316 * First check to see if any of the inodes with dup blocks is
317 * the bad block inode or the root inode; handle them as
318 * special cases.
319 */
320 for (p = dup_ino; p; p = p->next) {
321 if (p->ino < num_special_inodes) {
322 p->pathname = special_inode_name[p->ino];
323 p->flags |= DUP_INODE_DONT_FREE_PATHNAME;
324 inodes_left--;
325 }
326 }
327
328 /*
329 * Search through all directories to translate inodes to names
330 * (by searching for the containing directory for that inode.)
331 */
332 for (i=0; inodes_left && i < dir_block_count; i++) {
333 retval = io_channel_read_blk(fs->io, dir_blocks[i].blk,
334 1, block_buf);
335 entry = offset = 0;
336 while (offset < fs->blocksize) {
337 entry++;
338 dirent = (struct ext2_dir_entry *)
339 (block_buf + offset);
340 if (!dirent->inode ||
341 ((dir_blocks[i].blockcnt == 0) && (entry <= 2)))
342 goto next;
343
Theodore Ts'of3db3561997-04-26 13:34:30 +0000344 if (!ext2fs_test_inode_bitmap(inode_dup_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000345 dirent->inode))
346 goto next;
347
348 for (p = dup_ino; p; p = p->next) {
349 if (p->ino == dirent->inode)
350 break;
351 }
352
353 if (!p || p->pathname)
354 goto next;
355
356 (void) ext2fs_get_pathname(fs, dir_blocks[i].ino,
357 p->ino, &p->pathname);
358 inodes_left--;
359
360 next:
361 if (dirent->rec_len < 8)
362 break;
363 offset += dirent->rec_len;
364 }
365 }
366
367
368 /*
369 * If we can't get a name, then put in a generic one.
370 */
371 for (p = dup_ino; p; p = p->next) {
372 if (!p->pathname) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000373 sprintf(buf, "<Unknown inode #%lu>", p->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000374 p->pathname = malloc(strlen(buf)+1);
375 if (!p->pathname) {
376 fprintf(stderr, "pass1c: couldn't malloc "
377 "generic pathname\n");
378 fatal_error(0);
379 }
380 strcpy(p->pathname, buf);
381 }
382 }
383}
384
385static void pass1d(ext2_filsys fs, char *block_buf)
386{
387 struct dup_inode *p, *s;
388 struct dup_block *q, *r;
389 ino_t *shared;
390 int shared_len;
391 int i;
392 errcode_t retval;
393 char *time_str;
394 int file_ok;
395
396 printf("Pass 1D: Reconciling duplicate blocks\n");
397 read_bitmaps(fs);
398
399 printf("(There are %d inodes containing duplicate/bad blocks.)\n\n",
400 dup_inode_count);
401 shared = allocate_memory(sizeof(ino_t) * dup_inode_count,
402 "Shared inode list");
403 for (p = dup_ino; p; p = p->next) {
404 shared_len = 0;
405 file_ok = 1;
406 if (p->ino == EXT2_BAD_INO)
407 continue;
408
409 /*
410 * Search through the duplicate records to see which
411 * inodes share blocks with this one
412 */
413 for (q = dup_blk; q; q = q->next_block) {
414 /*
415 * See if this block is used by this inode.
416 * If it isn't, continue.
417 */
418 for (r = q; r; r = r->next_inode)
419 if (r->ino == p->ino)
420 break;
421 if (!r)
422 continue;
423 if (q->num_bad > 1)
424 file_ok = 0;
425 /*
426 * Add all inodes used by this block to the
427 * shared[] --- which is a unique list, so
428 * if an inode is already in shared[], don't
429 * add it again.
430 */
431 for (r = q; r; r = r->next_inode) {
432 if (r->ino == p->ino)
433 continue;
434 for (i = 0; i < shared_len; i++)
435 if (shared[i] == r->ino)
436 break;
437 if (i == shared_len) {
438 shared[shared_len++] = r->ino;
439 }
440 }
441 }
442 time_str = ctime(&p->mtime);
443 time_str[24] = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000444 printf("File %s (inode #%lu, mod time %s) \n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000445 p->pathname, p->ino, time_str);
446 printf(" has %d duplicate blocks, shared with %d file%s:\n",
447 p->num_dupblocks, shared_len,
448 (shared_len>1) ? "s" : "");
449 for (i = 0; i < shared_len; i++) {
450 for (s = dup_ino; s; s = s->next)
451 if (s->ino == shared[i])
452 break;
453 if (!s)
454 continue;
455 time_str = ctime(&s->mtime);
456 time_str[24] = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000457 printf("\t%s (inode #%lu, mod time %s)\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000458 s->pathname, s->ino, time_str);
459 }
460 if (file_ok) {
461 printf("Duplicated blocks already reassigned or cloned.\n\n");
462 continue;
463 }
464
465 if (ask("Clone duplicate/bad blocks", 1)) {
466 retval = clone_file(fs, p, block_buf);
467 if (retval)
468 printf("Couldn't clone file: %s\n",
469 error_message(retval));
470 else {
471 printf("\n");
472 continue;
473 }
474 }
475 if (ask("Delete file", 1))
476 delete_file(fs, p, block_buf);
477 else
478 ext2fs_unmark_valid(fs);
479 printf("\n");
480 }
481}
482
483static int delete_file_block(ext2_filsys fs,
484 blk_t *block_nr,
485 int blockcnt,
486 void *private)
487{
488 struct dup_block *p;
489
490 if (!*block_nr)
491 return 0;
492
Theodore Ts'of3db3561997-04-26 13:34:30 +0000493 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000494 for (p = dup_blk; p; p = p->next_block)
495 if (p->block == *block_nr)
496 break;
497 if (p) {
498 p->num_bad--;
499 if (p->num_bad == 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000500 ext2fs_unmark_block_bitmap(block_dup_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000501 *block_nr);
502 } else
503 com_err("delete_file_block", 0,
504 "internal error; can't find dup_blk for %d\n",
505 *block_nr);
506 } else {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000507 ext2fs_unmark_block_bitmap(block_found_map, *block_nr);
508 ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000509 }
510
511 return 0;
512}
513
514static void delete_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
515{
516 errcode_t retval;
517 struct process_block_struct pb;
518 struct ext2_inode inode;
519
520 pb.ino = dp->ino;
521 pb.dup_blocks = dp->num_dupblocks;
522
523 retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
524 delete_file_block, &pb);
525 if (retval)
526 com_err("delete_file", retval,
527 "while calling ext2fs_block_iterate for inode %d",
528 dp->ino);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000529 ext2fs_unmark_inode_bitmap(inode_used_map, dp->ino);
530 ext2fs_unmark_inode_bitmap(inode_dir_map, dp->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000531 if (inode_bad_map)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000532 ext2fs_unmark_inode_bitmap(inode_bad_map, dp->ino);
533 ext2fs_unmark_inode_bitmap(fs->inode_map, dp->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000534 ext2fs_mark_ib_dirty(fs);
535 ext2fs_mark_bb_dirty(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000536 e2fsck_read_inode(fs, dp->ino, &inode, "delete_file");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000537 inode.i_links_count = 0;
538 inode.i_dtime = time(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000539 e2fsck_write_inode(fs, dp->ino, &inode, "delete_file");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000540}
541
542struct clone_struct {
543 errcode_t errcode;
544 char *buf;
545};
546
547static int clone_file_block(ext2_filsys fs,
548 blk_t *block_nr,
549 int blockcnt,
550 void *private)
551{
552 struct dup_block *p;
553 blk_t new_block;
554 errcode_t retval;
555 struct clone_struct *cs = (struct clone_struct *) private;
556
557 if (!*block_nr)
558 return 0;
559
Theodore Ts'of3db3561997-04-26 13:34:30 +0000560 if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000561 for (p = dup_blk; p; p = p->next_block)
562 if (p->block == *block_nr)
563 break;
564 if (p) {
565 retval = ext2fs_new_block(fs, 0, block_found_map,
566 &new_block);
567 if (retval) {
568 cs->errcode = retval;
569 return BLOCK_ABORT;
570 }
571 retval = io_channel_read_blk(fs->io, *block_nr, 1,
572 cs->buf);
573 if (retval) {
574 cs->errcode = retval;
575 return BLOCK_ABORT;
576 }
577 retval = io_channel_write_blk(fs->io, new_block, 1,
578 cs->buf);
579 if (retval) {
580 cs->errcode = retval;
581 return BLOCK_ABORT;
582 }
583 p->num_bad--;
584 if (p->num_bad == 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000585 ext2fs_unmark_block_bitmap(block_dup_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000586 *block_nr);
587 *block_nr = new_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000588 ext2fs_mark_block_bitmap(block_found_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000589 new_block);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000590 ext2fs_mark_block_bitmap(fs->block_map, new_block);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000591 return BLOCK_CHANGED;
592 } else
593 com_err("clone_file_block", 0,
594 "internal error; can't find dup_blk for %d\n",
595 *block_nr);
596 }
597 return 0;
598}
599
600static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
601{
602 errcode_t retval;
603 struct clone_struct cs;
604
605 cs.errcode = 0;
606 cs.buf = malloc(fs->blocksize);
607 if (!cs.buf)
608 return ENOMEM;
609
610 retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
611 clone_file_block, &cs);
612 ext2fs_mark_bb_dirty(fs);
613 free(cs.buf);
614 if (retval) {
615 com_err("clone_file", retval,
616 "while calling ext2fs_block_iterate for inode %d",
617 dp->ino);
618 return retval;
619 }
620 if (cs.errcode) {
621 com_err("clone_file", retval,
622 "returned from clone_file_block");
623 return retval;
624 }
625 return 0;
626}
627
628
629
630
631