blob: 17b0939b2ed3e96502579338f571d8f76a551561 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
3 *
4 * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be
5 * redistributed under the terms of the GNU Public License.
6 *
7 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
8 * and applies the following tests to each inode:
9 *
10 * - The mode field of the inode must be legal.
11 * - The size and block count fields of the inode are correct.
12 * - A data block must not be used by another inode
13 *
14 * Pass 1 also gathers the collects the following information:
15 *
16 * - A bitmap of which inodes are in use. (inode_used_map)
17 * - A bitmap of which inodes are directories. (inode_dir_map)
18 * - A bitmap of which inodes have bad fields. (inode_bad_map)
19 * - A bitmap of which blocks are in use. (block_found_map)
20 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
21 * - The data blocks of the directory inodes. (dir_map)
22 *
23 * Pass 1 is designed to stash away enough information so that the
24 * other passes should not need to read in the inode information
25 * during the normal course of a filesystem check. (Althogh if an
26 * inconsistency is detected, other passes may need to read in an
27 * inode to fix it.)
28 *
29 * Note that pass 1B will be invoked if there are any duplicate blocks
30 * found.
31 */
32
33#include <time.h>
Theodore Ts'o50e1e101997-04-26 13:58:21 +000034#ifdef HAVE_ERRNO_H
35#include <errno.h>
36#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000037
38#include <et/com_err.h>
39#include "e2fsck.h"
40
Theodore Ts'o50e1e101997-04-26 13:58:21 +000041#ifdef NO_INLINE_FUNCS
42#define _INLINE_
43#else
44#define _INLINE_ inline
45#endif
46
Theodore Ts'o3839e651997-04-26 13:21:57 +000047/* Files counts */
48int fs_directory_count = 0;
49int fs_regular_count = 0;
50int fs_blockdev_count = 0;
51int fs_chardev_count = 0;
52int fs_links_count = 0;
53int fs_symlinks_count = 0;
54int fs_fast_symlinks_count = 0;
55int fs_fifo_count = 0;
56int fs_total_count = 0;
57int fs_badblocks_count = 0;
58int fs_sockets_count = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +000059int fs_ind_count = 0;
60int fs_dind_count = 0;
61int fs_tind_count = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000062
Theodore Ts'of3db3561997-04-26 13:34:30 +000063ext2fs_inode_bitmap inode_used_map = 0; /* Inodes which are in use */
64ext2fs_inode_bitmap inode_bad_map = 0; /* Inodes which are bad in some way */
65ext2fs_inode_bitmap inode_dir_map = 0; /* Inodes which are directories */
Theodore Ts'o3839e651997-04-26 13:21:57 +000066
Theodore Ts'of3db3561997-04-26 13:34:30 +000067ext2fs_block_bitmap block_found_map = 0;
68ext2fs_block_bitmap block_dup_map = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +000069ext2fs_block_bitmap block_illegal_map = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000070
71static int fix_link_count = -1;
72
73unsigned short * inode_link_info = NULL;
74
75static int process_block(ext2_filsys fs, blk_t *blocknr,
76 int blockcnt, void *private);
77static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
78 int blockcnt, void *private);
Theodore Ts'o3839e651997-04-26 13:21:57 +000079static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode,
80 char *block_buf);
81static void mark_table_blocks(ext2_filsys fs);
82static errcode_t pass1_check_directory(ext2_filsys fs, ino_t ino);
83static errcode_t pass1_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks);
84static void alloc_bad_map(ext2_filsys fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +000085static void handle_fs_bad_blocks(ext2_filsys fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +000086static void process_inodes(ext2_filsys fs, char *block_buf);
87static int process_inode_cmp(const void *a, const void *b);
88static int dir_block_cmp(const void *a, const void *b);
Theodore Ts'of3db3561997-04-26 13:34:30 +000089static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
90 dgrp_t group, void * private);
Theodore Ts'o50e1e101997-04-26 13:58:21 +000091static char *describe_illegal_block(ext2_filsys fs, blk_t block);
Theodore Ts'o3839e651997-04-26 13:21:57 +000092
93struct process_block_struct {
94 ino_t ino;
Theodore Ts'of3db3561997-04-26 13:34:30 +000095 int is_dir:1, clear:1, suppress:1;
Theodore Ts'o3839e651997-04-26 13:21:57 +000096 int num_blocks;
97 int last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +000098 int num_illegal_blocks;
Theodore Ts'o3839e651997-04-26 13:21:57 +000099 int fix;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000100 struct ext2_inode *inode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000101};
102
103struct process_inode_block {
104 ino_t ino;
105 struct ext2_inode inode;
106};
107
108/*
109 * For pass1_check_directory and pass1_get_blocks
110 */
111ino_t stashed_ino;
112struct ext2_inode *stashed_inode;
113
114/*
115 * For the inodes to process list.
116 */
117static struct process_inode_block *inodes_to_process;
118static int process_inode_count;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000119int process_inode_size = 256;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000120
121/*
122 * For the directory blocks list.
123 */
124struct dir_block_struct *dir_blocks = 0;
125int dir_block_count = 0;
126int dir_block_size = 0;
127
Theodore Ts'of3db3561997-04-26 13:34:30 +0000128/*
129 * Free all memory allocated by pass1 in preparation for restarting
130 * things.
131 */
132static void unwind_pass1(ext2_filsys fs)
133{
134 ext2fs_free_inode_bitmap(inode_used_map); inode_used_map = 0;
135 ext2fs_free_inode_bitmap(inode_dir_map); inode_dir_map = 0;
136 ext2fs_free_block_bitmap(block_found_map); block_found_map = 0;
137 free(inode_link_info); inode_link_info = 0;
138 free(inodes_to_process);inodes_to_process = 0;
139 free(dir_blocks); dir_blocks = 0;
140 dir_block_size = 0;
141 if (block_dup_map) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000142 ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000143 }
144
145 /* Clear statistic counters */
146 fs_directory_count = 0;
147 fs_regular_count = 0;
148 fs_blockdev_count = 0;
149 fs_chardev_count = 0;
150 fs_links_count = 0;
151 fs_symlinks_count = 0;
152 fs_fast_symlinks_count = 0;
153 fs_fifo_count = 0;
154 fs_total_count = 0;
155 fs_badblocks_count = 0;
156 fs_sockets_count = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000157 fs_ind_count = 0;
158 fs_dind_count = 0;
159 fs_tind_count = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000160}
161
Theodore Ts'o3839e651997-04-26 13:21:57 +0000162void pass1(ext2_filsys fs)
163{
164 ino_t ino;
165 struct ext2_inode inode;
166 ext2_inode_scan scan;
167 char *block_buf;
168 errcode_t retval;
169 struct resource_track rtrack;
170
171 init_resource_track(&rtrack);
172
173 if (!preen)
174 printf("Pass 1: Checking inodes, blocks, and sizes\n");
175
176#ifdef MTRACE
177 mtrace_print("Pass 1");
178#endif
179
180 /*
181 * Allocate bitmaps structures
182 */
Theodore Ts'of3db3561997-04-26 13:34:30 +0000183 retval = ext2fs_allocate_inode_bitmap(fs, "in-use inode map",
184 &inode_used_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000185 if (retval) {
186 com_err("ext2fs_allocate_inode_bitmap", retval,
187 "while allocating inode_used_map");
188 fatal_error(0);
189 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000190 retval = ext2fs_allocate_inode_bitmap(fs, "directory inode map",
191 &inode_dir_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000192 if (retval) {
193 com_err("ext2fs_allocate_inode_bitmap", retval,
194 "while allocating inode_dir_map");
195 fatal_error(0);
196 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000197 retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
198 &block_found_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000199 if (retval) {
200 com_err("ext2fs_allocate_block_bitmap", retval,
201 "while allocating block_found_map");
202 fatal_error(0);
203 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000204 retval = ext2fs_allocate_block_bitmap(fs, "illegal block map",
205 &block_illegal_map);
206 if (retval) {
207 com_err("ext2fs_allocate_block_bitmap", retval,
208 "while allocating block_illegal_map");
209 fatal_error(0);
210 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000211 inode_link_info = allocate_memory((fs->super->s_inodes_count + 1) *
212 sizeof(unsigned short),
213 "inode link count array");
214 inodes_to_process = allocate_memory(process_inode_size *
215 sizeof(struct process_inode_block),
216 "array of inodes to process");
217 process_inode_count = 0;
218
219 dir_block_size = get_num_dirs(fs) * 4;
220 dir_block_count = 0;
221 dir_blocks = allocate_memory(sizeof(struct dir_block_struct) *
222 dir_block_size,
223 "directory block information");
224
225 mark_table_blocks(fs);
226 block_buf = allocate_memory(fs->blocksize * 3, "block interate buffer");
227 fs->get_blocks = pass1_get_blocks;
228 fs->check_directory = pass1_check_directory;
229 ehandler_operation("doing inode scan");
230 retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
231 if (retval) {
232 com_err(program_name, retval, "while opening inode scan");
233 fatal_error(0);
234 }
235 retval = ext2fs_get_next_inode(scan, &ino, &inode);
236 if (retval) {
237 com_err(program_name, retval, "while starting inode scan");
238 fatal_error(0);
239 }
240 stashed_inode = &inode;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000241 ext2fs_set_inode_callback(scan, scan_callback, block_buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000242 while (ino) {
243 stashed_ino = ino;
244 inode_link_info[ino] = inode.i_links_count;
245 if (ino == EXT2_BAD_INO) {
246 struct process_block_struct pb;
247
248 pb.ino = EXT2_BAD_INO;
249 pb.num_blocks = pb.last_block = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000250 pb.num_illegal_blocks = 0;
251 pb.suppress = pb.clear = pb.is_dir = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000252 pb.fix = -1;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000253 pb.inode = &inode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000254 retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
255 process_bad_block, &pb);
256 if (retval)
257 com_err(program_name, retval, "while calling e2fsc_block_interate in pass 1");
258
Theodore Ts'of3db3561997-04-26 13:34:30 +0000259 ext2fs_mark_inode_bitmap(inode_used_map, ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000260 goto next;
261 }
262 if (ino == EXT2_ROOT_INO) {
263 /*
264 * Make sure the root inode is a directory; if
265 * not, offer to clear it. It will be
266 * regnerated in pass #3.
267 */
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000268 if (!LINUX_S_ISDIR(inode.i_mode)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000269 printf("Root inode is not a directory. ");
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000270 preenhalt(fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000271 if (ask("Clear", 1)) {
272 inode.i_dtime = time(0);
273 inode.i_links_count = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000274 inode_link_info[ino] = 0;
275 e2fsck_write_inode(fs, ino, &inode,
276 "pass1");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000277 } else
278 ext2fs_unmark_valid(fs);
279 }
280 /*
281 * If dtime is set, offer to clear it. mke2fs
282 * version 0.2b created filesystems with the
283 * dtime field set for the root and lost+found
284 * directories. We won't worry about
285 * /lost+found, since that can be regenerated
286 * easily. But we will fix the root directory
287 * as a special case.
288 */
289 if (inode.i_dtime && inode.i_links_count) {
290 if (ask("Root inode has dtime set "
291 "(probably due to old mke2fs). Fix",
292 1)) {
293 inode.i_dtime = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000294 e2fsck_write_inode(fs, ino, &inode,
295 "pass1");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000296 printf("Note: /lost+found will "
297 "probably be deleted as well, "
298 "due to the mke2fs bug.\n"
299 "Be sure to run mklost+found "
300 "to recreate it after e2fsck "
301 "finishes.\n\n");
302 } else
303 ext2fs_unmark_valid(fs);
304 }
305 }
306 if ((ino != EXT2_ROOT_INO) && (ino < EXT2_FIRST_INO)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000307 ext2fs_mark_inode_bitmap(inode_used_map, ino);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000308 if (inode.i_mode != 0) {
309 printf("Reserved inode %lu has bad mode. ", ino);
310 if (ask("Clear", 1)) {
311 inode.i_mode = 0;
312 e2fsck_write_inode(fs, ino, &inode,
313 "pass1");
314 } else
315 ext2fs_unmark_valid(fs);
316 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000317 check_blocks(fs, ino, &inode, block_buf);
318 goto next;
319 }
320 /*
321 * This code assumes that deleted inodes have
322 * i_links_count set to 0.
323 */
324 if (!inode.i_links_count) {
325 if (!inode.i_dtime && inode.i_mode) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000326 printf("Deleted inode %lu has zero dtime.\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000327 ino);
328 if (ask("Set dtime", 1)) {
329 inode.i_dtime = time(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000330 e2fsck_write_inode(fs, ino, &inode,
331 "pass1");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000332 } else
333 ext2fs_unmark_valid(fs);
334 }
335 goto next;
336 }
337 /*
338 * 0.3c ext2fs code didn't clear i_links_count for
339 * deleted files. Oops.
340 *
341 * In the future, when the new ext2fs behavior is the
342 * norm, we may want to handle the case of a non-zero
343 * i_links_count and non-zero dtime by clearing dtime
344 * and assuming the inode is in use, instead of
345 * assuming the inode is not in use.
346 */
347 if (inode.i_dtime) {
348 if (fix_link_count == -1) {
349 printf("\nDeleted inode detected with non-zero link count.\n");
350 printf("This is probably due to old ext2fs kernel code. \n");
351 fix_link_count = ask("Fix inode(s)", 1);
352 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000353 printf("Inode %lu is deleted w/ non-zero link_count. %s\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000354 ino, clear_msg[fix_link_count]);
355 if (fix_link_count) {
356 inode.i_links_count = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000357 inode_link_info[ino] = 0;
358 e2fsck_write_inode(fs, ino, &inode, "pass1");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000359 } else
360 ext2fs_unmark_valid(fs);
361 goto next;
362 }
363
Theodore Ts'of3db3561997-04-26 13:34:30 +0000364 ext2fs_mark_inode_bitmap(inode_used_map, ino);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000365 if (inode.i_faddr
366#if HAVE_EXT2_FRAGS
367 || inode.i_frag || inode.i_fsize
368#endif
369 || inode.i_file_acl || inode.i_dir_acl) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000370 if (!inode_bad_map)
371 alloc_bad_map(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000372 ext2fs_mark_inode_bitmap(inode_bad_map, ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000373 }
374
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000375 if (LINUX_S_ISDIR(inode.i_mode)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000376 ext2fs_mark_inode_bitmap(inode_dir_map, ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000377 add_dir_info(fs, ino, 0, &inode);
378 fs_directory_count++;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000379 } else if (LINUX_S_ISREG (inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000380 fs_regular_count++;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000381 else if (LINUX_S_ISCHR (inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000382 fs_chardev_count++;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000383 else if (LINUX_S_ISBLK (inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000384 fs_blockdev_count++;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000385 else if (LINUX_S_ISLNK (inode.i_mode)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000386 fs_symlinks_count++;
387 if (!inode.i_blocks)
388 fs_fast_symlinks_count++;
389 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000390 else if (LINUX_S_ISFIFO (inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000391 fs_fifo_count++;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000392 else if (LINUX_S_ISSOCK (inode.i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000393 fs_sockets_count++;
394 else {
395 if (!inode_bad_map)
396 alloc_bad_map(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000397 ext2fs_mark_inode_bitmap(inode_bad_map, ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000398 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000399 if (inode.i_block[EXT2_IND_BLOCK])
400 fs_ind_count++;
401 if (inode.i_block[EXT2_DIND_BLOCK])
402 fs_dind_count++;
403 if (inode.i_block[EXT2_TIND_BLOCK])
404 fs_tind_count++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000405 if (inode.i_block[EXT2_IND_BLOCK] ||
406 inode.i_block[EXT2_DIND_BLOCK] ||
407 inode.i_block[EXT2_TIND_BLOCK]) {
408 inodes_to_process[process_inode_count].ino = ino;
409 inodes_to_process[process_inode_count].inode = inode;
410 process_inode_count++;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000411 } else
Theodore Ts'o3839e651997-04-26 13:21:57 +0000412 check_blocks(fs, ino, &inode, block_buf);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000413
414 if (process_inode_count >= process_inode_size)
415 process_inodes(fs, block_buf);
416 next:
417 retval = ext2fs_get_next_inode(scan, &ino, &inode);
418 if (retval) {
419 com_err(program_name, retval,
420 "while doing inode scan");
421 fatal_error(0);
422 }
423 }
424 process_inodes(fs, block_buf);
425 ext2fs_close_inode_scan(scan);
426 ehandler_operation(0);
427
428 qsort(dir_blocks, dir_block_count, sizeof(struct dir_block_struct),
429 dir_block_cmp);
430
Theodore Ts'of3db3561997-04-26 13:34:30 +0000431 if (invalid_bitmaps)
432 handle_fs_bad_blocks(fs);
433
434 if (restart_e2fsck) {
435 unwind_pass1(fs);
436 goto endit;
437 }
438
Theodore Ts'o3839e651997-04-26 13:21:57 +0000439 if (block_dup_map) {
440 if (preen) {
441 printf("Duplicate or bad blocks in use!\n");
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000442 preenhalt(fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000443 }
444 pass1_dupblocks(fs, block_buf);
445 }
446 fs->get_blocks = 0;
447 fs->check_directory = 0;
448 free(inodes_to_process);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000449endit:
Theodore Ts'o3839e651997-04-26 13:21:57 +0000450 free(block_buf);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000451 ext2fs_free_block_bitmap(block_illegal_map);
452 block_illegal_map = 0;
453
Theodore Ts'o3839e651997-04-26 13:21:57 +0000454 if (tflag > 1) {
455 printf("Pass 1: ");
456 print_resource_track(&rtrack);
457 }
458}
459
460/*
Theodore Ts'of3db3561997-04-26 13:34:30 +0000461 * When the inode_scan routines call this callback at the end of the
462 * glock group, call process_inodes.
463 */
464static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
465 dgrp_t group, void * private)
466{
467 process_inodes(fs, (char *) private);
468 return 0;
469}
470
471/*
Theodore Ts'o3839e651997-04-26 13:21:57 +0000472 * Process the inodes in the "inodes to process" list.
473 */
474static void process_inodes(ext2_filsys fs, char *block_buf)
475{
476 int i;
477 struct ext2_inode *old_stashed_inode;
478 ino_t ino;
479 const char *old_operation;
480 char buf[80];
481
482#if 0
Theodore Ts'of3db3561997-04-26 13:34:30 +0000483 printf("begin process_inodes: ");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000484#endif
485 old_operation = ehandler_operation(0);
486 old_stashed_inode = stashed_inode;
487 qsort(inodes_to_process, process_inode_count,
488 sizeof(struct process_inode_block), process_inode_cmp);
489 for (i=0; i < process_inode_count; i++) {
490 stashed_inode = &inodes_to_process[i].inode;
491 ino = inodes_to_process[i].ino;
492 stashed_ino = ino;
493#if 0
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000494 printf("%u ", ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000495#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +0000496 sprintf(buf, "reading indirect blocks of inode %lu", ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000497 ehandler_operation(buf);
498 check_blocks(fs, ino, stashed_inode, block_buf);
499
500 }
501 stashed_inode = old_stashed_inode;
502 process_inode_count = 0;
503#if 0
Theodore Ts'of3db3561997-04-26 13:34:30 +0000504 printf("end process inodes\n");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000505#endif
506 ehandler_operation(old_operation);
507}
508
509static int process_inode_cmp(const void *a, const void *b)
510{
511 const struct process_inode_block *ib_a =
512 (const struct process_inode_block *) a;
513 const struct process_inode_block *ib_b =
514 (const struct process_inode_block *) b;
515
516 return (ib_a->inode.i_block[EXT2_IND_BLOCK] -
517 ib_b->inode.i_block[EXT2_IND_BLOCK]);
518}
519
520static int dir_block_cmp(const void *a, const void *b)
521{
522 const struct dir_block_struct *db_a =
523 (const struct dir_block_struct *) a;
524 const struct dir_block_struct *db_b =
525 (const struct dir_block_struct *) b;
526
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000527 if (db_a->blk != db_b->blk)
528 return (db_a->blk - db_b->blk);
529
530 if (db_a->ino != db_b->ino)
531 return (db_a->ino - db_b->ino);
532
533 return (db_a->blockcnt - db_b->blockcnt);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000534}
535
536/*
537 * This procedure will allocate the inode bad map table
538 */
539static void alloc_bad_map(ext2_filsys fs)
540{
541 errcode_t retval;
542
Theodore Ts'of3db3561997-04-26 13:34:30 +0000543 retval = ext2fs_allocate_inode_bitmap(fs, "bad inode map",
544 &inode_bad_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000545 if (retval) {
546 com_err("ext2fs_allocate_inode_bitmap", retval,
547 "while allocating inode_bad_map");
548 fatal_error(0);
549 }
550}
551
552/*
553 * Marks a block as in use, setting the dup_map if it's been set
554 * already. Called by process_block and process_bad_block.
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000555 *
556 * WARNING: Assumes checks have already been done to make sure block
557 * is valid. This is true in both process_block and process_bad_block.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000558 */
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000559static _INLINE_ void mark_block_used(ext2_filsys fs, blk_t block)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000560{
561 errcode_t retval;
562
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000563 if (ext2fs_fast_test_block_bitmap(block_found_map, block)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000564 if (!block_dup_map) {
565 retval = ext2fs_allocate_block_bitmap(fs,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000566 "multiply claimed block map", &block_dup_map);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000567 if (retval) {
568 com_err("ext2fs_allocate_block_bitmap", retval,
569 "while allocating block_dup_map");
570 fatal_error(0);
571 }
572 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000573 ext2fs_fast_mark_block_bitmap(block_dup_map, block);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000574 } else {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000575 ext2fs_fast_mark_block_bitmap(block_found_map, block);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000576 }
577}
578
579/*
580 * This subroutine is called on each inode to account for all of the
581 * blocks used by that inode.
582 */
583static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode,
584 char *block_buf)
585{
586 struct process_block_struct pb;
587 errcode_t retval;
588
589 if (!inode_has_valid_blocks(inode))
590 return;
591
592 pb.ino = ino;
593 pb.num_blocks = pb.last_block = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000594 pb.num_illegal_blocks = 0;
595 pb.suppress = pb.clear = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000596 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000597 pb.fix = -1;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000598 pb.inode = inode;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000599 retval = ext2fs_block_iterate(fs, ino,
600 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
601 block_buf, process_block, &pb);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000602 if (retval)
603 com_err(program_name, retval,
604 "while calling ext2fs_block_iterate in check_blocks");
605
Theodore Ts'of3db3561997-04-26 13:34:30 +0000606 if (pb.clear) {
607 e2fsck_read_inode(fs, ino, inode, "check_blocks");
608 if (retval) {
609 com_err("check_blocks", retval,
610 "while reading to be cleared inode %d", ino);
611 fatal_error(0);
612 }
613 inode->i_links_count = 0;
614 inode_link_info[ino] = 0;
615 inode->i_dtime = time(0);
616 e2fsck_write_inode(fs, ino, inode, "check_blocks");
617 ext2fs_unmark_inode_bitmap(inode_dir_map, ino);
618 ext2fs_unmark_inode_bitmap(inode_used_map, ino);
619 /*
620 * The inode was probably partially accounted for
621 * before processing was aborted, so we need to
622 * restart the pass 1 scan.
623 */
624 restart_e2fsck++;
625 return;
626 }
627
628 if (pb.fix > 0)
629 e2fsck_read_inode(fs, ino, inode, "check_blocks");
630
Theodore Ts'o3839e651997-04-26 13:21:57 +0000631 pb.num_blocks *= (fs->blocksize / 512);
632#if 0
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000633 printf("inode %u, i_size = %lu, last_block = %lu, i_blocks=%lu, num_blocks = %lu\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000634 ino, inode->i_size, pb.last_block, inode->i_blocks,
635 pb.num_blocks);
636#endif
637 if (!pb.num_blocks && pb.is_dir) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000638 printf("Inode %lu is a zero length directory. ", ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000639 if (ask("Clear", 1)) {
640 inode->i_links_count = 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000641 inode_link_info[ino] = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000642 inode->i_dtime = time(0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000643 e2fsck_write_inode(fs, ino, inode, "check_blocks");
644 ext2fs_unmark_inode_bitmap(inode_dir_map, ino);
645 ext2fs_unmark_inode_bitmap(inode_used_map, ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000646 fs_directory_count--;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000647 pb.is_dir = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000648 } else
649 ext2fs_unmark_valid(fs);
650 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000651 if ((pb.is_dir && (inode->i_size != (pb.last_block + 1) * fs->blocksize)) ||
652 (inode->i_size < pb.last_block * fs->blocksize)) {
653 printf ("%s %lu, incorrect size, %u (counted = %u). ",
654 pb.is_dir ? "Directory" : "Inode", ino, inode->i_size,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000655 (pb.last_block+1) * fs->blocksize);
656 if (ask ("Set size to counted", 1)) {
657 inode->i_size = (pb.last_block+1) * fs->blocksize;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000658 e2fsck_write_inode(fs, ino, inode, "check_blocks");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000659 } else
660 ext2fs_unmark_valid(fs);
661 }
662 if (pb.num_blocks != inode->i_blocks) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000663 printf ("Inode %lu, i_blocks wrong %u (counted=%u). ",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000664 ino, inode->i_blocks, pb.num_blocks);
665 if (ask ("Set i_blocks to counted", 1)) {
666 inode->i_blocks = pb.num_blocks;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000667 e2fsck_write_inode(fs, ino, inode, "check_blocks");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000668 } else
669 ext2fs_unmark_valid(fs);
670 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000671}
672
673/*
674 * Helper function called by process block when an illegal block is
675 * found. It returns a description about why the block is illegal
676 */
677static char *describe_illegal_block(ext2_filsys fs, blk_t block)
678{
679 blk_t super;
680 int i;
681 static char problem[80];
682
683 super = fs->super->s_first_data_block;
684 strcpy(problem, "PROGRAMMING ERROR: Unknown reason for illegal block");
685 if (block < super) {
686 sprintf(problem, "< FIRSTBLOCK (%u)", super);
687 return(problem);
688 } else if (block >= fs->super->s_blocks_count) {
689 sprintf(problem, "> BLOCKS (%u)", fs->super->s_blocks_count);
690 return(problem);
691 }
692 for (i = 0; i < fs->group_desc_count; i++) {
693 if (block == super) {
694 sprintf(problem, "is the superblock in group %d", i);
695 break;
696 }
697 if (block > super &&
698 block <= (super + fs->desc_blocks)) {
699 sprintf(problem, "is in the group descriptors "
700 "of group %d", i);
701 break;
702 }
703 if (block == fs->group_desc[i].bg_block_bitmap) {
704 sprintf(problem, "is the block bitmap of group %d", i);
705 break;
706 }
707 if (block == fs->group_desc[i].bg_inode_bitmap) {
708 sprintf(problem, "is the inode bitmap of group %d", i);
709 break;
710 }
711 if (block >= fs->group_desc[i].bg_inode_table &&
712 (block < fs->group_desc[i].bg_inode_table
713 + fs->inode_blocks_per_group)) {
714 sprintf(problem, "is in the inode table of group %d",
715 i);
716 break;
717 }
718 super += fs->super->s_blocks_per_group;
719 }
720 return(problem);
721}
Theodore Ts'o3839e651997-04-26 13:21:57 +0000722
723/*
724 * This is a helper function for check_blocks().
725 */
726int process_block(ext2_filsys fs,
727 blk_t *block_nr,
728 int blockcnt,
729 void *private)
730{
731 struct process_block_struct *p;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000732 char *problem;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000733 blk_t blk = *block_nr;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000734 int ret_code = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000735
Theodore Ts'o3839e651997-04-26 13:21:57 +0000736 p = (struct process_block_struct *) private;
737
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000738 if (blk == 0) {
739 if (p->is_dir == 0) {
740 printf("process_block() called with blk == 0, "
741 "inode %lu???", p->ino);
742 return 0;
743 }
744 if (blockcnt < 0)
745 return 0;
746 if (blockcnt * fs->blocksize < p->inode->i_size) {
747 printf("Hole found in directory inode %lu! "
748 "(blkcnt=%d)\n", p->ino, blockcnt);
749 goto mark_dir;
750 }
751 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000752 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000753
754#if 0
755 printf("Process_block, inode %lu, block %u, #%d\n", p->ino, blk,
756 blockcnt);
757#endif
758
759 if (blk < fs->super->s_first_data_block ||
760 blk >= fs->super->s_blocks_count ||
761 ext2fs_test_block_bitmap(block_illegal_map, blk)) {
762 problem = describe_illegal_block(fs, blk);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000763 if (preen) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000764 printf("Block %u of inode %lu %s\n", blk, p->ino,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000765 problem);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000766 preenhalt(fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000767 }
768 if (p->fix == -1) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000769 printf("Remove illegal block(s) in inode %lu", p->ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000770 p->fix = ask("", 1);
771 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000772 p->num_illegal_blocks++;
773 if (!p->suppress && (p->num_illegal_blocks % 20) == 0) {
774 printf("Too many illegal blocks in inode %lu.\n",
775 p->ino);
776 if (ask("Clear inode", 1)) {
777 p->clear = 1;
778 return BLOCK_ABORT;
779 }
780 if (ask("Supress messages", 0)) {
781 p->suppress = 1;
782 }
783 }
784 if (!p->suppress)
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000785 printf("Block #%d (%u) %s. %s\n", blockcnt, blk,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000786 problem, clear_msg[p->fix]);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000787 if (p->fix) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000788 blk = *block_nr = 0;
789 ret_code = BLOCK_CHANGED;
790 goto mark_dir;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000791 } else {
792 ext2fs_unmark_valid(fs);
793 return 0;
794 }
795 }
796
797 mark_block_used(fs, blk);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000798 p->num_blocks++;
799 if (blockcnt < 0)
800 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000801
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000802 p->last_block = blockcnt;
803mark_dir:
804 if (p->is_dir) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000805 if (dir_block_count >= dir_block_size) {
806 dir_block_size += 100;
807 dir_blocks = realloc(dir_blocks,
808 dir_block_size *
809 sizeof(struct dir_block_struct));
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000810 if (dir_blocks == 0)
811 fatal_error("Not enough memory to "
812 "realloc dir_blocks");
Theodore Ts'o3839e651997-04-26 13:21:57 +0000813 }
814
815 dir_blocks[dir_block_count].blk = blk;
816 dir_blocks[dir_block_count].ino = p->ino;
817 dir_blocks[dir_block_count].blockcnt = blockcnt;
818 dir_block_count++;
819 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000820 return ret_code;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000821}
822
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000823static void bad_block_indirect(ext2_filsys fs, blk_t blk)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000824{
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000825 printf("Bad block %u used as bad block indirect block?!?\n", blk);
826 preenhalt(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000827 printf("\nThis inconsistency can not be fixed with "
828 "e2fsck; to fix it, use\n"
829 """dumpe2fs -b"" to dump out the bad block "
830 "list and ""e2fsck -L filename""\n"
831 "to read it back in again.\n");
832 if (ask("Continue", 0))
833 return;
834 fatal_error(0);
835}
836
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000837static int bad_primary_block(ext2_filsys fs, blk_t *block_nr)
838{
839 printf("\nIf the block is really bad, the filesystem can not be "
840 "fixed.\n");
841 preenhalt(fs);
842 printf("You can clear the this block from the bad block list\n");
843 printf("and hope that block is really OK, but there are no "
844 "guarantees.\n\n");
845 if (ask("Clear (and hope for the best)", 1)) {
846 *block_nr = 0;
847 return 1;
848 }
849 ext2fs_unmark_valid(fs);
850 return 0;
851}
852
Theodore Ts'o3839e651997-04-26 13:21:57 +0000853int process_bad_block(ext2_filsys fs,
854 blk_t *block_nr,
855 int blockcnt,
856 void *private)
857{
858 struct process_block_struct *p;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000859 blk_t blk = *block_nr;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000860 int first_block;
861 int i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000862
863 if (!blk)
864 return 0;
865 p = (struct process_block_struct *) private;
866
867 if ((blk < fs->super->s_first_data_block) ||
868 (blk >= fs->super->s_blocks_count)) {
869 if (preen) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000870 printf("Illegal block %u in bad block inode\n", blk);
871 preenhalt(fs);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000872 }
873 if (p->fix == -1)
874 p->fix = ask("Remove illegal block(s) in bad block inode", 1);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000875 printf("Illegal block %u in bad block inode. %s\n", blk,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000876 clear_msg[p->fix]);
877 if (p->fix) {
878 *block_nr = 0;
879 return BLOCK_CHANGED;
880 } else {
881 ext2fs_unmark_valid(fs);
882 return 0;
883 }
884 }
885
886 if (blockcnt < 0) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000887 if (ext2fs_test_block_bitmap(block_found_map, blk))
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000888 bad_block_indirect(fs, blk);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000889 else
890 mark_block_used(fs, blk);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000891 return 0;
892 }
893#if 0
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000894 printf ("DEBUG: Marking %u as bad.\n", blk);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000895#endif
896 fs_badblocks_count++;
897 /*
898 * If the block is not used, then mark it as used and return.
899 * If it is already marked as found, this must mean that
900 * there's an overlap between the filesystem table blocks
901 * (bitmaps and inode table) and the bad block list.
902 */
Theodore Ts'of3db3561997-04-26 13:34:30 +0000903 if (!ext2fs_test_block_bitmap(block_found_map, blk)) {
904 ext2fs_mark_block_bitmap(block_found_map, blk);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000905 return 0;
906 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000907 /*
908 * Try to find the where the filesystem block was used...
909 */
910 first_block = fs->super->s_first_data_block;
911
912 for (i = 0; i < fs->group_desc_count; i++ ) {
913 if (blk == first_block) {
914 if (i == 0) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000915 printf("The primary superblock (%u) is "
916 "on the bad block list.\n", blk);
917 if (bad_primary_block(fs, block_nr))
918 return BLOCK_CHANGED;
919 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000920 }
921 if (!preen)
922 printf("Warning: Group %d's superblock "
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000923 "(%u) is bad.\n", i, blk);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000924 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000925 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000926 if ((blk > first_block) &&
927 (blk <= first_block + fs->desc_blocks)) {
928 if (i == 0) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000929 printf("Block %u in the primary group "
930 "descriptors is on the bad block "
931 "list\n", blk);
932 if (bad_primary_block(fs, block_nr))
933 return BLOCK_CHANGED;
934 return 0;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000935 }
936 if (!preen)
937 printf("Warning: Group %d's copy of the "
938 "group descriptors has a bad "
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000939 "block (%u).\n", i, blk);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000940 return 0;
941 }
942 if (blk == fs->group_desc[i].bg_block_bitmap) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000943 printf("Group %d's block bitmap (%u) is bad. ",
Theodore Ts'of3db3561997-04-26 13:34:30 +0000944 i, blk);
945 if (ask("Relocate", 1)) {
946 invalid_block_bitmap[i]++;
947 invalid_bitmaps++;
948 } else
949 ext2fs_unmark_valid(fs);
950 return 0;
951 }
952 if (blk == fs->group_desc[i].bg_inode_bitmap) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000953 printf("Group %d's inode bitmap (%u) is bad. ",
Theodore Ts'of3db3561997-04-26 13:34:30 +0000954 i, blk);
955 if (ask("Relocate", 1)) {
956 invalid_inode_bitmap[i]++;
957 invalid_bitmaps++;
958 } else
959 ext2fs_unmark_valid(fs);
960 return 0;
961 }
962 if ((blk >= fs->group_desc[i].bg_inode_table) &&
963 (blk < (fs->group_desc[i].bg_inode_table +
964 fs->inode_blocks_per_group))) {
965 printf("WARNING: Severe data loss possible!!!!\n");
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000966 printf("Bad block %u in group %d's inode table. ",
Theodore Ts'of3db3561997-04-26 13:34:30 +0000967 blk, i);
968 if (ask("Relocate", 1)) {
969 invalid_inode_table[i]++;
970 invalid_bitmaps++;
971 } else
972 ext2fs_unmark_valid(fs);
973 return 0;
974 }
975 first_block += fs->super->s_blocks_per_group;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000976 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000977 /*
978 * If we've gotten to this point, then the only
979 * possibility is that the bad block inode meta data
980 * is using a bad block.
981 */
982 if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
983 p->inode->i_block[EXT2_DIND_BLOCK]) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000984 bad_block_indirect(fs, blk);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000985 return 0;
986 }
987
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000988 printf("Programming error? block #%u claimed for no reason "
Theodore Ts'of3db3561997-04-26 13:34:30 +0000989 "in process_bad_block.\n", blk);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000990 return 0;
991}
992
Theodore Ts'of3db3561997-04-26 13:34:30 +0000993static void new_table_block(ext2_filsys fs, blk_t first_block, int group,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000994 const char *name, int num, blk_t *new_block)
995{
996 errcode_t retval;
997 blk_t old_block = *new_block;
998 int i;
999 char *buf;
1000
1001 retval = ext2fs_get_free_blocks(fs, first_block,
1002 first_block + fs->super->s_blocks_per_group,
1003 num, block_found_map, new_block);
1004 if (retval) {
1005 printf("Could not allocate %d block(s) for %s: %s\n",
1006 num, name, error_message(retval));
1007 ext2fs_unmark_valid(fs);
1008 return;
1009 }
1010 buf = malloc(fs->blocksize);
1011 if (!buf) {
1012 printf("Could not allocate block buffer for relocating %s\n",
1013 name);
1014 ext2fs_unmark_valid(fs);
1015 return;
1016 }
1017 ext2fs_mark_super_dirty(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001018 printf("Relocating group %d's %s ", group, name);
1019 if (old_block)
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001020 printf("from %u ", old_block);
1021 printf("to %u...\n", *new_block);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001022 for (i = 0; i < num; i++) {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001023 ext2fs_mark_block_bitmap(block_found_map, (*new_block)+i);
1024 if (old_block) {
1025 retval = io_channel_read_blk(fs->io, old_block + i,
1026 1, buf);
1027 if (retval)
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001028 printf("Warning: could not read block %u "
Theodore Ts'of3db3561997-04-26 13:34:30 +00001029 "of %s: %s\n",
1030 old_block + i, name,
1031 error_message(retval));
1032 } else
1033 memset(buf, 0, fs->blocksize);
1034
Theodore Ts'o3839e651997-04-26 13:21:57 +00001035 retval = io_channel_write_blk(fs->io, (*new_block) + i,
1036 1, buf);
1037 if (retval)
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001038 printf("Warning: could not write block %u for %s: %s\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +00001039 (*new_block) + i, name, error_message(retval));
Theodore Ts'o3839e651997-04-26 13:21:57 +00001040 }
1041 free(buf);
1042}
1043
1044/*
Theodore Ts'of3db3561997-04-26 13:34:30 +00001045 * This routine gets called at the end of pass 1 if bad blocks are
1046 * detected in the superblock, group descriptors, inode_bitmaps, or
1047 * block bitmaps. At this point, all of the blocks have been mapped
1048 * out, so we can try to allocate new block(s) to replace the bad
1049 * blocks.
Theodore Ts'o3839e651997-04-26 13:21:57 +00001050 */
Theodore Ts'of3db3561997-04-26 13:34:30 +00001051static void handle_fs_bad_blocks(ext2_filsys fs)
Theodore Ts'o3839e651997-04-26 13:21:57 +00001052{
Theodore Ts'of3db3561997-04-26 13:34:30 +00001053 int i;
1054 int first_block = fs->super->s_first_data_block;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001055
1056 for (i = 0; i < fs->group_desc_count; i++) {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001057 if (invalid_block_bitmap[i]) {
1058 new_table_block(fs, first_block, i, "block bitmap", 1,
Theodore Ts'o3839e651997-04-26 13:21:57 +00001059 &fs->group_desc[i].bg_block_bitmap);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001060 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001061 if (invalid_inode_bitmap[i]) {
1062 new_table_block(fs, first_block, i, "inode bitmap", 1,
Theodore Ts'o3839e651997-04-26 13:21:57 +00001063 &fs->group_desc[i].bg_inode_bitmap);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001064 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001065 if (invalid_inode_table[i]) {
1066 new_table_block(fs, first_block, i, "inode table",
1067 fs->inode_blocks_per_group,
Theodore Ts'o3839e651997-04-26 13:21:57 +00001068 &fs->group_desc[i].bg_inode_table);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001069 restart_e2fsck++;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001070 }
Theodore Ts'o3839e651997-04-26 13:21:57 +00001071 first_block += fs->super->s_blocks_per_group;
1072 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001073 invalid_bitmaps = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001074}
1075
1076/*
1077 * This routine marks all blocks which are used by the superblock,
1078 * group descriptors, inode bitmaps, and block bitmaps.
1079 */
1080static void mark_table_blocks(ext2_filsys fs)
1081{
Theodore Ts'of3db3561997-04-26 13:34:30 +00001082 blk_t block, b;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001083 int i,j;
1084
1085 block = fs->super->s_first_data_block;
1086 for (i = 0; i < fs->group_desc_count; i++) {
1087 /*
1088 * Mark block used for the block bitmap
1089 */
Theodore Ts'of3db3561997-04-26 13:34:30 +00001090 if (fs->group_desc[i].bg_block_bitmap) {
1091 if (ext2fs_test_block_bitmap(block_found_map,
1092 fs->group_desc[i].bg_block_bitmap)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001093 printf("Group %i's block bitmap at %u "
Theodore Ts'of3db3561997-04-26 13:34:30 +00001094 "conflicts with some other fs block.\n",
1095 i, fs->group_desc[i].bg_block_bitmap);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001096 preenhalt(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001097 if (ask("Relocate", 1)) {
1098 invalid_block_bitmap[i]++;
1099 invalid_bitmaps++;
1100 } else {
1101 ext2fs_unmark_valid(fs);
1102 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001103 } else {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001104 ext2fs_mark_block_bitmap(block_found_map,
1105 fs->group_desc[i].bg_block_bitmap);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001106 ext2fs_mark_block_bitmap(block_illegal_map,
1107 fs->group_desc[i].bg_block_bitmap);
1108 }
1109
Theodore Ts'of3db3561997-04-26 13:34:30 +00001110 }
Theodore Ts'o3839e651997-04-26 13:21:57 +00001111 /*
1112 * Mark block used for the inode bitmap
1113 */
Theodore Ts'of3db3561997-04-26 13:34:30 +00001114 if (fs->group_desc[i].bg_inode_bitmap) {
1115 if (ext2fs_test_block_bitmap(block_found_map,
1116 fs->group_desc[i].bg_inode_bitmap)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001117 printf("Group %i's inode bitmap at %u "
Theodore Ts'of3db3561997-04-26 13:34:30 +00001118 "conflicts with some other fs block.\n",
1119 i, fs->group_desc[i].bg_inode_bitmap);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001120 preenhalt(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001121 if (ask("Relocate", 1)) {
1122 invalid_inode_bitmap[i]++;
1123 invalid_bitmaps++;
1124 } else {
1125 ext2fs_unmark_valid(fs);
1126 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001127 } else {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001128 ext2fs_mark_block_bitmap(block_found_map,
1129 fs->group_desc[i].bg_inode_bitmap);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001130 ext2fs_mark_block_bitmap(block_illegal_map,
1131 fs->group_desc[i].bg_inode_bitmap);
1132 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001133 }
1134
Theodore Ts'o3839e651997-04-26 13:21:57 +00001135 /*
1136 * Mark the blocks used for the inode table
1137 */
Theodore Ts'of3db3561997-04-26 13:34:30 +00001138 if (fs->group_desc[i].bg_inode_table) {
1139 for (j = 0, b = fs->group_desc[i].bg_inode_table;
1140 j < fs->inode_blocks_per_group;
1141 j++, b++) {
1142 if (ext2fs_test_block_bitmap(block_found_map,
1143 b)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001144 printf("Group %i's inode table at %u "
Theodore Ts'of3db3561997-04-26 13:34:30 +00001145 "conflicts with some other "
1146 "fs block.\n",
1147 i, b);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001148 preenhalt(fs);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001149 if (ask("Relocate", 1)) {
1150 invalid_inode_table[i]++;
1151 invalid_bitmaps++;
1152 } else {
1153 ext2fs_unmark_valid(fs);
1154 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001155 } else {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001156 ext2fs_mark_block_bitmap(block_found_map,
1157 b);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001158 ext2fs_mark_block_bitmap(block_illegal_map,
1159 b);
1160 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001161 }
1162 }
1163
Theodore Ts'o3839e651997-04-26 13:21:57 +00001164 /*
1165 * Mark this group's copy of the superblock
1166 */
Theodore Ts'of3db3561997-04-26 13:34:30 +00001167 ext2fs_mark_block_bitmap(block_found_map, block);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001168 ext2fs_mark_block_bitmap(block_illegal_map, block);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001169
1170 /*
1171 * Mark this group's copy of the descriptors
1172 */
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001173 for (j = 0; j < fs->desc_blocks; j++) {
Theodore Ts'of3db3561997-04-26 13:34:30 +00001174 ext2fs_mark_block_bitmap(block_found_map,
Theodore Ts'o3839e651997-04-26 13:21:57 +00001175 block + j + 1);
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001176 ext2fs_mark_block_bitmap(block_illegal_map,
1177 block + j + 1);
1178 }
Theodore Ts'o3839e651997-04-26 13:21:57 +00001179 block += fs->super->s_blocks_per_group;
1180 }
1181}
1182
1183/*
1184 * This subroutines short circuits ext2fs_get_blocks and
1185 * ext2fs_check_directory; we use them since we already have the inode
1186 * structure, so there's no point in letting the ext2fs library read
1187 * the inode again.
1188 */
1189static errcode_t pass1_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
1190{
1191 int i;
1192
1193 if (ino == stashed_ino) {
1194 for (i=0; i < EXT2_N_BLOCKS; i++)
1195 blocks[i] = stashed_inode->i_block[i];
1196 return 0;
1197 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001198 printf("INTERNAL ERROR: pass1_get_blocks: unexpected inode #%lu\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +00001199 ino);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001200 printf("\t(was expecting %lu)\n", stashed_ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001201 exit(FSCK_ERROR);
1202}
1203
1204static errcode_t pass1_check_directory(ext2_filsys fs, ino_t ino)
1205{
1206 if (ino == stashed_ino) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +00001207 if (!LINUX_S_ISDIR(stashed_inode->i_mode))
Theodore Ts'o3839e651997-04-26 13:21:57 +00001208 return ENOTDIR;
1209 return 0;
1210 }
Theodore Ts'of3db3561997-04-26 13:34:30 +00001211 printf("INTERNAL ERROR: pass1_check_directory: unexpected inode #%lu\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +00001212 ino);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001213 printf("\t(was expecting %lu)\n", stashed_ino);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001214 exit(FSCK_ERROR);
1215}