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