blob: 7e1c82e54ce9048f0e0dab82fffe7e2f6409758f [file] [log] [blame]
Mike Frysinger51a43b42005-09-24 07:11:16 +00001/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002 * e2fsck
3 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
Rob Landley7c94bed2006-05-03 21:58:45 +00005 * Copyright (C) 2006 Garrett Kajmowicz
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006 * This file may be
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007 * redistributed under the terms of the GNU Public License.
8 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009 *
10 * Dictionary Abstract Data Type
11 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
12 * Free Software License:
13 * All rights are reserved by the author, with the following exceptions:
14 * Permission is granted to freely reproduce and distribute this software,
15 * possibly in exchange for a fee, provided that this copyright notice appears
16 * intact. Permission is also granted to adapt this software to produce
17 * derivative works, as long as the modified versions carry this copyright
18 * notice and additional notices stating that the work has been modified.
19 * This source code may be translated into executable form and incorporated
20 * into proprietary software; there is no requirement for such software to
21 * contain a copyright notice related to this source.
22 *
23 * linux/fs/recovery and linux/fs/revoke
24 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
25 *
26 * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
27 *
28 * This file is part of the Linux kernel and is made available under
29 * the terms of the GNU General Public License, version 2, or at your
30 * option, any later version, incorporated herein by reference.
31 *
32 * Journal recovery routines for the generic filesystem journaling code;
33 * part of the ext2fs journaling system.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000034 */
35
36#ifndef _GNU_SOURCE
37#define _GNU_SOURCE 1 /* get strnlen() */
38#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000039
Rob Landley43ac8882006-04-01 00:40:33 +000040#include "e2fsck.h" /*Put all of our defines here to clean things up*/
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000041
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000042/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000043 * Procedure declarations
44 */
45
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000046static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000047
48/* pass1.c */
49static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000050
51/* pass2.c */
52static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
53 ext2_ino_t ino, char *buf);
54
55/* pass3.c */
56static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
57static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
58 int num, int gauranteed_size);
59static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
60static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
61 int adj);
62
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000063/* rehash.c */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000064static void e2fsck_rehash_directories(e2fsck_t ctx);
65
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000066/* util.c */
67static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
68 const char *description);
69static int ask(e2fsck_t ctx, const char * string, int def);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000070static void e2fsck_read_bitmaps(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000071static void preenhalt(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000072static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
73 struct ext2_inode * inode, const char * proc);
74static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
75 struct ext2_inode * inode, const char * proc);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000076static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
77 const char *name, io_manager manager);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000078
79/* unix.c */
80static void e2fsck_clear_progbar(e2fsck_t ctx);
81static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
82 float percent, unsigned int dpynum);
Rob Landley43ac8882006-04-01 00:40:33 +000083
84
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000085/*
86 * problem.h --- e2fsck problem error codes
87 */
88
89typedef __u32 problem_t;
90
91struct problem_context {
92 errcode_t errcode;
93 ext2_ino_t ino, ino2, dir;
94 struct ext2_inode *inode;
95 struct ext2_dir_entry *dirent;
96 blk_t blk, blk2;
97 e2_blkcnt_t blkcount;
98 int group;
99 __u64 num;
100 const char *str;
101};
102
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000103
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000104
105/*
106 * Function declarations
107 */
108static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
109static int end_problem_latch(e2fsck_t ctx, int mask);
110static int set_latch_flags(int mask, int setflags, int clearflags);
111static void clear_problem_context(struct problem_context *ctx);
112
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000113/*
114 * Dictionary Abstract Data Type
115 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
116 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000117 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
118 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000119 */
120
121#ifndef DICT_H
122#define DICT_H
123
124/*
125 * Blurb for inclusion into C++ translation units
126 */
127
128typedef unsigned long dictcount_t;
129#define DICTCOUNT_T_MAX ULONG_MAX
130
131/*
132 * The dictionary is implemented as a red-black tree
133 */
134
135typedef enum { dnode_red, dnode_black } dnode_color_t;
136
137typedef struct dnode_t {
138 struct dnode_t *dict_left;
139 struct dnode_t *dict_right;
140 struct dnode_t *dict_parent;
141 dnode_color_t dict_color;
142 const void *dict_key;
143 void *dict_data;
144} dnode_t;
145
146typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000147typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000148
149typedef struct dict_t {
150 dnode_t dict_nilnode;
151 dictcount_t dict_nodecount;
152 dictcount_t dict_maxcount;
153 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000154 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000155 int dict_dupes;
156} dict_t;
157
158typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
159
160typedef struct dict_load_t {
161 dict_t *dict_dictptr;
162 dnode_t dict_nilnode;
163} dict_load_t;
164
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000165#define dict_count(D) ((D)->dict_nodecount)
166#define dnode_get(N) ((N)->dict_data)
167#define dnode_getkey(N) ((N)->dict_key)
168
169#endif
170
171/*
172 * Compatibility header file for e2fsck which should be included
173 * instead of linux/jfs.h
174 *
175 * Copyright (C) 2000 Stephen C. Tweedie
176 */
177
178/*
179 * Pull in the definition of the e2fsck context structure
180 */
181
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000182struct buffer_head {
183 char b_data[8192];
184 e2fsck_t b_ctx;
185 io_channel b_io;
186 int b_size;
187 blk_t b_blocknr;
188 int b_dirty;
189 int b_uptodate;
190 int b_err;
191};
192
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000193
194#define K_DEV_FS 1
195#define K_DEV_JOURNAL 2
196
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000197#define lock_buffer(bh) do {} while(0)
198#define unlock_buffer(bh) do {} while(0)
199#define buffer_req(bh) 1
200#define do_readahead(journal, start) do {} while(0)
201
202static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
203
204typedef struct {
205 int object_length;
206} kmem_cache_t;
207
208#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000209
210/*
211 * We use the standard libext2fs portability tricks for inline
212 * functions.
213 */
214
Rob Landley7c94bed2006-05-03 21:58:45 +0000215static kmem_cache_t * do_cache_create(int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000216{
217 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000218
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000219 new_cache = malloc(sizeof(*new_cache));
220 if (new_cache)
221 new_cache->object_length = len;
222 return new_cache;
223}
224
Rob Landley7c94bed2006-05-03 21:58:45 +0000225static void do_cache_destroy(kmem_cache_t *cache)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000226{
227 free(cache);
228}
229
230/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000231 * badblocks.c --- replace/append bad blocks to the bad block inode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000232 */
233
234static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
235 void *priv_data);
236
237
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +0000238static void invalid_block(ext2_filsys fs FSCK_ATTR((unused)), blk_t blk)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000239{
240 printf(_("Bad block %u out of range; ignored.\n"), blk);
241 return;
242}
243
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000244static void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000245 int replace_bad_blocks)
246{
247 ext2_filsys fs = ctx->fs;
248 errcode_t retval;
249 badblocks_list bb_list = 0;
250 FILE *f;
251 char buf[1024];
252
253 e2fsck_read_bitmaps(ctx);
254
255 /*
256 * Make sure the bad block inode is sane. If there are any
257 * illegal blocks, clear them.
258 */
259 retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
260 check_bb_inode_blocks, 0);
261 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000262 bb_error_msg(_("while sanity checking the bad blocks inode"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000263 goto fatal;
264 }
265
266 /*
267 * If we're appending to the bad blocks inode, read in the
268 * current bad blocks.
269 */
270 if (!replace_bad_blocks) {
271 retval = ext2fs_read_bb_inode(fs, &bb_list);
272 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000273 bb_error_msg(_("while reading the bad blocks inode"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000274 goto fatal;
275 }
276 }
277
278 /*
279 * Now read in the bad blocks from the file; if
280 * bad_blocks_file is null, then try to run the badblocks
281 * command.
282 */
283 if (bad_blocks_file) {
284 f = fopen(bad_blocks_file, "r");
285 if (!f) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000286 bb_error_msg(_("while trying to open %s"), bad_blocks_file);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000287 goto fatal;
288 }
289 } else {
290 sprintf(buf, "badblocks -b %d %s%s%s %d", fs->blocksize,
291 (ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
292 (ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
293 fs->device_name, fs->super->s_blocks_count);
294 f = popen(buf, "r");
295 if (!f) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000296 bb_error_msg(_("while trying popen '%s'"), buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000297 goto fatal;
298 }
299 }
300 retval = ext2fs_read_bb_FILE(fs, f, &bb_list, invalid_block);
301 if (bad_blocks_file)
302 fclose(f);
303 else
304 pclose(f);
305 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000306 bb_error_msg(_("while reading in list of bad blocks from file"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000307 goto fatal;
308 }
309
310 /*
311 * Finally, update the bad blocks from the bad_block_map
312 */
313 retval = ext2fs_update_bb_inode(fs, bb_list);
314 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +0000315 bb_error_msg(_("while updating bad block inode"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000316 goto fatal;
317 }
318
319 ext2fs_badblocks_list_free(bb_list);
320 return;
321
322fatal:
323 ctx->flags |= E2F_FLAG_ABORT;
324 return;
325
326}
327
328static int check_bb_inode_blocks(ext2_filsys fs,
329 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +0000330 int blockcnt FSCK_ATTR((unused)),
331 void *priv_data FSCK_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000332{
333 if (!*block_nr)
334 return 0;
335
336 /*
337 * If the block number is outrageous, clear it and ignore it.
338 */
339 if (*block_nr >= fs->super->s_blocks_count ||
340 *block_nr < fs->super->s_first_data_block) {
341 printf(_("Warning illegal block %u found in bad block inode. Cleared.\n"), *block_nr);
342 *block_nr = 0;
343 return BLOCK_CHANGED;
344 }
345
346 return 0;
347}
348
349/*
350 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000351 */
352
353
354/*
355 * These macros provide short convenient names for structure members,
356 * which are embellished with dict_ prefixes so that they are
357 * properly confined to the documented namespace. It's legal for a
358 * program which uses dict to define, for instance, a macro called ``parent''.
359 * Such a macro would interfere with the dnode_t struct definition.
360 * In general, highly portable and reusable C modules which expose their
361 * structures need to confine structure member names to well-defined spaces.
362 * The resulting identifiers aren't necessarily convenient to use, nor
363 * readable, in the implementation, however!
364 */
365
366#define left dict_left
367#define right dict_right
368#define parent dict_parent
369#define color dict_color
370#define key dict_key
371#define data dict_data
372
373#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000374#define maxcount dict_maxcount
375#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000376#define dupes dict_dupes
377
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000378#define dict_root(D) ((D)->nilnode.left)
379#define dict_nil(D) (&(D)->nilnode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000380
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000381static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000382
383/*
384 * Perform a ``left rotation'' adjustment on the tree. The given node P and
385 * its right child C are rearranged so that the P instead becomes the left
386 * child of C. The left subtree of C is inherited as the new right subtree
387 * for P. The ordering of the keys within the tree is thus preserved.
388 */
389
390static void rotate_left(dnode_t *upper)
391{
392 dnode_t *lower, *lowleft, *upparent;
393
394 lower = upper->right;
395 upper->right = lowleft = lower->left;
396 lowleft->parent = upper;
397
398 lower->parent = upparent = upper->parent;
399
400 /* don't need to check for root node here because root->parent is
401 the sentinel nil node, and root->parent->left points back to root */
402
403 if (upper == upparent->left) {
404 upparent->left = lower;
405 } else {
406 assert (upper == upparent->right);
407 upparent->right = lower;
408 }
409
410 lower->left = upper;
411 upper->parent = lower;
412}
413
414/*
415 * This operation is the ``mirror'' image of rotate_left. It is
416 * the same procedure, but with left and right interchanged.
417 */
418
419static void rotate_right(dnode_t *upper)
420{
421 dnode_t *lower, *lowright, *upparent;
422
423 lower = upper->left;
424 upper->left = lowright = lower->right;
425 lowright->parent = upper;
426
427 lower->parent = upparent = upper->parent;
428
429 if (upper == upparent->right) {
430 upparent->right = lower;
431 } else {
432 assert (upper == upparent->left);
433 upparent->left = lower;
434 }
435
436 lower->right = upper;
437 upper->parent = lower;
438}
439
440/*
441 * Do a postorder traversal of the tree rooted at the specified
442 * node and free everything under it. Used by dict_free().
443 */
444
445static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
446{
447 if (node == nil)
448 return;
449 free_nodes(dict, node->left, nil);
450 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000451 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000452}
453
454/*
455 * Verify that the tree contains the given node. This is done by
456 * traversing all of the nodes and comparing their pointers to the
457 * given pointer. Returns 1 if the node is found, otherwise
458 * returns zero. It is intended for debugging purposes.
459 */
460
461static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
462{
463 if (root != nil) {
464 return root == node
465 || verify_dict_has_node(nil, root->left, node)
466 || verify_dict_has_node(nil, root->right, node);
467 }
468 return 0;
469}
470
471
472/*
473 * Select a different set of node allocator routines.
474 */
475
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000476static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000477{
478 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000479 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000480}
481
482/*
483 * Free all the nodes in the dictionary by using the dictionary's
484 * installed free routine. The dictionary is emptied.
485 */
486
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000487static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000488{
489 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
490 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000491 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000492 dict->nilnode.left = &dict->nilnode;
493 dict->nilnode.right = &dict->nilnode;
494}
495
496/*
497 * Initialize a user-supplied dictionary object.
498 */
499
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000500static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000501{
502 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000503 dict->dict_freenode = dnode_free;
504 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000505 dict->maxcount = maxcount;
506 dict->nilnode.left = &dict->nilnode;
507 dict->nilnode.right = &dict->nilnode;
508 dict->nilnode.parent = &dict->nilnode;
509 dict->nilnode.color = dnode_black;
510 dict->dupes = 0;
511 return dict;
512}
513
514/*
515 * Locate a node in the dictionary having the given key.
516 * If the node is not found, a null a pointer is returned (rather than
517 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
518 * located node is returned.
519 */
520
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000521static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000522{
523 dnode_t *root = dict_root(dict);
524 dnode_t *nil = dict_nil(dict);
525 dnode_t *saved;
526 int result;
527
528 /* simple binary search adapted for trees that contain duplicate keys */
529
530 while (root != nil) {
531 result = dict->compare(key, root->key);
532 if (result < 0)
533 root = root->left;
534 else if (result > 0)
535 root = root->right;
536 else {
537 if (!dict->dupes) { /* no duplicates, return match */
538 return root;
539 } else { /* could be dupes, find leftmost one */
540 do {
541 saved = root;
542 root = root->left;
543 while (root != nil && dict->compare(key, root->key))
544 root = root->right;
545 } while (root != nil);
546 return saved;
547 }
548 }
549 }
550
551 return NULL;
552}
553
554/*
555 * Insert a node into the dictionary. The node should have been
556 * initialized with a data field. All other fields are ignored.
557 * The behavior is undefined if the user attempts to insert into
558 * a dictionary that is already full (for which the dict_isfull()
559 * function returns true).
560 */
561
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000562static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000563{
564 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
565 dnode_t *parent = nil, *uncle, *grandpa;
566 int result = -1;
567
568 node->key = key;
569
570 /* basic binary tree insert */
571
572 while (where != nil) {
573 parent = where;
574 result = dict->compare(key, where->key);
575 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
576 assert (dict->dupes || result != 0);
577 if (result < 0)
578 where = where->left;
579 else
580 where = where->right;
581 }
582
583 assert (where == nil);
584
585 if (result < 0)
586 parent->left = node;
587 else
588 parent->right = node;
589
590 node->parent = parent;
591 node->left = nil;
592 node->right = nil;
593
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000594 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000595
596 /* red black adjustments */
597
598 node->color = dnode_red;
599
600 while (parent->color == dnode_red) {
601 grandpa = parent->parent;
602 if (parent == grandpa->left) {
603 uncle = grandpa->right;
604 if (uncle->color == dnode_red) { /* red parent, red uncle */
605 parent->color = dnode_black;
606 uncle->color = dnode_black;
607 grandpa->color = dnode_red;
608 node = grandpa;
609 parent = grandpa->parent;
610 } else { /* red parent, black uncle */
611 if (node == parent->right) {
612 rotate_left(parent);
613 parent = node;
614 assert (grandpa == parent->parent);
615 /* rotation between parent and child preserves grandpa */
616 }
617 parent->color = dnode_black;
618 grandpa->color = dnode_red;
619 rotate_right(grandpa);
620 break;
621 }
622 } else { /* symmetric cases: parent == parent->parent->right */
623 uncle = grandpa->left;
624 if (uncle->color == dnode_red) {
625 parent->color = dnode_black;
626 uncle->color = dnode_black;
627 grandpa->color = dnode_red;
628 node = grandpa;
629 parent = grandpa->parent;
630 } else {
631 if (node == parent->left) {
632 rotate_right(parent);
633 parent = node;
634 assert (grandpa == parent->parent);
635 }
636 parent->color = dnode_black;
637 grandpa->color = dnode_red;
638 rotate_left(grandpa);
639 break;
640 }
641 }
642 }
643
644 dict_root(dict)->color = dnode_black;
645
646}
647
648/*
649 * Allocate a node using the dictionary's allocator routine, give it
650 * the data item.
651 */
652
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000653static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000654{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000655 dnode->data = data;
656 dnode->parent = NULL;
657 dnode->left = NULL;
658 dnode->right = NULL;
659 return dnode;
660}
661
662static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
663{
664 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000665
666 if (node) {
667 dnode_init(node, data);
668 dict_insert(dict, node, key);
669 return 1;
670 }
671 return 0;
672}
673
674/*
675 * Return the node with the lowest (leftmost) key. If the dictionary is empty
676 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
677 */
678
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000679static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000680{
681 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
682
683 if (root != nil)
684 while ((left = root->left) != nil)
685 root = left;
686
687 return (root == nil) ? NULL : root;
688}
689
690/*
691 * Return the given node's successor node---the node which has the
692 * next key in the the left to right ordering. If the node has
693 * no successor, a null pointer is returned rather than a pointer to
694 * the nil node.
695 */
696
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000697static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000698{
699 dnode_t *nil = dict_nil(dict), *parent, *left;
700
701 if (curr->right != nil) {
702 curr = curr->right;
703 while ((left = curr->left) != nil)
704 curr = left;
705 return curr;
706 }
707
708 parent = curr->parent;
709
710 while (parent != nil && curr == parent->right) {
711 curr = parent;
712 parent = curr->parent;
713 }
714
715 return (parent == nil) ? NULL : parent;
716}
717
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000718
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000719static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000720{
721 free(node);
722}
723
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000724
725#undef left
726#undef right
727#undef parent
728#undef color
729#undef key
730#undef data
731
732#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000733#undef maxcount
734#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000735#undef dupes
736
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000737
738/*
739 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000740 */
741
742/*
743 * This subroutine is called during pass1 to create a directory info
744 * entry. During pass1, the passed-in parent is 0; it will get filled
745 * in during pass2.
746 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000747static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000748{
749 struct dir_info *dir;
750 int i, j;
751 ext2_ino_t num_dirs;
752 errcode_t retval;
753 unsigned long old_size;
754
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000755 if (!ctx->dir_info) {
756 ctx->dir_info_count = 0;
757 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
758 if (retval)
759 num_dirs = 1024; /* Guess */
760 ctx->dir_info_size = num_dirs + 10;
761 ctx->dir_info = (struct dir_info *)
762 e2fsck_allocate_memory(ctx, ctx->dir_info_size
763 * sizeof (struct dir_info),
764 "directory map");
765 }
766
767 if (ctx->dir_info_count >= ctx->dir_info_size) {
768 old_size = ctx->dir_info_size * sizeof(struct dir_info);
769 ctx->dir_info_size += 10;
770 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
771 sizeof(struct dir_info),
772 &ctx->dir_info);
773 if (retval) {
774 ctx->dir_info_size -= 10;
775 return;
776 }
777 }
778
779 /*
780 * Normally, add_dir_info is called with each inode in
781 * sequential order; but once in a while (like when pass 3
782 * needs to recreate the root directory or lost+found
783 * directory) it is called out of order. In those cases, we
784 * need to move the dir_info entries down to make room, since
785 * the dir_info array needs to be sorted by inode number for
786 * get_dir_info()'s sake.
787 */
788 if (ctx->dir_info_count &&
789 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
790 for (i = ctx->dir_info_count-1; i > 0; i--)
791 if (ctx->dir_info[i-1].ino < ino)
792 break;
793 dir = &ctx->dir_info[i];
794 if (dir->ino != ino)
795 for (j = ctx->dir_info_count++; j > i; j--)
796 ctx->dir_info[j] = ctx->dir_info[j-1];
797 } else
798 dir = &ctx->dir_info[ctx->dir_info_count++];
799
800 dir->ino = ino;
801 dir->dotdot = parent;
802 dir->parent = parent;
803}
804
805/*
806 * get_dir_info() --- given an inode number, try to find the directory
807 * information entry for it.
808 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000809static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000810{
811 int low, high, mid;
812
813 low = 0;
814 high = ctx->dir_info_count-1;
815 if (!ctx->dir_info)
816 return 0;
817 if (ino == ctx->dir_info[low].ino)
818 return &ctx->dir_info[low];
819 if (ino == ctx->dir_info[high].ino)
820 return &ctx->dir_info[high];
821
822 while (low < high) {
823 mid = (low+high)/2;
824 if (mid == low || mid == high)
825 break;
826 if (ino == ctx->dir_info[mid].ino)
827 return &ctx->dir_info[mid];
828 if (ino < ctx->dir_info[mid].ino)
829 high = mid;
830 else
831 low = mid;
832 }
833 return 0;
834}
835
836/*
837 * Free the dir_info structure when it isn't needed any more.
838 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000839static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000840{
Rob Landleye7c43b62006-03-01 16:39:45 +0000841 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000842 ctx->dir_info_size = 0;
843 ctx->dir_info_count = 0;
844}
845
846/*
847 * Return the count of number of directories in the dir_info structure
848 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000849static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000850{
851 return ctx->dir_info_count;
852}
853
854/*
855 * A simple interator function
856 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000857static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000858{
859 if (*control >= ctx->dir_info_count)
860 return 0;
861
862 return(ctx->dir_info + (*control)++);
863}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000864
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000865/*
866 * dirinfo.c --- maintains the directory information table for e2fsck.
867 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000868 */
869
870#ifdef ENABLE_HTREE
871
872/*
873 * This subroutine is called during pass1 to create a directory info
874 * entry. During pass1, the passed-in parent is 0; it will get filled
875 * in during pass2.
876 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000877static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000878{
879 struct dx_dir_info *dir;
880 int i, j;
881 errcode_t retval;
882 unsigned long old_size;
883
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000884 if (!ctx->dx_dir_info) {
885 ctx->dx_dir_info_count = 0;
886 ctx->dx_dir_info_size = 100; /* Guess */
887 ctx->dx_dir_info = (struct dx_dir_info *)
888 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
889 * sizeof (struct dx_dir_info),
890 "directory map");
891 }
892
893 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
894 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
895 ctx->dx_dir_info_size += 10;
896 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
897 sizeof(struct dx_dir_info),
898 &ctx->dx_dir_info);
899 if (retval) {
900 ctx->dx_dir_info_size -= 10;
901 return;
902 }
903 }
904
905 /*
906 * Normally, add_dx_dir_info is called with each inode in
907 * sequential order; but once in a while (like when pass 3
908 * needs to recreate the root directory or lost+found
909 * directory) it is called out of order. In those cases, we
910 * need to move the dx_dir_info entries down to make room, since
911 * the dx_dir_info array needs to be sorted by inode number for
912 * get_dx_dir_info()'s sake.
913 */
914 if (ctx->dx_dir_info_count &&
915 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
916 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
917 if (ctx->dx_dir_info[i-1].ino < ino)
918 break;
919 dir = &ctx->dx_dir_info[i];
920 if (dir->ino != ino)
921 for (j = ctx->dx_dir_info_count++; j > i; j--)
922 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
923 } else
924 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
925
926 dir->ino = ino;
927 dir->numblocks = num_blocks;
928 dir->hashversion = 0;
929 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
930 * sizeof (struct dx_dirblock_info),
931 "dx_block info array");
932
933}
934
935/*
936 * get_dx_dir_info() --- given an inode number, try to find the directory
937 * information entry for it.
938 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000939static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000940{
941 int low, high, mid;
942
943 low = 0;
944 high = ctx->dx_dir_info_count-1;
945 if (!ctx->dx_dir_info)
946 return 0;
947 if (ino == ctx->dx_dir_info[low].ino)
948 return &ctx->dx_dir_info[low];
949 if (ino == ctx->dx_dir_info[high].ino)
950 return &ctx->dx_dir_info[high];
951
952 while (low < high) {
953 mid = (low+high)/2;
954 if (mid == low || mid == high)
955 break;
956 if (ino == ctx->dx_dir_info[mid].ino)
957 return &ctx->dx_dir_info[mid];
958 if (ino < ctx->dx_dir_info[mid].ino)
959 high = mid;
960 else
961 low = mid;
962 }
963 return 0;
964}
965
966/*
967 * Free the dx_dir_info structure when it isn't needed any more.
968 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000969static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000970{
971 int i;
972 struct dx_dir_info *dir;
973
974 if (ctx->dx_dir_info) {
975 dir = ctx->dx_dir_info;
976 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +0000977 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000978 }
979 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000980 }
981 ctx->dx_dir_info_size = 0;
982 ctx->dx_dir_info_count = 0;
983}
984
985/*
986 * A simple interator function
987 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000988static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000989{
990 if (*control >= ctx->dx_dir_info_count)
991 return 0;
992
993 return(ctx->dx_dir_info + (*control)++);
994}
995
996#endif /* ENABLE_HTREE */
997/*
998 * e2fsck.c - a consistency checker for the new extended file system.
999 *
Mike Frysinger51a43b42005-09-24 07:11:16 +00001000 */
1001
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001002/*
1003 * This function allocates an e2fsck context
1004 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001005static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001006{
1007 e2fsck_t context;
1008 errcode_t retval;
1009
1010 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
1011 if (retval)
1012 return retval;
1013
1014 memset(context, 0, sizeof(struct e2fsck_struct));
1015
1016 context->process_inode_size = 256;
1017 context->ext_attr_ver = 2;
1018
1019 *ret = context;
1020 return 0;
1021}
1022
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001023struct ea_refcount_el {
1024 blk_t ea_blk;
1025 int ea_count;
1026};
1027
1028struct ea_refcount {
1029 blk_t count;
1030 blk_t size;
1031 blk_t cursor;
1032 struct ea_refcount_el *list;
1033};
1034
1035static void ea_refcount_free(ext2_refcount_t refcount)
1036{
1037 if (!refcount)
1038 return;
1039
Rob Landleye7c43b62006-03-01 16:39:45 +00001040 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001041 ext2fs_free_mem(&refcount);
1042}
1043
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001044/*
1045 * This function resets an e2fsck context; it is called when e2fsck
1046 * needs to be restarted.
1047 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001048static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001049{
1050 ctx->flags = 0;
1051 ctx->lost_and_found = 0;
1052 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00001053 ext2fs_free_inode_bitmap(ctx->inode_used_map);
1054 ctx->inode_used_map = 0;
1055 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
1056 ctx->inode_dir_map = 0;
1057 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
1058 ctx->inode_reg_map = 0;
1059 ext2fs_free_block_bitmap(ctx->block_found_map);
1060 ctx->block_found_map = 0;
1061 ext2fs_free_icount(ctx->inode_link_info);
1062 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001063 if (ctx->journal_io) {
1064 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1065 io_channel_close(ctx->journal_io);
1066 ctx->journal_io = 0;
1067 }
Rob Landleye7c43b62006-03-01 16:39:45 +00001068 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001069 ext2fs_free_dblist(ctx->fs->dblist);
1070 ctx->fs->dblist = 0;
1071 }
1072 e2fsck_free_dir_info(ctx);
1073#ifdef ENABLE_HTREE
1074 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +00001075#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001076 ea_refcount_free(ctx->refcount);
1077 ctx->refcount = 0;
1078 ea_refcount_free(ctx->refcount_extra);
1079 ctx->refcount_extra = 0;
1080 ext2fs_free_block_bitmap(ctx->block_dup_map);
1081 ctx->block_dup_map = 0;
1082 ext2fs_free_block_bitmap(ctx->block_ea_map);
1083 ctx->block_ea_map = 0;
1084 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
1085 ctx->inode_bb_map = 0;
1086 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
1087 ctx->inode_bad_map = 0;
1088 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
1089 ctx->inode_imagic_map = 0;
1090 ext2fs_u32_list_free(ctx->dirs_to_hash);
1091 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001092
1093 /*
1094 * Clear the array of invalid meta-data flags
1095 */
Rob Landleye7c43b62006-03-01 16:39:45 +00001096 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
1097 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
1098 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001099
1100 /* Clear statistic counters */
1101 ctx->fs_directory_count = 0;
1102 ctx->fs_regular_count = 0;
1103 ctx->fs_blockdev_count = 0;
1104 ctx->fs_chardev_count = 0;
1105 ctx->fs_links_count = 0;
1106 ctx->fs_symlinks_count = 0;
1107 ctx->fs_fast_symlinks_count = 0;
1108 ctx->fs_fifo_count = 0;
1109 ctx->fs_total_count = 0;
1110 ctx->fs_badblocks_count = 0;
1111 ctx->fs_sockets_count = 0;
1112 ctx->fs_ind_count = 0;
1113 ctx->fs_dind_count = 0;
1114 ctx->fs_tind_count = 0;
1115 ctx->fs_fragmented = 0;
1116 ctx->large_files = 0;
1117
1118 /* Reset the superblock to the user's requested value */
1119 ctx->superblock = ctx->use_superblock;
1120
1121 return 0;
1122}
1123
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001124static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001125{
1126 if (!ctx)
1127 return;
1128
1129 e2fsck_reset_context(ctx);
1130 if (ctx->blkid)
1131 blkid_put_cache(ctx->blkid);
1132
1133 ext2fs_free_mem(&ctx);
1134}
1135
1136/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001137 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001138 */
1139
1140/*
1141 * The strategy we use for keeping track of EA refcounts is as
1142 * follows. We keep a sorted array of first EA blocks and its
1143 * reference counts. Once the refcount has dropped to zero, it is
1144 * removed from the array to save memory space. Once the EA block is
1145 * checked, its bit is set in the block_ea_map bitmap.
1146 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001147
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001148
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001149static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001150{
1151 ext2_refcount_t refcount;
1152 errcode_t retval;
1153 size_t bytes;
1154
1155 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
1156 if (retval)
1157 return retval;
1158 memset(refcount, 0, sizeof(struct ea_refcount));
1159
1160 if (!size)
1161 size = 500;
1162 refcount->size = size;
1163 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
1164#ifdef DEBUG
1165 printf("Refcount allocated %d entries, %d bytes.\n",
1166 refcount->size, bytes);
1167#endif
1168 retval = ext2fs_get_mem(bytes, &refcount->list);
1169 if (retval)
1170 goto errout;
1171 memset(refcount->list, 0, bytes);
1172
1173 refcount->count = 0;
1174 refcount->cursor = 0;
1175
1176 *ret = refcount;
1177 return 0;
1178
1179errout:
1180 ea_refcount_free(refcount);
1181 return(retval);
1182}
1183
1184/*
1185 * collapse_refcount() --- go through the refcount array, and get rid
1186 * of any count == zero entries
1187 */
1188static void refcount_collapse(ext2_refcount_t refcount)
1189{
1190 unsigned int i, j;
1191 struct ea_refcount_el *list;
1192
1193 list = refcount->list;
1194 for (i = 0, j = 0; i < refcount->count; i++) {
1195 if (list[i].ea_count) {
1196 if (i != j)
1197 list[j] = list[i];
1198 j++;
1199 }
1200 }
1201#if defined(DEBUG) || defined(TEST_PROGRAM)
1202 printf("Refcount_collapse: size was %d, now %d\n",
1203 refcount->count, j);
1204#endif
1205 refcount->count = j;
1206}
1207
1208
1209/*
1210 * insert_refcount_el() --- Insert a new entry into the sorted list at a
1211 * specified position.
1212 */
1213static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
1214 blk_t blk, int pos)
1215{
1216 struct ea_refcount_el *el;
1217 errcode_t retval;
1218 blk_t new_size = 0;
1219 int num;
1220
1221 if (refcount->count >= refcount->size) {
1222 new_size = refcount->size + 100;
1223#ifdef DEBUG
1224 printf("Reallocating refcount %d entries...\n", new_size);
1225#endif
1226 retval = ext2fs_resize_mem((size_t) refcount->size *
1227 sizeof(struct ea_refcount_el),
1228 (size_t) new_size *
1229 sizeof(struct ea_refcount_el),
1230 &refcount->list);
1231 if (retval)
1232 return 0;
1233 refcount->size = new_size;
1234 }
1235 num = (int) refcount->count - pos;
1236 if (num < 0)
1237 return 0; /* should never happen */
1238 if (num) {
1239 memmove(&refcount->list[pos+1], &refcount->list[pos],
1240 sizeof(struct ea_refcount_el) * num);
1241 }
1242 refcount->count++;
1243 el = &refcount->list[pos];
1244 el->ea_count = 0;
1245 el->ea_blk = blk;
1246 return el;
1247}
1248
1249
1250/*
1251 * get_refcount_el() --- given an block number, try to find refcount
1252 * information in the sorted list. If the create flag is set,
1253 * and we can't find an entry, create one in the sorted list.
1254 */
1255static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
1256 blk_t blk, int create)
1257{
1258 float range;
1259 int low, high, mid;
1260 blk_t lowval, highval;
1261
1262 if (!refcount || !refcount->list)
1263 return 0;
1264retry:
1265 low = 0;
1266 high = (int) refcount->count-1;
1267 if (create && ((refcount->count == 0) ||
1268 (blk > refcount->list[high].ea_blk))) {
1269 if (refcount->count >= refcount->size)
1270 refcount_collapse(refcount);
1271
1272 return insert_refcount_el(refcount, blk,
1273 (unsigned) refcount->count);
1274 }
1275 if (refcount->count == 0)
1276 return 0;
1277
1278 if (refcount->cursor >= refcount->count)
1279 refcount->cursor = 0;
1280 if (blk == refcount->list[refcount->cursor].ea_blk)
1281 return &refcount->list[refcount->cursor++];
1282#ifdef DEBUG
1283 printf("Non-cursor get_refcount_el: %u\n", blk);
1284#endif
1285 while (low <= high) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001286 if (low == high)
1287 mid = low;
1288 else {
1289 /* Interpolate for efficiency */
1290 lowval = refcount->list[low].ea_blk;
1291 highval = refcount->list[high].ea_blk;
1292
1293 if (blk < lowval)
1294 range = 0;
1295 else if (blk > highval)
1296 range = 1;
1297 else
1298 range = ((float) (blk - lowval)) /
1299 (highval - lowval);
1300 mid = low + ((int) (range * (high-low)));
1301 }
Rob Landley3e72c592006-04-06 22:49:04 +00001302
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001303 if (blk == refcount->list[mid].ea_blk) {
1304 refcount->cursor = mid+1;
1305 return &refcount->list[mid];
1306 }
1307 if (blk < refcount->list[mid].ea_blk)
1308 high = mid-1;
1309 else
1310 low = mid+1;
1311 }
1312 /*
1313 * If we need to create a new entry, it should be right at
1314 * low (where high will be left at low-1).
1315 */
1316 if (create) {
1317 if (refcount->count >= refcount->size) {
1318 refcount_collapse(refcount);
1319 if (refcount->count < refcount->size)
1320 goto retry;
1321 }
1322 return insert_refcount_el(refcount, blk, low);
1323 }
1324 return 0;
1325}
1326
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001327static errcode_t
1328ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001329{
1330 struct ea_refcount_el *el;
1331
1332 el = get_refcount_el(refcount, blk, 1);
1333 if (!el)
1334 return EXT2_ET_NO_MEMORY;
1335 el->ea_count++;
1336
1337 if (ret)
1338 *ret = el->ea_count;
1339 return 0;
1340}
1341
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001342static errcode_t
1343ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001344{
1345 struct ea_refcount_el *el;
1346
1347 el = get_refcount_el(refcount, blk, 0);
1348 if (!el || el->ea_count == 0)
1349 return EXT2_ET_INVALID_ARGUMENT;
1350
1351 el->ea_count--;
1352
1353 if (ret)
1354 *ret = el->ea_count;
1355 return 0;
1356}
1357
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001358static errcode_t
1359ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001360{
1361 struct ea_refcount_el *el;
1362
1363 /*
1364 * Get the refcount element
1365 */
1366 el = get_refcount_el(refcount, blk, count ? 1 : 0);
1367 if (!el)
1368 return count ? EXT2_ET_NO_MEMORY : 0;
1369 el->ea_count = count;
1370 return 0;
1371}
1372
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001373static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001374{
1375 refcount->cursor = 0;
1376}
1377
1378
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001379static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001380{
1381 struct ea_refcount_el *list;
1382
1383 while (1) {
1384 if (refcount->cursor >= refcount->count)
1385 return 0;
1386 list = refcount->list;
1387 if (list[refcount->cursor].ea_count) {
1388 if (ret)
1389 *ret = list[refcount->cursor].ea_count;
1390 return list[refcount->cursor++].ea_blk;
1391 }
1392 refcount->cursor++;
1393 }
1394}
1395
1396
1397/*
1398 * ehandler.c --- handle bad block errors which come up during the
1399 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001400 */
1401
1402
1403static const char *operation;
1404
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001405static errcode_t
1406e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001407 void *data, size_t size FSCK_ATTR((unused)),
1408 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001409{
1410 int i;
1411 char *p;
1412 ext2_filsys fs = (ext2_filsys) channel->app_data;
1413 e2fsck_t ctx;
1414
1415 ctx = (e2fsck_t) fs->priv_data;
1416
1417 /*
1418 * If more than one block was read, try reading each block
1419 * separately. We could use the actual bytes read to figure
1420 * out where to start, but we don't bother.
1421 */
1422 if (count > 1) {
1423 p = (char *) data;
1424 for (i=0; i < count; i++, p += channel->block_size, block++) {
1425 error = io_channel_read_blk(channel, block,
1426 1, p);
1427 if (error)
1428 return error;
1429 }
1430 return 0;
1431 }
1432 if (operation)
1433 printf(_("Error reading block %lu (%s) while %s. "), block,
1434 error_message(error), operation);
1435 else
1436 printf(_("Error reading block %lu (%s). "), block,
1437 error_message(error));
1438 preenhalt(ctx);
1439 if (ask(ctx, _("Ignore error"), 1)) {
1440 if (ask(ctx, _("Force rewrite"), 1))
1441 io_channel_write_blk(channel, block, 1, data);
1442 return 0;
1443 }
1444
1445 return error;
1446}
1447
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001448static errcode_t
1449e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001450 const void *data, size_t size FSCK_ATTR((unused)),
1451 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001452{
1453 int i;
1454 const char *p;
1455 ext2_filsys fs = (ext2_filsys) channel->app_data;
1456 e2fsck_t ctx;
1457
1458 ctx = (e2fsck_t) fs->priv_data;
1459
1460 /*
1461 * If more than one block was written, try writing each block
1462 * separately. We could use the actual bytes read to figure
1463 * out where to start, but we don't bother.
1464 */
1465 if (count > 1) {
1466 p = (const char *) data;
1467 for (i=0; i < count; i++, p += channel->block_size, block++) {
1468 error = io_channel_write_blk(channel, block,
1469 1, p);
1470 if (error)
1471 return error;
1472 }
1473 return 0;
1474 }
1475
1476 if (operation)
1477 printf(_("Error writing block %lu (%s) while %s. "), block,
1478 error_message(error), operation);
1479 else
1480 printf(_("Error writing block %lu (%s). "), block,
1481 error_message(error));
1482 preenhalt(ctx);
1483 if (ask(ctx, _("Ignore error"), 1))
1484 return 0;
1485
1486 return error;
1487}
1488
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001489static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001490{
1491 const char *ret = operation;
1492
1493 operation = op;
1494 return ret;
1495}
1496
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001497static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001498{
1499 channel->read_error = e2fsck_handle_read_error;
1500 channel->write_error = e2fsck_handle_write_error;
1501}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001502
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001503/*
1504 * journal.c --- code for handling the "ext3" journal
1505 *
1506 * Copyright (C) 2000 Andreas Dilger
1507 * Copyright (C) 2000 Theodore Ts'o
1508 *
1509 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
1510 * Copyright (C) 1999 Red Hat Software
1511 *
1512 * This file may be redistributed under the terms of the
1513 * GNU General Public License version 2 or at your discretion
1514 * any later version.
1515 */
1516
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001517/*
1518 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
1519 * This creates a larger static binary, and a smaller binary using
1520 * shared libraries. It's also probably slightly less CPU-efficient,
1521 * which is why it's not on by default. But, it's a good way of
1522 * testing the functions in inode_io.c and fileio.c.
1523 */
1524#undef USE_INODE_IO
1525
1526/* Kernel compatibility functions for handling the journal. These allow us
1527 * to use the recovery.c file virtually unchanged from the kernel, so we
1528 * don't have to do much to keep kernel and user recovery in sync.
1529 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001530static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001531{
1532#ifdef USE_INODE_IO
1533 *phys = block;
1534 return 0;
1535#else
1536 struct inode *inode = journal->j_inode;
1537 errcode_t retval;
1538 blk_t pblk;
1539
1540 if (!inode) {
1541 *phys = block;
1542 return 0;
1543 }
1544
1545 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
1546 &inode->i_ext2, NULL, 0, block, &pblk);
1547 *phys = pblk;
1548 return (retval);
1549#endif
1550}
1551
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001552static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001553{
1554 struct buffer_head *bh;
1555
1556 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
1557 if (!bh)
1558 return NULL;
1559
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001560 bh->b_ctx = kdev->k_ctx;
1561 if (kdev->k_dev == K_DEV_FS)
1562 bh->b_io = kdev->k_ctx->fs->io;
1563 else
1564 bh->b_io = kdev->k_ctx->journal_io;
1565 bh->b_size = blocksize;
1566 bh->b_blocknr = blocknr;
1567
1568 return bh;
1569}
1570
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001571static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001572{
1573 io_channel io;
1574
1575 if (kdev->k_dev == K_DEV_FS)
1576 io = kdev->k_ctx->fs->io;
1577 else
1578 io = kdev->k_ctx->journal_io;
1579
1580 io_channel_flush(io);
1581}
1582
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001583static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001584{
1585 int retval;
1586 struct buffer_head *bh;
1587
1588 for (; nr > 0; --nr) {
1589 bh = *bhp++;
1590 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001591 retval = io_channel_read_blk(bh->b_io,
1592 bh->b_blocknr,
1593 1, bh->b_data);
1594 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001595 bb_error_msg("while reading block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001596 (unsigned long) bh->b_blocknr);
1597 bh->b_err = retval;
1598 continue;
1599 }
1600 bh->b_uptodate = 1;
1601 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001602 retval = io_channel_write_blk(bh->b_io,
1603 bh->b_blocknr,
1604 1, bh->b_data);
1605 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001606 bb_error_msg("while writing block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001607 (unsigned long) bh->b_blocknr);
1608 bh->b_err = retval;
1609 continue;
1610 }
1611 bh->b_dirty = 0;
1612 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001613 }
1614 }
1615}
1616
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001617static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001618{
1619 bh->b_dirty = 1;
1620}
1621
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001622static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001623{
1624 bh->b_dirty = 0;
1625}
1626
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001627static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001628{
1629 if (bh->b_dirty)
1630 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001631 ext2fs_free_mem(&bh);
1632}
1633
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001634static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001635{
1636 return bh->b_uptodate;
1637}
1638
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001639static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001640{
1641 bh->b_uptodate = val;
1642}
1643
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001644static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001645{
1646 if (!bh->b_uptodate)
1647 ll_rw_block(READ, 1, &bh);
1648}
1649
1650
1651static void e2fsck_clear_recover(e2fsck_t ctx, int error)
1652{
1653 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
1654
1655 /* if we had an error doing journal recovery, we need a full fsck */
1656 if (error)
1657 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
1658 ext2fs_mark_super_dirty(ctx->fs);
1659}
1660
1661static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
1662{
1663 struct ext2_super_block *sb = ctx->fs->super;
1664 struct ext2_super_block jsuper;
1665 struct problem_context pctx;
1666 struct buffer_head *bh;
1667 struct inode *j_inode = NULL;
1668 struct kdev_s *dev_fs = NULL, *dev_journal;
1669 const char *journal_name = 0;
1670 journal_t *journal = NULL;
1671 errcode_t retval = 0;
1672 io_manager io_ptr = 0;
1673 unsigned long start = 0;
1674 blk_t blk;
1675 int ext_journal = 0;
1676 int tried_backup_jnl = 0;
1677 int i;
1678
1679 clear_problem_context(&pctx);
1680
1681 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
1682 if (!journal) {
1683 return EXT2_ET_NO_MEMORY;
1684 }
1685
1686 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
1687 if (!dev_fs) {
1688 retval = EXT2_ET_NO_MEMORY;
1689 goto errout;
1690 }
1691 dev_journal = dev_fs+1;
1692
1693 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
1694 dev_fs->k_dev = K_DEV_FS;
1695 dev_journal->k_dev = K_DEV_JOURNAL;
1696
1697 journal->j_dev = dev_journal;
1698 journal->j_fs_dev = dev_fs;
1699 journal->j_inode = NULL;
1700 journal->j_blocksize = ctx->fs->blocksize;
1701
1702 if (uuid_is_null(sb->s_journal_uuid)) {
1703 if (!sb->s_journal_inum)
1704 return EXT2_ET_BAD_INODE_NUM;
1705 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
1706 "journal inode");
1707 if (!j_inode) {
1708 retval = EXT2_ET_NO_MEMORY;
1709 goto errout;
1710 }
1711
1712 j_inode->i_ctx = ctx;
1713 j_inode->i_ino = sb->s_journal_inum;
1714
1715 if ((retval = ext2fs_read_inode(ctx->fs,
1716 sb->s_journal_inum,
1717 &j_inode->i_ext2))) {
1718 try_backup_journal:
1719 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
1720 tried_backup_jnl)
1721 goto errout;
1722 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
1723 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
1724 EXT2_N_BLOCKS*4);
1725 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
1726 j_inode->i_ext2.i_links_count = 1;
1727 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
1728 tried_backup_jnl++;
1729 }
1730 if (!j_inode->i_ext2.i_links_count ||
1731 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
1732 retval = EXT2_ET_NO_JOURNAL;
1733 goto try_backup_journal;
1734 }
1735 if (j_inode->i_ext2.i_size / journal->j_blocksize <
1736 JFS_MIN_JOURNAL_BLOCKS) {
1737 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1738 goto try_backup_journal;
1739 }
1740 for (i=0; i < EXT2_N_BLOCKS; i++) {
1741 blk = j_inode->i_ext2.i_block[i];
1742 if (!blk) {
1743 if (i < EXT2_NDIR_BLOCKS) {
1744 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1745 goto try_backup_journal;
1746 }
1747 continue;
1748 }
1749 if (blk < sb->s_first_data_block ||
1750 blk >= sb->s_blocks_count) {
1751 retval = EXT2_ET_BAD_BLOCK_NUM;
1752 goto try_backup_journal;
1753 }
1754 }
1755 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
1756
1757#ifdef USE_INODE_IO
1758 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
1759 &j_inode->i_ext2,
1760 &journal_name);
1761 if (retval)
1762 goto errout;
1763
1764 io_ptr = inode_io_manager;
1765#else
1766 journal->j_inode = j_inode;
1767 ctx->journal_io = ctx->fs->io;
1768 if ((retval = journal_bmap(journal, 0, &start)) != 0)
1769 goto errout;
1770#endif
1771 } else {
1772 ext_journal = 1;
1773 if (!ctx->journal_name) {
1774 char uuid[37];
1775
1776 uuid_unparse(sb->s_journal_uuid, uuid);
1777 ctx->journal_name = blkid_get_devname(ctx->blkid,
1778 "UUID", uuid);
1779 if (!ctx->journal_name)
1780 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
1781 }
1782 journal_name = ctx->journal_name;
1783
1784 if (!journal_name) {
1785 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
1786 return EXT2_ET_LOAD_EXT_JOURNAL;
1787 }
1788
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001789 io_ptr = unix_io_manager;
1790 }
1791
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001792#ifndef USE_INODE_IO
1793 if (ext_journal)
1794#endif
1795 retval = io_ptr->open(journal_name, IO_FLAG_RW,
1796 &ctx->journal_io);
1797 if (retval)
1798 goto errout;
1799
1800 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
1801
1802 if (ext_journal) {
1803 if (ctx->fs->blocksize == 1024)
1804 start = 1;
1805 bh = getblk(dev_journal, start, ctx->fs->blocksize);
1806 if (!bh) {
1807 retval = EXT2_ET_NO_MEMORY;
1808 goto errout;
1809 }
1810 ll_rw_block(READ, 1, &bh);
1811 if ((retval = bh->b_err) != 0)
1812 goto errout;
1813 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
1814 sizeof(jsuper));
1815 brelse(bh);
Rob Landley7c94bed2006-05-03 21:58:45 +00001816#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001817 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
1818 ext2fs_swap_super(&jsuper);
1819#endif
1820 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
1821 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
1822 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
1823 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1824 goto errout;
1825 }
1826 /* Make sure the journal UUID is correct */
1827 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
1828 sizeof(jsuper.s_uuid))) {
1829 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
1830 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1831 goto errout;
1832 }
1833
1834 journal->j_maxlen = jsuper.s_blocks_count;
1835 start++;
1836 }
1837
1838 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
1839 retval = EXT2_ET_NO_MEMORY;
1840 goto errout;
1841 }
1842
1843 journal->j_sb_buffer = bh;
1844 journal->j_superblock = (journal_superblock_t *)bh->b_data;
1845
1846#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001847 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001848#endif
1849
1850 *ret_journal = journal;
1851 return 0;
1852
1853errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00001854 ext2fs_free_mem(&dev_fs);
1855 ext2fs_free_mem(&j_inode);
1856 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001857 return retval;
1858
1859}
1860
1861static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
1862 struct problem_context *pctx)
1863{
1864 struct ext2_super_block *sb = ctx->fs->super;
1865 int recover = ctx->fs->super->s_feature_incompat &
1866 EXT3_FEATURE_INCOMPAT_RECOVER;
1867 int has_journal = ctx->fs->super->s_feature_compat &
1868 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1869
1870 if (has_journal || sb->s_journal_inum) {
1871 /* The journal inode is bogus, remove and force full fsck */
1872 pctx->ino = sb->s_journal_inum;
1873 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
1874 if (has_journal && sb->s_journal_inum)
1875 printf("*** ext3 journal has been deleted - "
1876 "filesystem is now ext2 only ***\n\n");
1877 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1878 sb->s_journal_inum = 0;
1879 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
1880 e2fsck_clear_recover(ctx, 1);
1881 return 0;
1882 }
1883 return EXT2_ET_BAD_INODE_NUM;
1884 } else if (recover) {
1885 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
1886 e2fsck_clear_recover(ctx, 1);
1887 return 0;
1888 }
1889 return EXT2_ET_UNSUPP_FEATURE;
1890 }
1891 return 0;
1892}
1893
1894#define V1_SB_SIZE 0x0024
1895static void clear_v2_journal_fields(journal_t *journal)
1896{
1897 e2fsck_t ctx = journal->j_dev->k_ctx;
1898 struct problem_context pctx;
1899
1900 clear_problem_context(&pctx);
1901
1902 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
1903 return;
1904
1905 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
1906 ctx->fs->blocksize-V1_SB_SIZE);
1907 mark_buffer_dirty(journal->j_sb_buffer);
1908}
1909
1910
1911static errcode_t e2fsck_journal_load(journal_t *journal)
1912{
1913 e2fsck_t ctx = journal->j_dev->k_ctx;
1914 journal_superblock_t *jsb;
1915 struct buffer_head *jbh = journal->j_sb_buffer;
1916 struct problem_context pctx;
1917
1918 clear_problem_context(&pctx);
1919
1920 ll_rw_block(READ, 1, &jbh);
1921 if (jbh->b_err) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001922 bb_error_msg(_("reading journal superblock\n"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001923 return jbh->b_err;
1924 }
1925
1926 jsb = journal->j_superblock;
1927 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
1928 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
1929 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
1930
1931 switch (ntohl(jsb->s_header.h_blocktype)) {
1932 case JFS_SUPERBLOCK_V1:
1933 journal->j_format_version = 1;
1934 if (jsb->s_feature_compat ||
1935 jsb->s_feature_incompat ||
1936 jsb->s_feature_ro_compat ||
1937 jsb->s_nr_users)
1938 clear_v2_journal_fields(journal);
1939 break;
1940
1941 case JFS_SUPERBLOCK_V2:
1942 journal->j_format_version = 2;
1943 if (ntohl(jsb->s_nr_users) > 1 &&
1944 uuid_is_null(ctx->fs->super->s_journal_uuid))
1945 clear_v2_journal_fields(journal);
1946 if (ntohl(jsb->s_nr_users) > 1) {
1947 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
1948 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1949 }
1950 break;
1951
1952 /*
1953 * These should never appear in a journal super block, so if
1954 * they do, the journal is badly corrupted.
1955 */
1956 case JFS_DESCRIPTOR_BLOCK:
1957 case JFS_COMMIT_BLOCK:
1958 case JFS_REVOKE_BLOCK:
1959 return EXT2_ET_CORRUPT_SUPERBLOCK;
1960
1961 /* If we don't understand the superblock major type, but there
1962 * is a magic number, then it is likely to be a new format we
1963 * just don't understand, so leave it alone. */
1964 default:
1965 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1966 }
1967
1968 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
1969 return EXT2_ET_UNSUPP_FEATURE;
1970
1971 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
1972 return EXT2_ET_RO_UNSUPP_FEATURE;
1973
1974 /* We have now checked whether we know enough about the journal
1975 * format to be able to proceed safely, so any other checks that
1976 * fail we should attempt to recover from. */
1977 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001978 bb_error_msg(_("%s: no valid journal superblock found\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001979 ctx->device_name);
1980 return EXT2_ET_CORRUPT_SUPERBLOCK;
1981 }
1982
1983 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
1984 journal->j_maxlen = ntohl(jsb->s_maxlen);
1985 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001986 bb_error_msg(_("%s: journal too short\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001987 ctx->device_name);
1988 return EXT2_ET_CORRUPT_SUPERBLOCK;
1989 }
1990
1991 journal->j_tail_sequence = ntohl(jsb->s_sequence);
1992 journal->j_transaction_sequence = journal->j_tail_sequence;
1993 journal->j_tail = ntohl(jsb->s_start);
1994 journal->j_first = ntohl(jsb->s_first);
1995 journal->j_last = ntohl(jsb->s_maxlen);
1996
1997 return 0;
1998}
1999
2000static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
2001 journal_t *journal)
2002{
2003 char *p;
2004 union {
2005 uuid_t uuid;
2006 __u32 val[4];
2007 } u;
2008 __u32 new_seq = 0;
2009 int i;
2010
2011 /* Leave a valid existing V1 superblock signature alone.
2012 * Anything unrecognisable we overwrite with a new V2
2013 * signature. */
2014
2015 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
2016 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
2017 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
2018 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
2019 }
2020
2021 /* Zero out everything else beyond the superblock header */
2022
2023 p = ((char *) jsb) + sizeof(journal_header_t);
2024 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
2025
2026 jsb->s_blocksize = htonl(ctx->fs->blocksize);
2027 jsb->s_maxlen = htonl(journal->j_maxlen);
2028 jsb->s_first = htonl(1);
2029
2030 /* Initialize the journal sequence number so that there is "no"
2031 * chance we will find old "valid" transactions in the journal.
2032 * This avoids the need to zero the whole journal (slow to do,
2033 * and risky when we are just recovering the filesystem).
2034 */
2035 uuid_generate(u.uuid);
2036 for (i = 0; i < 4; i ++)
2037 new_seq ^= u.val[i];
2038 jsb->s_sequence = htonl(new_seq);
2039
2040 mark_buffer_dirty(journal->j_sb_buffer);
2041 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
2042}
2043
2044static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
2045 journal_t *journal,
2046 struct problem_context *pctx)
2047{
2048 struct ext2_super_block *sb = ctx->fs->super;
2049 int recover = ctx->fs->super->s_feature_incompat &
2050 EXT3_FEATURE_INCOMPAT_RECOVER;
2051
2052 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
2053 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
2054 e2fsck_journal_reset_super(ctx, journal->j_superblock,
2055 journal);
2056 journal->j_transaction_sequence = 1;
2057 e2fsck_clear_recover(ctx, recover);
2058 return 0;
2059 }
2060 return EXT2_ET_CORRUPT_SUPERBLOCK;
2061 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
2062 return EXT2_ET_CORRUPT_SUPERBLOCK;
2063
2064 return 0;
2065}
2066
2067static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
2068 int reset, int drop)
2069{
2070 journal_superblock_t *jsb;
2071
2072 if (drop)
2073 mark_buffer_clean(journal->j_sb_buffer);
2074 else if (!(ctx->options & E2F_OPT_READONLY)) {
2075 jsb = journal->j_superblock;
2076 jsb->s_sequence = htonl(journal->j_transaction_sequence);
2077 if (reset)
2078 jsb->s_start = 0; /* this marks the journal as empty */
2079 mark_buffer_dirty(journal->j_sb_buffer);
2080 }
2081 brelse(journal->j_sb_buffer);
2082
2083 if (ctx->journal_io) {
2084 if (ctx->fs && ctx->fs->io != ctx->journal_io)
2085 io_channel_close(ctx->journal_io);
2086 ctx->journal_io = 0;
2087 }
2088
2089#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00002090 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002091#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00002092 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002093 ext2fs_free_mem(&journal);
2094}
2095
2096/*
2097 * This function makes sure that the superblock fields regarding the
2098 * journal are consistent.
2099 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002100static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002101{
2102 struct ext2_super_block *sb = ctx->fs->super;
2103 journal_t *journal;
2104 int recover = ctx->fs->super->s_feature_incompat &
2105 EXT3_FEATURE_INCOMPAT_RECOVER;
2106 struct problem_context pctx;
2107 problem_t problem;
2108 int reset = 0, force_fsck = 0;
2109 int retval;
2110
2111 /* If we don't have any journal features, don't do anything more */
2112 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
2113 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
2114 uuid_is_null(sb->s_journal_uuid))
2115 return 0;
2116
2117 clear_problem_context(&pctx);
2118 pctx.num = sb->s_journal_inum;
2119
2120 retval = e2fsck_get_journal(ctx, &journal);
2121 if (retval) {
2122 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
2123 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
2124 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
2125 (retval == EXT2_ET_NO_JOURNAL))
2126 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2127 return retval;
2128 }
2129
2130 retval = e2fsck_journal_load(journal);
2131 if (retval) {
2132 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
2133 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
2134 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
2135 &pctx))) ||
2136 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
2137 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
2138 &pctx))) ||
2139 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
2140 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
2141 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
2142 &pctx);
2143 e2fsck_journal_release(ctx, journal, 0, 1);
2144 return retval;
2145 }
2146
2147 /*
2148 * We want to make the flags consistent here. We will not leave with
2149 * needs_recovery set but has_journal clear. We can't get in a loop
2150 * with -y, -n, or -p, only if a user isn't making up their mind.
2151 */
2152no_has_journal:
2153 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
2154 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
2155 pctx.str = "inode";
2156 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
2157 if (recover &&
2158 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
2159 goto no_has_journal;
2160 /*
2161 * Need a full fsck if we are releasing a
2162 * journal stored on a reserved inode.
2163 */
2164 force_fsck = recover ||
2165 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
2166 /* Clear all of the journal fields */
2167 sb->s_journal_inum = 0;
2168 sb->s_journal_dev = 0;
2169 memset(sb->s_journal_uuid, 0,
2170 sizeof(sb->s_journal_uuid));
2171 e2fsck_clear_recover(ctx, force_fsck);
2172 } else if (!(ctx->options & E2F_OPT_READONLY)) {
2173 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2174 ext2fs_mark_super_dirty(ctx->fs);
2175 }
2176 }
2177
2178 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
2179 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
2180 journal->j_superblock->s_start != 0) {
2181 /* Print status information */
2182 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
2183 if (ctx->superblock)
2184 problem = PR_0_JOURNAL_RUN_DEFAULT;
2185 else
2186 problem = PR_0_JOURNAL_RUN;
2187 if (fix_problem(ctx, problem, &pctx)) {
2188 ctx->options |= E2F_OPT_FORCE;
2189 sb->s_feature_incompat |=
2190 EXT3_FEATURE_INCOMPAT_RECOVER;
2191 ext2fs_mark_super_dirty(ctx->fs);
2192 } else if (fix_problem(ctx,
2193 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
2194 reset = 1;
2195 sb->s_state &= ~EXT2_VALID_FS;
2196 ext2fs_mark_super_dirty(ctx->fs);
2197 }
2198 /*
2199 * If the user answers no to the above question, we
2200 * ignore the fact that journal apparently has data;
2201 * accidentally replaying over valid data would be far
2202 * worse than skipping a questionable recovery.
2203 *
2204 * XXX should we abort with a fatal error here? What
2205 * will the ext3 kernel code do if a filesystem with
2206 * !NEEDS_RECOVERY but with a non-zero
2207 * journal->j_superblock->s_start is mounted?
2208 */
2209 }
2210
2211 e2fsck_journal_release(ctx, journal, reset, 0);
2212 return retval;
2213}
2214
2215static errcode_t recover_ext3_journal(e2fsck_t ctx)
2216{
2217 journal_t *journal;
2218 int retval;
2219
2220 journal_init_revoke_caches();
2221 retval = e2fsck_get_journal(ctx, &journal);
2222 if (retval)
2223 return retval;
2224
2225 retval = e2fsck_journal_load(journal);
2226 if (retval)
2227 goto errout;
2228
2229 retval = journal_init_revoke(journal, 1024);
2230 if (retval)
2231 goto errout;
2232
2233 retval = -journal_recover(journal);
2234 if (retval)
2235 goto errout;
2236
2237 if (journal->j_superblock->s_errno) {
2238 ctx->fs->super->s_state |= EXT2_ERROR_FS;
2239 ext2fs_mark_super_dirty(ctx->fs);
2240 journal->j_superblock->s_errno = 0;
2241 mark_buffer_dirty(journal->j_sb_buffer);
2242 }
2243
2244errout:
2245 journal_destroy_revoke(journal);
2246 journal_destroy_revoke_caches();
2247 e2fsck_journal_release(ctx, journal, 1, 0);
2248 return retval;
2249}
2250
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002251static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002252{
2253 io_manager io_ptr = ctx->fs->io->manager;
2254 int blocksize = ctx->fs->blocksize;
2255 errcode_t retval, recover_retval;
2256
2257 printf(_("%s: recovering journal\n"), ctx->device_name);
2258 if (ctx->options & E2F_OPT_READONLY) {
2259 printf(_("%s: won't do journal recovery while read-only\n"),
2260 ctx->device_name);
2261 return EXT2_ET_FILE_RO;
2262 }
2263
2264 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
2265 ext2fs_flush(ctx->fs); /* Force out any modifications */
2266
2267 recover_retval = recover_ext3_journal(ctx);
2268
2269 /*
2270 * Reload the filesystem context to get up-to-date data from disk
2271 * because journal recovery will change the filesystem under us.
2272 */
2273 ext2fs_close(ctx->fs);
2274 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
2275 ctx->superblock, blocksize, io_ptr,
2276 &ctx->fs);
2277
2278 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00002279 bb_error_msg(_("while trying to re-open %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002280 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +00002281 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002282 }
2283 ctx->fs->priv_data = ctx;
2284
2285 /* Set the superblock flags */
2286 e2fsck_clear_recover(ctx, recover_retval);
2287 return recover_retval;
2288}
2289
2290/*
2291 * This function will move the journal inode from a visible file in
2292 * the filesystem directory hierarchy to the reserved inode if necessary.
2293 */
2294static const char * const journal_names[] = {
2295 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
2296
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002297static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002298{
2299 struct ext2_super_block *sb = ctx->fs->super;
2300 struct problem_context pctx;
2301 struct ext2_inode inode;
2302 ext2_filsys fs = ctx->fs;
2303 ext2_ino_t ino;
2304 errcode_t retval;
2305 const char * const * cpp;
2306 int group, mount_flags;
2307
2308 clear_problem_context(&pctx);
2309
2310 /*
2311 * If the filesystem is opened read-only, or there is no
2312 * journal, then do nothing.
2313 */
2314 if ((ctx->options & E2F_OPT_READONLY) ||
2315 (sb->s_journal_inum == 0) ||
2316 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
2317 return;
2318
2319 /*
2320 * Read in the journal inode
2321 */
2322 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
2323 return;
2324
2325 /*
2326 * If it's necessary to backup the journal inode, do so.
2327 */
2328 if ((sb->s_jnl_backup_type == 0) ||
2329 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
2330 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
2331 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
2332 memcpy(sb->s_jnl_blocks, inode.i_block,
2333 EXT2_N_BLOCKS*4);
2334 sb->s_jnl_blocks[16] = inode.i_size;
2335 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
2336 ext2fs_mark_super_dirty(fs);
2337 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2338 }
2339 }
2340
2341 /*
2342 * If the journal is already the hidden inode, then do nothing
2343 */
2344 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
2345 return;
2346
2347 /*
2348 * The journal inode had better have only one link and not be readable.
2349 */
2350 if (inode.i_links_count != 1)
2351 return;
2352
2353 /*
2354 * If the filesystem is mounted, or we can't tell whether
2355 * or not it's mounted, do nothing.
2356 */
2357 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
2358 if (retval || (mount_flags & EXT2_MF_MOUNTED))
2359 return;
2360
2361 /*
2362 * If we can't find the name of the journal inode, then do
2363 * nothing.
2364 */
2365 for (cpp = journal_names; *cpp; cpp++) {
2366 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
2367 strlen(*cpp), 0, &ino);
2368 if ((retval == 0) && (ino == sb->s_journal_inum))
2369 break;
2370 }
2371 if (*cpp == 0)
2372 return;
2373
2374 /* We need the inode bitmap to be loaded */
2375 retval = ext2fs_read_bitmaps(fs);
2376 if (retval)
2377 return;
2378
2379 pctx.str = *cpp;
2380 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
2381 return;
2382
2383 /*
2384 * OK, we've done all the checks, let's actually move the
2385 * journal inode. Errors at this point mean we need to force
2386 * an ext2 filesystem check.
2387 */
2388 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
2389 goto err_out;
2390 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
2391 goto err_out;
2392 sb->s_journal_inum = EXT2_JOURNAL_INO;
2393 ext2fs_mark_super_dirty(fs);
2394 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2395 inode.i_links_count = 0;
2396 inode.i_dtime = time(0);
2397 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
2398 goto err_out;
2399
2400 group = ext2fs_group_of_ino(fs, ino);
2401 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
2402 ext2fs_mark_ib_dirty(fs);
2403 fs->group_desc[group].bg_free_inodes_count++;
2404 fs->super->s_free_inodes_count++;
2405 return;
2406
2407err_out:
2408 pctx.errcode = retval;
2409 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
2410 fs->super->s_state &= ~EXT2_VALID_FS;
2411 ext2fs_mark_super_dirty(fs);
2412 return;
2413}
2414
2415/*
2416 * message.c --- print e2fsck messages (with compression)
2417 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002418 * print_e2fsck_message() prints a message to the user, using
2419 * compression techniques and expansions of abbreviations.
2420 *
2421 * The following % expansions are supported:
2422 *
2423 * %b <blk> block number
2424 * %B <blkcount> integer
2425 * %c <blk2> block number
2426 * %Di <dirent>->ino inode number
2427 * %Dn <dirent>->name string
2428 * %Dr <dirent>->rec_len
2429 * %Dl <dirent>->name_len
2430 * %Dt <dirent>->filetype
2431 * %d <dir> inode number
2432 * %g <group> integer
2433 * %i <ino> inode number
2434 * %Is <inode> -> i_size
2435 * %IS <inode> -> i_extra_isize
2436 * %Ib <inode> -> i_blocks
2437 * %Il <inode> -> i_links_count
2438 * %Im <inode> -> i_mode
2439 * %IM <inode> -> i_mtime
2440 * %IF <inode> -> i_faddr
2441 * %If <inode> -> i_file_acl
2442 * %Id <inode> -> i_dir_acl
2443 * %Iu <inode> -> i_uid
2444 * %Ig <inode> -> i_gid
2445 * %j <ino2> inode number
2446 * %m <com_err error message>
2447 * %N <num>
2448 * %p ext2fs_get_pathname of directory <ino>
2449 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
2450 * the containing directory. (If dirent is NULL
2451 * then return the pathname of directory <ino2>)
2452 * %q ext2fs_get_pathname of directory <dir>
2453 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
2454 * the containing directory.
2455 * %s <str> miscellaneous string
2456 * %S backup superblock
2457 * %X <num> hexadecimal format
2458 *
2459 * The following '@' expansions are supported:
2460 *
2461 * @a extended attribute
2462 * @A error allocating
2463 * @b block
2464 * @B bitmap
2465 * @c compress
2466 * @C conflicts with some other fs block
2467 * @D deleted
2468 * @d directory
2469 * @e entry
2470 * @E Entry '%Dn' in %p (%i)
2471 * @f filesystem
2472 * @F for @i %i (%Q) is
2473 * @g group
2474 * @h HTREE directory inode
2475 * @i inode
2476 * @I illegal
2477 * @j journal
2478 * @l lost+found
2479 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00002480 * @m multiply-claimed
2481 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002482 * @o orphaned
2483 * @p problem in
2484 * @r root inode
2485 * @s should be
2486 * @S superblock
2487 * @u unattached
2488 * @v device
2489 * @z zero-length
2490 */
2491
2492
2493/*
2494 * This structure defines the abbreviations used by the text strings
2495 * below. The first character in the string is the index letter. An
2496 * abbreviation of the form '@<i>' is expanded by looking up the index
2497 * letter <i> in the table below.
2498 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002499static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002500 N_("aextended attribute"),
2501 N_("Aerror allocating"),
2502 N_("bblock"),
2503 N_("Bbitmap"),
2504 N_("ccompress"),
2505 N_("Cconflicts with some other fs @b"),
2506 N_("iinode"),
2507 N_("Iillegal"),
2508 N_("jjournal"),
2509 N_("Ddeleted"),
2510 N_("ddirectory"),
2511 N_("eentry"),
2512 N_("E@e '%Dn' in %p (%i)"),
2513 N_("ffilesystem"),
2514 N_("Ffor @i %i (%Q) is"),
2515 N_("ggroup"),
2516 N_("hHTREE @d @i"),
2517 N_("llost+found"),
2518 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00002519 N_("mmultiply-claimed"),
2520 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002521 N_("oorphaned"),
2522 N_("pproblem in"),
2523 N_("rroot @i"),
2524 N_("sshould be"),
2525 N_("Ssuper@b"),
2526 N_("uunattached"),
2527 N_("vdevice"),
2528 N_("zzero-length"),
2529 "@@",
2530 0
2531 };
2532
2533/*
2534 * Give more user friendly names to the "special" inodes.
2535 */
2536#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002537static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002538{
2539 N_("<The NULL inode>"), /* 0 */
2540 N_("<The bad blocks inode>"), /* 1 */
2541 "/", /* 2 */
2542 N_("<The ACL index inode>"), /* 3 */
2543 N_("<The ACL data inode>"), /* 4 */
2544 N_("<The boot loader inode>"), /* 5 */
2545 N_("<The undelete directory inode>"), /* 6 */
2546 N_("<The group descriptor inode>"), /* 7 */
2547 N_("<The journal inode>"), /* 8 */
2548 N_("<Reserved inode 9>"), /* 9 */
2549 N_("<Reserved inode 10>"), /* 10 */
2550};
2551
2552/*
2553 * This function does "safe" printing. It will convert non-printable
2554 * ASCII characters using '^' and M- notation.
2555 */
2556static void safe_print(const char *cp, int len)
2557{
2558 unsigned char ch;
2559
2560 if (len < 0)
2561 len = strlen(cp);
2562
2563 while (len--) {
2564 ch = *cp++;
2565 if (ch > 128) {
2566 fputs("M-", stdout);
2567 ch -= 128;
2568 }
2569 if ((ch < 32) || (ch == 0x7f)) {
2570 fputc('^', stdout);
2571 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
2572 }
2573 fputc(ch, stdout);
2574 }
2575}
2576
2577
2578/*
2579 * This function prints a pathname, using the ext2fs_get_pathname
2580 * function
2581 */
2582static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
2583{
2584 errcode_t retval;
2585 char *path;
2586
2587 if (!dir && (ino < num_special_inodes)) {
2588 fputs(_(special_inode_name[ino]), stdout);
2589 return;
2590 }
2591
2592 retval = ext2fs_get_pathname(fs, dir, ino, &path);
2593 if (retval)
2594 fputs("???", stdout);
2595 else {
2596 safe_print(path, -1);
2597 ext2fs_free_mem(&path);
2598 }
2599}
2600
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002601static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
2602 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002603/*
2604 * This function handles the '@' expansion. We allow recursive
2605 * expansion; an @ expression can contain further '@' and '%'
2606 * expressions.
2607 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002608static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002609 struct problem_context *pctx,
2610 int *first)
2611{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002612 const char * const *cpp;
2613 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002614
2615 /* Search for the abbreviation */
2616 for (cpp = abbrevs; *cpp; cpp++) {
2617 if (ch == *cpp[0])
2618 break;
2619 }
2620 if (*cpp) {
2621 str = _(*cpp) + 1;
2622 if (*first && islower(*str)) {
2623 *first = 0;
2624 fputc(toupper(*str++), stdout);
2625 }
2626 print_e2fsck_message(ctx, str, pctx, *first);
2627 } else
2628 printf("@%c", ch);
2629}
2630
2631/*
2632 * This function expands '%IX' expressions
2633 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002634static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002635 struct problem_context *ctx)
2636{
2637 struct ext2_inode *inode;
2638 struct ext2_inode_large *large_inode;
2639 char * time_str;
2640 time_t t;
2641 int do_gmt = -1;
2642
2643 if (!ctx || !ctx->inode)
2644 goto no_inode;
2645
2646 inode = ctx->inode;
2647 large_inode = (struct ext2_inode_large *) inode;
2648
2649 switch (ch) {
2650 case 's':
2651 if (LINUX_S_ISDIR(inode->i_mode))
2652 printf("%u", inode->i_size);
2653 else {
2654#ifdef EXT2_NO_64_TYPE
2655 if (inode->i_size_high)
2656 printf("0x%x%08x", inode->i_size_high,
2657 inode->i_size);
2658 else
2659 printf("%u", inode->i_size);
2660#else
2661 printf("%llu", (inode->i_size |
2662 ((__u64) inode->i_size_high << 32)));
2663#endif
2664 }
2665 break;
2666 case 'S':
2667 printf("%u", large_inode->i_extra_isize);
2668 break;
2669 case 'b':
2670 printf("%u", inode->i_blocks);
2671 break;
2672 case 'l':
2673 printf("%d", inode->i_links_count);
2674 break;
2675 case 'm':
2676 printf("0%o", inode->i_mode);
2677 break;
2678 case 'M':
2679 /* The diet libc doesn't respect the TZ environemnt variable */
2680 if (do_gmt == -1) {
2681 time_str = getenv("TZ");
2682 if (!time_str)
2683 time_str = "";
2684 do_gmt = !strcmp(time_str, "GMT");
2685 }
2686 t = inode->i_mtime;
2687 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
2688 printf("%.24s", time_str);
2689 break;
2690 case 'F':
2691 printf("%u", inode->i_faddr);
2692 break;
2693 case 'f':
2694 printf("%u", inode->i_file_acl);
2695 break;
2696 case 'd':
2697 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
2698 inode->i_dir_acl : 0));
2699 break;
2700 case 'u':
2701 printf("%d", (inode->i_uid |
2702 (inode->osd2.linux2.l_i_uid_high << 16)));
2703 break;
2704 case 'g':
2705 printf("%d", (inode->i_gid |
2706 (inode->osd2.linux2.l_i_gid_high << 16)));
2707 break;
2708 default:
2709 no_inode:
2710 printf("%%I%c", ch);
2711 break;
2712 }
2713}
2714
2715/*
2716 * This function expands '%dX' expressions
2717 */
Rob Landley7c94bed2006-05-03 21:58:45 +00002718static void expand_dirent_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002719 struct problem_context *ctx)
2720{
2721 struct ext2_dir_entry *dirent;
2722 int len;
2723
2724 if (!ctx || !ctx->dirent)
2725 goto no_dirent;
2726
2727 dirent = ctx->dirent;
2728
2729 switch (ch) {
2730 case 'i':
2731 printf("%u", dirent->inode);
2732 break;
2733 case 'n':
2734 len = dirent->name_len & 0xFF;
2735 if (len > EXT2_NAME_LEN)
2736 len = EXT2_NAME_LEN;
2737 if (len > dirent->rec_len)
2738 len = dirent->rec_len;
2739 safe_print(dirent->name, len);
2740 break;
2741 case 'r':
2742 printf("%u", dirent->rec_len);
2743 break;
2744 case 'l':
2745 printf("%u", dirent->name_len & 0xFF);
2746 break;
2747 case 't':
2748 printf("%u", dirent->name_len >> 8);
2749 break;
2750 default:
2751 no_dirent:
2752 printf("%%D%c", ch);
2753 break;
2754 }
2755}
2756
Rob Landley7c94bed2006-05-03 21:58:45 +00002757static void expand_percent_expression(ext2_filsys fs, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002758 struct problem_context *ctx)
2759{
2760 if (!ctx)
2761 goto no_context;
2762
2763 switch (ch) {
2764 case '%':
2765 fputc('%', stdout);
2766 break;
2767 case 'b':
2768 printf("%u", ctx->blk);
2769 break;
2770 case 'B':
2771#ifdef EXT2_NO_64_TYPE
2772 printf("%d", ctx->blkcount);
2773#else
2774 printf("%lld", ctx->blkcount);
2775#endif
2776 break;
2777 case 'c':
2778 printf("%u", ctx->blk2);
2779 break;
2780 case 'd':
2781 printf("%u", ctx->dir);
2782 break;
2783 case 'g':
2784 printf("%d", ctx->group);
2785 break;
2786 case 'i':
2787 printf("%u", ctx->ino);
2788 break;
2789 case 'j':
2790 printf("%u", ctx->ino2);
2791 break;
2792 case 'm':
2793 printf("%s", error_message(ctx->errcode));
2794 break;
2795 case 'N':
2796#ifdef EXT2_NO_64_TYPE
2797 printf("%u", ctx->num);
2798#else
2799 printf("%llu", ctx->num);
2800#endif
2801 break;
2802 case 'p':
2803 print_pathname(fs, ctx->ino, 0);
2804 break;
2805 case 'P':
2806 print_pathname(fs, ctx->ino2,
2807 ctx->dirent ? ctx->dirent->inode : 0);
2808 break;
2809 case 'q':
2810 print_pathname(fs, ctx->dir, 0);
2811 break;
2812 case 'Q':
2813 print_pathname(fs, ctx->dir, ctx->ino);
2814 break;
2815 case 'S':
2816 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
2817 break;
2818 case 's':
2819 printf("%s", ctx->str ? ctx->str : "NULL");
2820 break;
2821 case 'X':
2822#ifdef EXT2_NO_64_TYPE
2823 printf("0x%x", ctx->num);
2824#else
2825 printf("0x%llx", ctx->num);
2826#endif
2827 break;
2828 default:
2829 no_context:
2830 printf("%%%c", ch);
2831 break;
2832 }
2833}
2834
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002835
2836static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002837 struct problem_context *pctx, int first)
2838{
2839 ext2_filsys fs = ctx->fs;
2840 const char * cp;
2841 int i;
2842
2843 e2fsck_clear_progbar(ctx);
2844 for (cp = msg; *cp; cp++) {
2845 if (cp[0] == '@') {
2846 cp++;
2847 expand_at_expression(ctx, *cp, pctx, &first);
2848 } else if (cp[0] == '%' && cp[1] == 'I') {
2849 cp += 2;
2850 expand_inode_expression(*cp, pctx);
2851 } else if (cp[0] == '%' && cp[1] == 'D') {
2852 cp += 2;
2853 expand_dirent_expression(*cp, pctx);
2854 } else if ((cp[0] == '%')) {
2855 cp++;
2856 expand_percent_expression(fs, *cp, pctx);
2857 } else {
2858 for (i=0; cp[i]; i++)
2859 if ((cp[i] == '@') || cp[i] == '%')
2860 break;
2861 printf("%.*s", i, cp);
2862 cp += i-1;
2863 }
2864 first = 0;
2865 }
2866}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002867
2868
2869/*
2870 * region.c --- code which manages allocations within a region.
2871 */
2872
2873struct region_el {
2874 region_addr_t start;
2875 region_addr_t end;
2876 struct region_el *next;
2877};
2878
2879struct region_struct {
2880 region_addr_t min;
2881 region_addr_t max;
2882 struct region_el *allocated;
2883};
2884
2885static region_t region_create(region_addr_t min, region_addr_t max)
2886{
2887 region_t region;
2888
2889 region = malloc(sizeof(struct region_struct));
2890 if (!region)
2891 return NULL;
2892 memset(region, 0, sizeof(struct region_struct));
2893 region->min = min;
2894 region->max = max;
2895 return region;
2896}
2897
2898static void region_free(region_t region)
2899{
2900 struct region_el *r, *next;
2901
2902 for (r = region->allocated; r; r = next) {
2903 next = r->next;
2904 free(r);
2905 }
2906 memset(region, 0, sizeof(struct region_struct));
2907 free(region);
2908}
2909
2910static int region_allocate(region_t region, region_addr_t start, int n)
2911{
2912 struct region_el *r, *new_region, *prev, *next;
2913 region_addr_t end;
2914
2915 end = start+n;
2916 if ((start < region->min) || (end > region->max))
2917 return -1;
2918 if (n == 0)
2919 return 1;
2920
2921 /*
2922 * Search through the linked list. If we find that it
2923 * conflicts witih something that's already allocated, return
2924 * 1; if we can find an existing region which we can grow, do
2925 * so. Otherwise, stop when we find the appropriate place
2926 * insert a new region element into the linked list.
2927 */
2928 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
2929 if (((start >= r->start) && (start < r->end)) ||
2930 ((end > r->start) && (end <= r->end)) ||
2931 ((start <= r->start) && (end >= r->end)))
2932 return 1;
2933 if (end == r->start) {
2934 r->start = start;
2935 return 0;
2936 }
2937 if (start == r->end) {
2938 if ((next = r->next)) {
2939 if (end > next->start)
2940 return 1;
2941 if (end == next->start) {
2942 r->end = next->end;
2943 r->next = next->next;
2944 free(next);
2945 return 0;
2946 }
2947 }
2948 r->end = end;
2949 return 0;
2950 }
2951 if (start < r->start)
2952 break;
2953 }
2954 /*
2955 * Insert a new region element structure into the linked list
2956 */
2957 new_region = malloc(sizeof(struct region_el));
2958 if (!new_region)
2959 return -1;
2960 new_region->start = start;
2961 new_region->end = start + n;
2962 new_region->next = r;
2963 if (prev)
2964 prev->next = new_region;
2965 else
2966 region->allocated = new_region;
2967 return 0;
2968}
2969
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002970/*
2971 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
2972 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002973 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
2974 * and applies the following tests to each inode:
2975 *
2976 * - The mode field of the inode must be legal.
2977 * - The size and block count fields of the inode are correct.
2978 * - A data block must not be used by another inode
2979 *
2980 * Pass 1 also gathers the collects the following information:
2981 *
2982 * - A bitmap of which inodes are in use. (inode_used_map)
2983 * - A bitmap of which inodes are directories. (inode_dir_map)
2984 * - A bitmap of which inodes are regular files. (inode_reg_map)
2985 * - A bitmap of which inodes have bad fields. (inode_bad_map)
2986 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
2987 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
2988 * - A bitmap of which blocks are in use. (block_found_map)
2989 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
2990 * - The data blocks of the directory inodes. (dir_map)
2991 *
2992 * Pass 1 is designed to stash away enough information so that the
2993 * other passes should not need to read in the inode information
2994 * during the normal course of a filesystem check. (Althogh if an
2995 * inconsistency is detected, other passes may need to read in an
2996 * inode to fix it.)
2997 *
2998 * Note that pass 1B will be invoked if there are any duplicate blocks
2999 * found.
3000 */
3001
3002
3003static int process_block(ext2_filsys fs, blk_t *blocknr,
3004 e2_blkcnt_t blockcnt, blk_t ref_blk,
3005 int ref_offset, void *priv_data);
3006static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
3007 e2_blkcnt_t blockcnt, blk_t ref_blk,
3008 int ref_offset, void *priv_data);
3009static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
3010 char *block_buf);
3011static void mark_table_blocks(e2fsck_t ctx);
3012static void alloc_bb_map(e2fsck_t ctx);
3013static void alloc_imagic_map(e2fsck_t ctx);
3014static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
3015static void handle_fs_bad_blocks(e2fsck_t ctx);
3016static void process_inodes(e2fsck_t ctx, char *block_buf);
Rob Landley7c94bed2006-05-03 21:58:45 +00003017static int process_inode_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003018static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
3019 dgrp_t group, void * priv_data);
3020static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3021 char *block_buf, int adjust_sign);
3022/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
3023
3024static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
3025 struct ext2_inode * inode, int bufsize,
3026 const char *proc);
3027
3028struct process_block_struct_1 {
3029 ext2_ino_t ino;
3030 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
3031 fragmented:1, compressed:1, bbcheck:1;
3032 blk_t num_blocks;
3033 blk_t max_blocks;
3034 e2_blkcnt_t last_block;
3035 int num_illegal_blocks;
3036 blk_t previous_block;
3037 struct ext2_inode *inode;
3038 struct problem_context *pctx;
3039 ext2fs_block_bitmap fs_meta_blocks;
3040 e2fsck_t ctx;
3041};
3042
3043struct process_inode_block {
3044 ext2_ino_t ino;
3045 struct ext2_inode inode;
3046};
3047
3048struct scan_callback_struct {
3049 e2fsck_t ctx;
3050 char *block_buf;
3051};
3052
3053/*
3054 * For the inodes to process list.
3055 */
3056static struct process_inode_block *inodes_to_process;
3057static int process_inode_count;
3058
3059static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
3060 EXT2_MIN_BLOCK_LOG_SIZE + 1];
3061
3062/*
3063 * Free all memory allocated by pass1 in preparation for restarting
3064 * things.
3065 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003066static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003067{
3068 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003069}
3070
3071/*
3072 * Check to make sure a device inode is real. Returns 1 if the device
3073 * checks out, 0 if not.
3074 *
3075 * Note: this routine is now also used to check FIFO's and Sockets,
3076 * since they have the same requirement; the i_block fields should be
3077 * zero.
3078 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003079static int
3080e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003081{
3082 int i;
3083
3084 /*
3085 * If i_blocks is non-zero, or the index flag is set, then
3086 * this is a bogus device/fifo/socket
3087 */
3088 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
3089 (inode->i_flags & EXT2_INDEX_FL))
3090 return 0;
3091
3092 /*
3093 * We should be able to do the test below all the time, but
3094 * because the kernel doesn't forcibly clear the device
3095 * inode's additional i_block fields, there are some rare
3096 * occasions when a legitimate device inode will have non-zero
3097 * additional i_block fields. So for now, we only complain
3098 * when the immutable flag is set, which should never happen
3099 * for devices. (And that's when the problem is caused, since
3100 * you can't set or clear immutable flags for devices.) Once
3101 * the kernel has been fixed we can change this...
3102 */
3103 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
3104 for (i=4; i < EXT2_N_BLOCKS; i++)
3105 if (inode->i_block[i])
3106 return 0;
3107 }
3108 return 1;
3109}
3110
3111/*
3112 * Check to make sure a symlink inode is real. Returns 1 if the symlink
3113 * checks out, 0 if not.
3114 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003115static int
3116e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003117{
3118 unsigned int len;
3119 int i;
3120 blk_t blocks;
3121
3122 if ((inode->i_size_high || inode->i_size == 0) ||
3123 (inode->i_flags & EXT2_INDEX_FL))
3124 return 0;
3125
3126 blocks = ext2fs_inode_data_blocks(fs, inode);
3127 if (blocks) {
3128 if ((inode->i_size >= fs->blocksize) ||
3129 (blocks != fs->blocksize >> 9) ||
3130 (inode->i_block[0] < fs->super->s_first_data_block) ||
3131 (inode->i_block[0] >= fs->super->s_blocks_count))
3132 return 0;
3133
3134 for (i = 1; i < EXT2_N_BLOCKS; i++)
3135 if (inode->i_block[i])
3136 return 0;
3137
3138 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
3139 return 0;
3140
3141 len = strnlen(buf, fs->blocksize);
3142 if (len == fs->blocksize)
3143 return 0;
3144 } else {
3145 if (inode->i_size >= sizeof(inode->i_block))
3146 return 0;
3147
3148 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
3149 if (len == sizeof(inode->i_block))
3150 return 0;
3151 }
3152 if (len != inode->i_size)
3153 return 0;
3154 return 1;
3155}
3156
3157/*
3158 * If the immutable (or append-only) flag is set on the inode, offer
3159 * to clear it.
3160 */
3161#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
3162static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
3163{
3164 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
3165 return;
3166
3167 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
3168 return;
3169
3170 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
3171 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3172}
3173
3174/*
3175 * If device, fifo or socket, check size is zero -- if not offer to
3176 * clear it
3177 */
3178static void check_size(e2fsck_t ctx, struct problem_context *pctx)
3179{
3180 struct ext2_inode *inode = pctx->inode;
3181
3182 if ((inode->i_size == 0) && (inode->i_size_high == 0))
3183 return;
3184
3185 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
3186 return;
3187
3188 inode->i_size = 0;
3189 inode->i_size_high = 0;
3190 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3191}
3192
3193static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
3194{
3195 struct ext2_super_block *sb = ctx->fs->super;
3196 struct ext2_inode_large *inode;
3197 struct ext2_ext_attr_entry *entry;
3198 char *start, *end;
3199 int storage_size, remain, offs;
3200 int problem = 0;
3201
3202 inode = (struct ext2_inode_large *) pctx->inode;
3203 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
3204 inode->i_extra_isize;
3205 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3206 inode->i_extra_isize + sizeof(__u32);
3207 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
3208 entry = (struct ext2_ext_attr_entry *) start;
3209
3210 /* scan all entry's headers first */
3211
3212 /* take finish entry 0UL into account */
3213 remain = storage_size - sizeof(__u32);
3214 offs = end - start;
3215
3216 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
3217
3218 /* header eats this space */
3219 remain -= sizeof(struct ext2_ext_attr_entry);
3220
3221 /* is attribute name valid? */
3222 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
3223 pctx->num = entry->e_name_len;
3224 problem = PR_1_ATTR_NAME_LEN;
3225 goto fix;
3226 }
3227
3228 /* attribute len eats this space */
3229 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
3230
3231 /* check value size */
3232 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
3233 pctx->num = entry->e_value_size;
3234 problem = PR_1_ATTR_VALUE_SIZE;
3235 goto fix;
3236 }
3237
3238 /* check value placement */
3239 if (entry->e_value_offs +
3240 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
3241 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
3242 pctx->num = entry->e_value_offs;
3243 problem = PR_1_ATTR_VALUE_OFFSET;
3244 goto fix;
3245 }
3246
3247 /* e_value_block must be 0 in inode's ea */
3248 if (entry->e_value_block != 0) {
3249 pctx->num = entry->e_value_block;
3250 problem = PR_1_ATTR_VALUE_BLOCK;
3251 goto fix;
3252 }
3253
3254 /* e_hash must be 0 in inode's ea */
3255 if (entry->e_hash != 0) {
3256 pctx->num = entry->e_hash;
3257 problem = PR_1_ATTR_HASH;
3258 goto fix;
3259 }
3260
3261 remain -= entry->e_value_size;
3262 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
3263
3264 entry = EXT2_EXT_ATTR_NEXT(entry);
3265 }
3266fix:
3267 /*
3268 * it seems like a corruption. it's very unlikely we could repair
3269 * EA(s) in automatic fashion -bzzz
3270 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003271 if (problem == 0 || !fix_problem(ctx, problem, pctx))
3272 return;
3273
3274 /* simple remove all possible EA(s) */
3275 *((__u32 *)start) = 0UL;
3276 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
3277 EXT2_INODE_SIZE(sb), "pass1");
3278}
3279
3280static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
3281{
3282 struct ext2_super_block *sb = ctx->fs->super;
3283 struct ext2_inode_large *inode;
3284 __u32 *eamagic;
3285 int min, max;
3286
3287 inode = (struct ext2_inode_large *) pctx->inode;
3288 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
3289 /* this isn't large inode. so, nothing to check */
3290 return;
3291 }
3292
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003293 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
3294 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
3295 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
3296 /*
3297 * For now we will allow i_extra_isize to be 0, but really
3298 * implementations should never allow i_extra_isize to be 0
3299 */
3300 if (inode->i_extra_isize &&
3301 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
3302 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
3303 return;
3304 inode->i_extra_isize = min;
3305 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
3306 EXT2_INODE_SIZE(sb), "pass1");
3307 return;
3308 }
3309
3310 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3311 inode->i_extra_isize);
3312 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
3313 /* it seems inode has an extended attribute(s) in body */
3314 check_ea_in_inode(ctx, pctx);
3315 }
3316}
3317
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003318static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003319{
3320 int i;
3321 __u64 max_sizes;
3322 ext2_filsys fs = ctx->fs;
3323 ext2_ino_t ino;
3324 struct ext2_inode *inode;
3325 ext2_inode_scan scan;
3326 char *block_buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003327 unsigned char frag, fsize;
3328 struct problem_context pctx;
3329 struct scan_callback_struct scan_struct;
3330 struct ext2_super_block *sb = ctx->fs->super;
3331 int imagic_fs;
3332 int busted_fs_time = 0;
3333 int inode_size;
3334
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003335 clear_problem_context(&pctx);
3336
3337 if (!(ctx->options & E2F_OPT_PREEN))
3338 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
3339
3340 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
3341 !(ctx->options & E2F_OPT_NO)) {
3342 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
3343 ctx->dirs_to_hash = 0;
3344 }
3345
Rob Landley3e72c592006-04-06 22:49:04 +00003346 /* Pass 1 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003347
3348#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
3349
3350 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
3351 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
3352 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
3353 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
3354 max_sizes = (max_sizes * (1UL << i)) - 1;
3355 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
3356 }
3357#undef EXT2_BPP
3358
3359 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
3360
3361 /*
3362 * Allocate bitmaps structures
3363 */
3364 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
3365 &ctx->inode_used_map);
3366 if (pctx.errcode) {
3367 pctx.num = 1;
3368 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3369 ctx->flags |= E2F_FLAG_ABORT;
3370 return;
3371 }
3372 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3373 _("directory inode map"), &ctx->inode_dir_map);
3374 if (pctx.errcode) {
3375 pctx.num = 2;
3376 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3377 ctx->flags |= E2F_FLAG_ABORT;
3378 return;
3379 }
3380 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3381 _("regular file inode map"), &ctx->inode_reg_map);
3382 if (pctx.errcode) {
3383 pctx.num = 6;
3384 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3385 ctx->flags |= E2F_FLAG_ABORT;
3386 return;
3387 }
3388 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
3389 &ctx->block_found_map);
3390 if (pctx.errcode) {
3391 pctx.num = 1;
3392 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3393 ctx->flags |= E2F_FLAG_ABORT;
3394 return;
3395 }
3396 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
3397 &ctx->inode_link_info);
3398 if (pctx.errcode) {
3399 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
3400 ctx->flags |= E2F_FLAG_ABORT;
3401 return;
3402 }
3403 inode_size = EXT2_INODE_SIZE(fs->super);
3404 inode = (struct ext2_inode *)
3405 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
3406
3407 inodes_to_process = (struct process_inode_block *)
3408 e2fsck_allocate_memory(ctx,
3409 (ctx->process_inode_size *
3410 sizeof(struct process_inode_block)),
3411 "array of inodes to process");
3412 process_inode_count = 0;
3413
3414 pctx.errcode = ext2fs_init_dblist(fs, 0);
3415 if (pctx.errcode) {
3416 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
3417 ctx->flags |= E2F_FLAG_ABORT;
3418 return;
3419 }
3420
3421 /*
3422 * If the last orphan field is set, clear it, since the pass1
3423 * processing will automatically find and clear the orphans.
3424 * In the future, we may want to try using the last_orphan
3425 * linked list ourselves, but for now, we clear it so that the
3426 * ext3 mount code won't get confused.
3427 */
3428 if (!(ctx->options & E2F_OPT_READONLY)) {
3429 if (fs->super->s_last_orphan) {
3430 fs->super->s_last_orphan = 0;
3431 ext2fs_mark_super_dirty(fs);
3432 }
3433 }
3434
3435 mark_table_blocks(ctx);
3436 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
3437 "block interate buffer");
3438 e2fsck_use_inode_shortcuts(ctx, 1);
3439 ehandler_operation(_("doing inode scan"));
3440 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
3441 &scan);
3442 if (pctx.errcode) {
3443 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3444 ctx->flags |= E2F_FLAG_ABORT;
3445 return;
3446 }
3447 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
3448 ctx->stashed_inode = inode;
3449 scan_struct.ctx = ctx;
3450 scan_struct.block_buf = block_buf;
3451 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
3452 if (ctx->progress)
3453 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
3454 return;
Mike Frysinger874af852006-03-08 07:03:27 +00003455 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
3456 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003457 busted_fs_time = 1;
3458
3459 while (1) {
3460 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
3461 inode, inode_size);
3462 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3463 return;
3464 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
3465 if (!ctx->inode_bb_map)
3466 alloc_bb_map(ctx);
3467 ext2fs_mark_inode_bitmap(ctx->inode_bb_map, ino);
3468 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3469 continue;
3470 }
3471 if (pctx.errcode) {
3472 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3473 ctx->flags |= E2F_FLAG_ABORT;
3474 return;
3475 }
3476 if (!ino)
3477 break;
3478 pctx.ino = ino;
3479 pctx.inode = inode;
3480 ctx->stashed_ino = ino;
3481 if (inode->i_links_count) {
3482 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
3483 ino, inode->i_links_count);
3484 if (pctx.errcode) {
3485 pctx.num = inode->i_links_count;
3486 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
3487 ctx->flags |= E2F_FLAG_ABORT;
3488 return;
3489 }
3490 }
3491 if (ino == EXT2_BAD_INO) {
3492 struct process_block_struct_1 pb;
3493
3494 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
3495 &pb.fs_meta_blocks);
3496 if (pctx.errcode) {
3497 pctx.num = 4;
3498 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3499 ctx->flags |= E2F_FLAG_ABORT;
3500 return;
3501 }
3502 pb.ino = EXT2_BAD_INO;
3503 pb.num_blocks = pb.last_block = 0;
3504 pb.num_illegal_blocks = 0;
3505 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
3506 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
3507 pb.inode = inode;
3508 pb.pctx = &pctx;
3509 pb.ctx = ctx;
3510 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
3511 block_buf, process_bad_block, &pb);
3512 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
3513 if (pctx.errcode) {
3514 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
3515 ctx->flags |= E2F_FLAG_ABORT;
3516 return;
3517 }
3518 if (pb.bbcheck)
3519 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
3520 ctx->flags |= E2F_FLAG_ABORT;
3521 return;
3522 }
3523 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3524 clear_problem_context(&pctx);
3525 continue;
3526 } else if (ino == EXT2_ROOT_INO) {
3527 /*
3528 * Make sure the root inode is a directory; if
3529 * not, offer to clear it. It will be
3530 * regnerated in pass #3.
3531 */
3532 if (!LINUX_S_ISDIR(inode->i_mode)) {
3533 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
3534 inode->i_dtime = time(0);
3535 inode->i_links_count = 0;
3536 ext2fs_icount_store(ctx->inode_link_info,
3537 ino, 0);
3538 e2fsck_write_inode(ctx, ino, inode,
3539 "pass1");
3540 }
3541
3542 }
3543 /*
3544 * If dtime is set, offer to clear it. mke2fs
3545 * version 0.2b created filesystems with the
3546 * dtime field set for the root and lost+found
3547 * directories. We won't worry about
3548 * /lost+found, since that can be regenerated
3549 * easily. But we will fix the root directory
3550 * as a special case.
3551 */
3552 if (inode->i_dtime && inode->i_links_count) {
3553 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
3554 inode->i_dtime = 0;
3555 e2fsck_write_inode(ctx, ino, inode,
3556 "pass1");
3557 }
3558 }
3559 } else if (ino == EXT2_JOURNAL_INO) {
3560 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3561 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
3562 if (!LINUX_S_ISREG(inode->i_mode) &&
3563 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
3564 &pctx)) {
3565 inode->i_mode = LINUX_S_IFREG;
3566 e2fsck_write_inode(ctx, ino, inode,
3567 "pass1");
3568 }
3569 check_blocks(ctx, &pctx, block_buf);
3570 continue;
3571 }
3572 if ((inode->i_links_count || inode->i_blocks ||
3573 inode->i_blocks || inode->i_block[0]) &&
3574 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
3575 &pctx)) {
3576 memset(inode, 0, inode_size);
3577 ext2fs_icount_store(ctx->inode_link_info,
3578 ino, 0);
3579 e2fsck_write_inode_full(ctx, ino, inode,
3580 inode_size, "pass1");
3581 }
3582 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
3583 int problem = 0;
3584
3585 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3586 if (ino == EXT2_BOOT_LOADER_INO) {
3587 if (LINUX_S_ISDIR(inode->i_mode))
3588 problem = PR_1_RESERVED_BAD_MODE;
3589 } else if (ino == EXT2_RESIZE_INO) {
3590 if (inode->i_mode &&
3591 !LINUX_S_ISREG(inode->i_mode))
3592 problem = PR_1_RESERVED_BAD_MODE;
3593 } else {
3594 if (inode->i_mode != 0)
3595 problem = PR_1_RESERVED_BAD_MODE;
3596 }
3597 if (problem) {
3598 if (fix_problem(ctx, problem, &pctx)) {
3599 inode->i_mode = 0;
3600 e2fsck_write_inode(ctx, ino, inode,
3601 "pass1");
3602 }
3603 }
3604 check_blocks(ctx, &pctx, block_buf);
3605 continue;
3606 }
3607 /*
3608 * Check for inodes who might have been part of the
3609 * orphaned list linked list. They should have gotten
3610 * dealt with by now, unless the list had somehow been
3611 * corrupted.
3612 *
3613 * FIXME: In the future, inodes which are still in use
3614 * (and which are therefore) pending truncation should
3615 * be handled specially. Right now we just clear the
3616 * dtime field, and the normal e2fsck handling of
3617 * inodes where i_size and the inode blocks are
3618 * inconsistent is to fix i_size, instead of releasing
3619 * the extra blocks. This won't catch the inodes that
3620 * was at the end of the orphan list, but it's better
3621 * than nothing. The right answer is that there
3622 * shouldn't be any bugs in the orphan list handling. :-)
3623 */
3624 if (inode->i_dtime && !busted_fs_time &&
3625 inode->i_dtime < ctx->fs->super->s_inodes_count) {
3626 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
3627 inode->i_dtime = inode->i_links_count ?
3628 0 : time(0);
3629 e2fsck_write_inode(ctx, ino, inode,
3630 "pass1");
3631 }
3632 }
3633
3634 /*
3635 * This code assumes that deleted inodes have
3636 * i_links_count set to 0.
3637 */
3638 if (!inode->i_links_count) {
3639 if (!inode->i_dtime && inode->i_mode) {
3640 if (fix_problem(ctx,
3641 PR_1_ZERO_DTIME, &pctx)) {
3642 inode->i_dtime = time(0);
3643 e2fsck_write_inode(ctx, ino, inode,
3644 "pass1");
3645 }
3646 }
3647 continue;
3648 }
3649 /*
3650 * n.b. 0.3c ext2fs code didn't clear i_links_count for
3651 * deleted files. Oops.
3652 *
3653 * Since all new ext2 implementations get this right,
3654 * we now assume that the case of non-zero
3655 * i_links_count and non-zero dtime means that we
3656 * should keep the file, not delete it.
3657 *
3658 */
3659 if (inode->i_dtime) {
3660 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
3661 inode->i_dtime = 0;
3662 e2fsck_write_inode(ctx, ino, inode, "pass1");
3663 }
3664 }
3665
3666 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3667 switch (fs->super->s_creator_os) {
3668 case EXT2_OS_LINUX:
3669 frag = inode->osd2.linux2.l_i_frag;
3670 fsize = inode->osd2.linux2.l_i_fsize;
3671 break;
3672 case EXT2_OS_HURD:
3673 frag = inode->osd2.hurd2.h_i_frag;
3674 fsize = inode->osd2.hurd2.h_i_fsize;
3675 break;
3676 case EXT2_OS_MASIX:
3677 frag = inode->osd2.masix2.m_i_frag;
3678 fsize = inode->osd2.masix2.m_i_fsize;
3679 break;
3680 default:
3681 frag = fsize = 0;
3682 }
3683
3684 if (inode->i_faddr || frag || fsize ||
3685 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
3686 mark_inode_bad(ctx, ino);
3687 if (inode->i_flags & EXT2_IMAGIC_FL) {
3688 if (imagic_fs) {
3689 if (!ctx->inode_imagic_map)
3690 alloc_imagic_map(ctx);
3691 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
3692 ino);
3693 } else {
3694 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
3695 inode->i_flags &= ~EXT2_IMAGIC_FL;
3696 e2fsck_write_inode(ctx, ino,
3697 inode, "pass1");
3698 }
3699 }
3700 }
3701
3702 check_inode_extra_space(ctx, &pctx);
3703
3704 if (LINUX_S_ISDIR(inode->i_mode)) {
3705 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
3706 e2fsck_add_dir_info(ctx, ino, 0);
3707 ctx->fs_directory_count++;
3708 } else if (LINUX_S_ISREG (inode->i_mode)) {
3709 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
3710 ctx->fs_regular_count++;
3711 } else if (LINUX_S_ISCHR (inode->i_mode) &&
3712 e2fsck_pass1_check_device_inode(fs, inode)) {
3713 check_immutable(ctx, &pctx);
3714 check_size(ctx, &pctx);
3715 ctx->fs_chardev_count++;
3716 } else if (LINUX_S_ISBLK (inode->i_mode) &&
3717 e2fsck_pass1_check_device_inode(fs, inode)) {
3718 check_immutable(ctx, &pctx);
3719 check_size(ctx, &pctx);
3720 ctx->fs_blockdev_count++;
3721 } else if (LINUX_S_ISLNK (inode->i_mode) &&
3722 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
3723 check_immutable(ctx, &pctx);
3724 ctx->fs_symlinks_count++;
3725 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
3726 ctx->fs_fast_symlinks_count++;
3727 check_blocks(ctx, &pctx, block_buf);
3728 continue;
3729 }
3730 }
3731 else if (LINUX_S_ISFIFO (inode->i_mode) &&
3732 e2fsck_pass1_check_device_inode(fs, inode)) {
3733 check_immutable(ctx, &pctx);
3734 check_size(ctx, &pctx);
3735 ctx->fs_fifo_count++;
3736 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
3737 e2fsck_pass1_check_device_inode(fs, inode)) {
3738 check_immutable(ctx, &pctx);
3739 check_size(ctx, &pctx);
3740 ctx->fs_sockets_count++;
3741 } else
3742 mark_inode_bad(ctx, ino);
3743 if (inode->i_block[EXT2_IND_BLOCK])
3744 ctx->fs_ind_count++;
3745 if (inode->i_block[EXT2_DIND_BLOCK])
3746 ctx->fs_dind_count++;
3747 if (inode->i_block[EXT2_TIND_BLOCK])
3748 ctx->fs_tind_count++;
3749 if (inode->i_block[EXT2_IND_BLOCK] ||
3750 inode->i_block[EXT2_DIND_BLOCK] ||
3751 inode->i_block[EXT2_TIND_BLOCK] ||
3752 inode->i_file_acl) {
3753 inodes_to_process[process_inode_count].ino = ino;
3754 inodes_to_process[process_inode_count].inode = *inode;
3755 process_inode_count++;
3756 } else
3757 check_blocks(ctx, &pctx, block_buf);
3758
3759 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3760 return;
3761
3762 if (process_inode_count >= ctx->process_inode_size) {
3763 process_inodes(ctx, block_buf);
3764
3765 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3766 return;
3767 }
3768 }
3769 process_inodes(ctx, block_buf);
3770 ext2fs_close_inode_scan(scan);
3771 ehandler_operation(0);
3772
3773 /*
3774 * If any extended attribute blocks' reference counts need to
3775 * be adjusted, either up (ctx->refcount_extra), or down
3776 * (ctx->refcount), then fix them.
3777 */
3778 if (ctx->refcount) {
3779 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
3780 ea_refcount_free(ctx->refcount);
3781 ctx->refcount = 0;
3782 }
3783 if (ctx->refcount_extra) {
3784 adjust_extattr_refcount(ctx, ctx->refcount_extra,
3785 block_buf, +1);
3786 ea_refcount_free(ctx->refcount_extra);
3787 ctx->refcount_extra = 0;
3788 }
3789
3790 if (ctx->invalid_bitmaps)
3791 handle_fs_bad_blocks(ctx);
3792
3793 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00003794 ext2fs_free_block_bitmap(ctx->block_ea_map);
3795 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003796
3797 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
3798 ext2fs_block_bitmap save_bmap;
3799
3800 save_bmap = fs->block_map;
3801 fs->block_map = ctx->block_found_map;
3802 clear_problem_context(&pctx);
3803 pctx.errcode = ext2fs_create_resize_inode(fs);
3804 if (pctx.errcode) {
3805 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
3806 /* Should never get here */
3807 ctx->flags |= E2F_FLAG_ABORT;
3808 return;
3809 }
Mike Frysinger874af852006-03-08 07:03:27 +00003810 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
3811 "recreate inode");
3812 inode->i_mtime = time(0);
3813 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
3814 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003815 fs->block_map = save_bmap;
3816 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
3817 }
3818
3819 if (ctx->flags & E2F_FLAG_RESTART) {
3820 /*
3821 * Only the master copy of the superblock and block
3822 * group descriptors are going to be written during a
3823 * restart, so set the superblock to be used to be the
3824 * master superblock.
3825 */
3826 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003827 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003828 goto endit;
3829 }
3830
3831 if (ctx->block_dup_map) {
3832 if (ctx->options & E2F_OPT_PREEN) {
3833 clear_problem_context(&pctx);
3834 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
3835 }
3836 e2fsck_pass1_dupblocks(ctx, block_buf);
3837 }
3838 ext2fs_free_mem(&inodes_to_process);
3839endit:
3840 e2fsck_use_inode_shortcuts(ctx, 0);
3841
3842 ext2fs_free_mem(&block_buf);
3843 ext2fs_free_mem(&inode);
3844
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003845}
3846
3847/*
3848 * When the inode_scan routines call this callback at the end of the
3849 * glock group, call process_inodes.
3850 */
3851static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00003852 ext2_inode_scan scan FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003853 dgrp_t group, void * priv_data)
3854{
3855 struct scan_callback_struct *scan_struct;
3856 e2fsck_t ctx;
3857
3858 scan_struct = (struct scan_callback_struct *) priv_data;
3859 ctx = scan_struct->ctx;
3860
3861 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
3862
3863 if (ctx->progress)
3864 if ((ctx->progress)(ctx, 1, group+1,
3865 ctx->fs->group_desc_count))
3866 return EXT2_ET_CANCEL_REQUESTED;
3867
3868 return 0;
3869}
3870
3871/*
3872 * Process the inodes in the "inodes to process" list.
3873 */
3874static void process_inodes(e2fsck_t ctx, char *block_buf)
3875{
3876 int i;
3877 struct ext2_inode *old_stashed_inode;
3878 ext2_ino_t old_stashed_ino;
3879 const char *old_operation;
3880 char buf[80];
3881 struct problem_context pctx;
3882
Rob Landley3e72c592006-04-06 22:49:04 +00003883 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003884 if (process_inode_count == 0)
3885 return;
3886 old_operation = ehandler_operation(0);
3887 old_stashed_inode = ctx->stashed_inode;
3888 old_stashed_ino = ctx->stashed_ino;
3889 qsort(inodes_to_process, process_inode_count,
3890 sizeof(struct process_inode_block), process_inode_cmp);
3891 clear_problem_context(&pctx);
3892 for (i=0; i < process_inode_count; i++) {
3893 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
3894 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003895 sprintf(buf, _("reading indirect blocks of inode %u"),
3896 pctx.ino);
3897 ehandler_operation(buf);
3898 check_blocks(ctx, &pctx, block_buf);
3899 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3900 break;
3901 }
3902 ctx->stashed_inode = old_stashed_inode;
3903 ctx->stashed_ino = old_stashed_ino;
3904 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00003905 /* end process inodes */
3906
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003907 ehandler_operation(old_operation);
3908}
3909
Rob Landley7c94bed2006-05-03 21:58:45 +00003910static int process_inode_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003911{
3912 const struct process_inode_block *ib_a =
3913 (const struct process_inode_block *) a;
3914 const struct process_inode_block *ib_b =
3915 (const struct process_inode_block *) b;
3916 int ret;
3917
3918 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
3919 ib_b->inode.i_block[EXT2_IND_BLOCK]);
3920 if (ret == 0)
3921 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
3922 return ret;
3923}
3924
3925/*
3926 * Mark an inode as being bad in some what
3927 */
3928static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
3929{
3930 struct problem_context pctx;
3931
3932 if (!ctx->inode_bad_map) {
3933 clear_problem_context(&pctx);
3934
3935 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3936 _("bad inode map"), &ctx->inode_bad_map);
3937 if (pctx.errcode) {
3938 pctx.num = 3;
3939 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3940 /* Should never get here */
3941 ctx->flags |= E2F_FLAG_ABORT;
3942 return;
3943 }
3944 }
3945 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
3946}
3947
3948
3949/*
3950 * This procedure will allocate the inode "bb" (badblock) map table
3951 */
3952static void alloc_bb_map(e2fsck_t ctx)
3953{
3954 struct problem_context pctx;
3955
3956 clear_problem_context(&pctx);
3957 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3958 _("inode in bad block map"),
3959 &ctx->inode_bb_map);
3960 if (pctx.errcode) {
3961 pctx.num = 4;
3962 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3963 /* Should never get here */
3964 ctx->flags |= E2F_FLAG_ABORT;
3965 return;
3966 }
3967}
3968
3969/*
3970 * This procedure will allocate the inode imagic table
3971 */
3972static void alloc_imagic_map(e2fsck_t ctx)
3973{
3974 struct problem_context pctx;
3975
3976 clear_problem_context(&pctx);
3977 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3978 _("imagic inode map"),
3979 &ctx->inode_imagic_map);
3980 if (pctx.errcode) {
3981 pctx.num = 5;
3982 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3983 /* Should never get here */
3984 ctx->flags |= E2F_FLAG_ABORT;
3985 return;
3986 }
3987}
3988
3989/*
3990 * Marks a block as in use, setting the dup_map if it's been set
3991 * already. Called by process_block and process_bad_block.
3992 *
3993 * WARNING: Assumes checks have already been done to make sure block
3994 * is valid. This is true in both process_block and process_bad_block.
3995 */
Rob Landley7c94bed2006-05-03 21:58:45 +00003996static void mark_block_used(e2fsck_t ctx, blk_t block)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003997{
3998 struct problem_context pctx;
3999
4000 clear_problem_context(&pctx);
4001
4002 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
4003 if (!ctx->block_dup_map) {
4004 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
4005 _("multiply claimed block map"),
4006 &ctx->block_dup_map);
4007 if (pctx.errcode) {
4008 pctx.num = 3;
4009 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
4010 &pctx);
4011 /* Should never get here */
4012 ctx->flags |= E2F_FLAG_ABORT;
4013 return;
4014 }
4015 }
4016 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
4017 } else {
4018 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
4019 }
4020}
4021
4022/*
4023 * Adjust the extended attribute block's reference counts at the end
4024 * of pass 1, either by subtracting out references for EA blocks that
4025 * are still referenced in ctx->refcount, or by adding references for
4026 * EA blocks that had extra references as accounted for in
4027 * ctx->refcount_extra.
4028 */
4029static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
4030 char *block_buf, int adjust_sign)
4031{
4032 struct ext2_ext_attr_header *header;
4033 struct problem_context pctx;
4034 ext2_filsys fs = ctx->fs;
4035 blk_t blk;
4036 __u32 should_be;
4037 int count;
4038
4039 clear_problem_context(&pctx);
4040
4041 ea_refcount_intr_begin(refcount);
4042 while (1) {
4043 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
4044 break;
4045 pctx.blk = blk;
4046 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4047 if (pctx.errcode) {
4048 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
4049 return;
4050 }
4051 header = (struct ext2_ext_attr_header *) block_buf;
4052 pctx.blkcount = header->h_refcount;
4053 should_be = header->h_refcount + adjust_sign * count;
4054 pctx.num = should_be;
4055 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
4056 header->h_refcount = should_be;
4057 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
4058 block_buf);
4059 if (pctx.errcode) {
4060 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
4061 continue;
4062 }
4063 }
4064 }
4065}
4066
4067/*
4068 * Handle processing the extended attribute blocks
4069 */
4070static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
4071 char *block_buf)
4072{
4073 ext2_filsys fs = ctx->fs;
4074 ext2_ino_t ino = pctx->ino;
4075 struct ext2_inode *inode = pctx->inode;
4076 blk_t blk;
4077 char * end;
4078 struct ext2_ext_attr_header *header;
4079 struct ext2_ext_attr_entry *entry;
4080 int count;
4081 region_t region;
4082
4083 blk = inode->i_file_acl;
4084 if (blk == 0)
4085 return 0;
4086
4087 /*
4088 * If the Extended attribute flag isn't set, then a non-zero
4089 * file acl means that the inode is corrupted.
4090 *
4091 * Or if the extended attribute block is an invalid block,
4092 * then the inode is also corrupted.
4093 */
4094 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
4095 (blk < fs->super->s_first_data_block) ||
4096 (blk >= fs->super->s_blocks_count)) {
4097 mark_inode_bad(ctx, ino);
4098 return 0;
4099 }
4100
4101 /* If ea bitmap hasn't been allocated, create it */
4102 if (!ctx->block_ea_map) {
4103 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
4104 _("ext attr block map"),
4105 &ctx->block_ea_map);
4106 if (pctx->errcode) {
4107 pctx->num = 2;
4108 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
4109 ctx->flags |= E2F_FLAG_ABORT;
4110 return 0;
4111 }
4112 }
4113
4114 /* Create the EA refcount structure if necessary */
4115 if (!ctx->refcount) {
4116 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
4117 if (pctx->errcode) {
4118 pctx->num = 1;
4119 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
4120 ctx->flags |= E2F_FLAG_ABORT;
4121 return 0;
4122 }
4123 }
4124
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004125 /* Have we seen this EA block before? */
4126 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
4127 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
4128 return 1;
4129 /* Ooops, this EA was referenced more than it stated */
4130 if (!ctx->refcount_extra) {
4131 pctx->errcode = ea_refcount_create(0,
4132 &ctx->refcount_extra);
4133 if (pctx->errcode) {
4134 pctx->num = 2;
4135 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
4136 ctx->flags |= E2F_FLAG_ABORT;
4137 return 0;
4138 }
4139 }
4140 ea_refcount_increment(ctx->refcount_extra, blk, 0);
4141 return 1;
4142 }
4143
4144 /*
4145 * OK, we haven't seen this EA block yet. So we need to
4146 * validate it
4147 */
4148 pctx->blk = blk;
4149 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4150 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
4151 goto clear_extattr;
4152 header = (struct ext2_ext_attr_header *) block_buf;
4153 pctx->blk = inode->i_file_acl;
4154 if (((ctx->ext_attr_ver == 1) &&
4155 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
4156 ((ctx->ext_attr_ver == 2) &&
4157 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
4158 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
4159 goto clear_extattr;
4160 }
4161
4162 if (header->h_blocks != 1) {
4163 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
4164 goto clear_extattr;
4165 }
4166
4167 region = region_create(0, fs->blocksize);
4168 if (!region) {
4169 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
4170 ctx->flags |= E2F_FLAG_ABORT;
4171 return 0;
4172 }
4173 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
4174 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4175 goto clear_extattr;
4176 }
4177
4178 entry = (struct ext2_ext_attr_entry *)(header+1);
4179 end = block_buf + fs->blocksize;
4180 while ((char *)entry < end && *(__u32 *)entry) {
4181 if (region_allocate(region, (char *)entry - (char *)header,
4182 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
4183 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4184 goto clear_extattr;
4185 }
4186 if ((ctx->ext_attr_ver == 1 &&
4187 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
4188 (ctx->ext_attr_ver == 2 &&
4189 entry->e_name_index == 0)) {
4190 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
4191 goto clear_extattr;
4192 }
4193 if (entry->e_value_block != 0) {
4194 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
4195 goto clear_extattr;
4196 }
4197 if (entry->e_value_size &&
4198 region_allocate(region, entry->e_value_offs,
4199 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
4200 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4201 goto clear_extattr;
4202 }
4203 entry = EXT2_EXT_ATTR_NEXT(entry);
4204 }
4205 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
4206 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4207 goto clear_extattr;
4208 }
4209 region_free(region);
4210
4211 count = header->h_refcount - 1;
4212 if (count)
4213 ea_refcount_store(ctx->refcount, blk, count);
4214 mark_block_used(ctx, blk);
4215 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
4216
4217 return 1;
4218
4219clear_extattr:
4220 inode->i_file_acl = 0;
4221 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
4222 return 0;
4223}
4224
4225/* Returns 1 if bad htree, 0 if OK */
4226static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004227 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004228 struct ext2_inode *inode,
4229 char *block_buf)
4230{
4231 struct ext2_dx_root_info *root;
4232 ext2_filsys fs = ctx->fs;
4233 errcode_t retval;
4234 blk_t blk;
4235
4236 if ((!LINUX_S_ISDIR(inode->i_mode) &&
4237 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
4238 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4239 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
4240 return 1;
4241
4242 blk = inode->i_block[0];
4243 if (((blk == 0) ||
4244 (blk < fs->super->s_first_data_block) ||
4245 (blk >= fs->super->s_blocks_count)) &&
4246 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4247 return 1;
4248
4249 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
4250 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4251 return 1;
4252
4253 /* XXX should check that beginning matches a directory */
4254 root = (struct ext2_dx_root_info *) (block_buf + 24);
4255
4256 if ((root->reserved_zero || root->info_length < 8) &&
4257 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4258 return 1;
4259
4260 pctx->num = root->hash_version;
4261 if ((root->hash_version != EXT2_HASH_LEGACY) &&
4262 (root->hash_version != EXT2_HASH_HALF_MD4) &&
4263 (root->hash_version != EXT2_HASH_TEA) &&
4264 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
4265 return 1;
4266
4267 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
4268 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
4269 return 1;
4270
4271 pctx->num = root->indirect_levels;
4272 if ((root->indirect_levels > 1) &&
4273 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
4274 return 1;
4275
4276 return 0;
4277}
4278
4279/*
4280 * This subroutine is called on each inode to account for all of the
4281 * blocks used by that inode.
4282 */
4283static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
4284 char *block_buf)
4285{
4286 ext2_filsys fs = ctx->fs;
4287 struct process_block_struct_1 pb;
4288 ext2_ino_t ino = pctx->ino;
4289 struct ext2_inode *inode = pctx->inode;
4290 int bad_size = 0;
4291 int dirty_inode = 0;
4292 __u64 size;
4293
4294 pb.ino = ino;
4295 pb.num_blocks = 0;
4296 pb.last_block = -1;
4297 pb.num_illegal_blocks = 0;
4298 pb.suppress = 0; pb.clear = 0;
4299 pb.fragmented = 0;
4300 pb.compressed = 0;
4301 pb.previous_block = 0;
4302 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
4303 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
4304 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
4305 pb.inode = inode;
4306 pb.pctx = pctx;
4307 pb.ctx = ctx;
4308 pctx->ino = ino;
4309 pctx->errcode = 0;
4310
4311 if (inode->i_flags & EXT2_COMPRBLK_FL) {
4312 if (fs->super->s_feature_incompat &
4313 EXT2_FEATURE_INCOMPAT_COMPRESSION)
4314 pb.compressed = 1;
4315 else {
4316 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
4317 inode->i_flags &= ~EXT2_COMPRBLK_FL;
4318 dirty_inode++;
4319 }
4320 }
4321 }
4322
4323 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
4324 pb.num_blocks++;
4325
4326 if (ext2fs_inode_has_valid_blocks(inode))
4327 pctx->errcode = ext2fs_block_iterate2(fs, ino,
4328 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
4329 block_buf, process_block, &pb);
4330 end_problem_latch(ctx, PR_LATCH_BLOCK);
4331 end_problem_latch(ctx, PR_LATCH_TOOBIG);
4332 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4333 goto out;
4334 if (pctx->errcode)
4335 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
4336
4337 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
4338 ctx->fs_fragmented++;
4339
4340 if (pb.clear) {
4341 inode->i_links_count = 0;
4342 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4343 inode->i_dtime = time(0);
4344 dirty_inode++;
4345 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4346 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4347 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4348 /*
4349 * The inode was probably partially accounted for
4350 * before processing was aborted, so we need to
4351 * restart the pass 1 scan.
4352 */
4353 ctx->flags |= E2F_FLAG_RESTART;
4354 goto out;
4355 }
4356
4357 if (inode->i_flags & EXT2_INDEX_FL) {
4358 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
4359 inode->i_flags &= ~EXT2_INDEX_FL;
4360 dirty_inode++;
4361 } else {
4362#ifdef ENABLE_HTREE
4363 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
4364#endif
4365 }
4366 }
4367 if (ctx->dirs_to_hash && pb.is_dir &&
4368 !(inode->i_flags & EXT2_INDEX_FL) &&
4369 ((inode->i_size / fs->blocksize) >= 3))
4370 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
4371
4372 if (!pb.num_blocks && pb.is_dir) {
4373 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
4374 inode->i_links_count = 0;
4375 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4376 inode->i_dtime = time(0);
4377 dirty_inode++;
4378 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4379 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4380 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4381 ctx->fs_directory_count--;
4382 goto out;
4383 }
4384 }
4385
4386 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00004387
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004388 if (pb.is_dir) {
4389 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
4390 if (nblock > (pb.last_block + 1))
4391 bad_size = 1;
4392 else if (nblock < (pb.last_block + 1)) {
4393 if (((pb.last_block + 1) - nblock) >
4394 fs->super->s_prealloc_dir_blocks)
4395 bad_size = 2;
4396 }
4397 } else {
4398 size = EXT2_I_SIZE(inode);
4399 if ((pb.last_block >= 0) &&
4400 (size < (__u64) pb.last_block * fs->blocksize))
4401 bad_size = 3;
4402 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
4403 bad_size = 4;
4404 }
4405 /* i_size for symlinks is checked elsewhere */
4406 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
4407 pctx->num = (pb.last_block+1) * fs->blocksize;
4408 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
4409 inode->i_size = pctx->num;
4410 if (!LINUX_S_ISDIR(inode->i_mode))
4411 inode->i_size_high = pctx->num >> 32;
4412 dirty_inode++;
4413 }
4414 pctx->num = 0;
4415 }
4416 if (LINUX_S_ISREG(inode->i_mode) &&
4417 (inode->i_size_high || inode->i_size & 0x80000000UL))
4418 ctx->large_files++;
4419 if (pb.num_blocks != inode->i_blocks) {
4420 pctx->num = pb.num_blocks;
4421 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
4422 inode->i_blocks = pb.num_blocks;
4423 dirty_inode++;
4424 }
4425 pctx->num = 0;
4426 }
4427out:
4428 if (dirty_inode)
4429 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
4430}
4431
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004432
4433/*
4434 * This is a helper function for check_blocks().
4435 */
4436static int process_block(ext2_filsys fs,
4437 blk_t *block_nr,
4438 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004439 blk_t ref_block FSCK_ATTR((unused)),
4440 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004441 void *priv_data)
4442{
4443 struct process_block_struct_1 *p;
4444 struct problem_context *pctx;
4445 blk_t blk = *block_nr;
4446 int ret_code = 0;
4447 int problem = 0;
4448 e2fsck_t ctx;
4449
4450 p = (struct process_block_struct_1 *) priv_data;
4451 pctx = p->pctx;
4452 ctx = p->ctx;
4453
4454 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
4455 /* todo: Check that the comprblk_fl is high, that the
4456 blkaddr pattern looks right (all non-holes up to
4457 first EXT2FS_COMPRESSED_BLKADDR, then all
4458 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
4459 that the feature_incompat bit is high, and that the
4460 inode is a regular file. If we're doing a "full
4461 check" (a concept introduced to e2fsck by e2compr,
4462 meaning that we look at data blocks as well as
4463 metadata) then call some library routine that
4464 checks the compressed data. I'll have to think
4465 about this, because one particularly important
4466 problem to be able to fix is to recalculate the
4467 cluster size if necessary. I think that perhaps
4468 we'd better do most/all e2compr-specific checks
4469 separately, after the non-e2compr checks. If not
4470 doing a full check, it may be useful to test that
4471 the personality is linux; e.g. if it isn't then
4472 perhaps this really is just an illegal block. */
4473 return 0;
4474 }
4475
4476 if (blk == 0) {
4477 if (p->is_dir == 0) {
4478 /*
4479 * Should never happen, since only directories
4480 * get called with BLOCK_FLAG_HOLE
4481 */
4482#if DEBUG_E2FSCK
4483 printf("process_block() called with blk == 0, "
4484 "blockcnt=%d, inode %lu???\n",
4485 blockcnt, p->ino);
4486#endif
4487 return 0;
4488 }
4489 if (blockcnt < 0)
4490 return 0;
4491 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004492 goto mark_dir;
4493 }
4494 return 0;
4495 }
4496
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004497 /*
4498 * Simplistic fragmentation check. We merely require that the
4499 * file be contiguous. (Which can never be true for really
4500 * big files that are greater than a block group.)
4501 */
4502 if (!HOLE_BLKADDR(p->previous_block)) {
4503 if (p->previous_block+1 != blk)
4504 p->fragmented = 1;
4505 }
4506 p->previous_block = blk;
4507
4508 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
4509 problem = PR_1_TOOBIG_DIR;
4510 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
4511 problem = PR_1_TOOBIG_REG;
4512 if (!p->is_dir && !p->is_reg && blockcnt > 0)
4513 problem = PR_1_TOOBIG_SYMLINK;
4514
4515 if (blk < fs->super->s_first_data_block ||
4516 blk >= fs->super->s_blocks_count)
4517 problem = PR_1_ILLEGAL_BLOCK_NUM;
4518
4519 if (problem) {
4520 p->num_illegal_blocks++;
4521 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
4522 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
4523 p->clear = 1;
4524 return BLOCK_ABORT;
4525 }
4526 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
4527 p->suppress = 1;
4528 set_latch_flags(PR_LATCH_BLOCK,
4529 PRL_SUPPRESS, 0);
4530 }
4531 }
4532 pctx->blk = blk;
4533 pctx->blkcount = blockcnt;
4534 if (fix_problem(ctx, problem, pctx)) {
4535 blk = *block_nr = 0;
4536 ret_code = BLOCK_CHANGED;
4537 goto mark_dir;
4538 } else
4539 return 0;
4540 }
4541
4542 if (p->ino == EXT2_RESIZE_INO) {
4543 /*
4544 * The resize inode has already be sanity checked
4545 * during pass #0 (the superblock checks). All we
4546 * have to do is mark the double indirect block as
4547 * being in use; all of the other blocks are handled
4548 * by mark_table_blocks()).
4549 */
4550 if (blockcnt == BLOCK_COUNT_DIND)
4551 mark_block_used(ctx, blk);
4552 } else
4553 mark_block_used(ctx, blk);
4554 p->num_blocks++;
4555 if (blockcnt >= 0)
4556 p->last_block = blockcnt;
4557mark_dir:
4558 if (p->is_dir && (blockcnt >= 0)) {
4559 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
4560 blk, blockcnt);
4561 if (pctx->errcode) {
4562 pctx->blk = blk;
4563 pctx->num = blockcnt;
4564 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
4565 /* Should never get here */
4566 ctx->flags |= E2F_FLAG_ABORT;
4567 return BLOCK_ABORT;
4568 }
4569 }
4570 return ret_code;
4571}
4572
4573static int process_bad_block(ext2_filsys fs,
4574 blk_t *block_nr,
4575 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004576 blk_t ref_block FSCK_ATTR((unused)),
4577 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004578 void *priv_data)
4579{
4580 struct process_block_struct_1 *p;
4581 blk_t blk = *block_nr;
4582 blk_t first_block;
4583 dgrp_t i;
4584 struct problem_context *pctx;
4585 e2fsck_t ctx;
4586
4587 /*
4588 * Note: This function processes blocks for the bad blocks
4589 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
4590 */
4591
4592 if (!blk)
4593 return 0;
4594
4595 p = (struct process_block_struct_1 *) priv_data;
4596 ctx = p->ctx;
4597 pctx = p->pctx;
4598
4599 pctx->ino = EXT2_BAD_INO;
4600 pctx->blk = blk;
4601 pctx->blkcount = blockcnt;
4602
4603 if ((blk < fs->super->s_first_data_block) ||
4604 (blk >= fs->super->s_blocks_count)) {
4605 if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
4606 *block_nr = 0;
4607 return BLOCK_CHANGED;
4608 } else
4609 return 0;
4610 }
4611
4612 if (blockcnt < 0) {
4613 if (ext2fs_test_block_bitmap(p->fs_meta_blocks, blk)) {
4614 p->bbcheck = 1;
4615 if (fix_problem(ctx, PR_1_BB_FS_BLOCK, pctx)) {
4616 *block_nr = 0;
4617 return BLOCK_CHANGED;
4618 }
4619 } else if (ext2fs_test_block_bitmap(ctx->block_found_map,
4620 blk)) {
4621 p->bbcheck = 1;
4622 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK,
4623 pctx)) {
4624 *block_nr = 0;
4625 return BLOCK_CHANGED;
4626 }
4627 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4628 return BLOCK_ABORT;
4629 } else
4630 mark_block_used(ctx, blk);
4631 return 0;
4632 }
Rob Landley3e72c592006-04-06 22:49:04 +00004633
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004634 ctx->fs_badblocks_count++;
4635 /*
4636 * If the block is not used, then mark it as used and return.
4637 * If it is already marked as found, this must mean that
4638 * there's an overlap between the filesystem table blocks
4639 * (bitmaps and inode table) and the bad block list.
4640 */
4641 if (!ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
4642 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
4643 return 0;
4644 }
4645 /*
4646 * Try to find the where the filesystem block was used...
4647 */
4648 first_block = fs->super->s_first_data_block;
4649
4650 for (i = 0; i < fs->group_desc_count; i++ ) {
4651 pctx->group = i;
4652 pctx->blk = blk;
4653 if (!ext2fs_bg_has_super(fs, i))
4654 goto skip_super;
4655 if (blk == first_block) {
4656 if (i == 0) {
4657 if (fix_problem(ctx,
4658 PR_1_BAD_PRIMARY_SUPERBLOCK,
4659 pctx)) {
4660 *block_nr = 0;
4661 return BLOCK_CHANGED;
4662 }
4663 return 0;
4664 }
4665 fix_problem(ctx, PR_1_BAD_SUPERBLOCK, pctx);
4666 return 0;
4667 }
4668 if ((blk > first_block) &&
4669 (blk <= first_block + fs->desc_blocks)) {
4670 if (i == 0) {
4671 pctx->blk = *block_nr;
4672 if (fix_problem(ctx,
4673 PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, pctx)) {
4674 *block_nr = 0;
4675 return BLOCK_CHANGED;
4676 }
4677 return 0;
4678 }
4679 fix_problem(ctx, PR_1_BAD_GROUP_DESCRIPTORS, pctx);
4680 return 0;
4681 }
4682 skip_super:
4683 if (blk == fs->group_desc[i].bg_block_bitmap) {
4684 if (fix_problem(ctx, PR_1_BB_BAD_BLOCK, pctx)) {
4685 ctx->invalid_block_bitmap_flag[i]++;
4686 ctx->invalid_bitmaps++;
4687 }
4688 return 0;
4689 }
4690 if (blk == fs->group_desc[i].bg_inode_bitmap) {
4691 if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
4692 ctx->invalid_inode_bitmap_flag[i]++;
4693 ctx->invalid_bitmaps++;
4694 }
4695 return 0;
4696 }
4697 if ((blk >= fs->group_desc[i].bg_inode_table) &&
4698 (blk < (fs->group_desc[i].bg_inode_table +
4699 fs->inode_blocks_per_group))) {
4700 /*
4701 * If there are bad blocks in the inode table,
4702 * the inode scan code will try to do
4703 * something reasonable automatically.
4704 */
4705 return 0;
4706 }
4707 first_block += fs->super->s_blocks_per_group;
4708 }
4709 /*
4710 * If we've gotten to this point, then the only
4711 * possibility is that the bad block inode meta data
4712 * is using a bad block.
4713 */
4714 if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
4715 (blk == p->inode->i_block[EXT2_DIND_BLOCK]) ||
4716 (blk == p->inode->i_block[EXT2_TIND_BLOCK])) {
4717 p->bbcheck = 1;
4718 if (fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK, pctx)) {
4719 *block_nr = 0;
4720 return BLOCK_CHANGED;
4721 }
4722 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4723 return BLOCK_ABORT;
4724 return 0;
4725 }
4726
4727 pctx->group = -1;
4728
4729 /* Warn user that the block wasn't claimed */
4730 fix_problem(ctx, PR_1_PROGERR_CLAIMED_BLOCK, pctx);
4731
4732 return 0;
4733}
4734
4735static void new_table_block(e2fsck_t ctx, blk_t first_block, int group,
4736 const char *name, int num, blk_t *new_block)
4737{
4738 ext2_filsys fs = ctx->fs;
4739 blk_t old_block = *new_block;
4740 int i;
4741 char *buf;
4742 struct problem_context pctx;
4743
4744 clear_problem_context(&pctx);
4745
4746 pctx.group = group;
4747 pctx.blk = old_block;
4748 pctx.str = name;
4749
4750 pctx.errcode = ext2fs_get_free_blocks(fs, first_block,
4751 first_block + fs->super->s_blocks_per_group,
4752 num, ctx->block_found_map, new_block);
4753 if (pctx.errcode) {
4754 pctx.num = num;
4755 fix_problem(ctx, PR_1_RELOC_BLOCK_ALLOCATE, &pctx);
4756 ext2fs_unmark_valid(fs);
4757 return;
4758 }
4759 pctx.errcode = ext2fs_get_mem(fs->blocksize, &buf);
4760 if (pctx.errcode) {
4761 fix_problem(ctx, PR_1_RELOC_MEMORY_ALLOCATE, &pctx);
4762 ext2fs_unmark_valid(fs);
4763 return;
4764 }
4765 ext2fs_mark_super_dirty(fs);
4766 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
4767 pctx.blk2 = *new_block;
4768 fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
4769 PR_1_RELOC_TO), &pctx);
4770 pctx.blk2 = 0;
4771 for (i = 0; i < num; i++) {
4772 pctx.blk = i;
4773 ext2fs_mark_block_bitmap(ctx->block_found_map, (*new_block)+i);
4774 if (old_block) {
4775 pctx.errcode = io_channel_read_blk(fs->io,
4776 old_block + i, 1, buf);
4777 if (pctx.errcode)
4778 fix_problem(ctx, PR_1_RELOC_READ_ERR, &pctx);
4779 } else
4780 memset(buf, 0, fs->blocksize);
4781
4782 pctx.blk = (*new_block) + i;
4783 pctx.errcode = io_channel_write_blk(fs->io, pctx.blk,
4784 1, buf);
4785 if (pctx.errcode)
4786 fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
4787 }
4788 ext2fs_free_mem(&buf);
4789}
4790
4791/*
4792 * This routine gets called at the end of pass 1 if bad blocks are
4793 * detected in the superblock, group descriptors, inode_bitmaps, or
4794 * block bitmaps. At this point, all of the blocks have been mapped
4795 * out, so we can try to allocate new block(s) to replace the bad
4796 * blocks.
4797 */
4798static void handle_fs_bad_blocks(e2fsck_t ctx)
4799{
4800 ext2_filsys fs = ctx->fs;
4801 dgrp_t i;
4802 int first_block = fs->super->s_first_data_block;
4803
4804 for (i = 0; i < fs->group_desc_count; i++) {
4805 if (ctx->invalid_block_bitmap_flag[i]) {
4806 new_table_block(ctx, first_block, i, _("block bitmap"),
4807 1, &fs->group_desc[i].bg_block_bitmap);
4808 }
4809 if (ctx->invalid_inode_bitmap_flag[i]) {
4810 new_table_block(ctx, first_block, i, _("inode bitmap"),
4811 1, &fs->group_desc[i].bg_inode_bitmap);
4812 }
4813 if (ctx->invalid_inode_table_flag[i]) {
4814 new_table_block(ctx, first_block, i, _("inode table"),
4815 fs->inode_blocks_per_group,
4816 &fs->group_desc[i].bg_inode_table);
4817 ctx->flags |= E2F_FLAG_RESTART;
4818 }
4819 first_block += fs->super->s_blocks_per_group;
4820 }
4821 ctx->invalid_bitmaps = 0;
4822}
4823
4824/*
4825 * This routine marks all blocks which are used by the superblock,
4826 * group descriptors, inode bitmaps, and block bitmaps.
4827 */
4828static void mark_table_blocks(e2fsck_t ctx)
4829{
4830 ext2_filsys fs = ctx->fs;
4831 blk_t block, b;
4832 dgrp_t i;
4833 int j;
4834 struct problem_context pctx;
4835
4836 clear_problem_context(&pctx);
4837
4838 block = fs->super->s_first_data_block;
4839 for (i = 0; i < fs->group_desc_count; i++) {
4840 pctx.group = i;
4841
4842 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
4843
4844 /*
4845 * Mark the blocks used for the inode table
4846 */
4847 if (fs->group_desc[i].bg_inode_table) {
4848 for (j = 0, b = fs->group_desc[i].bg_inode_table;
4849 j < fs->inode_blocks_per_group;
4850 j++, b++) {
4851 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4852 b)) {
4853 pctx.blk = b;
4854 if (fix_problem(ctx,
4855 PR_1_ITABLE_CONFLICT, &pctx)) {
4856 ctx->invalid_inode_table_flag[i]++;
4857 ctx->invalid_bitmaps++;
4858 }
4859 } else {
4860 ext2fs_mark_block_bitmap(ctx->block_found_map,
4861 b);
4862 }
4863 }
4864 }
4865
4866 /*
4867 * Mark block used for the block bitmap
4868 */
4869 if (fs->group_desc[i].bg_block_bitmap) {
4870 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4871 fs->group_desc[i].bg_block_bitmap)) {
4872 pctx.blk = fs->group_desc[i].bg_block_bitmap;
4873 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
4874 ctx->invalid_block_bitmap_flag[i]++;
4875 ctx->invalid_bitmaps++;
4876 }
4877 } else {
4878 ext2fs_mark_block_bitmap(ctx->block_found_map,
4879 fs->group_desc[i].bg_block_bitmap);
4880 }
4881
4882 }
4883 /*
4884 * Mark block used for the inode bitmap
4885 */
4886 if (fs->group_desc[i].bg_inode_bitmap) {
4887 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4888 fs->group_desc[i].bg_inode_bitmap)) {
4889 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
4890 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
4891 ctx->invalid_inode_bitmap_flag[i]++;
4892 ctx->invalid_bitmaps++;
4893 }
4894 } else {
4895 ext2fs_mark_block_bitmap(ctx->block_found_map,
4896 fs->group_desc[i].bg_inode_bitmap);
4897 }
4898 }
4899 block += fs->super->s_blocks_per_group;
4900 }
4901}
4902
4903/*
4904 * Thes subroutines short circuits ext2fs_get_blocks and
4905 * ext2fs_check_directory; we use them since we already have the inode
4906 * structure, so there's no point in letting the ext2fs library read
4907 * the inode again.
4908 */
4909static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
4910 blk_t *blocks)
4911{
4912 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4913 int i;
4914
4915 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4916 return EXT2_ET_CALLBACK_NOTHANDLED;
4917
4918 for (i=0; i < EXT2_N_BLOCKS; i++)
4919 blocks[i] = ctx->stashed_inode->i_block[i];
4920 return 0;
4921}
4922
4923static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
4924 struct ext2_inode *inode)
4925{
4926 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4927
4928 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4929 return EXT2_ET_CALLBACK_NOTHANDLED;
4930 *inode = *ctx->stashed_inode;
4931 return 0;
4932}
4933
4934static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
4935 struct ext2_inode *inode)
4936{
4937 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4938
4939 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
4940 *ctx->stashed_inode = *inode;
4941 return EXT2_ET_CALLBACK_NOTHANDLED;
4942}
4943
4944static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
4945{
4946 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4947
4948 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4949 return EXT2_ET_CALLBACK_NOTHANDLED;
4950
4951 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
4952 return EXT2_ET_NO_DIRECTORY;
4953 return 0;
4954}
4955
4956void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
4957{
4958 ext2_filsys fs = ctx->fs;
4959
4960 if (bool) {
4961 fs->get_blocks = pass1_get_blocks;
4962 fs->check_directory = pass1_check_directory;
4963 fs->read_inode = pass1_read_inode;
4964 fs->write_inode = pass1_write_inode;
4965 ctx->stashed_ino = 0;
4966 } else {
4967 fs->get_blocks = 0;
4968 fs->check_directory = 0;
4969 fs->read_inode = 0;
4970 fs->write_inode = 0;
4971 }
4972}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004973
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004974/*
4975 * pass1b.c --- Pass #1b of e2fsck
4976 *
4977 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
4978 * only invoked if pass 1 discovered blocks which are in use by more
4979 * than one inode.
4980 *
4981 * Pass1B scans the data blocks of all the inodes again, generating a
4982 * complete list of duplicate blocks and which inodes have claimed
4983 * them.
4984 *
4985 * Pass1C does a tree-traversal of the filesystem, to determine the
4986 * parent directories of these inodes. This step is necessary so that
4987 * e2fsck can print out the pathnames of affected inodes.
4988 *
4989 * Pass1D is a reconciliation pass. For each inode with duplicate
4990 * blocks, the user is prompted if s/he would like to clone the file
4991 * (so that the file gets a fresh copy of the duplicated blocks) or
4992 * simply to delete the file.
4993 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004994 */
4995
4996
4997/* Needed for architectures where sizeof(int) != sizeof(void *) */
4998#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
4999#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
5000
5001/* Define an extension to the ext2 library's block count information */
5002#define BLOCK_COUNT_EXTATTR (-5)
5003
5004struct block_el {
5005 blk_t block;
5006 struct block_el *next;
5007};
5008
5009struct inode_el {
5010 ext2_ino_t inode;
5011 struct inode_el *next;
5012};
5013
5014struct dup_block {
5015 int num_bad;
5016 struct inode_el *inode_list;
5017};
5018
5019/*
5020 * This structure stores information about a particular inode which
5021 * is sharing blocks with other inodes. This information is collected
5022 * to display to the user, so that the user knows what files he or she
5023 * is dealing with, when trying to decide how to resolve the conflict
5024 * of multiply-claimed blocks.
5025 */
5026struct dup_inode {
5027 ext2_ino_t dir;
5028 int num_dupblocks;
5029 struct ext2_inode inode;
5030 struct block_el *block_list;
5031};
5032
5033static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
5034 e2_blkcnt_t blockcnt, blk_t ref_blk,
5035 int ref_offset, void *priv_data);
5036static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5037 struct dup_inode *dp, char *block_buf);
5038static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5039 struct dup_inode *dp, char* block_buf);
5040static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
5041
5042static void pass1b(e2fsck_t ctx, char *block_buf);
5043static void pass1c(e2fsck_t ctx, char *block_buf);
5044static void pass1d(e2fsck_t ctx, char *block_buf);
5045
5046static int dup_inode_count = 0;
5047
5048static dict_t blk_dict, ino_dict;
5049
5050static ext2fs_inode_bitmap inode_dup_map;
5051
5052static int dict_int_cmp(const void *a, const void *b)
5053{
5054 intptr_t ia, ib;
5055
5056 ia = (intptr_t)a;
5057 ib = (intptr_t)b;
5058
5059 return (ia-ib);
5060}
5061
5062/*
5063 * Add a duplicate block record
5064 */
5065static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
5066 struct ext2_inode *inode)
5067{
5068 dnode_t *n;
5069 struct dup_block *db;
5070 struct dup_inode *di;
5071 struct block_el *blk_el;
5072 struct inode_el *ino_el;
5073
5074 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5075 if (n)
5076 db = (struct dup_block *) dnode_get(n);
5077 else {
5078 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
5079 sizeof(struct dup_block), "duplicate block header");
5080 db->num_bad = 0;
5081 db->inode_list = 0;
5082 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
5083 }
5084 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
5085 sizeof(struct inode_el), "inode element");
5086 ino_el->inode = ino;
5087 ino_el->next = db->inode_list;
5088 db->inode_list = ino_el;
5089 db->num_bad++;
5090
5091 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
5092 if (n)
5093 di = (struct dup_inode *) dnode_get(n);
5094 else {
5095 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
5096 sizeof(struct dup_inode), "duplicate inode header");
5097 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
5098 di->num_dupblocks = 0;
5099 di->block_list = 0;
5100 di->inode = *inode;
5101 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
5102 }
5103 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
5104 sizeof(struct block_el), "block element");
5105 blk_el->block = blk;
5106 blk_el->next = di->block_list;
5107 di->block_list = blk_el;
5108 di->num_dupblocks++;
5109}
5110
5111/*
5112 * Free a duplicate inode record
5113 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005114static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005115{
5116 struct dup_inode *di;
5117 struct block_el *p, *next;
5118
5119 di = (struct dup_inode *) dnode_get(node);
5120 for (p = di->block_list; p; p = next) {
5121 next = p->next;
5122 free(p);
5123 }
5124 free(node);
5125}
5126
5127/*
5128 * Free a duplicate block record
5129 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005130static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005131{
5132 struct dup_block *db;
5133 struct inode_el *p, *next;
5134
5135 db = (struct dup_block *) dnode_get(node);
5136 for (p = db->inode_list; p; p = next) {
5137 next = p->next;
5138 free(p);
5139 }
5140 free(node);
5141}
5142
5143
5144/*
5145 * Main procedure for handling duplicate blocks
5146 */
5147void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
5148{
5149 ext2_filsys fs = ctx->fs;
5150 struct problem_context pctx;
5151
5152 clear_problem_context(&pctx);
5153
5154 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
5155 _("multiply claimed inode map"), &inode_dup_map);
5156 if (pctx.errcode) {
5157 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
5158 ctx->flags |= E2F_FLAG_ABORT;
5159 return;
5160 }
5161
5162 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
5163 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005164 dict_set_allocator(&ino_dict, inode_dnode_free);
5165 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005166
5167 pass1b(ctx, block_buf);
5168 pass1c(ctx, block_buf);
5169 pass1d(ctx, block_buf);
5170
5171 /*
5172 * Time to free all of the accumulated data structures that we
5173 * don't need anymore.
5174 */
5175 dict_free_nodes(&ino_dict);
5176 dict_free_nodes(&blk_dict);
5177}
5178
5179/*
5180 * Scan the inodes looking for inodes that contain duplicate blocks.
5181 */
5182struct process_block_struct_1b {
5183 e2fsck_t ctx;
5184 ext2_ino_t ino;
5185 int dup_blocks;
5186 struct ext2_inode *inode;
5187 struct problem_context *pctx;
5188};
5189
5190static void pass1b(e2fsck_t ctx, char *block_buf)
5191{
5192 ext2_filsys fs = ctx->fs;
5193 ext2_ino_t ino;
5194 struct ext2_inode inode;
5195 ext2_inode_scan scan;
5196 struct process_block_struct_1b pb;
5197 struct problem_context pctx;
5198
5199 clear_problem_context(&pctx);
5200
5201 if (!(ctx->options & E2F_OPT_PREEN))
5202 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
5203 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
5204 &scan);
5205 if (pctx.errcode) {
5206 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
5207 ctx->flags |= E2F_FLAG_ABORT;
5208 return;
5209 }
5210 ctx->stashed_inode = &inode;
5211 pb.ctx = ctx;
5212 pb.pctx = &pctx;
5213 pctx.str = "pass1b";
5214 while (1) {
5215 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
5216 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
5217 continue;
5218 if (pctx.errcode) {
5219 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
5220 ctx->flags |= E2F_FLAG_ABORT;
5221 return;
5222 }
5223 if (!ino)
5224 break;
5225 pctx.ino = ctx->stashed_ino = ino;
5226 if ((ino != EXT2_BAD_INO) &&
5227 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
5228 continue;
5229
5230 pb.ino = ino;
5231 pb.dup_blocks = 0;
5232 pb.inode = &inode;
5233
5234 if (ext2fs_inode_has_valid_blocks(&inode) ||
5235 (ino == EXT2_BAD_INO))
5236 pctx.errcode = ext2fs_block_iterate2(fs, ino,
5237 0, block_buf, process_pass1b_block, &pb);
5238 if (inode.i_file_acl)
5239 process_pass1b_block(fs, &inode.i_file_acl,
5240 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5241 if (pb.dup_blocks) {
5242 end_problem_latch(ctx, PR_LATCH_DBLOCK);
5243 if (ino >= EXT2_FIRST_INODE(fs->super) ||
5244 ino == EXT2_ROOT_INO)
5245 dup_inode_count++;
5246 }
5247 if (pctx.errcode)
5248 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5249 }
5250 ext2fs_close_inode_scan(scan);
5251 e2fsck_use_inode_shortcuts(ctx, 0);
5252}
5253
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005254static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005255 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005256 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5257 blk_t ref_blk FSCK_ATTR((unused)),
5258 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005259 void *priv_data)
5260{
5261 struct process_block_struct_1b *p;
5262 e2fsck_t ctx;
5263
5264 if (HOLE_BLKADDR(*block_nr))
5265 return 0;
5266 p = (struct process_block_struct_1b *) priv_data;
5267 ctx = p->ctx;
5268
5269 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
5270 return 0;
5271
5272 /* OK, this is a duplicate block */
5273 if (p->ino != EXT2_BAD_INO) {
5274 p->pctx->blk = *block_nr;
5275 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
5276 }
5277 p->dup_blocks++;
5278 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
5279
5280 add_dupe(ctx, p->ino, *block_nr, p->inode);
5281
5282 return 0;
5283}
5284
5285/*
5286 * Pass 1c: Scan directories for inodes with duplicate blocks. This
5287 * is used so that we can print pathnames when prompting the user for
5288 * what to do.
5289 */
5290struct search_dir_struct {
5291 int count;
5292 ext2_ino_t first_inode;
5293 ext2_ino_t max_inode;
5294};
5295
5296static int search_dirent_proc(ext2_ino_t dir, int entry,
5297 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005298 int offset FSCK_ATTR((unused)),
5299 int blocksize FSCK_ATTR((unused)),
5300 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005301 void *priv_data)
5302{
5303 struct search_dir_struct *sd;
5304 struct dup_inode *p;
5305 dnode_t *n;
5306
5307 sd = (struct search_dir_struct *) priv_data;
5308
5309 if (dirent->inode > sd->max_inode)
5310 /* Should abort this inode, but not everything */
5311 return 0;
5312
5313 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
5314 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
5315 return 0;
5316
5317 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
5318 if (!n)
5319 return 0;
5320 p = (struct dup_inode *) dnode_get(n);
5321 p->dir = dir;
5322 sd->count--;
5323
5324 return(sd->count ? 0 : DIRENT_ABORT);
5325}
5326
5327
5328static void pass1c(e2fsck_t ctx, char *block_buf)
5329{
5330 ext2_filsys fs = ctx->fs;
5331 struct search_dir_struct sd;
5332 struct problem_context pctx;
5333
5334 clear_problem_context(&pctx);
5335
5336 if (!(ctx->options & E2F_OPT_PREEN))
5337 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
5338
5339 /*
5340 * Search through all directories to translate inodes to names
5341 * (by searching for the containing directory for that inode.)
5342 */
5343 sd.count = dup_inode_count;
5344 sd.first_inode = EXT2_FIRST_INODE(fs->super);
5345 sd.max_inode = fs->super->s_inodes_count;
5346 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
5347 search_dirent_proc, &sd);
5348}
5349
5350static void pass1d(e2fsck_t ctx, char *block_buf)
5351{
5352 ext2_filsys fs = ctx->fs;
5353 struct dup_inode *p, *t;
5354 struct dup_block *q;
5355 ext2_ino_t *shared, ino;
5356 int shared_len;
5357 int i;
5358 int file_ok;
5359 int meta_data = 0;
5360 struct problem_context pctx;
5361 dnode_t *n, *m;
5362 struct block_el *s;
5363 struct inode_el *r;
5364
5365 clear_problem_context(&pctx);
5366
5367 if (!(ctx->options & E2F_OPT_PREEN))
5368 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
5369 e2fsck_read_bitmaps(ctx);
5370
5371 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
5372 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
5373 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
5374 sizeof(ext2_ino_t) * dict_count(&ino_dict),
5375 "Shared inode list");
5376 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
5377 p = (struct dup_inode *) dnode_get(n);
5378 shared_len = 0;
5379 file_ok = 1;
5380 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00005381 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005382 continue;
5383
5384 /*
5385 * Find all of the inodes which share blocks with this
5386 * one. First we find all of the duplicate blocks
5387 * belonging to this inode, and then search each block
5388 * get the list of inodes, and merge them together.
5389 */
5390 for (s = p->block_list; s; s = s->next) {
5391 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
5392 if (!m)
5393 continue; /* Should never happen... */
5394 q = (struct dup_block *) dnode_get(m);
5395 if (q->num_bad > 1)
5396 file_ok = 0;
5397 if (check_if_fs_block(ctx, s->block)) {
5398 file_ok = 0;
5399 meta_data = 1;
5400 }
5401
5402 /*
5403 * Add all inodes used by this block to the
5404 * shared[] --- which is a unique list, so
5405 * if an inode is already in shared[], don't
5406 * add it again.
5407 */
5408 for (r = q->inode_list; r; r = r->next) {
5409 if (r->inode == ino)
5410 continue;
5411 for (i = 0; i < shared_len; i++)
5412 if (shared[i] == r->inode)
5413 break;
5414 if (i == shared_len) {
5415 shared[shared_len++] = r->inode;
5416 }
5417 }
5418 }
5419
5420 /*
5421 * Report the inode that we are working on
5422 */
5423 pctx.inode = &p->inode;
5424 pctx.ino = ino;
5425 pctx.dir = p->dir;
5426 pctx.blkcount = p->num_dupblocks;
5427 pctx.num = meta_data ? shared_len+1 : shared_len;
5428 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
5429 pctx.blkcount = 0;
5430 pctx.num = 0;
5431
5432 if (meta_data)
5433 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
5434
5435 for (i = 0; i < shared_len; i++) {
5436 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
5437 if (!m)
5438 continue; /* should never happen */
5439 t = (struct dup_inode *) dnode_get(m);
5440 /*
5441 * Report the inode that we are sharing with
5442 */
5443 pctx.inode = &t->inode;
5444 pctx.ino = shared[i];
5445 pctx.dir = t->dir;
5446 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
5447 }
5448 if (file_ok) {
5449 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
5450 continue;
5451 }
5452 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
5453 pctx.errcode = clone_file(ctx, ino, p, block_buf);
5454 if (pctx.errcode)
5455 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
5456 else
5457 continue;
5458 }
5459 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
5460 delete_file(ctx, ino, p, block_buf);
5461 else
5462 ext2fs_unmark_valid(fs);
5463 }
5464 ext2fs_free_mem(&shared);
5465}
5466
5467/*
5468 * Drop the refcount on the dup_block structure, and clear the entry
5469 * in the block_dup_map if appropriate.
5470 */
5471static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
5472{
5473 p->num_bad--;
5474 if (p->num_bad <= 0 ||
5475 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
5476 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
5477}
5478
5479static int delete_file_block(ext2_filsys fs,
5480 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005481 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5482 blk_t ref_block FSCK_ATTR((unused)),
5483 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005484 void *priv_data)
5485{
5486 struct process_block_struct_1b *pb;
5487 struct dup_block *p;
5488 dnode_t *n;
5489 e2fsck_t ctx;
5490
5491 pb = (struct process_block_struct_1b *) priv_data;
5492 ctx = pb->ctx;
5493
5494 if (HOLE_BLKADDR(*block_nr))
5495 return 0;
5496
5497 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5498 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5499 if (n) {
5500 p = (struct dup_block *) dnode_get(n);
5501 decrement_badcount(ctx, *block_nr, p);
5502 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005503 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005504 *block_nr);
5505 } else {
5506 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
5507 ext2fs_block_alloc_stats(fs, *block_nr, -1);
5508 }
5509
5510 return 0;
5511}
5512
5513static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5514 struct dup_inode *dp, char* block_buf)
5515{
5516 ext2_filsys fs = ctx->fs;
5517 struct process_block_struct_1b pb;
5518 struct ext2_inode inode;
5519 struct problem_context pctx;
5520 unsigned int count;
5521
5522 clear_problem_context(&pctx);
5523 pctx.ino = pb.ino = ino;
5524 pb.dup_blocks = dp->num_dupblocks;
5525 pb.ctx = ctx;
5526 pctx.str = "delete_file";
5527
5528 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5529 if (ext2fs_inode_has_valid_blocks(&inode))
5530 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5531 delete_file_block, &pb);
5532 if (pctx.errcode)
5533 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5534 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5535 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5536 if (ctx->inode_bad_map)
5537 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
5538 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
5539
5540 /* Inode may have changed by block_iterate, so reread it */
5541 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5542 inode.i_links_count = 0;
5543 inode.i_dtime = time(0);
5544 if (inode.i_file_acl &&
5545 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
5546 count = 1;
5547 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
5548 block_buf, -1, &count);
5549 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
5550 pctx.errcode = 0;
5551 count = 1;
5552 }
5553 if (pctx.errcode) {
5554 pctx.blk = inode.i_file_acl;
5555 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
5556 }
5557 /*
5558 * If the count is zero, then arrange to have the
5559 * block deleted. If the block is in the block_dup_map,
5560 * also call delete_file_block since it will take care
5561 * of keeping the accounting straight.
5562 */
5563 if ((count == 0) ||
5564 ext2fs_test_block_bitmap(ctx->block_dup_map,
5565 inode.i_file_acl))
5566 delete_file_block(fs, &inode.i_file_acl,
5567 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5568 }
5569 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
5570}
5571
5572struct clone_struct {
5573 errcode_t errcode;
5574 ext2_ino_t dir;
5575 char *buf;
5576 e2fsck_t ctx;
5577};
5578
5579static int clone_file_block(ext2_filsys fs,
5580 blk_t *block_nr,
5581 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005582 blk_t ref_block FSCK_ATTR((unused)),
5583 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005584 void *priv_data)
5585{
5586 struct dup_block *p;
5587 blk_t new_block;
5588 errcode_t retval;
5589 struct clone_struct *cs = (struct clone_struct *) priv_data;
5590 dnode_t *n;
5591 e2fsck_t ctx;
5592
5593 ctx = cs->ctx;
5594
5595 if (HOLE_BLKADDR(*block_nr))
5596 return 0;
5597
5598 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5599 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5600 if (n) {
5601 p = (struct dup_block *) dnode_get(n);
5602 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
5603 &new_block);
5604 if (retval) {
5605 cs->errcode = retval;
5606 return BLOCK_ABORT;
5607 }
5608 if (cs->dir && (blockcnt >= 0)) {
5609 retval = ext2fs_set_dir_block(fs->dblist,
5610 cs->dir, new_block, blockcnt);
5611 if (retval) {
5612 cs->errcode = retval;
5613 return BLOCK_ABORT;
5614 }
5615 }
Rob Landley3e72c592006-04-06 22:49:04 +00005616
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005617 retval = io_channel_read_blk(fs->io, *block_nr, 1,
5618 cs->buf);
5619 if (retval) {
5620 cs->errcode = retval;
5621 return BLOCK_ABORT;
5622 }
5623 retval = io_channel_write_blk(fs->io, new_block, 1,
5624 cs->buf);
5625 if (retval) {
5626 cs->errcode = retval;
5627 return BLOCK_ABORT;
5628 }
5629 decrement_badcount(ctx, *block_nr, p);
5630 *block_nr = new_block;
5631 ext2fs_mark_block_bitmap(ctx->block_found_map,
5632 new_block);
5633 ext2fs_mark_block_bitmap(fs->block_map, new_block);
5634 return BLOCK_CHANGED;
5635 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005636 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005637 *block_nr);
5638 }
5639 return 0;
5640}
5641
5642static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5643 struct dup_inode *dp, char* block_buf)
5644{
5645 ext2_filsys fs = ctx->fs;
5646 errcode_t retval;
5647 struct clone_struct cs;
5648 struct problem_context pctx;
5649 blk_t blk;
5650 dnode_t *n;
5651 struct inode_el *ino_el;
5652 struct dup_block *db;
5653 struct dup_inode *di;
5654
5655 clear_problem_context(&pctx);
5656 cs.errcode = 0;
5657 cs.dir = 0;
5658 cs.ctx = ctx;
5659 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
5660 if (retval)
5661 return retval;
5662
5663 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
5664 cs.dir = ino;
5665
5666 pctx.ino = ino;
5667 pctx.str = "clone_file";
5668 if (ext2fs_inode_has_valid_blocks(&dp->inode))
5669 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5670 clone_file_block, &cs);
5671 ext2fs_mark_bb_dirty(fs);
5672 if (pctx.errcode) {
5673 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5674 retval = pctx.errcode;
5675 goto errout;
5676 }
5677 if (cs.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +00005678 bb_error_msg(_("returned from clone_file_block"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005679 retval = cs.errcode;
5680 goto errout;
5681 }
5682 /* The inode may have changed on disk, so we have to re-read it */
5683 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
5684 blk = dp->inode.i_file_acl;
5685 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
5686 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
5687 BLOCK_CHANGED)) {
5688 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
5689 /*
5690 * If we cloned the EA block, find all other inodes
5691 * which refered to that EA block, and modify
5692 * them to point to the new EA block.
5693 */
5694 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5695 db = (struct dup_block *) dnode_get(n);
5696 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
5697 if (ino_el->inode == ino)
5698 continue;
5699 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
5700 di = (struct dup_inode *) dnode_get(n);
5701 if (di->inode.i_file_acl == blk) {
5702 di->inode.i_file_acl = dp->inode.i_file_acl;
5703 e2fsck_write_inode(ctx, ino_el->inode,
5704 &di->inode, "clone file EA");
5705 decrement_badcount(ctx, blk, db);
5706 }
5707 }
5708 }
5709 retval = 0;
5710errout:
5711 ext2fs_free_mem(&cs.buf);
5712 return retval;
5713}
5714
5715/*
5716 * This routine returns 1 if a block overlaps with one of the superblocks,
5717 * group descriptors, inode bitmaps, or block bitmaps.
5718 */
5719static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
5720{
5721 ext2_filsys fs = ctx->fs;
5722 blk_t block;
5723 dgrp_t i;
5724
5725 block = fs->super->s_first_data_block;
5726 for (i = 0; i < fs->group_desc_count; i++) {
5727
5728 /* Check superblocks/block group descriptros */
5729 if (ext2fs_bg_has_super(fs, i)) {
5730 if (test_block >= block &&
5731 (test_block <= block + fs->desc_blocks))
5732 return 1;
5733 }
5734
5735 /* Check the inode table */
5736 if ((fs->group_desc[i].bg_inode_table) &&
5737 (test_block >= fs->group_desc[i].bg_inode_table) &&
5738 (test_block < (fs->group_desc[i].bg_inode_table +
5739 fs->inode_blocks_per_group)))
5740 return 1;
5741
5742 /* Check the bitmap blocks */
5743 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
5744 (test_block == fs->group_desc[i].bg_inode_bitmap))
5745 return 1;
5746
5747 block += fs->super->s_blocks_per_group;
5748 }
5749 return 0;
5750}
5751/*
5752 * pass2.c --- check directory structure
5753 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005754 * Pass 2 of e2fsck iterates through all active directory inodes, and
5755 * applies to following tests to each directory entry in the directory
5756 * blocks in the inodes:
5757 *
5758 * - The length of the directory entry (rec_len) should be at
5759 * least 8 bytes, and no more than the remaining space
5760 * left in the directory block.
5761 * - The length of the name in the directory entry (name_len)
5762 * should be less than (rec_len - 8).
5763 * - The inode number in the directory entry should be within
5764 * legal bounds.
5765 * - The inode number should refer to a in-use inode.
5766 * - The first entry should be '.', and its inode should be
5767 * the inode of the directory.
5768 * - The second entry should be '..'.
5769 *
5770 * To minimize disk seek time, the directory blocks are processed in
5771 * sorted order of block numbers.
5772 *
5773 * Pass 2 also collects the following information:
5774 * - The inode numbers of the subdirectories for each directory.
5775 *
5776 * Pass 2 relies on the following information from previous passes:
5777 * - The directory information collected in pass 1.
5778 * - The inode_used_map bitmap
5779 * - The inode_bad_map bitmap
5780 * - The inode_dir_map bitmap
5781 *
5782 * Pass 2 frees the following data structures
5783 * - The inode_bad_map bitmap
5784 * - The inode_reg_map bitmap
5785 */
5786
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005787/*
5788 * Keeps track of how many times an inode is referenced.
5789 */
5790static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
5791static int check_dir_block(ext2_filsys fs,
5792 struct ext2_db_entry *dir_blocks_info,
5793 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005794static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
5795 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005796static int update_dir_block(ext2_filsys fs,
5797 blk_t *block_nr,
5798 e2_blkcnt_t blockcnt,
5799 blk_t ref_block,
5800 int ref_offset,
5801 void *priv_data);
5802static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
5803static int htree_depth(struct dx_dir_info *dx_dir,
5804 struct dx_dirblock_info *dx_db);
Rob Landley7c94bed2006-05-03 21:58:45 +00005805static int special_dir_block_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005806
5807struct check_dir_struct {
5808 char *buf;
5809 struct problem_context pctx;
5810 int count, max;
5811 e2fsck_t ctx;
5812};
5813
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005814static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005815{
5816 struct ext2_super_block *sb = ctx->fs->super;
5817 struct problem_context pctx;
5818 ext2_filsys fs = ctx->fs;
5819 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005820 struct dir_info *dir;
5821 struct check_dir_struct cd;
5822 struct dx_dir_info *dx_dir;
5823 struct dx_dirblock_info *dx_db, *dx_parent;
5824 int b;
5825 int i, depth;
5826 problem_t code;
5827 int bad_dir;
5828
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005829 clear_problem_context(&cd.pctx);
5830
Rob Landley3e72c592006-04-06 22:49:04 +00005831 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005832
5833 if (!(ctx->options & E2F_OPT_PREEN))
5834 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
5835
5836 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
5837 0, ctx->inode_link_info,
5838 &ctx->inode_count);
5839 if (cd.pctx.errcode) {
5840 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
5841 ctx->flags |= E2F_FLAG_ABORT;
5842 return;
5843 }
5844 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
5845 "directory scan buffer");
5846
5847 /*
5848 * Set up the parent pointer for the root directory, if
5849 * present. (If the root directory is not present, we will
5850 * create it in pass 3.)
5851 */
5852 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
5853 if (dir)
5854 dir->parent = EXT2_ROOT_INO;
5855
5856 cd.buf = buf;
5857 cd.ctx = ctx;
5858 cd.count = 1;
5859 cd.max = ext2fs_dblist_count(fs->dblist);
5860
5861 if (ctx->progress)
5862 (void) (ctx->progress)(ctx, 2, 0, cd.max);
5863
5864 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
5865 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
5866
5867 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
5868 &cd);
5869 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5870 return;
5871 if (cd.pctx.errcode) {
5872 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
5873 ctx->flags |= E2F_FLAG_ABORT;
5874 return;
5875 }
5876
5877#ifdef ENABLE_HTREE
5878 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
5879 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5880 return;
5881 if (dx_dir->numblocks == 0)
5882 continue;
5883 clear_problem_context(&pctx);
5884 bad_dir = 0;
5885 pctx.dir = dx_dir->ino;
5886 dx_db = dx_dir->dx_block;
5887 if (dx_db->flags & DX_FLAG_REFERENCED)
5888 dx_db->flags |= DX_FLAG_DUP_REF;
5889 else
5890 dx_db->flags |= DX_FLAG_REFERENCED;
5891 /*
5892 * Find all of the first and last leaf blocks, and
5893 * update their parent's min and max hash values
5894 */
5895 for (b=0, dx_db = dx_dir->dx_block;
5896 b < dx_dir->numblocks;
5897 b++, dx_db++) {
5898 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
5899 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
5900 continue;
5901 dx_parent = &dx_dir->dx_block[dx_db->parent];
5902 /*
5903 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
5904 */
5905 if (dx_db->flags & DX_FLAG_FIRST)
5906 dx_parent->min_hash = dx_db->min_hash;
5907 /*
5908 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
5909 */
5910 if (dx_db->flags & DX_FLAG_LAST)
5911 dx_parent->max_hash = dx_db->max_hash;
5912 }
5913
5914 for (b=0, dx_db = dx_dir->dx_block;
5915 b < dx_dir->numblocks;
5916 b++, dx_db++) {
5917 pctx.blkcount = b;
5918 pctx.group = dx_db->parent;
5919 code = 0;
5920 if (!(dx_db->flags & DX_FLAG_FIRST) &&
5921 (dx_db->min_hash < dx_db->node_min_hash)) {
5922 pctx.blk = dx_db->min_hash;
5923 pctx.blk2 = dx_db->node_min_hash;
5924 code = PR_2_HTREE_MIN_HASH;
5925 fix_problem(ctx, code, &pctx);
5926 bad_dir++;
5927 }
5928 if (dx_db->type == DX_DIRBLOCK_LEAF) {
5929 depth = htree_depth(dx_dir, dx_db);
5930 if (depth != dx_dir->depth) {
5931 code = PR_2_HTREE_BAD_DEPTH;
5932 fix_problem(ctx, code, &pctx);
5933 bad_dir++;
5934 }
5935 }
5936 /*
5937 * This test doesn't apply for the root block
5938 * at block #0
5939 */
5940 if (b &&
5941 (dx_db->max_hash > dx_db->node_max_hash)) {
5942 pctx.blk = dx_db->max_hash;
5943 pctx.blk2 = dx_db->node_max_hash;
5944 code = PR_2_HTREE_MAX_HASH;
5945 fix_problem(ctx, code, &pctx);
5946 bad_dir++;
5947 }
5948 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
5949 code = PR_2_HTREE_NOTREF;
5950 fix_problem(ctx, code, &pctx);
5951 bad_dir++;
5952 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
5953 code = PR_2_HTREE_DUPREF;
5954 fix_problem(ctx, code, &pctx);
5955 bad_dir++;
5956 }
5957 if (code == 0)
5958 continue;
5959 }
5960 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
5961 clear_htree(ctx, dx_dir->ino);
5962 dx_dir->numblocks = 0;
5963 }
5964 }
5965#endif
5966 ext2fs_free_mem(&buf);
5967 ext2fs_free_dblist(fs->dblist);
5968
Rob Landleye7c43b62006-03-01 16:39:45 +00005969 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
5970 ctx->inode_bad_map = 0;
5971 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
5972 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005973
5974 clear_problem_context(&pctx);
5975 if (ctx->large_files) {
5976 if (!(sb->s_feature_ro_compat &
5977 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
5978 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
5979 sb->s_feature_ro_compat |=
5980 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5981 ext2fs_mark_super_dirty(fs);
5982 }
5983 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
5984 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
5985 ext2fs_update_dynamic_rev(fs);
5986 ext2fs_mark_super_dirty(fs);
5987 }
5988 } else if (!ctx->large_files &&
5989 (sb->s_feature_ro_compat &
5990 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
5991 if (fs->flags & EXT2_FLAG_RW) {
5992 sb->s_feature_ro_compat &=
5993 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5994 ext2fs_mark_super_dirty(fs);
5995 }
5996 }
5997
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005998}
5999
6000#define MAX_DEPTH 32000
6001static int htree_depth(struct dx_dir_info *dx_dir,
6002 struct dx_dirblock_info *dx_db)
6003{
6004 int depth = 0;
6005
6006 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
6007 dx_db = &dx_dir->dx_block[dx_db->parent];
6008 depth++;
6009 }
6010 return depth;
6011}
6012
6013static int dict_de_cmp(const void *a, const void *b)
6014{
6015 const struct ext2_dir_entry *de_a, *de_b;
6016 int a_len, b_len;
6017
6018 de_a = (const struct ext2_dir_entry *) a;
6019 a_len = de_a->name_len & 0xFF;
6020 de_b = (const struct ext2_dir_entry *) b;
6021 b_len = de_b->name_len & 0xFF;
6022
6023 if (a_len != b_len)
6024 return (a_len - b_len);
6025
6026 return strncmp(de_a->name, de_b->name, a_len);
6027}
6028
6029/*
6030 * This is special sort function that makes sure that directory blocks
6031 * with a dirblock of zero are sorted to the beginning of the list.
6032 * This guarantees that the root node of the htree directories are
6033 * processed first, so we know what hash version to use.
6034 */
Rob Landley7c94bed2006-05-03 21:58:45 +00006035static int special_dir_block_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006036{
6037 const struct ext2_db_entry *db_a =
6038 (const struct ext2_db_entry *) a;
6039 const struct ext2_db_entry *db_b =
6040 (const struct ext2_db_entry *) b;
6041
6042 if (db_a->blockcnt && !db_b->blockcnt)
6043 return 1;
6044
6045 if (!db_a->blockcnt && db_b->blockcnt)
6046 return -1;
6047
6048 if (db_a->blk != db_b->blk)
6049 return (int) (db_a->blk - db_b->blk);
6050
6051 if (db_a->ino != db_b->ino)
6052 return (int) (db_a->ino - db_b->ino);
6053
6054 return (int) (db_a->blockcnt - db_b->blockcnt);
6055}
6056
6057
6058/*
6059 * Make sure the first entry in the directory is '.', and that the
6060 * directory entry is sane.
6061 */
6062static int check_dot(e2fsck_t ctx,
6063 struct ext2_dir_entry *dirent,
6064 ext2_ino_t ino, struct problem_context *pctx)
6065{
6066 struct ext2_dir_entry *nextdir;
6067 int status = 0;
6068 int created = 0;
6069 int new_len;
6070 int problem = 0;
6071
6072 if (!dirent->inode)
6073 problem = PR_2_MISSING_DOT;
6074 else if (((dirent->name_len & 0xFF) != 1) ||
6075 (dirent->name[0] != '.'))
6076 problem = PR_2_1ST_NOT_DOT;
6077 else if (dirent->name[1] != '\0')
6078 problem = PR_2_DOT_NULL_TERM;
6079
6080 if (problem) {
6081 if (fix_problem(ctx, problem, pctx)) {
6082 if (dirent->rec_len < 12)
6083 dirent->rec_len = 12;
6084 dirent->inode = ino;
6085 dirent->name_len = 1;
6086 dirent->name[0] = '.';
6087 dirent->name[1] = '\0';
6088 status = 1;
6089 created = 1;
6090 }
6091 }
6092 if (dirent->inode != ino) {
6093 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
6094 dirent->inode = ino;
6095 status = 1;
6096 }
6097 }
6098 if (dirent->rec_len > 12) {
6099 new_len = dirent->rec_len - 12;
6100 if (new_len > 12) {
6101 if (created ||
6102 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
6103 nextdir = (struct ext2_dir_entry *)
6104 ((char *) dirent + 12);
6105 dirent->rec_len = 12;
6106 nextdir->rec_len = new_len;
6107 nextdir->inode = 0;
6108 nextdir->name_len = 0;
6109 status = 1;
6110 }
6111 }
6112 }
6113 return status;
6114}
6115
6116/*
6117 * Make sure the second entry in the directory is '..', and that the
6118 * directory entry is sane. We do not check the inode number of '..'
6119 * here; this gets done in pass 3.
6120 */
6121static int check_dotdot(e2fsck_t ctx,
6122 struct ext2_dir_entry *dirent,
6123 struct dir_info *dir, struct problem_context *pctx)
6124{
6125 int problem = 0;
6126
6127 if (!dirent->inode)
6128 problem = PR_2_MISSING_DOT_DOT;
6129 else if (((dirent->name_len & 0xFF) != 2) ||
6130 (dirent->name[0] != '.') ||
6131 (dirent->name[1] != '.'))
6132 problem = PR_2_2ND_NOT_DOT_DOT;
6133 else if (dirent->name[2] != '\0')
6134 problem = PR_2_DOT_DOT_NULL_TERM;
6135
6136 if (problem) {
6137 if (fix_problem(ctx, problem, pctx)) {
6138 if (dirent->rec_len < 12)
6139 dirent->rec_len = 12;
6140 /*
6141 * Note: we don't have the parent inode just
6142 * yet, so we will fill it in with the root
6143 * inode. This will get fixed in pass 3.
6144 */
6145 dirent->inode = EXT2_ROOT_INO;
6146 dirent->name_len = 2;
6147 dirent->name[0] = '.';
6148 dirent->name[1] = '.';
6149 dirent->name[2] = '\0';
6150 return 1;
6151 }
6152 return 0;
6153 }
6154 dir->dotdot = dirent->inode;
6155 return 0;
6156}
6157
6158/*
6159 * Check to make sure a directory entry doesn't contain any illegal
6160 * characters.
6161 */
6162static int check_name(e2fsck_t ctx,
6163 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006164 struct problem_context *pctx)
6165{
6166 int i;
6167 int fixup = -1;
6168 int ret = 0;
6169
6170 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
6171 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
6172 if (fixup < 0) {
6173 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
6174 }
6175 if (fixup) {
6176 dirent->name[i] = '.';
6177 ret = 1;
6178 }
6179 }
6180 }
6181 return ret;
6182}
6183
6184/*
6185 * Check the directory filetype (if present)
6186 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006187
6188/*
6189 * Given a mode, return the ext2 file type
6190 */
6191static int ext2_file_type(unsigned int mode)
6192{
6193 if (LINUX_S_ISREG(mode))
6194 return EXT2_FT_REG_FILE;
6195
6196 if (LINUX_S_ISDIR(mode))
6197 return EXT2_FT_DIR;
6198
6199 if (LINUX_S_ISCHR(mode))
6200 return EXT2_FT_CHRDEV;
6201
6202 if (LINUX_S_ISBLK(mode))
6203 return EXT2_FT_BLKDEV;
6204
6205 if (LINUX_S_ISLNK(mode))
6206 return EXT2_FT_SYMLINK;
6207
6208 if (LINUX_S_ISFIFO(mode))
6209 return EXT2_FT_FIFO;
6210
6211 if (LINUX_S_ISSOCK(mode))
6212 return EXT2_FT_SOCK;
6213
6214 return 0;
6215}
6216
Rob Landley7c94bed2006-05-03 21:58:45 +00006217static int check_filetype(e2fsck_t ctx,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006218 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006219 struct problem_context *pctx)
6220{
6221 int filetype = dirent->name_len >> 8;
6222 int should_be = EXT2_FT_UNKNOWN;
6223 struct ext2_inode inode;
6224
6225 if (!(ctx->fs->super->s_feature_incompat &
6226 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
6227 if (filetype == 0 ||
6228 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
6229 return 0;
6230 dirent->name_len = dirent->name_len & 0xFF;
6231 return 1;
6232 }
6233
6234 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
6235 should_be = EXT2_FT_DIR;
6236 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
6237 dirent->inode)) {
6238 should_be = EXT2_FT_REG_FILE;
6239 } else if (ctx->inode_bad_map &&
6240 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6241 dirent->inode))
6242 should_be = 0;
6243 else {
6244 e2fsck_read_inode(ctx, dirent->inode, &inode,
6245 "check_filetype");
6246 should_be = ext2_file_type(inode.i_mode);
6247 }
6248 if (filetype == should_be)
6249 return 0;
6250 pctx->num = should_be;
6251
6252 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
6253 pctx) == 0)
6254 return 0;
6255
6256 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
6257 return 1;
6258}
6259
6260#ifdef ENABLE_HTREE
6261static void parse_int_node(ext2_filsys fs,
6262 struct ext2_db_entry *db,
6263 struct check_dir_struct *cd,
6264 struct dx_dir_info *dx_dir,
6265 char *block_buf)
6266{
6267 struct ext2_dx_root_info *root;
6268 struct ext2_dx_entry *ent;
6269 struct ext2_dx_countlimit *limit;
6270 struct dx_dirblock_info *dx_db;
6271 int i, expect_limit, count;
6272 blk_t blk;
6273 ext2_dirhash_t min_hash = 0xffffffff;
6274 ext2_dirhash_t max_hash = 0;
6275 ext2_dirhash_t hash = 0, prev_hash;
6276
6277 if (db->blockcnt == 0) {
6278 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006279 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
6280 } else {
6281 ent = (struct ext2_dx_entry *) (block_buf+8);
6282 }
6283 limit = (struct ext2_dx_countlimit *) ent;
6284
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006285 count = ext2fs_le16_to_cpu(limit->count);
6286 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
6287 sizeof(struct ext2_dx_entry);
6288 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
6289 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
6290 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
6291 goto clear_and_exit;
6292 }
6293 if (count > expect_limit) {
6294 cd->pctx.num = count;
6295 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
6296 goto clear_and_exit;
6297 count = expect_limit;
6298 }
6299
6300 for (i=0; i < count; i++) {
6301 prev_hash = hash;
6302 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006303 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
6304 /* Check to make sure the block is valid */
6305 if (blk > (blk_t) dx_dir->numblocks) {
6306 cd->pctx.blk = blk;
6307 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
6308 &cd->pctx))
6309 goto clear_and_exit;
6310 }
6311 if (hash < prev_hash &&
6312 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
6313 goto clear_and_exit;
6314 dx_db = &dx_dir->dx_block[blk];
6315 if (dx_db->flags & DX_FLAG_REFERENCED) {
6316 dx_db->flags |= DX_FLAG_DUP_REF;
6317 } else {
6318 dx_db->flags |= DX_FLAG_REFERENCED;
6319 dx_db->parent = db->blockcnt;
6320 }
6321 if (hash < min_hash)
6322 min_hash = hash;
6323 if (hash > max_hash)
6324 max_hash = hash;
6325 dx_db->node_min_hash = hash;
6326 if ((i+1) < count)
6327 dx_db->node_max_hash =
6328 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
6329 else {
6330 dx_db->node_max_hash = 0xfffffffe;
6331 dx_db->flags |= DX_FLAG_LAST;
6332 }
6333 if (i == 0)
6334 dx_db->flags |= DX_FLAG_FIRST;
6335 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006336 dx_db = &dx_dir->dx_block[db->blockcnt];
6337 dx_db->min_hash = min_hash;
6338 dx_db->max_hash = max_hash;
6339 return;
6340
6341clear_and_exit:
6342 clear_htree(cd->ctx, cd->pctx.ino);
6343 dx_dir->numblocks = 0;
6344}
6345#endif /* ENABLE_HTREE */
6346
6347/*
6348 * Given a busted directory, try to salvage it somehow.
6349 *
6350 */
6351static void salvage_directory(ext2_filsys fs,
6352 struct ext2_dir_entry *dirent,
6353 struct ext2_dir_entry *prev,
6354 unsigned int *offset)
6355{
6356 char *cp = (char *) dirent;
6357 int left = fs->blocksize - *offset - dirent->rec_len;
6358 int name_len = dirent->name_len & 0xFF;
6359
6360 /*
6361 * Special case of directory entry of size 8: copy what's left
6362 * of the directory block up to cover up the invalid hole.
6363 */
6364 if ((left >= 12) && (dirent->rec_len == 8)) {
6365 memmove(cp, cp+8, left);
6366 memset(cp + left, 0, 8);
6367 return;
6368 }
6369 /*
6370 * If the directory entry overruns the end of the directory
6371 * block, and the name is small enough to fit, then adjust the
6372 * record length.
6373 */
6374 if ((left < 0) &&
6375 (name_len + 8 <= dirent->rec_len + left) &&
6376 dirent->inode <= fs->super->s_inodes_count &&
6377 strnlen(dirent->name, name_len) == name_len) {
6378 dirent->rec_len += left;
6379 return;
6380 }
6381 /*
6382 * If the directory entry is a multiple of four, so it is
6383 * valid, let the previous directory entry absorb the invalid
6384 * one.
6385 */
6386 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
6387 prev->rec_len += dirent->rec_len;
6388 *offset += dirent->rec_len;
6389 return;
6390 }
6391 /*
6392 * Default salvage method --- kill all of the directory
6393 * entries for the rest of the block. We will either try to
6394 * absorb it into the previous directory entry, or create a
6395 * new empty directory entry the rest of the directory block.
6396 */
6397 if (prev) {
6398 prev->rec_len += fs->blocksize - *offset;
6399 *offset = fs->blocksize;
6400 } else {
6401 dirent->rec_len = fs->blocksize - *offset;
6402 dirent->name_len = 0;
6403 dirent->inode = 0;
6404 }
6405}
6406
6407static int check_dir_block(ext2_filsys fs,
6408 struct ext2_db_entry *db,
6409 void *priv_data)
6410{
6411 struct dir_info *subdir, *dir;
6412 struct dx_dir_info *dx_dir;
6413#ifdef ENABLE_HTREE
6414 struct dx_dirblock_info *dx_db = 0;
6415#endif /* ENABLE_HTREE */
6416 struct ext2_dir_entry *dirent, *prev;
6417 ext2_dirhash_t hash;
6418 unsigned int offset = 0;
6419 int dir_modified = 0;
6420 int dot_state;
6421 blk_t block_nr = db->blk;
6422 ext2_ino_t ino = db->ino;
6423 __u16 links;
6424 struct check_dir_struct *cd;
6425 char *buf;
6426 e2fsck_t ctx;
6427 int problem;
6428 struct ext2_dx_root_info *root;
6429 struct ext2_dx_countlimit *limit;
6430 static dict_t de_dict;
6431 struct problem_context pctx;
6432 int dups_found = 0;
6433
6434 cd = (struct check_dir_struct *) priv_data;
6435 buf = cd->buf;
6436 ctx = cd->ctx;
6437
6438 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6439 return DIRENT_ABORT;
6440
6441 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
6442 return DIRENT_ABORT;
6443
6444 /*
6445 * Make sure the inode is still in use (could have been
6446 * deleted in the duplicate/bad blocks pass.
6447 */
6448 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
6449 return 0;
6450
6451 cd->pctx.ino = ino;
6452 cd->pctx.blk = block_nr;
6453 cd->pctx.blkcount = db->blockcnt;
6454 cd->pctx.ino2 = 0;
6455 cd->pctx.dirent = 0;
6456 cd->pctx.num = 0;
6457
6458 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006459 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006460 return 0;
6461 block_nr = db->blk;
6462 }
6463
6464 if (db->blockcnt)
6465 dot_state = 2;
6466 else
6467 dot_state = 0;
6468
6469 if (ctx->dirs_to_hash &&
6470 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
6471 dups_found++;
6472
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006473 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
6474 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
6475 cd->pctx.errcode = 0; /* We'll handle this ourselves */
6476 if (cd->pctx.errcode) {
6477 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
6478 ctx->flags |= E2F_FLAG_ABORT;
6479 return DIRENT_ABORT;
6480 }
6481 memset(buf, 0, fs->blocksize);
6482 }
6483#ifdef ENABLE_HTREE
6484 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
6485 if (dx_dir && dx_dir->numblocks) {
6486 if (db->blockcnt >= dx_dir->numblocks) {
6487 printf("XXX should never happen!!!\n");
6488 abort();
6489 }
6490 dx_db = &dx_dir->dx_block[db->blockcnt];
6491 dx_db->type = DX_DIRBLOCK_LEAF;
6492 dx_db->phys = block_nr;
6493 dx_db->min_hash = ~0;
6494 dx_db->max_hash = 0;
6495
6496 dirent = (struct ext2_dir_entry *) buf;
6497 limit = (struct ext2_dx_countlimit *) (buf+8);
6498 if (db->blockcnt == 0) {
6499 root = (struct ext2_dx_root_info *) (buf + 24);
6500 dx_db->type = DX_DIRBLOCK_ROOT;
6501 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
6502 if ((root->reserved_zero ||
6503 root->info_length < 8 ||
6504 root->indirect_levels > 1) &&
6505 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
6506 clear_htree(ctx, ino);
6507 dx_dir->numblocks = 0;
6508 dx_db = 0;
6509 }
6510 dx_dir->hashversion = root->hash_version;
6511 dx_dir->depth = root->indirect_levels + 1;
6512 } else if ((dirent->inode == 0) &&
6513 (dirent->rec_len == fs->blocksize) &&
6514 (dirent->name_len == 0) &&
6515 (ext2fs_le16_to_cpu(limit->limit) ==
6516 ((fs->blocksize-8) /
6517 sizeof(struct ext2_dx_entry))))
6518 dx_db->type = DX_DIRBLOCK_NODE;
6519 }
6520#endif /* ENABLE_HTREE */
6521
6522 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
6523 prev = 0;
6524 do {
6525 problem = 0;
6526 dirent = (struct ext2_dir_entry *) (buf + offset);
6527 cd->pctx.dirent = dirent;
6528 cd->pctx.num = offset;
6529 if (((offset + dirent->rec_len) > fs->blocksize) ||
6530 (dirent->rec_len < 12) ||
6531 ((dirent->rec_len % 4) != 0) ||
6532 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
6533 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
6534 salvage_directory(fs, dirent, prev, &offset);
6535 dir_modified++;
6536 continue;
6537 } else
6538 goto abort_free_dict;
6539 }
6540 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
6541 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
6542 dirent->name_len = EXT2_NAME_LEN;
6543 dir_modified++;
6544 }
6545 }
6546
6547 if (dot_state == 0) {
6548 if (check_dot(ctx, dirent, ino, &cd->pctx))
6549 dir_modified++;
6550 } else if (dot_state == 1) {
6551 dir = e2fsck_get_dir_info(ctx, ino);
6552 if (!dir) {
6553 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6554 goto abort_free_dict;
6555 }
6556 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
6557 dir_modified++;
6558 } else if (dirent->inode == ino) {
6559 problem = PR_2_LINK_DOT;
6560 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
6561 dirent->inode = 0;
6562 dir_modified++;
6563 goto next;
6564 }
6565 }
6566 if (!dirent->inode)
6567 goto next;
6568
6569 /*
6570 * Make sure the inode listed is a legal one.
6571 */
6572 if (((dirent->inode != EXT2_ROOT_INO) &&
6573 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
6574 (dirent->inode > fs->super->s_inodes_count)) {
6575 problem = PR_2_BAD_INO;
6576 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
6577 dirent->inode))) {
6578 /*
6579 * If the inode is unused, offer to clear it.
6580 */
6581 problem = PR_2_UNUSED_INODE;
6582 } else if (ctx->inode_bb_map &&
6583 (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
6584 dirent->inode))) {
6585 /*
6586 * If the inode is in a bad block, offer to
6587 * clear it.
6588 */
6589 problem = PR_2_BB_INODE;
6590 } else if ((dot_state > 1) &&
6591 ((dirent->name_len & 0xFF) == 1) &&
6592 (dirent->name[0] == '.')) {
6593 /*
6594 * If there's a '.' entry in anything other
6595 * than the first directory entry, it's a
6596 * duplicate entry that should be removed.
6597 */
6598 problem = PR_2_DUP_DOT;
6599 } else if ((dot_state > 1) &&
6600 ((dirent->name_len & 0xFF) == 2) &&
6601 (dirent->name[0] == '.') &&
6602 (dirent->name[1] == '.')) {
6603 /*
6604 * If there's a '..' entry in anything other
6605 * than the second directory entry, it's a
6606 * duplicate entry that should be removed.
6607 */
6608 problem = PR_2_DUP_DOT_DOT;
6609 } else if ((dot_state > 1) &&
6610 (dirent->inode == EXT2_ROOT_INO)) {
6611 /*
6612 * Don't allow links to the root directory.
6613 * We check this specially to make sure we
6614 * catch this error case even if the root
6615 * directory hasn't been created yet.
6616 */
6617 problem = PR_2_LINK_ROOT;
6618 } else if ((dot_state > 1) &&
6619 (dirent->name_len & 0xFF) == 0) {
6620 /*
6621 * Don't allow zero-length directory names.
6622 */
6623 problem = PR_2_NULL_NAME;
6624 }
6625
6626 if (problem) {
6627 if (fix_problem(ctx, problem, &cd->pctx)) {
6628 dirent->inode = 0;
6629 dir_modified++;
6630 goto next;
6631 } else {
6632 ext2fs_unmark_valid(fs);
6633 if (problem == PR_2_BAD_INO)
6634 goto next;
6635 }
6636 }
6637
6638 /*
6639 * If the inode was marked as having bad fields in
6640 * pass1, process it and offer to fix/clear it.
6641 * (We wait until now so that we can display the
6642 * pathname to the user.)
6643 */
6644 if (ctx->inode_bad_map &&
6645 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6646 dirent->inode)) {
6647 if (e2fsck_process_bad_inode(ctx, ino,
6648 dirent->inode,
6649 buf + fs->blocksize)) {
6650 dirent->inode = 0;
6651 dir_modified++;
6652 goto next;
6653 }
6654 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6655 return DIRENT_ABORT;
6656 }
6657
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006658 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006659 dir_modified++;
6660
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006661 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006662 dir_modified++;
6663
6664#ifdef ENABLE_HTREE
6665 if (dx_db) {
6666 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
6667 (dirent->name_len & 0xFF),
6668 fs->super->s_hash_seed, &hash, 0);
6669 if (hash < dx_db->min_hash)
6670 dx_db->min_hash = hash;
6671 if (hash > dx_db->max_hash)
6672 dx_db->max_hash = hash;
6673 }
6674#endif
6675
6676 /*
6677 * If this is a directory, then mark its parent in its
6678 * dir_info structure. If the parent field is already
6679 * filled in, then this directory has more than one
6680 * hard link. We assume the first link is correct,
6681 * and ask the user if he/she wants to clear this one.
6682 */
6683 if ((dot_state > 1) &&
6684 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6685 dirent->inode))) {
6686 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
6687 if (!subdir) {
6688 cd->pctx.ino = dirent->inode;
6689 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6690 goto abort_free_dict;
6691 }
6692 if (subdir->parent) {
6693 cd->pctx.ino2 = subdir->parent;
6694 if (fix_problem(ctx, PR_2_LINK_DIR,
6695 &cd->pctx)) {
6696 dirent->inode = 0;
6697 dir_modified++;
6698 goto next;
6699 }
6700 cd->pctx.ino2 = 0;
6701 } else
6702 subdir->parent = ino;
6703 }
6704
6705 if (dups_found) {
6706 ;
6707 } else if (dict_lookup(&de_dict, dirent)) {
6708 clear_problem_context(&pctx);
6709 pctx.ino = ino;
6710 pctx.dirent = dirent;
6711 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
6712 if (!ctx->dirs_to_hash)
6713 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
6714 if (ctx->dirs_to_hash)
6715 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6716 dups_found++;
6717 } else
6718 dict_alloc_insert(&de_dict, dirent, dirent);
6719
6720 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
6721 &links);
6722 if (links > 1)
6723 ctx->fs_links_count++;
6724 ctx->fs_total_count++;
6725 next:
6726 prev = dirent;
6727 offset += dirent->rec_len;
6728 dot_state++;
6729 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006730#ifdef ENABLE_HTREE
6731 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006732 cd->pctx.dir = cd->pctx.ino;
6733 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
6734 (dx_db->type == DX_DIRBLOCK_NODE))
6735 parse_int_node(fs, db, cd, dx_dir, buf);
6736 }
6737#endif /* ENABLE_HTREE */
6738 if (offset != fs->blocksize) {
6739 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
6740 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
6741 dirent->rec_len = cd->pctx.num;
6742 dir_modified++;
6743 }
6744 }
6745 if (dir_modified) {
6746 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
6747 if (cd->pctx.errcode) {
6748 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
6749 &cd->pctx))
6750 goto abort_free_dict;
6751 }
6752 ext2fs_mark_changed(fs);
6753 }
6754 dict_free_nodes(&de_dict);
6755 return 0;
6756abort_free_dict:
6757 dict_free_nodes(&de_dict);
6758 ctx->flags |= E2F_FLAG_ABORT;
6759 return DIRENT_ABORT;
6760}
6761
6762/*
6763 * This function is called to deallocate a block, and is an interator
6764 * functioned called by deallocate inode via ext2fs_iterate_block().
6765 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006766static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006767 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6768 blk_t ref_block FSCK_ATTR((unused)),
6769 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006770 void *priv_data)
6771{
6772 e2fsck_t ctx = (e2fsck_t) priv_data;
6773
6774 if (HOLE_BLKADDR(*block_nr))
6775 return 0;
6776 if ((*block_nr < fs->super->s_first_data_block) ||
6777 (*block_nr >= fs->super->s_blocks_count))
6778 return 0;
6779 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6780 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6781 return 0;
6782}
6783
6784/*
6785 * This fuction deallocates an inode
6786 */
6787static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
6788{
6789 ext2_filsys fs = ctx->fs;
6790 struct ext2_inode inode;
6791 struct problem_context pctx;
6792 __u32 count;
6793
6794 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
6795 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
6796 inode.i_links_count = 0;
6797 inode.i_dtime = time(0);
6798 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
6799 clear_problem_context(&pctx);
6800 pctx.ino = ino;
6801
6802 /*
6803 * Fix up the bitmaps...
6804 */
6805 e2fsck_read_bitmaps(ctx);
6806 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6807 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6808 if (ctx->inode_bad_map)
6809 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6810 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6811
6812 if (inode.i_file_acl &&
6813 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6814 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6815 block_buf, -1, &count);
6816 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6817 pctx.errcode = 0;
6818 count = 1;
6819 }
6820 if (pctx.errcode) {
6821 pctx.blk = inode.i_file_acl;
6822 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
6823 ctx->flags |= E2F_FLAG_ABORT;
6824 return;
6825 }
6826 if (count == 0) {
6827 ext2fs_unmark_block_bitmap(ctx->block_found_map,
6828 inode.i_file_acl);
6829 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
6830 }
6831 inode.i_file_acl = 0;
6832 }
6833
6834 if (!ext2fs_inode_has_valid_blocks(&inode))
6835 return;
6836
6837 if (LINUX_S_ISREG(inode.i_mode) &&
6838 (inode.i_size_high || inode.i_size & 0x80000000UL))
6839 ctx->large_files--;
6840
6841 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6842 deallocate_inode_block, ctx);
6843 if (pctx.errcode) {
6844 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
6845 ctx->flags |= E2F_FLAG_ABORT;
6846 return;
6847 }
6848}
6849
6850/*
6851 * This fuction clears the htree flag on an inode
6852 */
6853static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
6854{
6855 struct ext2_inode inode;
6856
6857 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
6858 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
6859 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
6860 if (ctx->dirs_to_hash)
6861 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6862}
6863
6864
6865static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
6866 ext2_ino_t ino, char *buf)
6867{
6868 ext2_filsys fs = ctx->fs;
6869 struct ext2_inode inode;
6870 int inode_modified = 0;
6871 int not_fixed = 0;
6872 unsigned char *frag, *fsize;
6873 struct problem_context pctx;
6874 int problem = 0;
6875
6876 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
6877
6878 clear_problem_context(&pctx);
6879 pctx.ino = ino;
6880 pctx.dir = dir;
6881 pctx.inode = &inode;
6882
6883 if (inode.i_file_acl &&
6884 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
6885 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
6886 inode.i_file_acl = 0;
Rob Landley7c94bed2006-05-03 21:58:45 +00006887#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006888 /*
6889 * This is a special kludge to deal with long symlinks
6890 * on big endian systems. i_blocks had already been
6891 * decremented earlier in pass 1, but since i_file_acl
6892 * hadn't yet been cleared, ext2fs_read_inode()
6893 * assumed that the file was short symlink and would
6894 * not have byte swapped i_block[0]. Hence, we have
6895 * to byte-swap it here.
6896 */
6897 if (LINUX_S_ISLNK(inode.i_mode) &&
6898 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
6899 (inode.i_blocks == fs->blocksize >> 9))
6900 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
6901#endif
6902 inode_modified++;
6903 } else
6904 not_fixed++;
6905
6906 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
6907 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
6908 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
6909 !(LINUX_S_ISSOCK(inode.i_mode)))
6910 problem = PR_2_BAD_MODE;
6911 else if (LINUX_S_ISCHR(inode.i_mode)
6912 && !e2fsck_pass1_check_device_inode(fs, &inode))
6913 problem = PR_2_BAD_CHAR_DEV;
6914 else if (LINUX_S_ISBLK(inode.i_mode)
6915 && !e2fsck_pass1_check_device_inode(fs, &inode))
6916 problem = PR_2_BAD_BLOCK_DEV;
6917 else if (LINUX_S_ISFIFO(inode.i_mode)
6918 && !e2fsck_pass1_check_device_inode(fs, &inode))
6919 problem = PR_2_BAD_FIFO;
6920 else if (LINUX_S_ISSOCK(inode.i_mode)
6921 && !e2fsck_pass1_check_device_inode(fs, &inode))
6922 problem = PR_2_BAD_SOCKET;
6923 else if (LINUX_S_ISLNK(inode.i_mode)
6924 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
6925 problem = PR_2_INVALID_SYMLINK;
6926 }
6927
6928 if (problem) {
6929 if (fix_problem(ctx, problem, &pctx)) {
6930 deallocate_inode(ctx, ino, 0);
6931 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6932 return 0;
6933 return 1;
6934 } else
6935 not_fixed++;
6936 problem = 0;
6937 }
6938
6939 if (inode.i_faddr) {
6940 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
6941 inode.i_faddr = 0;
6942 inode_modified++;
6943 } else
6944 not_fixed++;
6945 }
6946
6947 switch (fs->super->s_creator_os) {
6948 case EXT2_OS_LINUX:
6949 frag = &inode.osd2.linux2.l_i_frag;
6950 fsize = &inode.osd2.linux2.l_i_fsize;
6951 break;
6952 case EXT2_OS_HURD:
6953 frag = &inode.osd2.hurd2.h_i_frag;
6954 fsize = &inode.osd2.hurd2.h_i_fsize;
6955 break;
6956 case EXT2_OS_MASIX:
6957 frag = &inode.osd2.masix2.m_i_frag;
6958 fsize = &inode.osd2.masix2.m_i_fsize;
6959 break;
6960 default:
6961 frag = fsize = 0;
6962 }
6963 if (frag && *frag) {
6964 pctx.num = *frag;
6965 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
6966 *frag = 0;
6967 inode_modified++;
6968 } else
6969 not_fixed++;
6970 pctx.num = 0;
6971 }
6972 if (fsize && *fsize) {
6973 pctx.num = *fsize;
6974 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
6975 *fsize = 0;
6976 inode_modified++;
6977 } else
6978 not_fixed++;
6979 pctx.num = 0;
6980 }
6981
6982 if (inode.i_file_acl &&
6983 ((inode.i_file_acl < fs->super->s_first_data_block) ||
6984 (inode.i_file_acl >= fs->super->s_blocks_count))) {
6985 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
6986 inode.i_file_acl = 0;
6987 inode_modified++;
6988 } else
6989 not_fixed++;
6990 }
6991 if (inode.i_dir_acl &&
6992 LINUX_S_ISDIR(inode.i_mode)) {
6993 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
6994 inode.i_dir_acl = 0;
6995 inode_modified++;
6996 } else
6997 not_fixed++;
6998 }
6999
7000 if (inode_modified)
7001 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
7002 if (!not_fixed)
7003 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
7004 return 0;
7005}
7006
7007
7008/*
7009 * allocate_dir_block --- this function allocates a new directory
7010 * block for a particular inode; this is done if a directory has
7011 * a "hole" in it, or if a directory has a illegal block number
7012 * that was zeroed out and now needs to be replaced.
7013 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007014static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007015 struct problem_context *pctx)
7016{
7017 ext2_filsys fs = ctx->fs;
7018 blk_t blk;
7019 char *block;
7020 struct ext2_inode inode;
7021
7022 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
7023 return 1;
7024
7025 /*
7026 * Read the inode and block bitmaps in; we'll be messing with
7027 * them.
7028 */
7029 e2fsck_read_bitmaps(ctx);
7030
7031 /*
7032 * First, find a free block
7033 */
7034 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7035 if (pctx->errcode) {
7036 pctx->str = "ext2fs_new_block";
7037 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7038 return 1;
7039 }
7040 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7041 ext2fs_mark_block_bitmap(fs->block_map, blk);
7042 ext2fs_mark_bb_dirty(fs);
7043
7044 /*
7045 * Now let's create the actual data block for the inode
7046 */
7047 if (db->blockcnt)
7048 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
7049 else
7050 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
7051 EXT2_ROOT_INO, &block);
7052
7053 if (pctx->errcode) {
7054 pctx->str = "ext2fs_new_dir_block";
7055 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7056 return 1;
7057 }
7058
7059 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
7060 ext2fs_free_mem(&block);
7061 if (pctx->errcode) {
7062 pctx->str = "ext2fs_write_dir_block";
7063 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7064 return 1;
7065 }
7066
7067 /*
7068 * Update the inode block count
7069 */
7070 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
7071 inode.i_blocks += fs->blocksize / 512;
7072 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
7073 inode.i_size = (db->blockcnt+1) * fs->blocksize;
7074 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
7075
7076 /*
7077 * Finally, update the block pointers for the inode
7078 */
7079 db->blk = blk;
7080 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
7081 0, update_dir_block, db);
7082 if (pctx->errcode) {
7083 pctx->str = "ext2fs_block_iterate";
7084 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
7085 return 1;
7086 }
7087
7088 return 0;
7089}
7090
7091/*
7092 * This is a helper function for allocate_dir_block().
7093 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007094static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007095 blk_t *block_nr,
7096 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007097 blk_t ref_block FSCK_ATTR((unused)),
7098 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007099 void *priv_data)
7100{
7101 struct ext2_db_entry *db;
7102
7103 db = (struct ext2_db_entry *) priv_data;
7104 if (db->blockcnt == (int) blockcnt) {
7105 *block_nr = db->blk;
7106 return BLOCK_CHANGED;
7107 }
7108 return 0;
7109}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007110
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007111/*
7112 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
7113 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007114 * Pass #3 assures that all directories are connected to the
7115 * filesystem tree, using the following algorithm:
7116 *
7117 * First, the root directory is checked to make sure it exists; if
7118 * not, e2fsck will offer to create a new one. It is then marked as
7119 * "done".
7120 *
7121 * Then, pass3 interates over all directory inodes; for each directory
7122 * it attempts to trace up the filesystem tree, using dirinfo.parent
7123 * until it reaches a directory which has been marked "done". If it
7124 * can not do so, then the directory must be disconnected, and e2fsck
7125 * will offer to reconnect it to /lost+found. While it is chasing
7126 * parent pointers up the filesystem tree, if pass3 sees a directory
7127 * twice, then it has detected a filesystem loop, and it will again
7128 * offer to reconnect the directory to /lost+found in to break the
7129 * filesystem loop.
7130 *
7131 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
7132 * reconnect inodes to /lost+found; this subroutine is also used by
7133 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
7134 * is responsible for creating /lost+found if it does not exist.
7135 *
7136 * Pass 3 frees the following data structures:
7137 * - The dirinfo directory information cache.
7138 */
7139
7140static void check_root(e2fsck_t ctx);
7141static int check_directory(e2fsck_t ctx, struct dir_info *dir,
7142 struct problem_context *pctx);
7143static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
7144
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007145static ext2fs_inode_bitmap inode_loop_detect;
7146static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007147
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007148static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007149{
7150 ext2_filsys fs = ctx->fs;
7151 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007152 struct problem_context pctx;
7153 struct dir_info *dir;
7154 unsigned long maxdirs, count;
7155
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007156 clear_problem_context(&pctx);
7157
Rob Landley3e72c592006-04-06 22:49:04 +00007158 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007159
7160 if (!(ctx->options & E2F_OPT_PREEN))
7161 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
7162
7163 /*
7164 * Allocate some bitmaps to do loop detection.
7165 */
7166 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
7167 &inode_done_map);
7168 if (pctx.errcode) {
7169 pctx.num = 2;
7170 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
7171 ctx->flags |= E2F_FLAG_ABORT;
7172 goto abort_exit;
7173 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007174 check_root(ctx);
7175 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7176 goto abort_exit;
7177
7178 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
7179
7180 maxdirs = e2fsck_get_num_dirinfo(ctx);
7181 count = 1;
7182
7183 if (ctx->progress)
7184 if ((ctx->progress)(ctx, 3, 0, maxdirs))
7185 goto abort_exit;
7186
7187 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
7188 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7189 goto abort_exit;
7190 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
7191 goto abort_exit;
7192 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
7193 if (check_directory(ctx, dir, &pctx))
7194 goto abort_exit;
7195 }
7196
7197 /*
7198 * Force the creation of /lost+found if not present
7199 */
7200 if ((ctx->flags & E2F_OPT_READONLY) == 0)
7201 e2fsck_get_lost_and_found(ctx, 1);
7202
7203 /*
7204 * If there are any directories that need to be indexed or
7205 * optimized, do it here.
7206 */
7207 e2fsck_rehash_directories(ctx);
7208
7209abort_exit:
7210 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00007211 ext2fs_free_inode_bitmap(inode_loop_detect);
7212 inode_loop_detect = 0;
7213 ext2fs_free_inode_bitmap(inode_done_map);
7214 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007215}
7216
7217/*
7218 * This makes sure the root inode is present; if not, we ask if the
7219 * user wants us to create it. Not creating it is a fatal error.
7220 */
7221static void check_root(e2fsck_t ctx)
7222{
7223 ext2_filsys fs = ctx->fs;
7224 blk_t blk;
7225 struct ext2_inode inode;
7226 char * block;
7227 struct problem_context pctx;
7228
7229 clear_problem_context(&pctx);
7230
7231 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
7232 /*
7233 * If the root inode is not a directory, die here. The
7234 * user must have answered 'no' in pass1 when we
7235 * offered to clear it.
7236 */
7237 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
7238 EXT2_ROOT_INO))) {
7239 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
7240 ctx->flags |= E2F_FLAG_ABORT;
7241 }
7242 return;
7243 }
7244
7245 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
7246 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
7247 ctx->flags |= E2F_FLAG_ABORT;
7248 return;
7249 }
7250
7251 e2fsck_read_bitmaps(ctx);
7252
7253 /*
7254 * First, find a free block
7255 */
7256 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7257 if (pctx.errcode) {
7258 pctx.str = "ext2fs_new_block";
7259 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
7260 ctx->flags |= E2F_FLAG_ABORT;
7261 return;
7262 }
7263 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7264 ext2fs_mark_block_bitmap(fs->block_map, blk);
7265 ext2fs_mark_bb_dirty(fs);
7266
7267 /*
7268 * Now let's create the actual data block for the inode
7269 */
7270 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
7271 &block);
7272 if (pctx.errcode) {
7273 pctx.str = "ext2fs_new_dir_block";
7274 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
7275 ctx->flags |= E2F_FLAG_ABORT;
7276 return;
7277 }
7278
7279 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
7280 if (pctx.errcode) {
7281 pctx.str = "ext2fs_write_dir_block";
7282 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
7283 ctx->flags |= E2F_FLAG_ABORT;
7284 return;
7285 }
7286 ext2fs_free_mem(&block);
7287
7288 /*
7289 * Set up the inode structure
7290 */
7291 memset(&inode, 0, sizeof(inode));
7292 inode.i_mode = 040755;
7293 inode.i_size = fs->blocksize;
7294 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7295 inode.i_links_count = 2;
7296 inode.i_blocks = fs->blocksize / 512;
7297 inode.i_block[0] = blk;
7298
7299 /*
7300 * Write out the inode.
7301 */
7302 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
7303 if (pctx.errcode) {
7304 pctx.str = "ext2fs_write_inode";
7305 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
7306 ctx->flags |= E2F_FLAG_ABORT;
7307 return;
7308 }
7309
7310 /*
7311 * Miscellaneous bookkeeping...
7312 */
7313 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
7314 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
7315 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
7316
7317 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
7318 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
7319 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
7320 ext2fs_mark_ib_dirty(fs);
7321}
7322
7323/*
7324 * This subroutine is responsible for making sure that a particular
7325 * directory is connected to the root; if it isn't we trace it up as
7326 * far as we can go, and then offer to connect the resulting parent to
7327 * the lost+found. We have to do loop detection; if we ever discover
7328 * a loop, we treat that as a disconnected directory and offer to
7329 * reparent it to lost+found.
7330 *
7331 * However, loop detection is expensive, because for very large
7332 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
7333 * is non-trivial. Loops in filesystems are also a rare error case,
7334 * and we shouldn't optimize for error cases. So we try two passes of
7335 * the algorithm. The first time, we ignore loop detection and merely
7336 * increment a counter; if the counter exceeds some extreme threshold,
7337 * then we try again with the loop detection bitmap enabled.
7338 */
7339static int check_directory(e2fsck_t ctx, struct dir_info *dir,
7340 struct problem_context *pctx)
7341{
7342 ext2_filsys fs = ctx->fs;
7343 struct dir_info *p = dir;
7344 int loop_pass = 0, parent_count = 0;
7345
7346 if (!p)
7347 return 0;
7348
7349 while (1) {
7350 /*
7351 * Mark this inode as being "done"; by the time we
7352 * return from this function, the inode we either be
7353 * verified as being connected to the directory tree,
7354 * or we will have offered to reconnect this to
7355 * lost+found.
7356 *
7357 * If it was marked done already, then we've reached a
7358 * parent we've already checked.
7359 */
7360 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
7361 break;
7362
7363 /*
7364 * If this directory doesn't have a parent, or we've
7365 * seen the parent once already, then offer to
7366 * reparent it to lost+found
7367 */
7368 if (!p->parent ||
7369 (loop_pass &&
7370 (ext2fs_test_inode_bitmap(inode_loop_detect,
7371 p->parent)))) {
7372 pctx->ino = p->ino;
7373 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
7374 if (e2fsck_reconnect_file(ctx, pctx->ino))
7375 ext2fs_unmark_valid(fs);
7376 else {
7377 p = e2fsck_get_dir_info(ctx, pctx->ino);
7378 p->parent = ctx->lost_and_found;
7379 fix_dotdot(ctx, p, ctx->lost_and_found);
7380 }
7381 }
7382 break;
7383 }
7384 p = e2fsck_get_dir_info(ctx, p->parent);
7385 if (!p) {
7386 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
7387 return 0;
7388 }
7389 if (loop_pass) {
7390 ext2fs_mark_inode_bitmap(inode_loop_detect,
7391 p->ino);
7392 } else if (parent_count++ > 2048) {
7393 /*
7394 * If we've run into a path depth that's
7395 * greater than 2048, try again with the inode
7396 * loop bitmap turned on and start from the
7397 * top.
7398 */
7399 loop_pass = 1;
7400 if (inode_loop_detect)
7401 ext2fs_clear_inode_bitmap(inode_loop_detect);
7402 else {
7403 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
7404 if (pctx->errcode) {
7405 pctx->num = 1;
7406 fix_problem(ctx,
7407 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
7408 ctx->flags |= E2F_FLAG_ABORT;
7409 return -1;
7410 }
7411 }
7412 p = dir;
7413 }
7414 }
7415
7416 /*
7417 * Make sure that .. and the parent directory are the same;
7418 * offer to fix it if not.
7419 */
7420 if (dir->parent != dir->dotdot) {
7421 pctx->ino = dir->ino;
7422 pctx->ino2 = dir->dotdot;
7423 pctx->dir = dir->parent;
7424 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
7425 fix_dotdot(ctx, dir, dir->parent);
7426 }
7427 return 0;
7428}
7429
7430/*
7431 * This routine gets the lost_and_found inode, making it a directory
7432 * if necessary
7433 */
7434ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
7435{
7436 ext2_filsys fs = ctx->fs;
7437 ext2_ino_t ino;
7438 blk_t blk;
7439 errcode_t retval;
7440 struct ext2_inode inode;
7441 char * block;
7442 static const char name[] = "lost+found";
7443 struct problem_context pctx;
7444 struct dir_info *dirinfo;
7445
7446 if (ctx->lost_and_found)
7447 return ctx->lost_and_found;
7448
7449 clear_problem_context(&pctx);
7450
7451 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
7452 sizeof(name)-1, 0, &ino);
7453 if (retval && !fix)
7454 return 0;
7455 if (!retval) {
7456 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
7457 ctx->lost_and_found = ino;
7458 return ino;
7459 }
7460
7461 /* Lost+found isn't a directory! */
7462 if (!fix)
7463 return 0;
7464 pctx.ino = ino;
7465 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
7466 return 0;
7467
7468 /* OK, unlink the old /lost+found file. */
7469 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
7470 if (pctx.errcode) {
7471 pctx.str = "ext2fs_unlink";
7472 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7473 return 0;
7474 }
7475 dirinfo = e2fsck_get_dir_info(ctx, ino);
7476 if (dirinfo)
7477 dirinfo->parent = 0;
7478 e2fsck_adjust_inode_count(ctx, ino, -1);
7479 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
7480 pctx.errcode = retval;
7481 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
7482 }
7483 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
7484 return 0;
7485
7486 /*
7487 * Read the inode and block bitmaps in; we'll be messing with
7488 * them.
7489 */
7490 e2fsck_read_bitmaps(ctx);
7491
7492 /*
7493 * First, find a free block
7494 */
7495 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7496 if (retval) {
7497 pctx.errcode = retval;
7498 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
7499 return 0;
7500 }
7501 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7502 ext2fs_block_alloc_stats(fs, blk, +1);
7503
7504 /*
7505 * Next find a free inode.
7506 */
7507 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
7508 ctx->inode_used_map, &ino);
7509 if (retval) {
7510 pctx.errcode = retval;
7511 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
7512 return 0;
7513 }
7514 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
7515 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
7516 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
7517
7518 /*
7519 * Now let's create the actual data block for the inode
7520 */
7521 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
7522 if (retval) {
7523 pctx.errcode = retval;
7524 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
7525 return 0;
7526 }
7527
7528 retval = ext2fs_write_dir_block(fs, blk, block);
7529 ext2fs_free_mem(&block);
7530 if (retval) {
7531 pctx.errcode = retval;
7532 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
7533 return 0;
7534 }
7535
7536 /*
7537 * Set up the inode structure
7538 */
7539 memset(&inode, 0, sizeof(inode));
7540 inode.i_mode = 040700;
7541 inode.i_size = fs->blocksize;
7542 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7543 inode.i_links_count = 2;
7544 inode.i_blocks = fs->blocksize / 512;
7545 inode.i_block[0] = blk;
7546
7547 /*
7548 * Next, write out the inode.
7549 */
7550 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
7551 if (pctx.errcode) {
7552 pctx.str = "ext2fs_write_inode";
7553 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7554 return 0;
7555 }
7556 /*
7557 * Finally, create the directory link
7558 */
7559 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
7560 if (pctx.errcode) {
7561 pctx.str = "ext2fs_link";
7562 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7563 return 0;
7564 }
7565
7566 /*
7567 * Miscellaneous bookkeeping that needs to be kept straight.
7568 */
7569 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
7570 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
7571 ext2fs_icount_store(ctx->inode_count, ino, 2);
7572 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
7573 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007574 return ino;
7575}
7576
7577/*
7578 * This routine will connect a file to lost+found
7579 */
7580int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
7581{
7582 ext2_filsys fs = ctx->fs;
7583 errcode_t retval;
7584 char name[80];
7585 struct problem_context pctx;
7586 struct ext2_inode inode;
7587 int file_type = 0;
7588
7589 clear_problem_context(&pctx);
7590 pctx.ino = ino;
7591
7592 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
7593 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
7594 ctx->bad_lost_and_found++;
7595 }
7596 if (ctx->bad_lost_and_found) {
7597 fix_problem(ctx, PR_3_NO_LPF, &pctx);
7598 return 1;
7599 }
7600
7601 sprintf(name, "#%u", ino);
7602 if (ext2fs_read_inode(fs, ino, &inode) == 0)
7603 file_type = ext2_file_type(inode.i_mode);
7604 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
7605 if (retval == EXT2_ET_DIR_NO_SPACE) {
7606 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
7607 return 1;
7608 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
7609 1, 0);
7610 if (retval) {
7611 pctx.errcode = retval;
7612 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
7613 return 1;
7614 }
7615 retval = ext2fs_link(fs, ctx->lost_and_found, name,
7616 ino, file_type);
7617 }
7618 if (retval) {
7619 pctx.errcode = retval;
7620 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
7621 return 1;
7622 }
7623 e2fsck_adjust_inode_count(ctx, ino, 1);
7624
7625 return 0;
7626}
7627
7628/*
7629 * Utility routine to adjust the inode counts on an inode.
7630 */
7631errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
7632{
7633 ext2_filsys fs = ctx->fs;
7634 errcode_t retval;
7635 struct ext2_inode inode;
7636
7637 if (!ino)
7638 return 0;
7639
7640 retval = ext2fs_read_inode(fs, ino, &inode);
7641 if (retval)
7642 return retval;
7643
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007644 if (adj == 1) {
7645 ext2fs_icount_increment(ctx->inode_count, ino, 0);
7646 if (inode.i_links_count == (__u16) ~0)
7647 return 0;
7648 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
7649 inode.i_links_count++;
7650 } else if (adj == -1) {
7651 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
7652 if (inode.i_links_count == 0)
7653 return 0;
7654 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
7655 inode.i_links_count--;
7656 }
7657
7658 retval = ext2fs_write_inode(fs, ino, &inode);
7659 if (retval)
7660 return retval;
7661
7662 return 0;
7663}
7664
7665/*
7666 * Fix parent --- this routine fixes up the parent of a directory.
7667 */
7668struct fix_dotdot_struct {
7669 ext2_filsys fs;
7670 ext2_ino_t parent;
7671 int done;
7672 e2fsck_t ctx;
7673};
7674
7675static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007676 int offset FSCK_ATTR((unused)),
7677 int blocksize FSCK_ATTR((unused)),
7678 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007679 void *priv_data)
7680{
7681 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
7682 errcode_t retval;
7683 struct problem_context pctx;
7684
7685 if ((dirent->name_len & 0xFF) != 2)
7686 return 0;
7687 if (strncmp(dirent->name, "..", 2))
7688 return 0;
7689
7690 clear_problem_context(&pctx);
7691
7692 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
7693 if (retval) {
7694 pctx.errcode = retval;
7695 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7696 }
7697 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
7698 if (retval) {
7699 pctx.errcode = retval;
7700 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7701 }
7702 dirent->inode = fp->parent;
7703
7704 fp->done++;
7705 return DIRENT_ABORT | DIRENT_CHANGED;
7706}
7707
7708static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
7709{
7710 ext2_filsys fs = ctx->fs;
7711 errcode_t retval;
7712 struct fix_dotdot_struct fp;
7713 struct problem_context pctx;
7714
7715 fp.fs = fs;
7716 fp.parent = parent;
7717 fp.done = 0;
7718 fp.ctx = ctx;
7719
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007720 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
7721 0, fix_dotdot_proc, &fp);
7722 if (retval || !fp.done) {
7723 clear_problem_context(&pctx);
7724 pctx.ino = dir->ino;
7725 pctx.errcode = retval;
7726 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
7727 PR_3_FIX_PARENT_NOFIND, &pctx);
7728 ext2fs_unmark_valid(fs);
7729 }
7730 dir->dotdot = parent;
7731
7732 return;
7733}
7734
7735/*
7736 * These routines are responsible for expanding a /lost+found if it is
7737 * too small.
7738 */
7739
7740struct expand_dir_struct {
7741 int num;
7742 int guaranteed_size;
7743 int newblocks;
7744 int last_block;
7745 errcode_t err;
7746 e2fsck_t ctx;
7747};
7748
7749static int expand_dir_proc(ext2_filsys fs,
7750 blk_t *blocknr,
7751 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007752 blk_t ref_block FSCK_ATTR((unused)),
7753 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007754 void *priv_data)
7755{
7756 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
7757 blk_t new_blk;
7758 static blk_t last_blk = 0;
7759 char *block;
7760 errcode_t retval;
7761 e2fsck_t ctx;
7762
7763 ctx = es->ctx;
7764
7765 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
7766 return BLOCK_ABORT;
7767
7768 if (blockcnt > 0)
7769 es->last_block = blockcnt;
7770 if (*blocknr) {
7771 last_blk = *blocknr;
7772 return 0;
7773 }
7774 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
7775 &new_blk);
7776 if (retval) {
7777 es->err = retval;
7778 return BLOCK_ABORT;
7779 }
7780 if (blockcnt > 0) {
7781 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
7782 if (retval) {
7783 es->err = retval;
7784 return BLOCK_ABORT;
7785 }
7786 es->num--;
7787 retval = ext2fs_write_dir_block(fs, new_blk, block);
7788 } else {
7789 retval = ext2fs_get_mem(fs->blocksize, &block);
7790 if (retval) {
7791 es->err = retval;
7792 return BLOCK_ABORT;
7793 }
7794 memset(block, 0, fs->blocksize);
7795 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
7796 }
7797 if (retval) {
7798 es->err = retval;
7799 return BLOCK_ABORT;
7800 }
7801 ext2fs_free_mem(&block);
7802 *blocknr = new_blk;
7803 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
7804 ext2fs_block_alloc_stats(fs, new_blk, +1);
7805 es->newblocks++;
7806
7807 if (es->num == 0)
7808 return (BLOCK_CHANGED | BLOCK_ABORT);
7809 else
7810 return BLOCK_CHANGED;
7811}
7812
7813errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
7814 int num, int guaranteed_size)
7815{
7816 ext2_filsys fs = ctx->fs;
7817 errcode_t retval;
7818 struct expand_dir_struct es;
7819 struct ext2_inode inode;
7820
7821 if (!(fs->flags & EXT2_FLAG_RW))
7822 return EXT2_ET_RO_FILSYS;
7823
7824 /*
7825 * Read the inode and block bitmaps in; we'll be messing with
7826 * them.
7827 */
7828 e2fsck_read_bitmaps(ctx);
7829
7830 retval = ext2fs_check_directory(fs, dir);
7831 if (retval)
7832 return retval;
7833
7834 es.num = num;
7835 es.guaranteed_size = guaranteed_size;
7836 es.last_block = 0;
7837 es.err = 0;
7838 es.newblocks = 0;
7839 es.ctx = ctx;
7840
7841 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
7842 0, expand_dir_proc, &es);
7843
7844 if (es.err)
7845 return es.err;
7846
7847 /*
7848 * Update the size and block count fields in the inode.
7849 */
7850 retval = ext2fs_read_inode(fs, dir, &inode);
7851 if (retval)
7852 return retval;
7853
7854 inode.i_size = (es.last_block + 1) * fs->blocksize;
7855 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
7856
7857 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
7858
7859 return 0;
7860}
7861
7862/*
7863 * pass4.c -- pass #4 of e2fsck: Check reference counts
7864 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007865 * Pass 4 frees the following data structures:
7866 * - A bitmap of which inodes are in bad blocks. (inode_bb_map)
7867 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
7868 */
7869
7870/*
7871 * This routine is called when an inode is not connected to the
7872 * directory tree.
7873 *
7874 * This subroutine returns 1 then the caller shouldn't bother with the
7875 * rest of the pass 4 tests.
7876 */
7877static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
7878{
7879 ext2_filsys fs = ctx->fs;
7880 struct ext2_inode inode;
7881 struct problem_context pctx;
7882
7883 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
7884 clear_problem_context(&pctx);
7885 pctx.ino = i;
7886 pctx.inode = &inode;
7887
7888 /*
7889 * Offer to delete any zero-length files that does not have
7890 * blocks. If there is an EA block, it might have useful
7891 * information, so we won't prompt to delete it, but let it be
7892 * reconnected to lost+found.
7893 */
7894 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
7895 LINUX_S_ISDIR(inode.i_mode))) {
7896 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
7897 ext2fs_icount_store(ctx->inode_link_info, i, 0);
7898 inode.i_links_count = 0;
7899 inode.i_dtime = time(0);
7900 e2fsck_write_inode(ctx, i, &inode,
7901 "disconnect_inode");
7902 /*
7903 * Fix up the bitmaps...
7904 */
7905 e2fsck_read_bitmaps(ctx);
7906 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
7907 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
7908 ext2fs_inode_alloc_stats2(fs, i, -1,
7909 LINUX_S_ISDIR(inode.i_mode));
7910 return 0;
7911 }
7912 }
7913
7914 /*
7915 * Prompt to reconnect.
7916 */
7917 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
7918 if (e2fsck_reconnect_file(ctx, i))
7919 ext2fs_unmark_valid(fs);
7920 } else {
7921 /*
7922 * If we don't attach the inode, then skip the
7923 * i_links_test since there's no point in trying to
7924 * force i_links_count to zero.
7925 */
7926 ext2fs_unmark_valid(fs);
7927 return 1;
7928 }
7929 return 0;
7930}
7931
7932
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007933static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007934{
7935 ext2_filsys fs = ctx->fs;
7936 ext2_ino_t i;
7937 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007938 struct problem_context pctx;
7939 __u16 link_count, link_counted;
7940 char *buf = 0;
7941 int group, maxgroup;
7942
Rob Landley3e72c592006-04-06 22:49:04 +00007943 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007944
7945 clear_problem_context(&pctx);
7946
7947 if (!(ctx->options & E2F_OPT_PREEN))
7948 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
7949
7950 group = 0;
7951 maxgroup = fs->group_desc_count;
7952 if (ctx->progress)
7953 if ((ctx->progress)(ctx, 4, 0, maxgroup))
7954 return;
7955
7956 for (i=1; i <= fs->super->s_inodes_count; i++) {
7957 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7958 return;
7959 if ((i % fs->super->s_inodes_per_group) == 0) {
7960 group++;
7961 if (ctx->progress)
7962 if ((ctx->progress)(ctx, 4, group, maxgroup))
7963 return;
7964 }
7965 if (i == EXT2_BAD_INO ||
7966 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
7967 continue;
7968 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
7969 (ctx->inode_imagic_map &&
7970 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) ||
7971 (ctx->inode_bb_map &&
7972 ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
7973 continue;
7974 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
7975 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
7976 if (link_counted == 0) {
7977 if (!buf)
7978 buf = e2fsck_allocate_memory(ctx,
7979 fs->blocksize, "bad_inode buffer");
7980 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
7981 continue;
7982 if (disconnect_inode(ctx, i))
7983 continue;
7984 ext2fs_icount_fetch(ctx->inode_link_info, i,
7985 &link_count);
7986 ext2fs_icount_fetch(ctx->inode_count, i,
7987 &link_counted);
7988 }
7989 if (link_counted != link_count) {
7990 e2fsck_read_inode(ctx, i, &inode, "pass4");
7991 pctx.ino = i;
7992 pctx.inode = &inode;
7993 if (link_count != inode.i_links_count) {
7994 pctx.num = link_count;
7995 fix_problem(ctx,
7996 PR_4_INCONSISTENT_COUNT, &pctx);
7997 }
7998 pctx.num = link_counted;
7999 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
8000 inode.i_links_count = link_counted;
8001 e2fsck_write_inode(ctx, i, &inode, "pass4");
8002 }
8003 }
8004 }
8005 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
8006 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
8007 ext2fs_free_inode_bitmap(ctx->inode_bb_map);
8008 ctx->inode_bb_map = 0;
8009 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
8010 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00008011 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008012}
8013
8014/*
8015 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008016 */
8017
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008018#define NO_BLK ((blk_t) -1)
8019
8020static void print_bitmap_problem(e2fsck_t ctx, int problem,
8021 struct problem_context *pctx)
8022{
8023 switch (problem) {
8024 case PR_5_BLOCK_UNUSED:
8025 if (pctx->blk == pctx->blk2)
8026 pctx->blk2 = 0;
8027 else
8028 problem = PR_5_BLOCK_RANGE_UNUSED;
8029 break;
8030 case PR_5_BLOCK_USED:
8031 if (pctx->blk == pctx->blk2)
8032 pctx->blk2 = 0;
8033 else
8034 problem = PR_5_BLOCK_RANGE_USED;
8035 break;
8036 case PR_5_INODE_UNUSED:
8037 if (pctx->ino == pctx->ino2)
8038 pctx->ino2 = 0;
8039 else
8040 problem = PR_5_INODE_RANGE_UNUSED;
8041 break;
8042 case PR_5_INODE_USED:
8043 if (pctx->ino == pctx->ino2)
8044 pctx->ino2 = 0;
8045 else
8046 problem = PR_5_INODE_RANGE_USED;
8047 break;
8048 }
8049 fix_problem(ctx, problem, pctx);
8050 pctx->blk = pctx->blk2 = NO_BLK;
8051 pctx->ino = pctx->ino2 = 0;
8052}
8053
8054static void check_block_bitmaps(e2fsck_t ctx)
8055{
8056 ext2_filsys fs = ctx->fs;
8057 blk_t i;
8058 int *free_array;
8059 int group = 0;
8060 unsigned int blocks = 0;
8061 unsigned int free_blocks = 0;
8062 int group_free = 0;
8063 int actual, bitmap;
8064 struct problem_context pctx;
8065 int problem, save_problem, fixit, had_problem;
8066 errcode_t retval;
8067
8068 clear_problem_context(&pctx);
8069 free_array = (int *) e2fsck_allocate_memory(ctx,
8070 fs->group_desc_count * sizeof(int), "free block count array");
8071
8072 if ((fs->super->s_first_data_block <
8073 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
8074 (fs->super->s_blocks_count-1 >
8075 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
8076 pctx.num = 1;
8077 pctx.blk = fs->super->s_first_data_block;
8078 pctx.blk2 = fs->super->s_blocks_count -1;
8079 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
8080 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
8081 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8082
8083 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8084 return;
8085 }
8086
8087 if ((fs->super->s_first_data_block <
8088 ext2fs_get_block_bitmap_start(fs->block_map)) ||
8089 (fs->super->s_blocks_count-1 >
8090 ext2fs_get_block_bitmap_end(fs->block_map))) {
8091 pctx.num = 2;
8092 pctx.blk = fs->super->s_first_data_block;
8093 pctx.blk2 = fs->super->s_blocks_count -1;
8094 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
8095 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
8096 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8097
8098 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8099 return;
8100 }
8101
8102redo_counts:
8103 had_problem = 0;
8104 save_problem = 0;
8105 pctx.blk = pctx.blk2 = NO_BLK;
8106 for (i = fs->super->s_first_data_block;
8107 i < fs->super->s_blocks_count;
8108 i++) {
8109 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
8110 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
8111
8112 if (actual == bitmap)
8113 goto do_counts;
8114
8115 if (!actual && bitmap) {
8116 /*
8117 * Block not used, but marked in use in the bitmap.
8118 */
8119 problem = PR_5_BLOCK_UNUSED;
8120 } else {
8121 /*
8122 * Block used, but not marked in use in the bitmap.
8123 */
8124 problem = PR_5_BLOCK_USED;
8125 }
8126 if (pctx.blk == NO_BLK) {
8127 pctx.blk = pctx.blk2 = i;
8128 save_problem = problem;
8129 } else {
8130 if ((problem == save_problem) &&
8131 (pctx.blk2 == i-1))
8132 pctx.blk2++;
8133 else {
8134 print_bitmap_problem(ctx, save_problem, &pctx);
8135 pctx.blk = pctx.blk2 = i;
8136 save_problem = problem;
8137 }
8138 }
8139 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
8140 had_problem++;
8141
8142 do_counts:
8143 if (!bitmap) {
8144 group_free++;
8145 free_blocks++;
8146 }
8147 blocks ++;
8148 if ((blocks == fs->super->s_blocks_per_group) ||
8149 (i == fs->super->s_blocks_count-1)) {
8150 free_array[group] = group_free;
8151 group ++;
8152 blocks = 0;
8153 group_free = 0;
8154 if (ctx->progress)
8155 if ((ctx->progress)(ctx, 5, group,
8156 fs->group_desc_count*2))
8157 return;
8158 }
8159 }
8160 if (pctx.blk != NO_BLK)
8161 print_bitmap_problem(ctx, save_problem, &pctx);
8162 if (had_problem)
8163 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
8164 else
8165 fixit = -1;
8166 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
8167
8168 if (fixit == 1) {
8169 ext2fs_free_block_bitmap(fs->block_map);
8170 retval = ext2fs_copy_bitmap(ctx->block_found_map,
8171 &fs->block_map);
8172 if (retval) {
8173 clear_problem_context(&pctx);
8174 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
8175 ctx->flags |= E2F_FLAG_ABORT;
8176 return;
8177 }
8178 ext2fs_set_bitmap_padding(fs->block_map);
8179 ext2fs_mark_bb_dirty(fs);
8180
8181 /* Redo the counts */
8182 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
8183 memset(free_array, 0, fs->group_desc_count * sizeof(int));
8184 goto redo_counts;
8185 } else if (fixit == 0)
8186 ext2fs_unmark_valid(fs);
8187
8188 for (i = 0; i < fs->group_desc_count; i++) {
8189 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
8190 pctx.group = i;
8191 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
8192 pctx.blk2 = free_array[i];
8193
8194 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
8195 &pctx)) {
8196 fs->group_desc[i].bg_free_blocks_count =
8197 free_array[i];
8198 ext2fs_mark_super_dirty(fs);
8199 } else
8200 ext2fs_unmark_valid(fs);
8201 }
8202 }
8203 if (free_blocks != fs->super->s_free_blocks_count) {
8204 pctx.group = 0;
8205 pctx.blk = fs->super->s_free_blocks_count;
8206 pctx.blk2 = free_blocks;
8207
8208 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
8209 fs->super->s_free_blocks_count = free_blocks;
8210 ext2fs_mark_super_dirty(fs);
8211 } else
8212 ext2fs_unmark_valid(fs);
8213 }
8214 ext2fs_free_mem(&free_array);
8215}
8216
8217static void check_inode_bitmaps(e2fsck_t ctx)
8218{
8219 ext2_filsys fs = ctx->fs;
8220 ext2_ino_t i;
8221 unsigned int free_inodes = 0;
8222 int group_free = 0;
8223 int dirs_count = 0;
8224 int group = 0;
8225 unsigned int inodes = 0;
8226 int *free_array;
8227 int *dir_array;
8228 int actual, bitmap;
8229 errcode_t retval;
8230 struct problem_context pctx;
8231 int problem, save_problem, fixit, had_problem;
8232
8233 clear_problem_context(&pctx);
8234 free_array = (int *) e2fsck_allocate_memory(ctx,
8235 fs->group_desc_count * sizeof(int), "free inode count array");
8236
8237 dir_array = (int *) e2fsck_allocate_memory(ctx,
8238 fs->group_desc_count * sizeof(int), "directory count array");
8239
8240 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
8241 (fs->super->s_inodes_count >
8242 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
8243 pctx.num = 3;
8244 pctx.blk = 1;
8245 pctx.blk2 = fs->super->s_inodes_count;
8246 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
8247 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
8248 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8249
8250 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8251 return;
8252 }
8253 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
8254 (fs->super->s_inodes_count >
8255 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
8256 pctx.num = 4;
8257 pctx.blk = 1;
8258 pctx.blk2 = fs->super->s_inodes_count;
8259 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
8260 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
8261 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
8262
8263 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8264 return;
8265 }
8266
8267redo_counts:
8268 had_problem = 0;
8269 save_problem = 0;
8270 pctx.ino = pctx.ino2 = 0;
8271 for (i = 1; i <= fs->super->s_inodes_count; i++) {
8272 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
8273 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
8274
8275 if (actual == bitmap)
8276 goto do_counts;
8277
8278 if (!actual && bitmap) {
8279 /*
8280 * Inode wasn't used, but marked in bitmap
8281 */
8282 problem = PR_5_INODE_UNUSED;
8283 } else /* if (actual && !bitmap) */ {
8284 /*
8285 * Inode used, but not in bitmap
8286 */
8287 problem = PR_5_INODE_USED;
8288 }
8289 if (pctx.ino == 0) {
8290 pctx.ino = pctx.ino2 = i;
8291 save_problem = problem;
8292 } else {
8293 if ((problem == save_problem) &&
8294 (pctx.ino2 == i-1))
8295 pctx.ino2++;
8296 else {
8297 print_bitmap_problem(ctx, save_problem, &pctx);
8298 pctx.ino = pctx.ino2 = i;
8299 save_problem = problem;
8300 }
8301 }
8302 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
8303 had_problem++;
8304
8305do_counts:
8306 if (!bitmap) {
8307 group_free++;
8308 free_inodes++;
8309 } else {
8310 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
8311 dirs_count++;
8312 }
8313 inodes++;
8314 if ((inodes == fs->super->s_inodes_per_group) ||
8315 (i == fs->super->s_inodes_count)) {
8316 free_array[group] = group_free;
8317 dir_array[group] = dirs_count;
8318 group ++;
8319 inodes = 0;
8320 group_free = 0;
8321 dirs_count = 0;
8322 if (ctx->progress)
8323 if ((ctx->progress)(ctx, 5,
8324 group + fs->group_desc_count,
8325 fs->group_desc_count*2))
8326 return;
8327 }
8328 }
8329 if (pctx.ino)
8330 print_bitmap_problem(ctx, save_problem, &pctx);
8331
8332 if (had_problem)
8333 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
8334 else
8335 fixit = -1;
8336 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
8337
8338 if (fixit == 1) {
8339 ext2fs_free_inode_bitmap(fs->inode_map);
8340 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
8341 &fs->inode_map);
8342 if (retval) {
8343 clear_problem_context(&pctx);
8344 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
8345 ctx->flags |= E2F_FLAG_ABORT;
8346 return;
8347 }
8348 ext2fs_set_bitmap_padding(fs->inode_map);
8349 ext2fs_mark_ib_dirty(fs);
8350
8351 /* redo counts */
8352 inodes = 0; free_inodes = 0; group_free = 0;
8353 dirs_count = 0; group = 0;
8354 memset(free_array, 0, fs->group_desc_count * sizeof(int));
8355 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
8356 goto redo_counts;
8357 } else if (fixit == 0)
8358 ext2fs_unmark_valid(fs);
8359
8360 for (i = 0; i < fs->group_desc_count; i++) {
8361 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
8362 pctx.group = i;
8363 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
8364 pctx.ino2 = free_array[i];
8365 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
8366 &pctx)) {
8367 fs->group_desc[i].bg_free_inodes_count =
8368 free_array[i];
8369 ext2fs_mark_super_dirty(fs);
8370 } else
8371 ext2fs_unmark_valid(fs);
8372 }
8373 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
8374 pctx.group = i;
8375 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
8376 pctx.ino2 = dir_array[i];
8377
8378 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
8379 &pctx)) {
8380 fs->group_desc[i].bg_used_dirs_count =
8381 dir_array[i];
8382 ext2fs_mark_super_dirty(fs);
8383 } else
8384 ext2fs_unmark_valid(fs);
8385 }
8386 }
8387 if (free_inodes != fs->super->s_free_inodes_count) {
8388 pctx.group = -1;
8389 pctx.ino = fs->super->s_free_inodes_count;
8390 pctx.ino2 = free_inodes;
8391
8392 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
8393 fs->super->s_free_inodes_count = free_inodes;
8394 ext2fs_mark_super_dirty(fs);
8395 } else
8396 ext2fs_unmark_valid(fs);
8397 }
8398 ext2fs_free_mem(&free_array);
8399 ext2fs_free_mem(&dir_array);
8400}
8401
8402static void check_inode_end(e2fsck_t ctx)
8403{
8404 ext2_filsys fs = ctx->fs;
8405 ext2_ino_t end, save_inodes_count, i;
8406 struct problem_context pctx;
8407
8408 clear_problem_context(&pctx);
8409
8410 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
8411 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
8412 &save_inodes_count);
8413 if (pctx.errcode) {
8414 pctx.num = 1;
8415 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8416 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8417 return;
8418 }
8419 if (save_inodes_count == end)
8420 return;
8421
8422 for (i = save_inodes_count + 1; i <= end; i++) {
8423 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
8424 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
8425 for (i = save_inodes_count + 1; i <= end; i++)
8426 ext2fs_mark_inode_bitmap(fs->inode_map,
8427 i);
8428 ext2fs_mark_ib_dirty(fs);
8429 } else
8430 ext2fs_unmark_valid(fs);
8431 break;
8432 }
8433 }
8434
8435 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
8436 save_inodes_count, 0);
8437 if (pctx.errcode) {
8438 pctx.num = 2;
8439 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8440 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8441 return;
8442 }
8443}
8444
8445static void check_block_end(e2fsck_t ctx)
8446{
8447 ext2_filsys fs = ctx->fs;
8448 blk_t end, save_blocks_count, i;
8449 struct problem_context pctx;
8450
8451 clear_problem_context(&pctx);
8452
8453 end = fs->block_map->start +
8454 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
8455 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
8456 &save_blocks_count);
8457 if (pctx.errcode) {
8458 pctx.num = 3;
8459 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8460 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8461 return;
8462 }
8463 if (save_blocks_count == end)
8464 return;
8465
8466 for (i = save_blocks_count + 1; i <= end; i++) {
8467 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
8468 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
8469 for (i = save_blocks_count + 1; i <= end; i++)
8470 ext2fs_mark_block_bitmap(fs->block_map,
8471 i);
8472 ext2fs_mark_bb_dirty(fs);
8473 } else
8474 ext2fs_unmark_valid(fs);
8475 break;
8476 }
8477 }
8478
8479 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
8480 save_blocks_count, 0);
8481 if (pctx.errcode) {
8482 pctx.num = 4;
8483 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8484 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8485 return;
8486 }
8487}
8488
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008489static void e2fsck_pass5(e2fsck_t ctx)
8490{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008491 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008492
Rob Landley3e72c592006-04-06 22:49:04 +00008493 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008494
8495 clear_problem_context(&pctx);
8496
8497 if (!(ctx->options & E2F_OPT_PREEN))
8498 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
8499
8500 if (ctx->progress)
8501 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
8502 return;
8503
8504 e2fsck_read_bitmaps(ctx);
8505
8506 check_block_bitmaps(ctx);
8507 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8508 return;
8509 check_inode_bitmaps(ctx);
8510 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8511 return;
8512 check_inode_end(ctx);
8513 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8514 return;
8515 check_block_end(ctx);
8516 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8517 return;
8518
8519 ext2fs_free_inode_bitmap(ctx->inode_used_map);
8520 ctx->inode_used_map = 0;
8521 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
8522 ctx->inode_dir_map = 0;
8523 ext2fs_free_block_bitmap(ctx->block_found_map);
8524 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008525}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008526
8527/*
8528 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008529 */
8530
8531#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
8532#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
8533#define PR_NO_DEFAULT 0x000004 /* Default to no */
8534#define PR_MSG_ONLY 0x000008 /* Print message only */
8535
8536/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
8537
8538#define PR_FATAL 0x001000 /* Fatal error */
8539#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
8540 /* ask another */
8541#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
8542#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
8543#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
8544#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
8545#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
8546
8547
8548#define PROMPT_NONE 0
8549#define PROMPT_FIX 1
8550#define PROMPT_CLEAR 2
8551#define PROMPT_RELOCATE 3
8552#define PROMPT_ALLOCATE 4
8553#define PROMPT_EXPAND 5
8554#define PROMPT_CONNECT 6
8555#define PROMPT_CREATE 7
8556#define PROMPT_SALVAGE 8
8557#define PROMPT_TRUNCATE 9
8558#define PROMPT_CLEAR_INODE 10
8559#define PROMPT_ABORT 11
8560#define PROMPT_SPLIT 12
8561#define PROMPT_CONTINUE 13
8562#define PROMPT_CLONE 14
8563#define PROMPT_DELETE 15
8564#define PROMPT_SUPPRESS 16
8565#define PROMPT_UNLINK 17
8566#define PROMPT_CLEAR_HTREE 18
8567#define PROMPT_RECREATE 19
8568#define PROMPT_NULL 20
8569
8570struct e2fsck_problem {
8571 problem_t e2p_code;
8572 const char * e2p_description;
8573 char prompt;
8574 int flags;
8575 problem_t second_code;
8576};
8577
8578struct latch_descr {
8579 int latch_code;
8580 problem_t question;
8581 problem_t end_message;
8582 int flags;
8583};
8584
8585/*
8586 * These are the prompts which are used to ask the user if they want
8587 * to fix a problem.
8588 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008589static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008590 N_("(no prompt)"), /* 0 */
8591 N_("Fix"), /* 1 */
8592 N_("Clear"), /* 2 */
8593 N_("Relocate"), /* 3 */
8594 N_("Allocate"), /* 4 */
8595 N_("Expand"), /* 5 */
8596 N_("Connect to /lost+found"), /* 6 */
8597 N_("Create"), /* 7 */
8598 N_("Salvage"), /* 8 */
8599 N_("Truncate"), /* 9 */
8600 N_("Clear inode"), /* 10 */
8601 N_("Abort"), /* 11 */
8602 N_("Split"), /* 12 */
8603 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008604 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008605 N_("Delete file"), /* 15 */
8606 N_("Suppress messages"),/* 16 */
8607 N_("Unlink"), /* 17 */
8608 N_("Clear HTree index"),/* 18 */
8609 N_("Recreate"), /* 19 */
8610 "", /* 20 */
8611};
8612
8613/*
8614 * These messages are printed when we are preen mode and we will be
8615 * automatically fixing the problem.
8616 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008617static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008618 N_("(NONE)"), /* 0 */
8619 N_("FIXED"), /* 1 */
8620 N_("CLEARED"), /* 2 */
8621 N_("RELOCATED"), /* 3 */
8622 N_("ALLOCATED"), /* 4 */
8623 N_("EXPANDED"), /* 5 */
8624 N_("RECONNECTED"), /* 6 */
8625 N_("CREATED"), /* 7 */
8626 N_("SALVAGED"), /* 8 */
8627 N_("TRUNCATED"), /* 9 */
8628 N_("INODE CLEARED"), /* 10 */
8629 N_("ABORTED"), /* 11 */
8630 N_("SPLIT"), /* 12 */
8631 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008632 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008633 N_("FILE DELETED"), /* 15 */
8634 N_("SUPPRESSED"), /* 16 */
8635 N_("UNLINKED"), /* 17 */
8636 N_("HTREE INDEX CLEARED"),/* 18 */
8637 N_("WILL RECREATE"), /* 19 */
8638 "", /* 20 */
8639};
8640
8641static const struct e2fsck_problem problem_table[] = {
8642
8643 /* Pre-Pass 1 errors */
8644
8645 /* Block bitmap not in group */
8646 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
8647 PROMPT_RELOCATE, PR_LATCH_RELOC },
8648
8649 /* Inode bitmap not in group */
8650 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
8651 PROMPT_RELOCATE, PR_LATCH_RELOC },
8652
8653 /* Inode table not in group */
8654 { PR_0_ITABLE_NOT_GROUP,
8655 N_("@i table for @g %g is not in @g. (@b %b)\n"
8656 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
8657 PROMPT_RELOCATE, PR_LATCH_RELOC },
8658
8659 /* Superblock corrupt */
8660 { PR_0_SB_CORRUPT,
8661 N_("\nThe @S could not be read or does not describe a correct ext2\n"
8662 "@f. If the @v is valid and it really contains an ext2\n"
8663 "@f (and not swap or ufs or something else), then the @S\n"
8664 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
8665 " e2fsck -b %S <@v>\n\n"),
8666 PROMPT_NONE, PR_FATAL },
8667
8668 /* Filesystem size is wrong */
8669 { PR_0_FS_SIZE_WRONG,
8670 N_("The @f size (according to the @S) is %b @bs\n"
8671 "The physical size of the @v is %c @bs\n"
8672 "Either the @S or the partition table is likely to be corrupt!\n"),
8673 PROMPT_ABORT, 0 },
8674
8675 /* Fragments not supported */
8676 { PR_0_NO_FRAGMENTS,
8677 N_("@S @b_size = %b, fragsize = %c.\n"
8678 "This version of e2fsck does not support fragment sizes different\n"
8679 "from the @b size.\n"),
8680 PROMPT_NONE, PR_FATAL },
8681
8682 /* Bad blocks_per_group */
8683 { PR_0_BLOCKS_PER_GROUP,
8684 N_("@S @bs_per_group = %b, should have been %c\n"),
8685 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8686
8687 /* Bad first_data_block */
8688 { PR_0_FIRST_DATA_BLOCK,
8689 N_("@S first_data_@b = %b, should have been %c\n"),
8690 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8691
8692 /* Adding UUID to filesystem */
8693 { PR_0_ADD_UUID,
8694 N_("@f did not have a UUID; generating one.\n\n"),
8695 PROMPT_NONE, 0 },
8696
8697 /* Relocate hint */
8698 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00008699 N_("Note: if several inode or block bitmap blocks or part\n"
8700 "of the inode table require relocation, you may wish to try\n"
8701 "running e2fsck with the '-b %S' option first. The problem\n"
8702 "may lie only with the primary block group descriptors, and\n"
8703 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008704 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
8705
8706 /* Miscellaneous superblock corruption */
8707 { PR_0_MISC_CORRUPT_SUPER,
8708 N_("Corruption found in @S. (%s = %N).\n"),
8709 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8710
8711 /* Error determing physical device size of filesystem */
8712 { PR_0_GETSIZE_ERROR,
8713 N_("Error determining size of the physical @v: %m\n"),
8714 PROMPT_NONE, PR_FATAL },
8715
8716 /* Inode count in superblock is incorrect */
8717 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00008718 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008719 PROMPT_FIX, 0 },
8720
8721 { PR_0_HURD_CLEAR_FILETYPE,
8722 N_("The Hurd does not support the filetype feature.\n"),
8723 PROMPT_CLEAR, 0 },
8724
8725 /* Journal inode is invalid */
8726 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008727 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008728 PROMPT_CLEAR, PR_PREEN_OK },
8729
8730 /* The external journal has (unsupported) multiple filesystems */
8731 { PR_0_JOURNAL_UNSUPP_MULTIFS,
8732 N_("External @j has multiple @f users (unsupported).\n"),
8733 PROMPT_NONE, PR_FATAL },
8734
8735 /* Can't find external journal */
8736 { PR_0_CANT_FIND_JOURNAL,
8737 N_("Can't find external @j\n"),
8738 PROMPT_NONE, PR_FATAL },
8739
8740 /* External journal has bad superblock */
8741 { PR_0_EXT_JOURNAL_BAD_SUPER,
8742 N_("External @j has bad @S\n"),
8743 PROMPT_NONE, PR_FATAL },
8744
8745 /* Superblock has a bad journal UUID */
8746 { PR_0_JOURNAL_BAD_UUID,
8747 N_("External @j does not support this @f\n"),
8748 PROMPT_NONE, PR_FATAL },
8749
8750 /* Journal has an unknown superblock type */
8751 { PR_0_JOURNAL_UNSUPP_SUPER,
8752 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
8753 "It is likely that your copy of e2fsck is old and/or doesn't "
8754 "support this @j format.\n"
8755 "It is also possible the @j @S is corrupt.\n"),
8756 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
8757
8758 /* Journal superblock is corrupt */
8759 { PR_0_JOURNAL_BAD_SUPER,
8760 N_("Ext3 @j @S is corrupt.\n"),
8761 PROMPT_FIX, PR_PREEN_OK },
8762
8763 /* Superblock flag should be cleared */
8764 { PR_0_JOURNAL_HAS_JOURNAL,
8765 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
8766 PROMPT_CLEAR, PR_PREEN_OK },
8767
8768 /* Superblock flag is incorrect */
8769 { PR_0_JOURNAL_RECOVER_SET,
8770 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
8771 PROMPT_CLEAR, PR_PREEN_OK },
8772
8773 /* Journal has data, but recovery flag is clear */
8774 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008775 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008776 PROMPT_NONE, 0 },
8777
8778 /* Ask if we should clear the journal */
8779 { PR_0_JOURNAL_RESET_JOURNAL,
8780 N_("Clear @j"),
8781 PROMPT_NULL, PR_PREEN_NOMSG },
8782
8783 /* Ask if we should run the journal anyway */
8784 { PR_0_JOURNAL_RUN,
8785 N_("Run @j anyway"),
8786 PROMPT_NULL, 0 },
8787
8788 /* Run the journal by default */
8789 { PR_0_JOURNAL_RUN_DEFAULT,
8790 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
8791 PROMPT_NONE, 0 },
8792
8793 /* Clearing orphan inode */
8794 { PR_0_ORPHAN_CLEAR_INODE,
8795 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
8796 PROMPT_NONE, 0 },
8797
8798 /* Illegal block found in orphaned inode */
8799 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
8800 N_("@I @b #%B (%b) found in @o @i %i.\n"),
8801 PROMPT_NONE, 0 },
8802
8803 /* Already cleared block found in orphaned inode */
8804 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
8805 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
8806 PROMPT_NONE, 0 },
8807
8808 /* Illegal orphan inode in superblock */
8809 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
8810 N_("@I @o @i %i in @S.\n"),
8811 PROMPT_NONE, 0 },
8812
8813 /* Illegal inode in orphaned inode list */
8814 { PR_0_ORPHAN_ILLEGAL_INODE,
8815 N_("@I @i %i in @o @i list.\n"),
8816 PROMPT_NONE, 0 },
8817
8818 /* Filesystem revision is 0, but feature flags are set */
8819 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008820 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008821 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8822
8823 /* Journal superblock has an unknown read-only feature flag set */
8824 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
8825 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
8826 PROMPT_ABORT, 0 },
8827
8828 /* Journal superblock has an unknown incompatible feature flag set */
8829 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
8830 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
8831 PROMPT_ABORT, 0 },
8832
8833 /* Journal has unsupported version number */
8834 { PR_0_JOURNAL_UNSUPP_VERSION,
8835 N_("@j version not supported by this e2fsck.\n"),
8836 PROMPT_ABORT, 0 },
8837
8838 /* Moving journal to hidden file */
8839 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008840 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008841 PROMPT_NONE, 0 },
8842
8843 /* Error moving journal to hidden file */
8844 { PR_0_ERR_MOVE_JOURNAL,
8845 N_("Error moving @j: %m\n\n"),
8846 PROMPT_NONE, 0 },
8847
8848 /* Clearing V2 journal superblock */
8849 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008850 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008851 "Clearing fields beyond the V1 @j @S...\n\n"),
8852 PROMPT_NONE, 0 },
8853
8854 /* Backup journal inode blocks */
8855 { PR_0_BACKUP_JNL,
8856 N_("Backing up @j @i @b information.\n\n"),
8857 PROMPT_NONE, 0 },
8858
8859 /* Reserved blocks w/o resize_inode */
8860 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
8861 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
8862 "is %N; @s zero. "),
8863 PROMPT_FIX, 0 },
8864
8865 /* Resize_inode not enabled, but resize inode is non-zero */
8866 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008867 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008868 PROMPT_CLEAR, 0 },
8869
8870 /* Resize inode invalid */
8871 { PR_0_RESIZE_INODE_INVALID,
8872 N_("Resize @i not valid. "),
8873 PROMPT_RECREATE, 0 },
8874
8875 /* Pass 1 errors */
8876
8877 /* Pass 1: Checking inodes, blocks, and sizes */
8878 { PR_1_PASS_HEADER,
8879 N_("Pass 1: Checking @is, @bs, and sizes\n"),
8880 PROMPT_NONE, 0 },
8881
8882 /* Root directory is not an inode */
8883 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
8884 PROMPT_CLEAR, 0 },
8885
8886 /* Root directory has dtime set */
8887 { PR_1_ROOT_DTIME,
8888 N_("@r has dtime set (probably due to old mke2fs). "),
8889 PROMPT_FIX, PR_PREEN_OK },
8890
8891 /* Reserved inode has bad mode */
8892 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008893 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008894 PROMPT_CLEAR, PR_PREEN_OK },
8895
8896 /* Deleted inode has zero dtime */
8897 { PR_1_ZERO_DTIME,
8898 N_("@D @i %i has zero dtime. "),
8899 PROMPT_FIX, PR_PREEN_OK },
8900
8901 /* Inode in use, but dtime set */
8902 { PR_1_SET_DTIME,
8903 N_("@i %i is in use, but has dtime set. "),
8904 PROMPT_FIX, PR_PREEN_OK },
8905
8906 /* Zero-length directory */
8907 { PR_1_ZERO_LENGTH_DIR,
8908 N_("@i %i is a @z @d. "),
8909 PROMPT_CLEAR, PR_PREEN_OK },
8910
8911 /* Block bitmap conflicts with some other fs block */
8912 { PR_1_BB_CONFLICT,
8913 N_("@g %g's @b @B at %b @C.\n"),
8914 PROMPT_RELOCATE, 0 },
8915
8916 /* Inode bitmap conflicts with some other fs block */
8917 { PR_1_IB_CONFLICT,
8918 N_("@g %g's @i @B at %b @C.\n"),
8919 PROMPT_RELOCATE, 0 },
8920
8921 /* Inode table conflicts with some other fs block */
8922 { PR_1_ITABLE_CONFLICT,
8923 N_("@g %g's @i table at %b @C.\n"),
8924 PROMPT_RELOCATE, 0 },
8925
8926 /* Block bitmap is on a bad block */
8927 { PR_1_BB_BAD_BLOCK,
8928 N_("@g %g's @b @B (%b) is bad. "),
8929 PROMPT_RELOCATE, 0 },
8930
8931 /* Inode bitmap is on a bad block */
8932 { PR_1_IB_BAD_BLOCK,
8933 N_("@g %g's @i @B (%b) is bad. "),
8934 PROMPT_RELOCATE, 0 },
8935
8936 /* Inode has incorrect i_size */
8937 { PR_1_BAD_I_SIZE,
8938 N_("@i %i, i_size is %Is, @s %N. "),
8939 PROMPT_FIX, PR_PREEN_OK },
8940
8941 /* Inode has incorrect i_blocks */
8942 { PR_1_BAD_I_BLOCKS,
8943 N_("@i %i, i_@bs is %Ib, @s %N. "),
8944 PROMPT_FIX, PR_PREEN_OK },
8945
8946 /* Illegal blocknumber in inode */
8947 { PR_1_ILLEGAL_BLOCK_NUM,
8948 N_("@I @b #%B (%b) in @i %i. "),
8949 PROMPT_CLEAR, PR_LATCH_BLOCK },
8950
8951 /* Block number overlaps fs metadata */
8952 { PR_1_BLOCK_OVERLAPS_METADATA,
8953 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
8954 PROMPT_CLEAR, PR_LATCH_BLOCK },
8955
8956 /* Inode has illegal blocks (latch question) */
8957 { PR_1_INODE_BLOCK_LATCH,
8958 N_("@i %i has illegal @b(s). "),
8959 PROMPT_CLEAR, 0 },
8960
8961 /* Too many bad blocks in inode */
8962 { PR_1_TOO_MANY_BAD_BLOCKS,
8963 N_("Too many illegal @bs in @i %i.\n"),
8964 PROMPT_CLEAR_INODE, PR_NO_OK },
8965
8966 /* Illegal block number in bad block inode */
8967 { PR_1_BB_ILLEGAL_BLOCK_NUM,
8968 N_("@I @b #%B (%b) in bad @b @i. "),
8969 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8970
8971 /* Bad block inode has illegal blocks (latch question) */
8972 { PR_1_INODE_BBLOCK_LATCH,
8973 N_("Bad @b @i has illegal @b(s). "),
8974 PROMPT_CLEAR, 0 },
8975
8976 /* Duplicate or bad blocks in use! */
8977 { PR_1_DUP_BLOCKS_PREENSTOP,
8978 N_("Duplicate or bad @b in use!\n"),
8979 PROMPT_NONE, 0 },
8980
8981 /* Bad block used as bad block indirect block */
8982 { PR_1_BBINODE_BAD_METABLOCK,
8983 N_("Bad @b %b used as bad @b @i indirect @b. "),
8984 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8985
8986 /* Inconsistency can't be fixed prompt */
8987 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
8988 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
8989 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
8990 "in the @f.\n"),
8991 PROMPT_CONTINUE, PR_PREEN_NOMSG },
8992
8993 /* Bad primary block */
8994 { PR_1_BAD_PRIMARY_BLOCK,
8995 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
8996 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
8997
8998 /* Bad primary block prompt */
8999 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00009000 N_("You can remove this @b from the bad @b list and hope\n"
9001 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009002 PROMPT_CLEAR, PR_PREEN_NOMSG },
9003
9004 /* Bad primary superblock */
9005 { PR_1_BAD_PRIMARY_SUPERBLOCK,
9006 N_("The primary @S (%b) is on the bad @b list.\n"),
9007 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
9008
9009 /* Bad primary block group descriptors */
9010 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
9011 N_("Block %b in the primary @g descriptors "
9012 "is on the bad @b list\n"),
9013 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
9014
9015 /* Bad superblock in group */
9016 { PR_1_BAD_SUPERBLOCK,
9017 N_("Warning: Group %g's @S (%b) is bad.\n"),
9018 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9019
9020 /* Bad block group descriptors in group */
9021 { PR_1_BAD_GROUP_DESCRIPTORS,
9022 N_("Warning: Group %g's copy of the @g descriptors has a bad "
9023 "@b (%b).\n"),
9024 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9025
9026 /* Block claimed for no reason */
9027 { PR_1_PROGERR_CLAIMED_BLOCK,
9028 N_("Programming error? @b #%b claimed for no reason in "
9029 "process_bad_@b.\n"),
9030 PROMPT_NONE, PR_PREEN_OK },
9031
9032 /* Error allocating blocks for relocating metadata */
9033 { PR_1_RELOC_BLOCK_ALLOCATE,
9034 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
9035 PROMPT_NONE, PR_PREEN_OK },
9036
9037 /* Error allocating block buffer during relocation process */
9038 { PR_1_RELOC_MEMORY_ALLOCATE,
9039 N_("@A @b buffer for relocating %s\n"),
9040 PROMPT_NONE, PR_PREEN_OK },
9041
9042 /* Relocating metadata group information from X to Y */
9043 { PR_1_RELOC_FROM_TO,
9044 N_("Relocating @g %g's %s from %b to %c...\n"),
9045 PROMPT_NONE, PR_PREEN_OK },
9046
9047 /* Relocating metatdata group information to X */
9048 { PR_1_RELOC_TO,
9049 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
9050 PROMPT_NONE, PR_PREEN_OK },
9051
9052 /* Block read error during relocation process */
9053 { PR_1_RELOC_READ_ERR,
9054 N_("Warning: could not read @b %b of %s: %m\n"),
9055 PROMPT_NONE, PR_PREEN_OK },
9056
9057 /* Block write error during relocation process */
9058 { PR_1_RELOC_WRITE_ERR,
9059 N_("Warning: could not write @b %b for %s: %m\n"),
9060 PROMPT_NONE, PR_PREEN_OK },
9061
9062 /* Error allocating inode bitmap */
9063 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009064 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009065 PROMPT_NONE, PR_FATAL },
9066
9067 /* Error allocating block bitmap */
9068 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009069 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009070 PROMPT_NONE, PR_FATAL },
9071
9072 /* Error allocating icount structure */
9073 { PR_1_ALLOCATE_ICOUNT,
9074 N_("@A icount link information: %m\n"),
9075 PROMPT_NONE, PR_FATAL },
9076
9077 /* Error allocating dbcount */
9078 { PR_1_ALLOCATE_DBCOUNT,
9079 N_("@A @d @b array: %m\n"),
9080 PROMPT_NONE, PR_FATAL },
9081
9082 /* Error while scanning inodes */
9083 { PR_1_ISCAN_ERROR,
9084 N_("Error while scanning @is (%i): %m\n"),
9085 PROMPT_NONE, PR_FATAL },
9086
9087 /* Error while iterating over blocks */
9088 { PR_1_BLOCK_ITERATE,
9089 N_("Error while iterating over @bs in @i %i: %m\n"),
9090 PROMPT_NONE, PR_FATAL },
9091
9092 /* Error while storing inode count information */
9093 { PR_1_ICOUNT_STORE,
9094 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
9095 PROMPT_NONE, PR_FATAL },
9096
9097 /* Error while storing directory block information */
9098 { PR_1_ADD_DBLOCK,
9099 N_("Error storing @d @b information "
9100 "(@i=%i, @b=%b, num=%N): %m\n"),
9101 PROMPT_NONE, PR_FATAL },
9102
9103 /* Error while reading inode (for clearing) */
9104 { PR_1_READ_INODE,
9105 N_("Error reading @i %i: %m\n"),
9106 PROMPT_NONE, PR_FATAL },
9107
9108 /* Suppress messages prompt */
9109 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
9110
9111 /* Imagic flag set on an inode when filesystem doesn't support it */
9112 { PR_1_SET_IMAGIC,
9113 N_("@i %i has imagic flag set. "),
9114 PROMPT_CLEAR, 0 },
9115
9116 /* Immutable flag set on a device or socket inode */
9117 { PR_1_SET_IMMUTABLE,
9118 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
9119 "or append-only flag set. "),
9120 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
9121
9122 /* Compression flag set on an inode when filesystem doesn't support it */
9123 { PR_1_COMPR_SET,
9124 N_("@i %i has @cion flag set on @f without @cion support. "),
9125 PROMPT_CLEAR, 0 },
9126
9127 /* Non-zero size for device, fifo or socket inode */
9128 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00009129 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009130 PROMPT_FIX, PR_PREEN_OK },
9131
9132 /* Filesystem revision is 0, but feature flags are set */
9133 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00009134 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009135 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
9136
9137 /* Journal inode is not in use, but contains data */
9138 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009139 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009140 PROMPT_CLEAR, PR_PREEN_OK },
9141
9142 /* Journal has bad mode */
9143 { PR_1_JOURNAL_BAD_MODE,
9144 N_("@j is not regular file. "),
9145 PROMPT_FIX, PR_PREEN_OK },
9146
9147 /* Deal with inodes that were part of orphan linked list */
9148 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +00009149 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009150 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
9151
9152 /* Deal with inodes that were part of corrupted orphan linked
9153 list (latch question) */
9154 { PR_1_ORPHAN_LIST_REFUGEES,
9155 N_("@is that were part of a corrupted orphan linked list found. "),
9156 PROMPT_FIX, 0 },
9157
9158 /* Error allocating refcount structure */
9159 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009160 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009161 PROMPT_NONE, PR_FATAL },
9162
9163 /* Error reading extended attribute block */
9164 { PR_1_READ_EA_BLOCK,
9165 N_("Error reading @a @b %b for @i %i. "),
9166 PROMPT_CLEAR, 0 },
9167
9168 /* Invalid extended attribute block */
9169 { PR_1_BAD_EA_BLOCK,
9170 N_("@i %i has a bad @a @b %b. "),
9171 PROMPT_CLEAR, 0 },
9172
9173 /* Error reading Extended Attribute block while fixing refcount */
9174 { PR_1_EXTATTR_READ_ABORT,
9175 N_("Error reading @a @b %b (%m). "),
9176 PROMPT_ABORT, 0 },
9177
9178 /* Extended attribute reference count incorrect */
9179 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009180 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009181 PROMPT_FIX, 0 },
9182
9183 /* Error writing Extended Attribute block while fixing refcount */
9184 { PR_1_EXTATTR_WRITE,
9185 N_("Error writing @a @b %b (%m). "),
9186 PROMPT_ABORT, 0 },
9187
9188 /* Multiple EA blocks not supported */
9189 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00009190 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009191 PROMPT_CLEAR, 0},
9192
9193 /* Error allocating EA region allocation structure */
9194 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +00009195 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009196 PROMPT_ABORT, 0},
9197
9198 /* Error EA allocation collision */
9199 { PR_1_EA_ALLOC_COLLISION,
9200 N_("@a @b %b is corrupt (allocation collision). "),
9201 PROMPT_CLEAR, 0},
9202
9203 /* Bad extended attribute name */
9204 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009205 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009206 PROMPT_CLEAR, 0},
9207
9208 /* Bad extended attribute value */
9209 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +00009210 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009211 PROMPT_CLEAR, 0},
9212
9213 /* Inode too big (latch question) */
9214 { PR_1_INODE_TOOBIG,
9215 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
9216
9217 /* Directory too big */
9218 { PR_1_TOOBIG_DIR,
9219 N_("@b #%B (%b) causes @d to be too big. "),
9220 PROMPT_CLEAR, PR_LATCH_TOOBIG },
9221
9222 /* Regular file too big */
9223 { PR_1_TOOBIG_REG,
9224 N_("@b #%B (%b) causes file to be too big. "),
9225 PROMPT_CLEAR, PR_LATCH_TOOBIG },
9226
9227 /* Symlink too big */
9228 { PR_1_TOOBIG_SYMLINK,
9229 N_("@b #%B (%b) causes symlink to be too big. "),
9230 PROMPT_CLEAR, PR_LATCH_TOOBIG },
9231
9232 /* INDEX_FL flag set on a non-HTREE filesystem */
9233 { PR_1_HTREE_SET,
9234 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
9235 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9236
9237 /* INDEX_FL flag set on a non-directory */
9238 { PR_1_HTREE_NODIR,
9239 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
9240 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9241
9242 /* Invalid root node in HTREE directory */
9243 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009244 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009245 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9246
9247 /* Unsupported hash version in HTREE directory */
9248 { PR_1_HTREE_HASHV,
9249 N_("@h %i has an unsupported hash version (%N)\n"),
9250 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9251
9252 /* Incompatible flag in HTREE root node */
9253 { PR_1_HTREE_INCOMPAT,
9254 N_("@h %i uses an incompatible htree root node flag.\n"),
9255 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9256
9257 /* HTREE too deep */
9258 { PR_1_HTREE_DEPTH,
9259 N_("@h %i has a tree depth (%N) which is too big\n"),
9260 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9261
9262 /* Bad block has indirect block that conflicts with filesystem block */
9263 { PR_1_BB_FS_BLOCK,
9264 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
9265 "@f metadata. "),
9266 PROMPT_CLEAR, PR_LATCH_BBLOCK },
9267
9268 /* Resize inode failed */
9269 { PR_1_RESIZE_INODE_CREATE,
9270 N_("Resize @i (re)creation failed: %m."),
9271 PROMPT_ABORT, 0 },
9272
9273 /* invalid inode->i_extra_isize */
9274 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00009275 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009276 PROMPT_FIX, PR_PREEN_OK },
9277
9278 /* invalid ea entry->e_name_len */
9279 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009280 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009281 PROMPT_CLEAR, PR_PREEN_OK },
9282
9283 /* invalid ea entry->e_value_size */
9284 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00009285 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009286 PROMPT_CLEAR, PR_PREEN_OK },
9287
9288 /* invalid ea entry->e_value_offs */
9289 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +00009290 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009291 PROMPT_CLEAR, PR_PREEN_OK },
9292
9293 /* invalid ea entry->e_value_block */
9294 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00009295 N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009296 PROMPT_CLEAR, PR_PREEN_OK },
9297
9298 /* invalid ea entry->e_hash */
9299 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +00009300 N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009301 PROMPT_CLEAR, PR_PREEN_OK },
9302
9303 /* Pass 1b errors */
9304
9305 /* Pass 1B: Rescan for duplicate/bad blocks */
9306 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00009307 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
9308 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009309 PROMPT_NONE, 0 },
9310
9311 /* Duplicate/bad block(s) header */
9312 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00009313 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009314 PROMPT_NONE, 0 },
9315
9316 /* Duplicate/bad block(s) in inode */
9317 { PR_1B_DUP_BLOCK,
9318 " %b",
9319 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
9320
9321 /* Duplicate/bad block(s) end */
9322 { PR_1B_DUP_BLOCK_END,
9323 "\n",
9324 PROMPT_NONE, PR_PREEN_NOHDR },
9325
9326 /* Error while scanning inodes */
9327 { PR_1B_ISCAN_ERROR,
9328 N_("Error while scanning inodes (%i): %m\n"),
9329 PROMPT_NONE, PR_FATAL },
9330
9331 /* Error allocating inode bitmap */
9332 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009333 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009334 PROMPT_NONE, PR_FATAL },
9335
9336 /* Error while iterating over blocks */
9337 { PR_1B_BLOCK_ITERATE,
9338 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
9339 PROMPT_NONE, 0 },
9340
9341 /* Error adjusting EA refcount */
9342 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009343 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009344 PROMPT_NONE, 0 },
9345
9346
Mike Frysinger874af852006-03-08 07:03:27 +00009347 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009348 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00009349 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009350 PROMPT_NONE, 0 },
9351
9352
Mike Frysinger874af852006-03-08 07:03:27 +00009353 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009354 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00009355 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009356 PROMPT_NONE, 0 },
9357
9358 /* File has duplicate blocks */
9359 { PR_1D_DUP_FILE,
9360 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +00009361 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009362 PROMPT_NONE, 0 },
9363
9364 /* List of files sharing duplicate blocks */
9365 { PR_1D_DUP_FILE_LIST,
9366 N_("\t%Q (@i #%i, mod time %IM)\n"),
9367 PROMPT_NONE, 0 },
9368
9369 /* File sharing blocks with filesystem metadata */
9370 { PR_1D_SHARE_METADATA,
9371 N_("\t<@f metadata>\n"),
9372 PROMPT_NONE, 0 },
9373
9374 /* Report of how many duplicate/bad inodes */
9375 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +00009376 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009377 PROMPT_NONE, 0 },
9378
9379 /* Duplicated blocks already reassigned or cloned. */
9380 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +00009381 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009382 PROMPT_NONE, 0 },
9383
9384 /* Clone duplicate/bad blocks? */
9385 { PR_1D_CLONE_QUESTION,
9386 "", PROMPT_CLONE, PR_NO_OK },
9387
9388 /* Delete file? */
9389 { PR_1D_DELETE_QUESTION,
9390 "", PROMPT_DELETE, 0 },
9391
9392 /* Couldn't clone file (error) */
9393 { PR_1D_CLONE_ERROR,
9394 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
9395
9396 /* Pass 2 errors */
9397
9398 /* Pass 2: Checking directory structure */
9399 { PR_2_PASS_HEADER,
9400 N_("Pass 2: Checking @d structure\n"),
9401 PROMPT_NONE, 0 },
9402
9403 /* Bad inode number for '.' */
9404 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009405 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009406 PROMPT_FIX, 0 },
9407
9408 /* Directory entry has bad inode number */
9409 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +00009410 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009411 PROMPT_CLEAR, 0 },
9412
9413 /* Directory entry has deleted or unused inode */
9414 { PR_2_UNUSED_INODE,
9415 N_("@E has @D/unused @i %Di. "),
9416 PROMPT_CLEAR, PR_PREEN_OK },
9417
9418 /* Directry entry is link to '.' */
9419 { PR_2_LINK_DOT,
9420 N_("@E @L to '.' "),
9421 PROMPT_CLEAR, 0 },
9422
9423 /* Directory entry points to inode now located in a bad block */
9424 { PR_2_BB_INODE,
9425 N_("@E points to @i (%Di) located in a bad @b.\n"),
9426 PROMPT_CLEAR, 0 },
9427
9428 /* Directory entry contains a link to a directory */
9429 { PR_2_LINK_DIR,
9430 N_("@E @L to @d %P (%Di).\n"),
9431 PROMPT_CLEAR, 0 },
9432
9433 /* Directory entry contains a link to the root directry */
9434 { PR_2_LINK_ROOT,
9435 N_("@E @L to the @r.\n"),
9436 PROMPT_CLEAR, 0 },
9437
9438 /* Directory entry has illegal characters in its name */
9439 { PR_2_BAD_NAME,
9440 N_("@E has illegal characters in its name.\n"),
9441 PROMPT_FIX, 0 },
9442
9443 /* Missing '.' in directory inode */
9444 { PR_2_MISSING_DOT,
9445 N_("Missing '.' in @d @i %i.\n"),
9446 PROMPT_FIX, 0 },
9447
9448 /* Missing '..' in directory inode */
9449 { PR_2_MISSING_DOT_DOT,
9450 N_("Missing '..' in @d @i %i.\n"),
9451 PROMPT_FIX, 0 },
9452
9453 /* First entry in directory inode doesn't contain '.' */
9454 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009455 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009456 PROMPT_FIX, 0 },
9457
9458 /* Second entry in directory inode doesn't contain '..' */
9459 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009460 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009461 PROMPT_FIX, 0 },
9462
9463 /* i_faddr should be zero */
9464 { PR_2_FADDR_ZERO,
9465 N_("i_faddr @F %IF, @s zero.\n"),
9466 PROMPT_CLEAR, 0 },
9467
9468 /* i_file_acl should be zero */
9469 { PR_2_FILE_ACL_ZERO,
9470 N_("i_file_acl @F %If, @s zero.\n"),
9471 PROMPT_CLEAR, 0 },
9472
9473 /* i_dir_acl should be zero */
9474 { PR_2_DIR_ACL_ZERO,
9475 N_("i_dir_acl @F %Id, @s zero.\n"),
9476 PROMPT_CLEAR, 0 },
9477
9478 /* i_frag should be zero */
9479 { PR_2_FRAG_ZERO,
9480 N_("i_frag @F %N, @s zero.\n"),
9481 PROMPT_CLEAR, 0 },
9482
9483 /* i_fsize should be zero */
9484 { PR_2_FSIZE_ZERO,
9485 N_("i_fsize @F %N, @s zero.\n"),
9486 PROMPT_CLEAR, 0 },
9487
9488 /* inode has bad mode */
9489 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009490 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009491 PROMPT_CLEAR, 0 },
9492
9493 /* directory corrupted */
9494 { PR_2_DIR_CORRUPTED,
9495 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
9496 PROMPT_SALVAGE, 0 },
9497
9498 /* filename too long */
9499 { PR_2_FILENAME_LONG,
9500 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
9501 PROMPT_TRUNCATE, 0 },
9502
9503 /* Directory inode has a missing block (hole) */
9504 { PR_2_DIRECTORY_HOLE,
9505 N_("@d @i %i has an unallocated @b #%B. "),
9506 PROMPT_ALLOCATE, 0 },
9507
9508 /* '.' is not NULL terminated */
9509 { PR_2_DOT_NULL_TERM,
9510 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
9511 PROMPT_FIX, 0 },
9512
9513 /* '..' is not NULL terminated */
9514 { PR_2_DOT_DOT_NULL_TERM,
9515 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
9516 PROMPT_FIX, 0 },
9517
9518 /* Illegal character device inode */
9519 { PR_2_BAD_CHAR_DEV,
9520 N_("@i %i (%Q) is an @I character @v.\n"),
9521 PROMPT_CLEAR, 0 },
9522
9523 /* Illegal block device inode */
9524 { PR_2_BAD_BLOCK_DEV,
9525 N_("@i %i (%Q) is an @I @b @v.\n"),
9526 PROMPT_CLEAR, 0 },
9527
9528 /* Duplicate '.' entry */
9529 { PR_2_DUP_DOT,
9530 N_("@E is duplicate '.' @e.\n"),
9531 PROMPT_FIX, 0 },
9532
9533 /* Duplicate '..' entry */
9534 { PR_2_DUP_DOT_DOT,
9535 N_("@E is duplicate '..' @e.\n"),
9536 PROMPT_FIX, 0 },
9537
9538 /* Internal error: couldn't find dir_info */
9539 { PR_2_NO_DIRINFO,
9540 N_("Internal error: couldn't find dir_info for %i.\n"),
9541 PROMPT_NONE, PR_FATAL },
9542
9543 /* Final rec_len is wrong */
9544 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009545 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009546 PROMPT_FIX, 0 },
9547
9548 /* Error allocating icount structure */
9549 { PR_2_ALLOCATE_ICOUNT,
9550 N_("@A icount structure: %m\n"),
9551 PROMPT_NONE, PR_FATAL },
9552
9553 /* Error iterating over directory blocks */
9554 { PR_2_DBLIST_ITERATE,
9555 N_("Error iterating over @d @bs: %m\n"),
9556 PROMPT_NONE, PR_FATAL },
9557
9558 /* Error reading directory block */
9559 { PR_2_READ_DIRBLOCK,
9560 N_("Error reading @d @b %b (@i %i): %m\n"),
9561 PROMPT_CONTINUE, 0 },
9562
9563 /* Error writing directory block */
9564 { PR_2_WRITE_DIRBLOCK,
9565 N_("Error writing @d @b %b (@i %i): %m\n"),
9566 PROMPT_CONTINUE, 0 },
9567
9568 /* Error allocating new directory block */
9569 { PR_2_ALLOC_DIRBOCK,
9570 N_("@A new @d @b for @i %i (%s): %m\n"),
9571 PROMPT_NONE, 0 },
9572
9573 /* Error deallocating inode */
9574 { PR_2_DEALLOC_INODE,
9575 N_("Error deallocating @i %i: %m\n"),
9576 PROMPT_NONE, PR_FATAL },
9577
9578 /* Directory entry for '.' is big. Split? */
9579 { PR_2_SPLIT_DOT,
9580 N_("@d @e for '.' is big. "),
9581 PROMPT_SPLIT, PR_NO_OK },
9582
9583 /* Illegal FIFO inode */
9584 { PR_2_BAD_FIFO,
9585 N_("@i %i (%Q) is an @I FIFO.\n"),
9586 PROMPT_CLEAR, 0 },
9587
9588 /* Illegal socket inode */
9589 { PR_2_BAD_SOCKET,
9590 N_("@i %i (%Q) is an @I socket.\n"),
9591 PROMPT_CLEAR, 0 },
9592
9593 /* Directory filetype not set */
9594 { PR_2_SET_FILETYPE,
9595 N_("Setting filetype for @E to %N.\n"),
9596 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
9597
9598 /* Directory filetype incorrect */
9599 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +00009600 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009601 PROMPT_FIX, 0 },
9602
9603 /* Directory filetype set on filesystem */
9604 { PR_2_CLEAR_FILETYPE,
9605 N_("@E has filetype set.\n"),
9606 PROMPT_CLEAR, PR_PREEN_OK },
9607
9608 /* Directory filename is null */
9609 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009610 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009611 PROMPT_CLEAR, 0 },
9612
9613 /* Invalid symlink */
9614 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +00009615 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009616 PROMPT_CLEAR, 0 },
9617
9618 /* i_file_acl (extended attribute block) is bad */
9619 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +00009620 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009621 PROMPT_CLEAR, 0 },
9622
9623 /* Filesystem contains large files, but has no such flag in sb */
9624 { PR_2_FEATURE_LARGE_FILES,
9625 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
9626 PROMPT_FIX, 0 },
9627
9628 /* Node in HTREE directory not referenced */
9629 { PR_2_HTREE_NOTREF,
9630 N_("@p @h %d: node (%B) not referenced\n"),
9631 PROMPT_NONE, 0 },
9632
9633 /* Node in HTREE directory referenced twice */
9634 { PR_2_HTREE_DUPREF,
9635 N_("@p @h %d: node (%B) referenced twice\n"),
9636 PROMPT_NONE, 0 },
9637
9638 /* Node in HTREE directory has bad min hash */
9639 { PR_2_HTREE_MIN_HASH,
9640 N_("@p @h %d: node (%B) has bad min hash\n"),
9641 PROMPT_NONE, 0 },
9642
9643 /* Node in HTREE directory has bad max hash */
9644 { PR_2_HTREE_MAX_HASH,
9645 N_("@p @h %d: node (%B) has bad max hash\n"),
9646 PROMPT_NONE, 0 },
9647
9648 /* Clear invalid HTREE directory */
9649 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009650 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009651
9652 /* Bad block in htree interior node */
9653 { PR_2_HTREE_BADBLK,
9654 N_("@p @h %d (%q): bad @b number %b.\n"),
9655 PROMPT_CLEAR_HTREE, 0 },
9656
9657 /* Error adjusting EA refcount */
9658 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009659 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009660 PROMPT_NONE, PR_FATAL },
9661
9662 /* Invalid HTREE root node */
9663 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009664 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009665 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9666
9667 /* Invalid HTREE limit */
9668 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +00009669 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009670 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9671
9672 /* Invalid HTREE count */
9673 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009674 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009675 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9676
9677 /* HTREE interior node has out-of-order hashes in table */
9678 { PR_2_HTREE_HASH_ORDER,
9679 N_("@p @h %d: node (%B) has an unordered hash table\n"),
9680 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9681
Mike Frysinger874af852006-03-08 07:03:27 +00009682 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009683 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +00009684 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009685 PROMPT_NONE, 0 },
9686
9687 /* Duplicate directory entry found */
9688 { PR_2_DUPLICATE_DIRENT,
9689 N_("Duplicate @E found. "),
9690 PROMPT_CLEAR, 0 },
9691
9692 /* Non-unique filename found */
9693 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
9694 N_("@E has a non-unique filename.\nRename to %s"),
9695 PROMPT_NULL, 0 },
9696
9697 /* Duplicate directory entry found */
9698 { PR_2_REPORT_DUP_DIRENT,
9699 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
9700 PROMPT_NONE, 0 },
9701
9702 /* Pass 3 errors */
9703
9704 /* Pass 3: Checking directory connectivity */
9705 { PR_3_PASS_HEADER,
9706 N_("Pass 3: Checking @d connectivity\n"),
9707 PROMPT_NONE, 0 },
9708
9709 /* Root inode not allocated */
9710 { PR_3_NO_ROOT_INODE,
9711 N_("@r not allocated. "),
9712 PROMPT_ALLOCATE, 0 },
9713
9714 /* No room in lost+found */
9715 { PR_3_EXPAND_LF_DIR,
9716 N_("No room in @l @d. "),
9717 PROMPT_EXPAND, 0 },
9718
9719 /* Unconnected directory inode */
9720 { PR_3_UNCONNECTED_DIR,
9721 N_("Unconnected @d @i %i (%p)\n"),
9722 PROMPT_CONNECT, 0 },
9723
9724 /* /lost+found not found */
9725 { PR_3_NO_LF_DIR,
9726 N_("/@l not found. "),
9727 PROMPT_CREATE, PR_PREEN_OK },
9728
9729 /* .. entry is incorrect */
9730 { PR_3_BAD_DOT_DOT,
9731 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
9732 PROMPT_FIX, 0 },
9733
9734 /* Bad or non-existent /lost+found. Cannot reconnect */
9735 { PR_3_NO_LPF,
9736 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
9737 PROMPT_NONE, 0 },
9738
9739 /* Could not expand /lost+found */
9740 { PR_3_CANT_EXPAND_LPF,
9741 N_("Could not expand /@l: %m\n"),
9742 PROMPT_NONE, 0 },
9743
9744 /* Could not reconnect inode */
9745 { PR_3_CANT_RECONNECT,
9746 N_("Could not reconnect %i: %m\n"),
9747 PROMPT_NONE, 0 },
9748
9749 /* Error while trying to find /lost+found */
9750 { PR_3_ERR_FIND_LPF,
9751 N_("Error while trying to find /@l: %m\n"),
9752 PROMPT_NONE, 0 },
9753
9754 /* Error in ext2fs_new_block while creating /lost+found */
9755 { PR_3_ERR_LPF_NEW_BLOCK,
9756 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
9757 PROMPT_NONE, 0 },
9758
9759 /* Error in ext2fs_new_inode while creating /lost+found */
9760 { PR_3_ERR_LPF_NEW_INODE,
9761 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
9762 PROMPT_NONE, 0 },
9763
9764 /* Error in ext2fs_new_dir_block while creating /lost+found */
9765 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
9766 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
9767 PROMPT_NONE, 0 },
9768
9769 /* Error while writing directory block for /lost+found */
9770 { PR_3_ERR_LPF_WRITE_BLOCK,
9771 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
9772 PROMPT_NONE, 0 },
9773
9774 /* Error while adjusting inode count */
9775 { PR_3_ADJUST_INODE,
9776 N_("Error while adjusting @i count on @i %i\n"),
9777 PROMPT_NONE, 0 },
9778
9779 /* Couldn't fix parent directory -- error */
9780 { PR_3_FIX_PARENT_ERR,
9781 N_("Couldn't fix parent of @i %i: %m\n\n"),
9782 PROMPT_NONE, 0 },
9783
9784 /* Couldn't fix parent directory -- couldn't find it */
9785 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +00009786 N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009787 PROMPT_NONE, 0 },
9788
9789 /* Error allocating inode bitmap */
9790 { PR_3_ALLOCATE_IBITMAP_ERROR,
9791 N_("@A @i @B (%N): %m\n"),
9792 PROMPT_NONE, PR_FATAL },
9793
9794 /* Error creating root directory */
9795 { PR_3_CREATE_ROOT_ERROR,
9796 N_("Error creating root @d (%s): %m\n"),
9797 PROMPT_NONE, PR_FATAL },
9798
9799 /* Error creating lost and found directory */
9800 { PR_3_CREATE_LPF_ERROR,
9801 N_("Error creating /@l @d (%s): %m\n"),
9802 PROMPT_NONE, PR_FATAL },
9803
9804 /* Root inode is not directory; aborting */
9805 { PR_3_ROOT_NOT_DIR_ABORT,
9806 N_("@r is not a @d; aborting.\n"),
9807 PROMPT_NONE, PR_FATAL },
9808
9809 /* Cannot proceed without a root inode. */
9810 { PR_3_NO_ROOT_INODE_ABORT,
9811 N_("Cannot proceed without a @r.\n"),
9812 PROMPT_NONE, PR_FATAL },
9813
9814 /* Internal error: couldn't find dir_info */
9815 { PR_3_NO_DIRINFO,
9816 N_("Internal error: couldn't find dir_info for %i.\n"),
9817 PROMPT_NONE, PR_FATAL },
9818
9819 /* Lost+found not a directory */
9820 { PR_3_LPF_NOTDIR,
9821 N_("/@l is not a @d (ino=%i)\n"),
9822 PROMPT_UNLINK, 0 },
9823
9824 /* Pass 3A Directory Optimization */
9825
9826 /* Pass 3A: Optimizing directories */
9827 { PR_3A_PASS_HEADER,
9828 N_("Pass 3A: Optimizing directories\n"),
9829 PROMPT_NONE, PR_PREEN_NOMSG },
9830
9831 /* Error iterating over directories */
9832 { PR_3A_OPTIMIZE_ITER,
9833 N_("Failed to create dirs_to_hash iterator: %m"),
9834 PROMPT_NONE, 0 },
9835
9836 /* Error rehash directory */
9837 { PR_3A_OPTIMIZE_DIR_ERR,
9838 N_("Failed to optimize directory %q (%d): %m"),
9839 PROMPT_NONE, 0 },
9840
9841 /* Rehashing dir header */
9842 { PR_3A_OPTIMIZE_DIR_HEADER,
9843 N_("Optimizing directories: "),
9844 PROMPT_NONE, PR_MSG_ONLY },
9845
9846 /* Rehashing directory %d */
9847 { PR_3A_OPTIMIZE_DIR,
9848 " %d",
9849 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
9850
9851 /* Rehashing dir end */
9852 { PR_3A_OPTIMIZE_DIR_END,
9853 "\n",
9854 PROMPT_NONE, PR_PREEN_NOHDR },
9855
9856 /* Pass 4 errors */
9857
9858 /* Pass 4: Checking reference counts */
9859 { PR_4_PASS_HEADER,
9860 N_("Pass 4: Checking reference counts\n"),
9861 PROMPT_NONE, 0 },
9862
9863 /* Unattached zero-length inode */
9864 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009865 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009866 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
9867
9868 /* Unattached inode */
9869 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009870 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009871 PROMPT_CONNECT, 0 },
9872
9873 /* Inode ref count wrong */
9874 { PR_4_BAD_REF_COUNT,
9875 N_("@i %i ref count is %Il, @s %N. "),
9876 PROMPT_FIX, PR_PREEN_OK },
9877
9878 { PR_4_INCONSISTENT_COUNT,
9879 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
9880 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
9881 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +00009882 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009883 PROMPT_NONE, 0 },
9884
9885 /* Pass 5 errors */
9886
9887 /* Pass 5: Checking group summary information */
9888 { PR_5_PASS_HEADER,
9889 N_("Pass 5: Checking @g summary information\n"),
9890 PROMPT_NONE, 0 },
9891
9892 /* Padding at end of inode bitmap is not set. */
9893 { PR_5_INODE_BMAP_PADDING,
9894 N_("Padding at end of @i @B is not set. "),
9895 PROMPT_FIX, PR_PREEN_OK },
9896
9897 /* Padding at end of block bitmap is not set. */
9898 { PR_5_BLOCK_BMAP_PADDING,
9899 N_("Padding at end of @b @B is not set. "),
9900 PROMPT_FIX, PR_PREEN_OK },
9901
9902 /* Block bitmap differences header */
9903 { PR_5_BLOCK_BITMAP_HEADER,
9904 N_("@b @B differences: "),
9905 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
9906
9907 /* Block not used, but marked in bitmap */
9908 { PR_5_BLOCK_UNUSED,
9909 " -%b",
9910 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9911
9912 /* Block used, but not marked used in bitmap */
9913 { PR_5_BLOCK_USED,
9914 " +%b",
9915 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9916
9917 /* Block bitmap differences end */
9918 { PR_5_BLOCK_BITMAP_END,
9919 "\n",
9920 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9921
9922 /* Inode bitmap differences header */
9923 { PR_5_INODE_BITMAP_HEADER,
9924 N_("@i @B differences: "),
9925 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9926
9927 /* Inode not used, but marked in bitmap */
9928 { PR_5_INODE_UNUSED,
9929 " -%i",
9930 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9931
9932 /* Inode used, but not marked used in bitmap */
9933 { PR_5_INODE_USED,
9934 " +%i",
9935 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9936
9937 /* Inode bitmap differences end */
9938 { PR_5_INODE_BITMAP_END,
9939 "\n",
9940 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9941
9942 /* Free inodes count for group wrong */
9943 { PR_5_FREE_INODE_COUNT_GROUP,
9944 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
9945 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9946
9947 /* Directories count for group wrong */
9948 { PR_5_FREE_DIR_COUNT_GROUP,
9949 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
9950 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9951
9952 /* Free inodes count wrong */
9953 { PR_5_FREE_INODE_COUNT,
9954 N_("Free @is count wrong (%i, counted=%j).\n"),
9955 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9956
9957 /* Free blocks count for group wrong */
9958 { PR_5_FREE_BLOCK_COUNT_GROUP,
9959 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
9960 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9961
9962 /* Free blocks count wrong */
9963 { PR_5_FREE_BLOCK_COUNT,
9964 N_("Free @bs count wrong (%b, counted=%c).\n"),
9965 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9966
9967 /* Programming error: bitmap endpoints don't match */
9968 { PR_5_BMAP_ENDPOINTS,
9969 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
9970 "match calculated @B endpoints (%i, %j)\n"),
9971 PROMPT_NONE, PR_FATAL },
9972
9973 /* Internal error: fudging end of bitmap */
9974 { PR_5_FUDGE_BITMAP_ERROR,
9975 N_("Internal error: fudging end of bitmap (%N)\n"),
9976 PROMPT_NONE, PR_FATAL },
9977
9978 /* Error copying in replacement inode bitmap */
9979 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009980 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009981 PROMPT_NONE, PR_FATAL },
9982
9983 /* Error copying in replacement block bitmap */
9984 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009985 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009986 PROMPT_NONE, PR_FATAL },
9987
9988 /* Block range not used, but marked in bitmap */
9989 { PR_5_BLOCK_RANGE_UNUSED,
9990 " -(%b--%c)",
9991 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9992
9993 /* Block range used, but not marked used in bitmap */
9994 { PR_5_BLOCK_RANGE_USED,
9995 " +(%b--%c)",
9996 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9997
9998 /* Inode range not used, but marked in bitmap */
9999 { PR_5_INODE_RANGE_UNUSED,
10000 " -(%i--%j)",
10001 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10002
10003 /* Inode range used, but not marked used in bitmap */
10004 { PR_5_INODE_RANGE_USED,
10005 " +(%i--%j)",
10006 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
10007
10008 { 0 }
10009};
10010
10011/*
10012 * This is the latch flags register. It allows several problems to be
10013 * "latched" together. This means that the user has to answer but one
10014 * question for the set of problems, and all of the associated
10015 * problems will be either fixed or not fixed.
10016 */
10017static struct latch_descr pr_latch_info[] = {
10018 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
10019 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
10020 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
10021 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
10022 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
10023 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
10024 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
10025 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
10026 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
10027 { -1, 0, 0 },
10028};
10029
10030static const struct e2fsck_problem *find_problem(problem_t code)
10031{
10032 int i;
10033
10034 for (i=0; problem_table[i].e2p_code; i++) {
10035 if (problem_table[i].e2p_code == code)
10036 return &problem_table[i];
10037 }
10038 return 0;
10039}
10040
10041static struct latch_descr *find_latch(int code)
10042{
10043 int i;
10044
10045 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
10046 if (pr_latch_info[i].latch_code == code)
10047 return &pr_latch_info[i];
10048 }
10049 return 0;
10050}
10051
10052int end_problem_latch(e2fsck_t ctx, int mask)
10053{
10054 struct latch_descr *ldesc;
10055 struct problem_context pctx;
10056 int answer = -1;
10057
10058 ldesc = find_latch(mask);
10059 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
10060 clear_problem_context(&pctx);
10061 answer = fix_problem(ctx, ldesc->end_message, &pctx);
10062 }
10063 ldesc->flags &= ~(PRL_VARIABLE);
10064 return answer;
10065}
10066
10067int set_latch_flags(int mask, int setflags, int clearflags)
10068{
10069 struct latch_descr *ldesc;
10070
10071 ldesc = find_latch(mask);
10072 if (!ldesc)
10073 return -1;
10074 ldesc->flags |= setflags;
10075 ldesc->flags &= ~clearflags;
10076 return 0;
10077}
10078
10079void clear_problem_context(struct problem_context *ctx)
10080{
10081 memset(ctx, 0, sizeof(struct problem_context));
10082 ctx->blkcount = -1;
10083 ctx->group = -1;
10084}
10085
10086int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
10087{
10088 ext2_filsys fs = ctx->fs;
10089 const struct e2fsck_problem *ptr;
10090 struct latch_descr *ldesc = 0;
10091 const char *message;
10092 int def_yn, answer, ans;
10093 int print_answer = 0;
10094 int suppress = 0;
10095
10096 ptr = find_problem(code);
10097 if (!ptr) {
10098 printf(_("Unhandled error code (0x%x)!\n"), code);
10099 return 0;
10100 }
10101 def_yn = 1;
10102 if ((ptr->flags & PR_NO_DEFAULT) ||
10103 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
10104 (ctx->options & E2F_OPT_NO))
10105 def_yn= 0;
10106
10107 /*
10108 * Do special latch processing. This is where we ask the
10109 * latch question, if it exists
10110 */
10111 if (ptr->flags & PR_LATCH_MASK) {
10112 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
10113 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
10114 ans = fix_problem(ctx, ldesc->question, pctx);
10115 if (ans == 1)
10116 ldesc->flags |= PRL_YES;
10117 if (ans == 0)
10118 ldesc->flags |= PRL_NO;
10119 ldesc->flags |= PRL_LATCHED;
10120 }
10121 if (ldesc->flags & PRL_SUPPRESS)
10122 suppress++;
10123 }
10124 if ((ptr->flags & PR_PREEN_NOMSG) &&
10125 (ctx->options & E2F_OPT_PREEN))
10126 suppress++;
10127 if ((ptr->flags & PR_NO_NOMSG) &&
10128 (ctx->options & E2F_OPT_NO))
10129 suppress++;
10130 if (!suppress) {
10131 message = ptr->e2p_description;
10132 if ((ctx->options & E2F_OPT_PREEN) &&
10133 !(ptr->flags & PR_PREEN_NOHDR)) {
10134 printf("%s: ", ctx->device_name ?
10135 ctx->device_name : ctx->filesystem_name);
10136 }
10137 if (*message)
10138 print_e2fsck_message(ctx, _(message), pctx, 1);
10139 }
10140 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
10141 preenhalt(ctx);
10142
10143 if (ptr->flags & PR_FATAL)
Rob Landley7c94bed2006-05-03 21:58:45 +000010144 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010145
10146 if (ptr->prompt == PROMPT_NONE) {
10147 if (ptr->flags & PR_NOCOLLATE)
10148 answer = -1;
10149 else
10150 answer = def_yn;
10151 } else {
10152 if (ctx->options & E2F_OPT_PREEN) {
10153 answer = def_yn;
10154 if (!(ptr->flags & PR_PREEN_NOMSG))
10155 print_answer = 1;
10156 } else if ((ptr->flags & PR_LATCH_MASK) &&
10157 (ldesc->flags & (PRL_YES | PRL_NO))) {
10158 if (!suppress)
10159 print_answer = 1;
10160 if (ldesc->flags & PRL_YES)
10161 answer = 1;
10162 else
10163 answer = 0;
10164 } else
10165 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
10166 if (!answer && !(ptr->flags & PR_NO_OK))
10167 ext2fs_unmark_valid(fs);
10168
10169 if (print_answer)
10170 printf("%s.\n", answer ?
10171 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
10172
10173 }
10174
10175 if ((ptr->prompt == PROMPT_ABORT) && answer)
Rob Landley7c94bed2006-05-03 21:58:45 +000010176 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010177
10178 if (ptr->flags & PR_AFTER_CODE)
10179 answer = fix_problem(ctx, ptr->second_code, pctx);
10180
10181 return answer;
10182}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010183
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010184/*
10185 * linux/fs/recovery.c
10186 *
10187 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010188 */
10189
10190/*
10191 * Maintain information about the progress of the recovery job, so that
10192 * the different passes can carry information between them.
10193 */
10194struct recovery_info
10195{
10196 tid_t start_transaction;
10197 tid_t end_transaction;
10198
10199 int nr_replays;
10200 int nr_revokes;
10201 int nr_revoke_hits;
10202};
10203
10204enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
10205static int do_one_pass(journal_t *journal,
10206 struct recovery_info *info, enum passtype pass);
10207static int scan_revoke_records(journal_t *, struct buffer_head *,
10208 tid_t, struct recovery_info *);
10209
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010210/*
10211 * Read a block from the journal
10212 */
10213
10214static int jread(struct buffer_head **bhp, journal_t *journal,
10215 unsigned int offset)
10216{
10217 int err;
10218 unsigned long blocknr;
10219 struct buffer_head *bh;
10220
10221 *bhp = NULL;
10222
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010223 err = journal_bmap(journal, offset, &blocknr);
10224
10225 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +000010226 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010227 return err;
10228 }
10229
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010230 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010231 if (!bh)
10232 return -ENOMEM;
10233
10234 if (!buffer_uptodate(bh)) {
10235 /* If this is a brand new buffer, start readahead.
10236 Otherwise, we assume we are already reading it. */
10237 if (!buffer_req(bh))
10238 do_readahead(journal, offset);
10239 wait_on_buffer(bh);
10240 }
10241
10242 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +000010243 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010244 brelse(bh);
10245 return -EIO;
10246 }
10247
10248 *bhp = bh;
10249 return 0;
10250}
10251
10252
10253/*
10254 * Count the number of in-use tags in a journal descriptor block.
10255 */
10256
10257static int count_tags(struct buffer_head *bh, int size)
10258{
10259 char * tagp;
10260 journal_block_tag_t * tag;
10261 int nr = 0;
10262
10263 tagp = &bh->b_data[sizeof(journal_header_t)];
10264
10265 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
10266 tag = (journal_block_tag_t *) tagp;
10267
10268 nr++;
10269 tagp += sizeof(journal_block_tag_t);
10270 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
10271 tagp += 16;
10272
10273 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
10274 break;
10275 }
10276
10277 return nr;
10278}
10279
10280
10281/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000010282#define wrap(journal, var) \
10283do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010284 if (var >= (journal)->j_last) \
10285 var -= ((journal)->j_last - (journal)->j_first); \
10286} while (0)
10287
10288/**
10289 * int journal_recover(journal_t *journal) - recovers a on-disk journal
10290 * @journal: the journal to recover
10291 *
10292 * The primary function for recovering the log contents when mounting a
10293 * journaled device.
10294 *
10295 * Recovery is done in three passes. In the first pass, we look for the
10296 * end of the log. In the second, we assemble the list of revoke
10297 * blocks. In the third and final pass, we replay any un-revoked blocks
10298 * in the log.
10299 */
10300int journal_recover(journal_t *journal)
10301{
10302 int err;
10303 journal_superblock_t * sb;
10304
10305 struct recovery_info info;
10306
10307 memset(&info, 0, sizeof(info));
10308 sb = journal->j_superblock;
10309
10310 /*
10311 * The journal superblock's s_start field (the current log head)
10312 * is always zero if, and only if, the journal was cleanly
10313 * unmounted.
10314 */
10315
10316 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010317 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
10318 return 0;
10319 }
10320
10321 err = do_one_pass(journal, &info, PASS_SCAN);
10322 if (!err)
10323 err = do_one_pass(journal, &info, PASS_REVOKE);
10324 if (!err)
10325 err = do_one_pass(journal, &info, PASS_REPLAY);
10326
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010327 /* Restart the log at the next transaction ID, thus invalidating
10328 * any existing commit records in the log. */
10329 journal->j_transaction_sequence = ++info.end_transaction;
10330
10331 journal_clear_revoke(journal);
10332 sync_blockdev(journal->j_fs_dev);
10333 return err;
10334}
10335
10336static int do_one_pass(journal_t *journal,
10337 struct recovery_info *info, enum passtype pass)
10338{
10339 unsigned int first_commit_ID, next_commit_ID;
10340 unsigned long next_log_block;
10341 int err, success = 0;
10342 journal_superblock_t * sb;
10343 journal_header_t * tmp;
10344 struct buffer_head * bh;
10345 unsigned int sequence;
10346 int blocktype;
10347
10348 /* Precompute the maximum metadata descriptors in a descriptor block */
10349 int MAX_BLOCKS_PER_DESC;
10350 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
10351 / sizeof(journal_block_tag_t));
10352
10353 /*
10354 * First thing is to establish what we expect to find in the log
10355 * (in terms of transaction IDs), and where (in terms of log
10356 * block offsets): query the superblock.
10357 */
10358
10359 sb = journal->j_superblock;
10360 next_commit_ID = ntohl(sb->s_sequence);
10361 next_log_block = ntohl(sb->s_start);
10362
10363 first_commit_ID = next_commit_ID;
10364 if (pass == PASS_SCAN)
10365 info->start_transaction = first_commit_ID;
10366
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010367 /*
10368 * Now we walk through the log, transaction by transaction,
10369 * making sure that each transaction has a commit block in the
10370 * expected place. Each complete transaction gets replayed back
10371 * into the main filesystem.
10372 */
10373
10374 while (1) {
10375 int flags;
10376 char * tagp;
10377 journal_block_tag_t * tag;
10378 struct buffer_head * obh;
10379 struct buffer_head * nbh;
10380
10381 /* If we already know where to stop the log traversal,
10382 * check right now that we haven't gone past the end of
10383 * the log. */
10384
10385 if (pass != PASS_SCAN)
10386 if (tid_geq(next_commit_ID, info->end_transaction))
10387 break;
10388
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010389 /* Skip over each chunk of the transaction looking
10390 * either the next descriptor block or the final commit
10391 * record. */
10392
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010393 err = jread(&bh, journal, next_log_block);
10394 if (err)
10395 goto failed;
10396
10397 next_log_block++;
10398 wrap(journal, next_log_block);
10399
10400 /* What kind of buffer is it?
10401 *
10402 * If it is a descriptor block, check that it has the
10403 * expected sequence number. Otherwise, we're all done
10404 * here. */
10405
10406 tmp = (journal_header_t *)bh->b_data;
10407
10408 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
10409 brelse(bh);
10410 break;
10411 }
10412
10413 blocktype = ntohl(tmp->h_blocktype);
10414 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010415
10416 if (sequence != next_commit_ID) {
10417 brelse(bh);
10418 break;
10419 }
10420
10421 /* OK, we have a valid descriptor block which matches
10422 * all of the sequence number checks. What are we going
10423 * to do with it? That depends on the pass... */
10424
10425 switch(blocktype) {
10426 case JFS_DESCRIPTOR_BLOCK:
10427 /* If it is a valid descriptor block, replay it
10428 * in pass REPLAY; otherwise, just skip over the
10429 * blocks it describes. */
10430 if (pass != PASS_REPLAY) {
10431 next_log_block +=
10432 count_tags(bh, journal->j_blocksize);
10433 wrap(journal, next_log_block);
10434 brelse(bh);
10435 continue;
10436 }
10437
10438 /* A descriptor block: we can now write all of
10439 * the data blocks. Yay, useful work is finally
10440 * getting done here! */
10441
10442 tagp = &bh->b_data[sizeof(journal_header_t)];
10443 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
10444 <= journal->j_blocksize) {
10445 unsigned long io_block;
10446
10447 tag = (journal_block_tag_t *) tagp;
10448 flags = ntohl(tag->t_flags);
10449
10450 io_block = next_log_block++;
10451 wrap(journal, next_log_block);
10452 err = jread(&obh, journal, io_block);
10453 if (err) {
10454 /* Recover what we can, but
10455 * report failure at the end. */
10456 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000010457 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010458 "block %ld in log\n",
10459 err, io_block);
10460 } else {
10461 unsigned long blocknr;
10462
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010463 blocknr = ntohl(tag->t_blocknr);
10464
10465 /* If the block has been
10466 * revoked, then we're all done
10467 * here. */
10468 if (journal_test_revoke
10469 (journal, blocknr,
10470 next_commit_ID)) {
10471 brelse(obh);
10472 ++info->nr_revoke_hits;
10473 goto skip_write;
10474 }
10475
10476 /* Find a buffer for the new
10477 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010478 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010479 blocknr,
10480 journal->j_blocksize);
10481 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000010482 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010483 "during recovery.\n");
10484 err = -ENOMEM;
10485 brelse(bh);
10486 brelse(obh);
10487 goto failed;
10488 }
10489
10490 lock_buffer(nbh);
10491 memcpy(nbh->b_data, obh->b_data,
10492 journal->j_blocksize);
10493 if (flags & JFS_FLAG_ESCAPE) {
10494 *((unsigned int *)bh->b_data) =
10495 htonl(JFS_MAGIC_NUMBER);
10496 }
10497
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010498 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010499 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010500 ++info->nr_replays;
10501 /* ll_rw_block(WRITE, 1, &nbh); */
10502 unlock_buffer(nbh);
10503 brelse(obh);
10504 brelse(nbh);
10505 }
10506
10507 skip_write:
10508 tagp += sizeof(journal_block_tag_t);
10509 if (!(flags & JFS_FLAG_SAME_UUID))
10510 tagp += 16;
10511
10512 if (flags & JFS_FLAG_LAST_TAG)
10513 break;
10514 }
10515
10516 brelse(bh);
10517 continue;
10518
10519 case JFS_COMMIT_BLOCK:
10520 /* Found an expected commit block: not much to
10521 * do other than move on to the next sequence
10522 * number. */
10523 brelse(bh);
10524 next_commit_ID++;
10525 continue;
10526
10527 case JFS_REVOKE_BLOCK:
10528 /* If we aren't in the REVOKE pass, then we can
10529 * just skip over this block. */
10530 if (pass != PASS_REVOKE) {
10531 brelse(bh);
10532 continue;
10533 }
10534
10535 err = scan_revoke_records(journal, bh,
10536 next_commit_ID, info);
10537 brelse(bh);
10538 if (err)
10539 goto failed;
10540 continue;
10541
10542 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010543 goto done;
10544 }
10545 }
10546
10547 done:
10548 /*
10549 * We broke out of the log scan loop: either we came to the
10550 * known end of the log or we found an unexpected block in the
10551 * log. If the latter happened, then we know that the "current"
10552 * transaction marks the end of the valid log.
10553 */
10554
10555 if (pass == PASS_SCAN)
10556 info->end_transaction = next_commit_ID;
10557 else {
10558 /* It's really bad news if different passes end up at
10559 * different places (but possible due to IO errors). */
10560 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000010561 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010562 "transaction %u, expected %u\n",
10563 pass, next_commit_ID, info->end_transaction);
10564 if (!success)
10565 success = -EIO;
10566 }
10567 }
10568
10569 return success;
10570
10571 failed:
10572 return err;
10573}
10574
10575
10576/* Scan a revoke record, marking all blocks mentioned as revoked. */
10577
10578static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
10579 tid_t sequence, struct recovery_info *info)
10580{
10581 journal_revoke_header_t *header;
10582 int offset, max;
10583
10584 header = (journal_revoke_header_t *) bh->b_data;
10585 offset = sizeof(journal_revoke_header_t);
10586 max = ntohl(header->r_count);
10587
10588 while (offset < max) {
10589 unsigned long blocknr;
10590 int err;
10591
10592 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
10593 offset += 4;
10594 err = journal_set_revoke(journal, blocknr, sequence);
10595 if (err)
10596 return err;
10597 ++info->nr_revokes;
10598 }
10599 return 0;
10600}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010601
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010602
10603/*
10604 * rehash.c --- rebuild hash tree directories
10605 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010606 * This algorithm is designed for simplicity of implementation and to
10607 * pack the directory as much as possible. It however requires twice
10608 * as much memory as the size of the directory. The maximum size
10609 * directory supported using a 4k blocksize is roughly a gigabyte, and
10610 * so there may very well be problems with machines that don't have
10611 * virtual memory, and obscenely large directories.
10612 *
10613 * An alternate algorithm which is much more disk intensive could be
10614 * written, and probably will need to be written in the future. The
10615 * design goals of such an algorithm are: (a) use (roughly) constant
10616 * amounts of memory, no matter how large the directory, (b) the
10617 * directory must be safe at all times, even if e2fsck is interrupted
10618 * in the middle, (c) we must use minimal amounts of extra disk
10619 * blocks. This pretty much requires an incremental approach, where
10620 * we are reading from one part of the directory, and inserting into
10621 * the front half. So the algorithm will have to keep track of a
10622 * moving block boundary between the new tree and the old tree, and
10623 * files will need to be moved from the old directory and inserted
10624 * into the new tree. If the new directory requires space which isn't
10625 * yet available, blocks from the beginning part of the old directory
10626 * may need to be moved to the end of the directory to make room for
10627 * the new tree:
10628 *
10629 * --------------------------------------------------------
10630 * | new tree | | old tree |
10631 * --------------------------------------------------------
10632 * ^ ptr ^ptr
10633 * tail new head old
10634 *
10635 * This is going to be a pain in the tuckus to implement, and will
10636 * require a lot more disk accesses. So I'm going to skip it for now;
10637 * it's only really going to be an issue for really, really big
10638 * filesystems (when we reach the level of tens of millions of files
10639 * in a single directory). It will probably be easier to simply
10640 * require that e2fsck use VM first.
10641 */
10642
10643struct fill_dir_struct {
10644 char *buf;
10645 struct ext2_inode *inode;
10646 int err;
10647 e2fsck_t ctx;
10648 struct hash_entry *harray;
10649 int max_array, num_array;
10650 int dir_size;
10651 int compress;
10652 ino_t parent;
10653};
10654
10655struct hash_entry {
10656 ext2_dirhash_t hash;
10657 ext2_dirhash_t minor_hash;
10658 struct ext2_dir_entry *dir;
10659};
10660
10661struct out_dir {
10662 int num;
10663 int max;
10664 char *buf;
10665 ext2_dirhash_t *hashes;
10666};
10667
10668static int fill_dir_block(ext2_filsys fs,
10669 blk_t *block_nr,
10670 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010671 blk_t ref_block FSCK_ATTR((unused)),
10672 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010673 void *priv_data)
10674{
10675 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
10676 struct hash_entry *new_array, *ent;
10677 struct ext2_dir_entry *dirent;
10678 char *dir;
10679 unsigned int offset, dir_offset;
10680
10681 if (blockcnt < 0)
10682 return 0;
10683
10684 offset = blockcnt * fs->blocksize;
10685 if (offset + fs->blocksize > fd->inode->i_size) {
10686 fd->err = EXT2_ET_DIR_CORRUPTED;
10687 return BLOCK_ABORT;
10688 }
10689 dir = (fd->buf+offset);
10690 if (HOLE_BLKADDR(*block_nr)) {
10691 memset(dir, 0, fs->blocksize);
10692 dirent = (struct ext2_dir_entry *) dir;
10693 dirent->rec_len = fs->blocksize;
10694 } else {
10695 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
10696 if (fd->err)
10697 return BLOCK_ABORT;
10698 }
10699 /* While the directory block is "hot", index it. */
10700 dir_offset = 0;
10701 while (dir_offset < fs->blocksize) {
10702 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
10703 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
10704 (dirent->rec_len < 8) ||
10705 ((dirent->rec_len % 4) != 0) ||
10706 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
10707 fd->err = EXT2_ET_DIR_CORRUPTED;
10708 return BLOCK_ABORT;
10709 }
10710 dir_offset += dirent->rec_len;
10711 if (dirent->inode == 0)
10712 continue;
10713 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
10714 (dirent->name[0] == '.'))
10715 continue;
10716 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
10717 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
10718 fd->parent = dirent->inode;
10719 continue;
10720 }
10721 if (fd->num_array >= fd->max_array) {
10722 new_array = realloc(fd->harray,
10723 sizeof(struct hash_entry) * (fd->max_array+500));
10724 if (!new_array) {
10725 fd->err = ENOMEM;
10726 return BLOCK_ABORT;
10727 }
10728 fd->harray = new_array;
10729 fd->max_array += 500;
10730 }
10731 ent = fd->harray + fd->num_array++;
10732 ent->dir = dirent;
10733 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
10734 if (fd->compress)
10735 ent->hash = ent->minor_hash = 0;
10736 else {
10737 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
10738 dirent->name,
10739 dirent->name_len & 0xFF,
10740 fs->super->s_hash_seed,
10741 &ent->hash, &ent->minor_hash);
10742 if (fd->err)
10743 return BLOCK_ABORT;
10744 }
10745 }
10746
10747 return 0;
10748}
10749
10750/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010751static int name_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010752{
10753 const struct hash_entry *he_a = (const struct hash_entry *) a;
10754 const struct hash_entry *he_b = (const struct hash_entry *) b;
10755 int ret;
10756 int min_len;
10757
10758 min_len = he_a->dir->name_len;
10759 if (min_len > he_b->dir->name_len)
10760 min_len = he_b->dir->name_len;
10761
10762 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
10763 if (ret == 0) {
10764 if (he_a->dir->name_len > he_b->dir->name_len)
10765 ret = 1;
10766 else if (he_a->dir->name_len < he_b->dir->name_len)
10767 ret = -1;
10768 else
10769 ret = he_b->dir->inode - he_a->dir->inode;
10770 }
10771 return ret;
10772}
10773
10774/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010775static int hash_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010776{
10777 const struct hash_entry *he_a = (const struct hash_entry *) a;
10778 const struct hash_entry *he_b = (const struct hash_entry *) b;
10779 int ret;
10780
10781 if (he_a->hash > he_b->hash)
10782 ret = 1;
10783 else if (he_a->hash < he_b->hash)
10784 ret = -1;
10785 else {
10786 if (he_a->minor_hash > he_b->minor_hash)
10787 ret = 1;
10788 else if (he_a->minor_hash < he_b->minor_hash)
10789 ret = -1;
10790 else
10791 ret = name_cmp(a, b);
10792 }
10793 return ret;
10794}
10795
10796static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
10797 int blocks)
10798{
10799 void *new_mem;
10800
10801 if (outdir->max) {
10802 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
10803 if (!new_mem)
10804 return ENOMEM;
10805 outdir->buf = new_mem;
10806 new_mem = realloc(outdir->hashes,
10807 blocks * sizeof(ext2_dirhash_t));
10808 if (!new_mem)
10809 return ENOMEM;
10810 outdir->hashes = new_mem;
10811 } else {
10812 outdir->buf = malloc(blocks * fs->blocksize);
10813 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
10814 outdir->num = 0;
10815 }
10816 outdir->max = blocks;
10817 return 0;
10818}
10819
10820static void free_out_dir(struct out_dir *outdir)
10821{
Rob Landleye7c43b62006-03-01 16:39:45 +000010822 free(outdir->buf);
10823 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010824 outdir->max = 0;
10825 outdir->num =0;
10826}
10827
10828static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
10829 char ** ret)
10830{
10831 errcode_t retval;
10832
10833 if (outdir->num >= outdir->max) {
10834 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
10835 if (retval)
10836 return retval;
10837 }
10838 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
10839 memset(*ret, 0, fs->blocksize);
10840 return 0;
10841}
10842
10843/*
10844 * This function is used to make a unique filename. We do this by
10845 * appending ~0, and then incrementing the number. However, we cannot
10846 * expand the length of the filename beyond the padding available in
10847 * the directory entry.
10848 */
10849static void mutate_name(char *str, __u16 *len)
10850{
10851 int i;
10852 __u16 l = *len & 0xFF, h = *len & 0xff00;
10853
10854 /*
10855 * First check to see if it looks the name has been mutated
10856 * already
10857 */
10858 for (i = l-1; i > 0; i--) {
10859 if (!isdigit(str[i]))
10860 break;
10861 }
10862 if ((i == l-1) || (str[i] != '~')) {
10863 if (((l-1) & 3) < 2)
10864 l += 2;
10865 else
10866 l = (l+3) & ~3;
10867 str[l-2] = '~';
10868 str[l-1] = '0';
10869 *len = l | h;
10870 return;
10871 }
10872 for (i = l-1; i >= 0; i--) {
10873 if (isdigit(str[i])) {
10874 if (str[i] == '9')
10875 str[i] = '0';
10876 else {
10877 str[i]++;
10878 return;
10879 }
10880 continue;
10881 }
10882 if (i == 1) {
10883 if (str[0] == 'z')
10884 str[0] = 'A';
10885 else if (str[0] == 'Z') {
10886 str[0] = '~';
10887 str[1] = '0';
10888 } else
10889 str[0]++;
10890 } else if (i > 0) {
10891 str[i] = '1';
10892 str[i-1] = '~';
10893 } else {
10894 if (str[0] == '~')
10895 str[0] = 'a';
10896 else
10897 str[0]++;
10898 }
10899 break;
10900 }
10901}
10902
10903static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
10904 ext2_ino_t ino,
10905 struct fill_dir_struct *fd)
10906{
10907 struct problem_context pctx;
10908 struct hash_entry *ent, *prev;
10909 int i, j;
10910 int fixed = 0;
10911 char new_name[256];
10912 __u16 new_len;
10913
10914 clear_problem_context(&pctx);
10915 pctx.ino = ino;
10916
10917 for (i=1; i < fd->num_array; i++) {
10918 ent = fd->harray + i;
10919 prev = ent - 1;
10920 if (!ent->dir->inode ||
10921 ((ent->dir->name_len & 0xFF) !=
10922 (prev->dir->name_len & 0xFF)) ||
10923 (strncmp(ent->dir->name, prev->dir->name,
10924 ent->dir->name_len & 0xFF)))
10925 continue;
10926 pctx.dirent = ent->dir;
10927 if ((ent->dir->inode == prev->dir->inode) &&
10928 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
10929 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
10930 ent->dir->inode = 0;
10931 fixed++;
10932 continue;
10933 }
10934 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
10935 new_len = ent->dir->name_len;
10936 mutate_name(new_name, &new_len);
10937 for (j=0; j < fd->num_array; j++) {
10938 if ((i==j) ||
10939 ((ent->dir->name_len & 0xFF) !=
10940 (fd->harray[j].dir->name_len & 0xFF)) ||
10941 (strncmp(new_name, fd->harray[j].dir->name,
10942 new_len & 0xFF)))
10943 continue;
10944 mutate_name(new_name, &new_len);
10945
10946 j = -1;
10947 }
10948 new_name[new_len & 0xFF] = 0;
10949 pctx.str = new_name;
10950 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
10951 memcpy(ent->dir->name, new_name, new_len & 0xFF);
10952 ent->dir->name_len = new_len;
10953 ext2fs_dirhash(fs->super->s_def_hash_version,
10954 ent->dir->name,
10955 ent->dir->name_len & 0xFF,
10956 fs->super->s_hash_seed,
10957 &ent->hash, &ent->minor_hash);
10958 fixed++;
10959 }
10960 }
10961 return fixed;
10962}
10963
10964
10965static errcode_t copy_dir_entries(ext2_filsys fs,
10966 struct fill_dir_struct *fd,
10967 struct out_dir *outdir)
10968{
10969 errcode_t retval;
10970 char *block_start;
10971 struct hash_entry *ent;
10972 struct ext2_dir_entry *dirent;
10973 int i, rec_len, left;
10974 ext2_dirhash_t prev_hash;
10975 int offset;
10976
10977 outdir->max = 0;
10978 retval = alloc_size_dir(fs, outdir,
10979 (fd->dir_size / fs->blocksize) + 2);
10980 if (retval)
10981 return retval;
10982 outdir->num = fd->compress ? 0 : 1;
10983 offset = 0;
10984 outdir->hashes[0] = 0;
10985 prev_hash = 1;
10986 if ((retval = get_next_block(fs, outdir, &block_start)))
10987 return retval;
10988 dirent = (struct ext2_dir_entry *) block_start;
10989 left = fs->blocksize;
10990 for (i=0; i < fd->num_array; i++) {
10991 ent = fd->harray + i;
10992 if (ent->dir->inode == 0)
10993 continue;
10994 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
10995 if (rec_len > left) {
10996 if (left)
10997 dirent->rec_len += left;
10998 if ((retval = get_next_block(fs, outdir,
10999 &block_start)))
11000 return retval;
11001 offset = 0;
11002 }
11003 left = fs->blocksize - offset;
11004 dirent = (struct ext2_dir_entry *) (block_start + offset);
11005 if (offset == 0) {
11006 if (ent->hash == prev_hash)
11007 outdir->hashes[outdir->num-1] = ent->hash | 1;
11008 else
11009 outdir->hashes[outdir->num-1] = ent->hash;
11010 }
11011 dirent->inode = ent->dir->inode;
11012 dirent->name_len = ent->dir->name_len;
11013 dirent->rec_len = rec_len;
11014 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
11015 offset += rec_len;
11016 left -= rec_len;
11017 if (left < 12) {
11018 dirent->rec_len += left;
11019 offset += left;
11020 left = 0;
11021 }
11022 prev_hash = ent->hash;
11023 }
11024 if (left)
11025 dirent->rec_len += left;
11026
11027 return 0;
11028}
11029
11030
11031static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
11032 ext2_ino_t ino, ext2_ino_t parent)
11033{
11034 struct ext2_dir_entry *dir;
11035 struct ext2_dx_root_info *root;
11036 struct ext2_dx_countlimit *limits;
11037 int filetype = 0;
11038
11039 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
11040 filetype = EXT2_FT_DIR << 8;
11041
11042 memset(buf, 0, fs->blocksize);
11043 dir = (struct ext2_dir_entry *) buf;
11044 dir->inode = ino;
11045 dir->name[0] = '.';
11046 dir->name_len = 1 | filetype;
11047 dir->rec_len = 12;
11048 dir = (struct ext2_dir_entry *) (buf + 12);
11049 dir->inode = parent;
11050 dir->name[0] = '.';
11051 dir->name[1] = '.';
11052 dir->name_len = 2 | filetype;
11053 dir->rec_len = fs->blocksize - 12;
11054
11055 root = (struct ext2_dx_root_info *) (buf+24);
11056 root->reserved_zero = 0;
11057 root->hash_version = fs->super->s_def_hash_version;
11058 root->info_length = 8;
11059 root->indirect_levels = 0;
11060 root->unused_flags = 0;
11061
11062 limits = (struct ext2_dx_countlimit *) (buf+32);
11063 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
11064 limits->count = 0;
11065
11066 return root;
11067}
11068
11069
11070static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
11071{
11072 struct ext2_dir_entry *dir;
11073 struct ext2_dx_countlimit *limits;
11074
11075 memset(buf, 0, fs->blocksize);
11076 dir = (struct ext2_dir_entry *) buf;
11077 dir->inode = 0;
11078 dir->rec_len = fs->blocksize;
11079
11080 limits = (struct ext2_dx_countlimit *) (buf+8);
11081 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
11082 limits->count = 0;
11083
11084 return (struct ext2_dx_entry *) limits;
11085}
11086
11087/*
11088 * This function takes the leaf nodes which have been written in
11089 * outdir, and populates the root node and any necessary interior nodes.
11090 */
11091static errcode_t calculate_tree(ext2_filsys fs,
11092 struct out_dir *outdir,
11093 ext2_ino_t ino,
11094 ext2_ino_t parent)
11095{
11096 struct ext2_dx_root_info *root_info;
11097 struct ext2_dx_entry *root, *dx_ent = 0;
11098 struct ext2_dx_countlimit *root_limit, *limit;
11099 errcode_t retval;
11100 char * block_start;
11101 int i, c1, c2, nblks;
11102 int limit_offset, root_offset;
11103
11104 root_info = set_root_node(fs, outdir->buf, ino, parent);
11105 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
11106 root_info->info_length;
11107 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
11108 c1 = root_limit->limit;
11109 nblks = outdir->num;
11110
11111 /* Write out the pointer blocks */
11112 if (nblks-1 <= c1) {
11113 /* Just write out the root block, and we're done */
11114 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
11115 for (i=1; i < nblks; i++) {
11116 root->block = ext2fs_cpu_to_le32(i);
11117 if (i != 1)
11118 root->hash =
11119 ext2fs_cpu_to_le32(outdir->hashes[i]);
11120 root++;
11121 c1--;
11122 }
11123 } else {
11124 c2 = 0;
11125 limit = 0;
11126 root_info->indirect_levels = 1;
11127 for (i=1; i < nblks; i++) {
11128 if (c1 == 0)
11129 return ENOSPC;
11130 if (c2 == 0) {
11131 if (limit)
11132 limit->limit = limit->count =
11133 ext2fs_cpu_to_le16(limit->limit);
11134 root = (struct ext2_dx_entry *)
11135 (outdir->buf + root_offset);
11136 root->block = ext2fs_cpu_to_le32(outdir->num);
11137 if (i != 1)
11138 root->hash =
11139 ext2fs_cpu_to_le32(outdir->hashes[i]);
11140 if ((retval = get_next_block(fs, outdir,
11141 &block_start)))
11142 return retval;
11143 dx_ent = set_int_node(fs, block_start);
11144 limit = (struct ext2_dx_countlimit *) dx_ent;
11145 c2 = limit->limit;
11146 root_offset += sizeof(struct ext2_dx_entry);
11147 c1--;
11148 }
11149 dx_ent->block = ext2fs_cpu_to_le32(i);
11150 if (c2 != limit->limit)
11151 dx_ent->hash =
11152 ext2fs_cpu_to_le32(outdir->hashes[i]);
11153 dx_ent++;
11154 c2--;
11155 }
11156 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
11157 limit->limit = ext2fs_cpu_to_le16(limit->limit);
11158 }
11159 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
11160 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
11161 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
11162
11163 return 0;
11164}
11165
11166struct write_dir_struct {
11167 struct out_dir *outdir;
11168 errcode_t err;
11169 e2fsck_t ctx;
11170 int cleared;
11171};
11172
11173/*
11174 * Helper function which writes out a directory block.
11175 */
11176static int write_dir_block(ext2_filsys fs,
11177 blk_t *block_nr,
11178 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011179 blk_t ref_block FSCK_ATTR((unused)),
11180 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011181 void *priv_data)
11182{
11183 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
11184 blk_t blk;
11185 char *dir;
11186
11187 if (*block_nr == 0)
11188 return 0;
11189 if (blockcnt >= wd->outdir->num) {
11190 e2fsck_read_bitmaps(wd->ctx);
11191 blk = *block_nr;
11192 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
11193 ext2fs_block_alloc_stats(fs, blk, -1);
11194 *block_nr = 0;
11195 wd->cleared++;
11196 return BLOCK_CHANGED;
11197 }
11198 if (blockcnt < 0)
11199 return 0;
11200
11201 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
11202 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
11203 if (wd->err)
11204 return BLOCK_ABORT;
11205 return 0;
11206}
11207
11208static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
11209 struct out_dir *outdir,
11210 ext2_ino_t ino, int compress)
11211{
11212 struct write_dir_struct wd;
11213 errcode_t retval;
11214 struct ext2_inode inode;
11215
11216 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
11217 if (retval)
11218 return retval;
11219
11220 wd.outdir = outdir;
11221 wd.err = 0;
11222 wd.ctx = ctx;
11223 wd.cleared = 0;
11224
11225 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
11226 write_dir_block, &wd);
11227 if (retval)
11228 return retval;
11229 if (wd.err)
11230 return wd.err;
11231
11232 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
11233 if (compress)
11234 inode.i_flags &= ~EXT2_INDEX_FL;
11235 else
11236 inode.i_flags |= EXT2_INDEX_FL;
11237 inode.i_size = outdir->num * fs->blocksize;
11238 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
11239 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
11240
11241 return 0;
11242}
11243
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011244static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011245{
11246 ext2_filsys fs = ctx->fs;
11247 errcode_t retval;
11248 struct ext2_inode inode;
11249 char *dir_buf = 0;
11250 struct fill_dir_struct fd;
11251 struct out_dir outdir;
11252
11253 outdir.max = outdir.num = 0;
11254 outdir.buf = 0;
11255 outdir.hashes = 0;
11256 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
11257
11258 retval = ENOMEM;
11259 fd.harray = 0;
11260 dir_buf = malloc(inode.i_size);
11261 if (!dir_buf)
11262 goto errout;
11263
11264 fd.max_array = inode.i_size / 32;
11265 fd.num_array = 0;
11266 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
11267 if (!fd.harray)
11268 goto errout;
11269
11270 fd.ctx = ctx;
11271 fd.buf = dir_buf;
11272 fd.inode = &inode;
11273 fd.err = 0;
11274 fd.dir_size = 0;
11275 fd.compress = 0;
11276 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
11277 (inode.i_size / fs->blocksize) < 2)
11278 fd.compress = 1;
11279 fd.parent = 0;
11280
11281 /* Read in the entire directory into memory */
11282 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
11283 fill_dir_block, &fd);
11284 if (fd.err) {
11285 retval = fd.err;
11286 goto errout;
11287 }
11288
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011289 /* Sort the list */
11290resort:
11291 if (fd.compress)
11292 qsort(fd.harray+2, fd.num_array-2,
11293 sizeof(struct hash_entry), name_cmp);
11294 else
11295 qsort(fd.harray, fd.num_array,
11296 sizeof(struct hash_entry), hash_cmp);
11297
11298 /*
11299 * Look for duplicates
11300 */
11301 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
11302 goto resort;
11303
11304 if (ctx->options & E2F_OPT_NO) {
11305 retval = 0;
11306 goto errout;
11307 }
11308
11309 /*
11310 * Copy the directory entries. In a htree directory these
11311 * will become the leaf nodes.
11312 */
11313 retval = copy_dir_entries(fs, &fd, &outdir);
11314 if (retval)
11315 goto errout;
11316
11317 free(dir_buf); dir_buf = 0;
11318
11319 if (!fd.compress) {
11320 /* Calculate the interior nodes */
11321 retval = calculate_tree(fs, &outdir, ino, fd.parent);
11322 if (retval)
11323 goto errout;
11324 }
11325
11326 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011327
11328errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000011329 free(dir_buf);
11330 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011331
11332 free_out_dir(&outdir);
11333 return retval;
11334}
11335
11336void e2fsck_rehash_directories(e2fsck_t ctx)
11337{
11338 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011339 struct dir_info *dir;
11340 ext2_u32_iterate iter;
11341 ext2_ino_t ino;
11342 errcode_t retval;
11343 int i, cur, max, all_dirs, dir_index, first = 1;
11344
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011345 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
11346
11347 if (!ctx->dirs_to_hash && !all_dirs)
11348 return;
11349
11350 e2fsck_get_lost_and_found(ctx, 0);
11351
11352 clear_problem_context(&pctx);
11353
11354 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
11355 cur = 0;
11356 if (all_dirs) {
11357 i = 0;
11358 max = e2fsck_get_num_dirinfo(ctx);
11359 } else {
11360 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
11361 &iter);
11362 if (retval) {
11363 pctx.errcode = retval;
11364 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
11365 return;
11366 }
11367 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
11368 }
11369 while (1) {
11370 if (all_dirs) {
11371 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
11372 break;
11373 ino = dir->ino;
11374 } else {
11375 if (!ext2fs_u32_list_iterate(iter, &ino))
11376 break;
11377 }
11378 if (ino == ctx->lost_and_found)
11379 continue;
11380 pctx.dir = ino;
11381 if (first) {
11382 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
11383 first = 0;
11384 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011385 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
11386 if (pctx.errcode) {
11387 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11388 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
11389 }
11390 if (ctx->progress && !ctx->progress_fd)
11391 e2fsck_simple_progress(ctx, "Rebuilding directory",
11392 100.0 * (float) (++cur) / (float) max, ino);
11393 }
11394 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11395 if (!all_dirs)
11396 ext2fs_u32_list_iterate_end(iter);
11397
Rob Landleye7c43b62006-03-01 16:39:45 +000011398 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011399 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011400}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011401
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011402/*
11403 * linux/fs/revoke.c
11404 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011405 * Journal revoke routines for the generic filesystem journaling code;
11406 * part of the ext2fs journaling system.
11407 *
11408 * Revoke is the mechanism used to prevent old log records for deleted
11409 * metadata from being replayed on top of newer data using the same
11410 * blocks. The revoke mechanism is used in two separate places:
11411 *
11412 * + Commit: during commit we write the entire list of the current
11413 * transaction's revoked blocks to the journal
11414 *
11415 * + Recovery: during recovery we record the transaction ID of all
11416 * revoked blocks. If there are multiple revoke records in the log
11417 * for a single block, only the last one counts, and if there is a log
11418 * entry for a block beyond the last revoke, then that log entry still
11419 * gets replayed.
11420 *
11421 * We can get interactions between revokes and new log data within a
11422 * single transaction:
11423 *
11424 * Block is revoked and then journaled:
11425 * The desired end result is the journaling of the new block, so we
11426 * cancel the revoke before the transaction commits.
11427 *
11428 * Block is journaled and then revoked:
11429 * The revoke must take precedence over the write of the block, so we
11430 * need either to cancel the journal entry or to write the revoke
11431 * later in the log than the log block. In this case, we choose the
11432 * latter: journaling a block cancels any revoke record for that block
11433 * in the current transaction, so any revoke for that block in the
11434 * transaction must have happened after the block was journaled and so
11435 * the revoke must take precedence.
11436 *
11437 * Block is revoked and then written as data:
11438 * The data write is allowed to succeed, but the revoke is _not_
11439 * cancelled. We still need to prevent old log records from
11440 * overwriting the new data. We don't even need to clear the revoke
11441 * bit here.
11442 *
11443 * Revoke information on buffers is a tri-state value:
11444 *
11445 * RevokeValid clear: no cached revoke status, need to look it up
11446 * RevokeValid set, Revoked clear:
11447 * buffer has not been revoked, and cancel_revoke
11448 * need do nothing.
11449 * RevokeValid set, Revoked set:
11450 * buffer has been revoked.
11451 */
11452
11453static kmem_cache_t *revoke_record_cache;
11454static kmem_cache_t *revoke_table_cache;
11455
11456/* Each revoke record represents one single revoked block. During
11457 journal replay, this involves recording the transaction ID of the
11458 last transaction to revoke this block. */
11459
11460struct jbd_revoke_record_s
11461{
11462 struct list_head hash;
11463 tid_t sequence; /* Used for recovery only */
11464 unsigned long blocknr;
11465};
11466
11467
11468/* The revoke table is just a simple hash table of revoke records. */
11469struct jbd_revoke_table_s
11470{
11471 /* It is conceivable that we might want a larger hash table
11472 * for recovery. Must be a power of two. */
11473 int hash_size;
11474 int hash_shift;
11475 struct list_head *hash_table;
11476};
11477
11478
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011479/* Utility functions to maintain the revoke table */
11480
11481/* Borrowed from buffer.c: this is a tried and tested block hash function */
11482static inline int hash(journal_t *journal, unsigned long block)
11483{
11484 struct jbd_revoke_table_s *table = journal->j_revoke;
11485 int hash_shift = table->hash_shift;
11486
11487 return ((block << (hash_shift - 6)) ^
11488 (block >> 13) ^
11489 (block << (hash_shift - 12))) & (table->hash_size - 1);
11490}
11491
11492static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
11493 tid_t seq)
11494{
11495 struct list_head *hash_list;
11496 struct jbd_revoke_record_s *record;
11497
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011498 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
11499 if (!record)
11500 goto oom;
11501
11502 record->sequence = seq;
11503 record->blocknr = blocknr;
11504 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11505 list_add(&record->hash, hash_list);
11506 return 0;
11507
11508oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011509 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011510}
11511
11512/* Find a revoke record in the journal's hash table. */
11513
11514static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
11515 unsigned long blocknr)
11516{
11517 struct list_head *hash_list;
11518 struct jbd_revoke_record_s *record;
11519
11520 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11521
11522 record = (struct jbd_revoke_record_s *) hash_list->next;
11523 while (&(record->hash) != hash_list) {
11524 if (record->blocknr == blocknr)
11525 return record;
11526 record = (struct jbd_revoke_record_s *) record->hash.next;
11527 }
11528 return NULL;
11529}
11530
11531int journal_init_revoke_caches(void)
11532{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011533 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011534 if (revoke_record_cache == 0)
11535 return -ENOMEM;
11536
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011537 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011538 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011539 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011540 revoke_record_cache = NULL;
11541 return -ENOMEM;
11542 }
11543 return 0;
11544}
11545
11546void journal_destroy_revoke_caches(void)
11547{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011548 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011549 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011550 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011551 revoke_table_cache = 0;
11552}
11553
11554/* Initialise the revoke table for a given journal to a given size. */
11555
11556int journal_init_revoke(journal_t *journal, int hash_size)
11557{
11558 int shift, tmp;
11559
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011560 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
11561 if (!journal->j_revoke)
11562 return -ENOMEM;
11563
11564 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011565 journal->j_revoke->hash_size = hash_size;
11566
11567 shift = 0;
11568 tmp = hash_size;
11569 while((tmp >>= 1UL) != 0UL)
11570 shift++;
11571 journal->j_revoke->hash_shift = shift;
11572
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011573 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011574 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011575 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011576 journal->j_revoke = NULL;
11577 return -ENOMEM;
11578 }
11579
11580 for (tmp = 0; tmp < hash_size; tmp++)
11581 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
11582
11583 return 0;
11584}
11585
11586/* Destoy a journal's revoke table. The table must already be empty! */
11587
11588void journal_destroy_revoke(journal_t *journal)
11589{
11590 struct jbd_revoke_table_s *table;
11591 struct list_head *hash_list;
11592 int i;
11593
11594 table = journal->j_revoke;
11595 if (!table)
11596 return;
11597
11598 for (i=0; i<table->hash_size; i++) {
11599 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011600 }
11601
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011602 free(table->hash_table);
11603 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011604 journal->j_revoke = NULL;
11605}
11606
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011607/*
11608 * Revoke support for recovery.
11609 *
11610 * Recovery needs to be able to:
11611 *
11612 * record all revoke records, including the tid of the latest instance
11613 * of each revoke in the journal
11614 *
11615 * check whether a given block in a given transaction should be replayed
11616 * (ie. has not been revoked by a revoke record in that or a subsequent
11617 * transaction)
11618 *
11619 * empty the revoke table after recovery.
11620 */
11621
11622/*
11623 * First, setting revoke records. We create a new revoke record for
11624 * every block ever revoked in the log as we scan it for recovery, and
11625 * we update the existing records if we find multiple revokes for a
11626 * single block.
11627 */
11628
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011629int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011630 tid_t sequence)
11631{
11632 struct jbd_revoke_record_s *record;
11633
11634 record = find_revoke_record(journal, blocknr);
11635 if (record) {
11636 /* If we have multiple occurences, only record the
11637 * latest sequence number in the hashed record */
11638 if (tid_gt(sequence, record->sequence))
11639 record->sequence = sequence;
11640 return 0;
11641 }
11642 return insert_revoke_hash(journal, blocknr, sequence);
11643}
11644
11645/*
11646 * Test revoke records. For a given block referenced in the log, has
11647 * that block been revoked? A revoke record with a given transaction
11648 * sequence number revokes all blocks in that transaction and earlier
11649 * ones, but later transactions still need replayed.
11650 */
11651
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011652int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011653 tid_t sequence)
11654{
11655 struct jbd_revoke_record_s *record;
11656
11657 record = find_revoke_record(journal, blocknr);
11658 if (!record)
11659 return 0;
11660 if (tid_gt(sequence, record->sequence))
11661 return 0;
11662 return 1;
11663}
11664
11665/*
11666 * Finally, once recovery is over, we need to clear the revoke table so
11667 * that it can be reused by the running filesystem.
11668 */
11669
11670void journal_clear_revoke(journal_t *journal)
11671{
11672 int i;
11673 struct list_head *hash_list;
11674 struct jbd_revoke_record_s *record;
11675 struct jbd_revoke_table_s *revoke_var;
11676
11677 revoke_var = journal->j_revoke;
11678
11679 for (i = 0; i < revoke_var->hash_size; i++) {
11680 hash_list = &revoke_var->hash_table[i];
11681 while (!list_empty(hash_list)) {
11682 record = (struct jbd_revoke_record_s*) hash_list->next;
11683 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011684 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011685 }
11686 }
11687}
11688
11689/*
11690 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011691 */
11692
11693#define MIN_CHECK 1
11694#define MAX_CHECK 2
11695
11696static void check_super_value(e2fsck_t ctx, const char *descr,
11697 unsigned long value, int flags,
11698 unsigned long min_val, unsigned long max_val)
11699{
11700 struct problem_context pctx;
11701
11702 if (((flags & MIN_CHECK) && (value < min_val)) ||
11703 ((flags & MAX_CHECK) && (value > max_val))) {
11704 clear_problem_context(&pctx);
11705 pctx.num = value;
11706 pctx.str = descr;
11707 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11708 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11709 }
11710}
11711
11712/*
11713 * This routine may get stubbed out in special compilations of the
11714 * e2fsck code..
11715 */
11716#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011717static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011718{
11719 return (ext2fs_get_device_size(ctx->filesystem_name,
11720 EXT2_BLOCK_SIZE(ctx->fs->super),
11721 &ctx->num_blocks));
11722}
11723#endif
11724
11725/*
11726 * helper function to release an inode
11727 */
11728struct process_block_struct {
11729 e2fsck_t ctx;
11730 char *buf;
11731 struct problem_context *pctx;
11732 int truncating;
11733 int truncate_offset;
11734 e2_blkcnt_t truncate_block;
11735 int truncated_blocks;
11736 int abort;
11737 errcode_t errcode;
11738};
11739
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011740static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011741 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011742 blk_t ref_blk FSCK_ATTR((unused)),
11743 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011744 void *priv_data)
11745{
11746 struct process_block_struct *pb;
11747 e2fsck_t ctx;
11748 struct problem_context *pctx;
11749 blk_t blk = *block_nr;
11750 int retval = 0;
11751
11752 pb = (struct process_block_struct *) priv_data;
11753 ctx = pb->ctx;
11754 pctx = pb->pctx;
11755
11756 pctx->blk = blk;
11757 pctx->blkcount = blockcnt;
11758
11759 if (HOLE_BLKADDR(blk))
11760 return 0;
11761
11762 if ((blk < fs->super->s_first_data_block) ||
11763 (blk >= fs->super->s_blocks_count)) {
11764 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
11765 return_abort:
11766 pb->abort = 1;
11767 return BLOCK_ABORT;
11768 }
11769
11770 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
11771 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
11772 goto return_abort;
11773 }
11774
11775 /*
11776 * If we are deleting an orphan, then we leave the fields alone.
11777 * If we are truncating an orphan, then update the inode fields
11778 * and clean up any partial block data.
11779 */
11780 if (pb->truncating) {
11781 /*
11782 * We only remove indirect blocks if they are
11783 * completely empty.
11784 */
11785 if (blockcnt < 0) {
11786 int i, limit;
11787 blk_t *bp;
11788
11789 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11790 pb->buf);
11791 if (pb->errcode)
11792 goto return_abort;
11793
11794 limit = fs->blocksize >> 2;
11795 for (i = 0, bp = (blk_t *) pb->buf;
11796 i < limit; i++, bp++)
11797 if (*bp)
11798 return 0;
11799 }
11800 /*
11801 * We don't remove direct blocks until we've reached
11802 * the truncation block.
11803 */
11804 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
11805 return 0;
11806 /*
11807 * If part of the last block needs truncating, we do
11808 * it here.
11809 */
11810 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
11811 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11812 pb->buf);
11813 if (pb->errcode)
11814 goto return_abort;
11815 memset(pb->buf + pb->truncate_offset, 0,
11816 fs->blocksize - pb->truncate_offset);
11817 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
11818 pb->buf);
11819 if (pb->errcode)
11820 goto return_abort;
11821 }
11822 pb->truncated_blocks++;
11823 *block_nr = 0;
11824 retval |= BLOCK_CHANGED;
11825 }
11826
11827 ext2fs_block_alloc_stats(fs, blk, -1);
11828 return retval;
11829}
11830
11831/*
11832 * This function releases an inode. Returns 1 if an inconsistency was
11833 * found. If the inode has a link count, then it is being truncated and
11834 * not deleted.
11835 */
11836static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
11837 struct ext2_inode *inode, char *block_buf,
11838 struct problem_context *pctx)
11839{
11840 struct process_block_struct pb;
11841 ext2_filsys fs = ctx->fs;
11842 errcode_t retval;
11843 __u32 count;
11844
11845 if (!ext2fs_inode_has_valid_blocks(inode))
11846 return 0;
11847
11848 pb.buf = block_buf + 3 * ctx->fs->blocksize;
11849 pb.ctx = ctx;
11850 pb.abort = 0;
11851 pb.errcode = 0;
11852 pb.pctx = pctx;
11853 if (inode->i_links_count) {
11854 pb.truncating = 1;
11855 pb.truncate_block = (e2_blkcnt_t)
11856 ((((long long)inode->i_size_high << 32) +
11857 inode->i_size + fs->blocksize - 1) /
11858 fs->blocksize);
11859 pb.truncate_offset = inode->i_size % fs->blocksize;
11860 } else {
11861 pb.truncating = 0;
11862 pb.truncate_block = 0;
11863 pb.truncate_offset = 0;
11864 }
11865 pb.truncated_blocks = 0;
11866 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
11867 block_buf, release_inode_block, &pb);
11868 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011869 bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011870 ino);
11871 return 1;
11872 }
11873 if (pb.abort)
11874 return 1;
11875
11876 /* Refresh the inode since ext2fs_block_iterate may have changed it */
11877 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
11878
11879 if (pb.truncated_blocks)
11880 inode->i_blocks -= pb.truncated_blocks *
11881 (fs->blocksize / 512);
11882
11883 if (inode->i_file_acl) {
11884 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
11885 block_buf, -1, &count);
11886 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
11887 retval = 0;
11888 count = 1;
11889 }
11890 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011891 bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011892 ino);
11893 return 1;
11894 }
11895 if (count == 0)
11896 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
11897 inode->i_file_acl = 0;
11898 }
11899 return 0;
11900}
11901
11902/*
11903 * This function releases all of the orphan inodes. It returns 1 if
11904 * it hit some error, and 0 on success.
11905 */
11906static int release_orphan_inodes(e2fsck_t ctx)
11907{
11908 ext2_filsys fs = ctx->fs;
11909 ext2_ino_t ino, next_ino;
11910 struct ext2_inode inode;
11911 struct problem_context pctx;
11912 char *block_buf;
11913
11914 if ((ino = fs->super->s_last_orphan) == 0)
11915 return 0;
11916
11917 /*
11918 * Win or lose, we won't be using the head of the orphan inode
11919 * list again.
11920 */
11921 fs->super->s_last_orphan = 0;
11922 ext2fs_mark_super_dirty(fs);
11923
11924 /*
11925 * If the filesystem contains errors, don't run the orphan
11926 * list, since the orphan list can't be trusted; and we're
11927 * going to be running a full e2fsck run anyway...
11928 */
11929 if (fs->super->s_state & EXT2_ERROR_FS)
11930 return 0;
11931
11932 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
11933 (ino > fs->super->s_inodes_count)) {
11934 clear_problem_context(&pctx);
11935 pctx.ino = ino;
11936 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
11937 return 1;
11938 }
11939
11940 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
11941 "block iterate buffer");
11942 e2fsck_read_bitmaps(ctx);
11943
11944 while (ino) {
11945 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
11946 clear_problem_context(&pctx);
11947 pctx.ino = ino;
11948 pctx.inode = &inode;
11949 pctx.str = inode.i_links_count ? _("Truncating") :
11950 _("Clearing");
11951
11952 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
11953
11954 next_ino = inode.i_dtime;
11955 if (next_ino &&
11956 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
11957 (next_ino > fs->super->s_inodes_count))) {
11958 pctx.ino = next_ino;
11959 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
11960 goto return_abort;
11961 }
11962
11963 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
11964 goto return_abort;
11965
11966 if (!inode.i_links_count) {
11967 ext2fs_inode_alloc_stats2(fs, ino, -1,
11968 LINUX_S_ISDIR(inode.i_mode));
11969 inode.i_dtime = time(0);
11970 } else {
11971 inode.i_dtime = 0;
11972 }
11973 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
11974 ino = next_ino;
11975 }
11976 ext2fs_free_mem(&block_buf);
11977 return 0;
11978return_abort:
11979 ext2fs_free_mem(&block_buf);
11980 return 1;
11981}
11982
11983/*
11984 * Check the resize inode to make sure it is sane. We check both for
11985 * the case where on-line resizing is not enabled (in which case the
11986 * resize inode should be cleared) as well as the case where on-line
11987 * resizing is enabled.
11988 */
11989static void check_resize_inode(e2fsck_t ctx)
11990{
11991 ext2_filsys fs = ctx->fs;
11992 struct ext2_inode inode;
11993 struct problem_context pctx;
11994 int i, j, gdt_off, ind_off;
11995 blk_t blk, pblk, expect;
11996 __u32 *dind_buf = 0, *ind_buf;
11997 errcode_t retval;
11998
11999 clear_problem_context(&pctx);
12000
12001 /*
12002 * If the resize inode feature isn't set, then
12003 * s_reserved_gdt_blocks must be zero.
12004 */
12005 if (!(fs->super->s_feature_compat &
12006 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
12007 if (fs->super->s_reserved_gdt_blocks) {
12008 pctx.num = fs->super->s_reserved_gdt_blocks;
12009 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
12010 &pctx)) {
12011 fs->super->s_reserved_gdt_blocks = 0;
12012 ext2fs_mark_super_dirty(fs);
12013 }
12014 }
12015 }
12016
Mike Frysinger874af852006-03-08 07:03:27 +000012017 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012018 pctx.ino = EXT2_RESIZE_INO;
12019 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
12020 if (retval) {
12021 if (fs->super->s_feature_compat &
12022 EXT2_FEATURE_COMPAT_RESIZE_INODE)
12023 ctx->flags |= E2F_FLAG_RESIZE_INODE;
12024 return;
12025 }
12026
12027 /*
12028 * If the resize inode feature isn't set, check to make sure
12029 * the resize inode is cleared; then we're done.
12030 */
12031 if (!(fs->super->s_feature_compat &
12032 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
12033 for (i=0; i < EXT2_N_BLOCKS; i++) {
12034 if (inode.i_block[i])
12035 break;
12036 }
12037 if ((i < EXT2_N_BLOCKS) &&
12038 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
12039 memset(&inode, 0, sizeof(inode));
12040 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
12041 "clear_resize");
12042 }
12043 return;
12044 }
12045
12046 /*
12047 * The resize inode feature is enabled; check to make sure the
12048 * only block in use is the double indirect block
12049 */
12050 blk = inode.i_block[EXT2_DIND_BLOCK];
12051 for (i=0; i < EXT2_N_BLOCKS; i++) {
12052 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
12053 break;
12054 }
12055 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
12056 !(inode.i_mode & LINUX_S_IFREG) ||
12057 (blk < fs->super->s_first_data_block ||
12058 blk >= fs->super->s_blocks_count)) {
12059 resize_inode_invalid:
12060 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
12061 memset(&inode, 0, sizeof(inode));
12062 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
12063 "clear_resize");
12064 ctx->flags |= E2F_FLAG_RESIZE_INODE;
12065 }
12066 if (!(ctx->options & E2F_OPT_READONLY)) {
12067 fs->super->s_state &= ~EXT2_VALID_FS;
12068 ext2fs_mark_super_dirty(fs);
12069 }
12070 goto cleanup;
12071 }
12072 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
12073 "resize dind buffer");
12074 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
12075
12076 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
12077 if (retval)
12078 goto resize_inode_invalid;
12079
12080 gdt_off = fs->desc_blocks;
12081 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
12082 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
12083 i++, gdt_off++, pblk++) {
12084 gdt_off %= fs->blocksize/4;
12085 if (dind_buf[gdt_off] != pblk)
12086 goto resize_inode_invalid;
12087 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
12088 if (retval)
12089 goto resize_inode_invalid;
12090 ind_off = 0;
12091 for (j = 1; j < fs->group_desc_count; j++) {
12092 if (!ext2fs_bg_has_super(fs, j))
12093 continue;
12094 expect = pblk + (j * fs->super->s_blocks_per_group);
12095 if (ind_buf[ind_off] != expect)
12096 goto resize_inode_invalid;
12097 ind_off++;
12098 }
12099 }
12100
12101cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000012102 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012103
12104 }
12105
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012106static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012107{
12108 ext2_filsys fs = ctx->fs;
12109 blk_t first_block, last_block;
12110 struct ext2_super_block *sb = fs->super;
12111 struct ext2_group_desc *gd;
12112 blk_t blocks_per_group = fs->super->s_blocks_per_group;
12113 blk_t bpg_max;
12114 int inodes_per_block;
12115 int ipg_max;
12116 int inode_size;
12117 dgrp_t i;
12118 blk_t should_be;
12119 struct problem_context pctx;
12120 __u32 free_blocks = 0, free_inodes = 0;
12121
12122 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
12123 ipg_max = inodes_per_block * (blocks_per_group - 4);
12124 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
12125 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
12126 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
12127 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
12128 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
12129
12130 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
12131 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
12132 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
12133 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
12134 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
12135 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
12136
12137 clear_problem_context(&pctx);
12138
12139 /*
12140 * Verify the super block constants...
12141 */
12142 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
12143 MIN_CHECK, 1, 0);
12144 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
12145 MIN_CHECK, 1, 0);
12146 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
12147 MAX_CHECK, 0, sb->s_blocks_count);
12148 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
12149 MIN_CHECK | MAX_CHECK, 0,
12150 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
12151 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
12152 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
12153 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
12154 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
12155 bpg_max);
12156 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
12157 MIN_CHECK | MAX_CHECK, 8, bpg_max);
12158 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
12159 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
12160 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
12161 MAX_CHECK, 0, sb->s_blocks_count / 2);
12162 check_super_value(ctx, "reserved_gdt_blocks",
12163 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
12164 fs->blocksize/4);
12165 inode_size = EXT2_INODE_SIZE(sb);
12166 check_super_value(ctx, "inode_size",
12167 inode_size, MIN_CHECK | MAX_CHECK,
12168 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
12169 if (inode_size & (inode_size - 1)) {
12170 pctx.num = inode_size;
12171 pctx.str = "inode_size";
12172 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
12173 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
12174 return;
12175 }
12176
12177 if (!ctx->num_blocks) {
12178 pctx.errcode = e2fsck_get_device_size(ctx);
12179 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
12180 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
12181 ctx->flags |= E2F_FLAG_ABORT;
12182 return;
12183 }
12184 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
12185 (ctx->num_blocks < sb->s_blocks_count)) {
12186 pctx.blk = sb->s_blocks_count;
12187 pctx.blk2 = ctx->num_blocks;
12188 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
12189 ctx->flags |= E2F_FLAG_ABORT;
12190 return;
12191 }
12192 }
12193 }
12194
12195 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
12196 pctx.blk = EXT2_BLOCK_SIZE(sb);
12197 pctx.blk2 = EXT2_FRAG_SIZE(sb);
12198 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
12199 ctx->flags |= E2F_FLAG_ABORT;
12200 return;
12201 }
12202
12203 should_be = sb->s_frags_per_group >>
12204 (sb->s_log_block_size - sb->s_log_frag_size);
12205 if (sb->s_blocks_per_group != should_be) {
12206 pctx.blk = sb->s_blocks_per_group;
12207 pctx.blk2 = should_be;
12208 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
12209 ctx->flags |= E2F_FLAG_ABORT;
12210 return;
12211 }
12212
12213 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
12214 if (sb->s_first_data_block != should_be) {
12215 pctx.blk = sb->s_first_data_block;
12216 pctx.blk2 = should_be;
12217 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
12218 ctx->flags |= E2F_FLAG_ABORT;
12219 return;
12220 }
12221
12222 should_be = sb->s_inodes_per_group * fs->group_desc_count;
12223 if (sb->s_inodes_count != should_be) {
12224 pctx.ino = sb->s_inodes_count;
12225 pctx.ino2 = should_be;
12226 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
12227 sb->s_inodes_count = should_be;
12228 ext2fs_mark_super_dirty(fs);
12229 }
12230 }
12231
12232 /*
12233 * Verify the group descriptors....
12234 */
12235 first_block = sb->s_first_data_block;
12236 last_block = first_block + blocks_per_group;
12237
12238 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
12239 pctx.group = i;
12240
12241 if (i == fs->group_desc_count - 1)
12242 last_block = sb->s_blocks_count;
12243 if ((gd->bg_block_bitmap < first_block) ||
12244 (gd->bg_block_bitmap >= last_block)) {
12245 pctx.blk = gd->bg_block_bitmap;
12246 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
12247 gd->bg_block_bitmap = 0;
12248 }
12249 if (gd->bg_block_bitmap == 0) {
12250 ctx->invalid_block_bitmap_flag[i]++;
12251 ctx->invalid_bitmaps++;
12252 }
12253 if ((gd->bg_inode_bitmap < first_block) ||
12254 (gd->bg_inode_bitmap >= last_block)) {
12255 pctx.blk = gd->bg_inode_bitmap;
12256 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
12257 gd->bg_inode_bitmap = 0;
12258 }
12259 if (gd->bg_inode_bitmap == 0) {
12260 ctx->invalid_inode_bitmap_flag[i]++;
12261 ctx->invalid_bitmaps++;
12262 }
12263 if ((gd->bg_inode_table < first_block) ||
12264 ((gd->bg_inode_table +
12265 fs->inode_blocks_per_group - 1) >= last_block)) {
12266 pctx.blk = gd->bg_inode_table;
12267 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
12268 gd->bg_inode_table = 0;
12269 }
12270 if (gd->bg_inode_table == 0) {
12271 ctx->invalid_inode_table_flag[i]++;
12272 ctx->invalid_bitmaps++;
12273 }
12274 free_blocks += gd->bg_free_blocks_count;
12275 free_inodes += gd->bg_free_inodes_count;
12276 first_block += sb->s_blocks_per_group;
12277 last_block += sb->s_blocks_per_group;
12278
12279 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
12280 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
12281 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
12282 ext2fs_unmark_valid(fs);
12283
12284 }
12285
12286 /*
12287 * Update the global counts from the block group counts. This
12288 * is needed for an experimental patch which eliminates
12289 * locking the entire filesystem when allocating blocks or
12290 * inodes; if the filesystem is not unmounted cleanly, the
12291 * global counts may not be accurate.
12292 */
12293 if ((free_blocks != sb->s_free_blocks_count) ||
12294 (free_inodes != sb->s_free_inodes_count)) {
12295 if (ctx->options & E2F_OPT_READONLY)
12296 ext2fs_unmark_valid(fs);
12297 else {
12298 sb->s_free_blocks_count = free_blocks;
12299 sb->s_free_inodes_count = free_inodes;
12300 ext2fs_mark_super_dirty(fs);
12301 }
12302 }
12303
12304 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
12305 (sb->s_free_inodes_count > sb->s_inodes_count))
12306 ext2fs_unmark_valid(fs);
12307
12308
12309 /*
12310 * If we have invalid bitmaps, set the error state of the
12311 * filesystem.
12312 */
12313 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
12314 sb->s_state &= ~EXT2_VALID_FS;
12315 ext2fs_mark_super_dirty(fs);
12316 }
12317
12318 clear_problem_context(&pctx);
12319
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012320 /*
12321 * If the UUID field isn't assigned, assign it.
12322 */
12323 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
12324 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
12325 uuid_generate(sb->s_uuid);
12326 ext2fs_mark_super_dirty(fs);
12327 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12328 }
12329 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012330
Rob Landley3e72c592006-04-06 22:49:04 +000012331 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012332 * For the Hurd, check to see if the filetype option is set,
12333 * since it doesn't support it.
12334 */
12335 if (!(ctx->options & E2F_OPT_READONLY) &&
12336 fs->super->s_creator_os == EXT2_OS_HURD &&
12337 (fs->super->s_feature_incompat &
12338 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
12339 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
12340 fs->super->s_feature_incompat &=
12341 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
12342 ext2fs_mark_super_dirty(fs);
12343
12344 }
12345 }
12346
12347 /*
12348 * If we have any of the compatibility flags set, we need to have a
12349 * revision 1 filesystem. Most kernels will not check the flags on
12350 * a rev 0 filesystem and we may have corruption issues because of
12351 * the incompatible changes to the filesystem.
12352 */
12353 if (!(ctx->options & E2F_OPT_READONLY) &&
12354 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
12355 (fs->super->s_feature_compat ||
12356 fs->super->s_feature_ro_compat ||
12357 fs->super->s_feature_incompat) &&
12358 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
12359 ext2fs_update_dynamic_rev(fs);
12360 ext2fs_mark_super_dirty(fs);
12361 }
12362
12363 check_resize_inode(ctx);
12364
12365 /*
12366 * Clean up any orphan inodes, if present.
12367 */
12368 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
12369 fs->super->s_state &= ~EXT2_VALID_FS;
12370 ext2fs_mark_super_dirty(fs);
12371 }
12372
12373 /*
12374 * Move the ext3 journal file, if necessary.
12375 */
12376 e2fsck_move_ext3_journal(ctx);
12377 return;
12378}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012379
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012380/*
12381 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012382 */
12383
12384#ifdef ENABLE_SWAPFS
12385
12386struct swap_block_struct {
12387 ext2_ino_t ino;
12388 int isdir;
12389 errcode_t errcode;
12390 char *dir_buf;
12391 struct ext2_inode *inode;
12392};
12393
12394/*
12395 * This is a helper function for block_iterate. We mark all of the
12396 * indirect and direct blocks as changed, so that block_iterate will
12397 * write them out.
12398 */
12399static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
12400 void *priv_data)
12401{
12402 errcode_t retval;
12403
12404 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
12405
12406 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
12407 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
12408 if (retval) {
12409 sb->errcode = retval;
12410 return BLOCK_ABORT;
12411 }
12412 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
12413 if (retval) {
12414 sb->errcode = retval;
12415 return BLOCK_ABORT;
12416 }
12417 }
12418 if (blockcnt >= 0) {
12419 if (blockcnt < EXT2_NDIR_BLOCKS)
12420 return 0;
12421 return BLOCK_CHANGED;
12422 }
12423 if (blockcnt == BLOCK_COUNT_IND) {
12424 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
12425 return 0;
12426 return BLOCK_CHANGED;
12427 }
12428 if (blockcnt == BLOCK_COUNT_DIND) {
12429 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
12430 return 0;
12431 return BLOCK_CHANGED;
12432 }
12433 if (blockcnt == BLOCK_COUNT_TIND) {
12434 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
12435 return 0;
12436 return BLOCK_CHANGED;
12437 }
12438 return BLOCK_CHANGED;
12439}
12440
12441/*
12442 * This function is responsible for byte-swapping all of the indirect,
12443 * block pointers. It is also responsible for byte-swapping directories.
12444 */
12445static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
12446 struct ext2_inode *inode)
12447{
12448 errcode_t retval;
12449 struct swap_block_struct sb;
12450
12451 sb.ino = ino;
12452 sb.inode = inode;
12453 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
12454 sb.errcode = 0;
12455 sb.isdir = 0;
12456 if (LINUX_S_ISDIR(inode->i_mode))
12457 sb.isdir = 1;
12458
12459 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
12460 swap_block, &sb);
12461 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012462 bb_error_msg(_("while calling ext2fs_block_iterate"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012463 ctx->flags |= E2F_FLAG_ABORT;
12464 return;
12465 }
12466 if (sb.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012467 bb_error_msg(_("while calling iterator function"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012468 ctx->flags |= E2F_FLAG_ABORT;
12469 return;
12470 }
12471}
12472
12473static void swap_inodes(e2fsck_t ctx)
12474{
12475 ext2_filsys fs = ctx->fs;
12476 dgrp_t group;
12477 unsigned int i;
12478 ext2_ino_t ino = 1;
12479 char *buf, *block_buf;
12480 errcode_t retval;
12481 struct ext2_inode * inode;
12482
12483 e2fsck_use_inode_shortcuts(ctx, 1);
12484
12485 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
12486 &buf);
12487 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012488 bb_error_msg(_("while allocating inode buffer"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012489 ctx->flags |= E2F_FLAG_ABORT;
12490 return;
12491 }
12492 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12493 "block interate buffer");
12494 for (group = 0; group < fs->group_desc_count; group++) {
12495 retval = io_channel_read_blk(fs->io,
12496 fs->group_desc[group].bg_inode_table,
12497 fs->inode_blocks_per_group, buf);
12498 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012499 bb_error_msg(_("while reading inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012500 group);
12501 ctx->flags |= E2F_FLAG_ABORT;
12502 return;
12503 }
12504 inode = (struct ext2_inode *) buf;
12505 for (i=0; i < fs->super->s_inodes_per_group;
12506 i++, ino++, inode++) {
12507 ctx->stashed_ino = ino;
12508 ctx->stashed_inode = inode;
12509
12510 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
12511 ext2fs_swap_inode(fs, inode, inode, 0);
12512
12513 /*
12514 * Skip deleted files.
12515 */
12516 if (inode->i_links_count == 0)
12517 continue;
12518
12519 if (LINUX_S_ISDIR(inode->i_mode) ||
12520 ((inode->i_block[EXT2_IND_BLOCK] ||
12521 inode->i_block[EXT2_DIND_BLOCK] ||
12522 inode->i_block[EXT2_TIND_BLOCK]) &&
12523 ext2fs_inode_has_valid_blocks(inode)))
12524 swap_inode_blocks(ctx, ino, block_buf, inode);
12525
12526 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12527 return;
12528
12529 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12530 ext2fs_swap_inode(fs, inode, inode, 1);
12531 }
12532 retval = io_channel_write_blk(fs->io,
12533 fs->group_desc[group].bg_inode_table,
12534 fs->inode_blocks_per_group, buf);
12535 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012536 bb_error_msg(_("while writing inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012537 group);
12538 ctx->flags |= E2F_FLAG_ABORT;
12539 return;
12540 }
12541 }
12542 ext2fs_free_mem(&buf);
12543 ext2fs_free_mem(&block_buf);
12544 e2fsck_use_inode_shortcuts(ctx, 0);
12545 ext2fs_flush_icache(fs);
12546}
12547
Rob Landley7c94bed2006-05-03 21:58:45 +000012548#if defined(__powerpc__) && BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012549/*
12550 * On the PowerPC, the big-endian variant of the ext2 filesystem
12551 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
12552 * of each word. Thus a bitmap with only bit 0 set would be, as
12553 * a string of bytes, 00 00 00 01 00 ...
12554 * To cope with this, we byte-reverse each word of a bitmap if
12555 * we have a big-endian filesystem, that is, if we are *not*
12556 * byte-swapping other word-sized numbers.
12557 */
12558#define EXT2_BIG_ENDIAN_BITMAPS
12559#endif
12560
12561#ifdef EXT2_BIG_ENDIAN_BITMAPS
12562static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
12563{
12564 __u32 *p = (__u32 *) bmap->bitmap;
12565 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
12566
12567 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
12568 *p = ext2fs_swab32(*p);
12569}
12570#endif
12571
12572
12573#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012574static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012575{
12576 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012577 if (!(ctx->options & E2F_OPT_PREEN))
12578 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
12579
Rob Landley3e72c592006-04-06 22:49:04 +000012580 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012581
12582 if (fs->super->s_mnt_count) {
12583 fprintf(stderr, _("%s: the filesystem must be freshly "
12584 "checked using fsck\n"
12585 "and not mounted before trying to "
12586 "byte-swap it.\n"), ctx->device_name);
12587 ctx->flags |= E2F_FLAG_ABORT;
12588 return;
12589 }
12590 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
12591 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
12592 EXT2_FLAG_SWAP_BYTES_WRITE);
12593 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
12594 } else {
12595 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
12596 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
12597 }
12598 swap_inodes(ctx);
12599 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12600 return;
12601 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12602 fs->flags |= EXT2_FLAG_SWAP_BYTES;
12603 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
12604 EXT2_FLAG_SWAP_BYTES_WRITE);
12605
12606#ifdef EXT2_BIG_ENDIAN_BITMAPS
12607 e2fsck_read_bitmaps(ctx);
12608 ext2fs_swap_bitmap(fs->inode_map);
12609 ext2fs_swap_bitmap(fs->block_map);
12610 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
12611#endif
12612 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12613 ext2fs_flush(fs);
12614 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012615}
12616#endif /* ENABLE_SWAPFS */
12617
12618#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012619
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012620/*
12621 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012622 */
12623
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012624
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012625void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
12626 const char *description)
12627{
12628 void *ret;
12629 char buf[256];
12630
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012631 ret = malloc(size);
12632 if (!ret) {
12633 sprintf(buf, "Can't allocate %s\n", description);
Rob Landley7c94bed2006-05-03 21:58:45 +000012634 bb_error_msg_and_die(buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012635 }
12636 memset(ret, 0, size);
12637 return ret;
12638}
12639
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012640static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012641{
12642 char *ret;
12643
12644 if (!str)
12645 return NULL;
12646 if (!len)
12647 len = strlen(str);
12648 ret = malloc(len+1);
12649 if (ret) {
12650 strncpy(ret, str, len);
12651 ret[len] = 0;
12652 }
12653 return ret;
12654}
12655
12656#ifndef HAVE_CONIO_H
12657static int read_a_char(void)
12658{
12659 char c;
12660 int r;
12661 int fail = 0;
12662
12663 while(1) {
12664 if (e2fsck_global_ctx &&
12665 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
12666 return 3;
12667 }
12668 r = read(0, &c, 1);
12669 if (r == 1)
12670 return c;
12671 if (fail++ > 100)
12672 break;
12673 }
12674 return EOF;
12675}
12676#endif
12677
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012678static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012679{
12680 int c;
12681 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012682 static const char short_yes[] = "yY";
12683 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012684
12685#ifdef HAVE_TERMIOS_H
12686 struct termios termios, tmp;
12687
12688 tcgetattr (0, &termios);
12689 tmp = termios;
12690 tmp.c_lflag &= ~(ICANON | ECHO);
12691 tmp.c_cc[VMIN] = 1;
12692 tmp.c_cc[VTIME] = 0;
12693 tcsetattr (0, TCSANOW, &tmp);
12694#endif
12695
12696 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012697 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012698 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012699 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012700 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012701 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012702 printf("%s%s? ", string, defstr);
12703 while (1) {
12704 fflush (stdout);
12705 if ((c = read_a_char()) == EOF)
12706 break;
12707 if (c == 3) {
12708#ifdef HAVE_TERMIOS_H
12709 tcsetattr (0, TCSANOW, &termios);
12710#endif
12711 if (e2fsck_global_ctx &&
12712 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
12713 puts("\n");
12714 longjmp(e2fsck_global_ctx->abort_loc, 1);
12715 }
12716 puts(_("cancelled!\n"));
12717 return 0;
12718 }
12719 if (strchr(short_yes, (char) c)) {
12720 def = 1;
12721 break;
12722 }
12723 else if (strchr(short_no, (char) c)) {
12724 def = 0;
12725 break;
12726 }
12727 else if ((c == ' ' || c == '\n') && (def != -1))
12728 break;
12729 }
12730 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012731 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012732 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012733 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012734#ifdef HAVE_TERMIOS_H
12735 tcsetattr (0, TCSANOW, &termios);
12736#endif
12737 return def;
12738}
12739
12740int ask (e2fsck_t ctx, const char * string, int def)
12741{
12742 if (ctx->options & E2F_OPT_NO) {
12743 printf (_("%s? no\n\n"), string);
12744 return 0;
12745 }
12746 if (ctx->options & E2F_OPT_YES) {
12747 printf (_("%s? yes\n\n"), string);
12748 return 1;
12749 }
12750 if (ctx->options & E2F_OPT_PREEN) {
12751 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
12752 return def;
12753 }
12754 return ask_yn(string, def);
12755}
12756
12757void e2fsck_read_bitmaps(e2fsck_t ctx)
12758{
12759 ext2_filsys fs = ctx->fs;
12760 errcode_t retval;
12761
12762 if (ctx->invalid_bitmaps) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012763 bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012764 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012765 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012766 }
12767
12768 ehandler_operation(_("reading inode and block bitmaps"));
12769 retval = ext2fs_read_bitmaps(fs);
12770 ehandler_operation(0);
12771 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012772 bb_error_msg(_("while retrying to read bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012773 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012774 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012775 }
12776}
12777
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012778static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012779{
12780 ext2_filsys fs = ctx->fs;
12781 errcode_t retval;
12782
12783 if (ext2fs_test_bb_dirty(fs)) {
12784 ehandler_operation(_("writing block bitmaps"));
12785 retval = ext2fs_write_block_bitmap(fs);
12786 ehandler_operation(0);
12787 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012788 bb_error_msg(_("while retrying to write block bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012789 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012790 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012791 }
12792 }
12793
12794 if (ext2fs_test_ib_dirty(fs)) {
12795 ehandler_operation(_("writing inode bitmaps"));
12796 retval = ext2fs_write_inode_bitmap(fs);
12797 ehandler_operation(0);
12798 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012799 bb_error_msg(_("while retrying to write inode bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012800 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012801 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012802 }
12803 }
12804}
12805
12806void preenhalt(e2fsck_t ctx)
12807{
12808 ext2_filsys fs = ctx->fs;
12809
12810 if (!(ctx->options & E2F_OPT_PREEN))
12811 return;
12812 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
12813 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
12814 ctx->device_name);
12815 if (fs != NULL) {
12816 fs->super->s_state |= EXT2_ERROR_FS;
12817 ext2fs_mark_super_dirty(fs);
12818 ext2fs_close(fs);
12819 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012820 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012821}
12822
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012823void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
12824 struct ext2_inode * inode, const char *proc)
12825{
12826 int retval;
12827
12828 retval = ext2fs_read_inode(ctx->fs, ino, inode);
12829 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012830 bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
12831 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012832 }
12833}
12834
12835extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
12836 struct ext2_inode * inode, int bufsize,
12837 const char *proc)
12838{
12839 int retval;
12840
12841 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
12842 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012843 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12844 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012845 }
12846}
12847
12848extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
12849 struct ext2_inode * inode, const char *proc)
12850{
12851 int retval;
12852
12853 retval = ext2fs_write_inode(ctx->fs, ino, inode);
12854 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012855 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12856 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012857 }
12858}
12859
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012860blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
12861 io_manager manager)
12862{
12863 struct ext2_super_block *sb;
12864 io_channel io = NULL;
12865 void *buf = NULL;
12866 int blocksize;
12867 blk_t superblock, ret_sb = 8193;
12868
12869 if (fs && fs->super) {
12870 ret_sb = (fs->super->s_blocks_per_group +
12871 fs->super->s_first_data_block);
12872 if (ctx) {
12873 ctx->superblock = ret_sb;
12874 ctx->blocksize = fs->blocksize;
12875 }
12876 return ret_sb;
12877 }
12878
12879 if (ctx) {
12880 if (ctx->blocksize) {
12881 ret_sb = ctx->blocksize * 8;
12882 if (ctx->blocksize == 1024)
12883 ret_sb++;
12884 ctx->superblock = ret_sb;
12885 return ret_sb;
12886 }
12887 ctx->superblock = ret_sb;
12888 ctx->blocksize = 1024;
12889 }
12890
12891 if (!name || !manager)
12892 goto cleanup;
12893
12894 if (manager->open(name, 0, &io) != 0)
12895 goto cleanup;
12896
12897 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
12898 goto cleanup;
12899 sb = (struct ext2_super_block *) buf;
12900
12901 for (blocksize = EXT2_MIN_BLOCK_SIZE;
12902 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
12903 superblock = blocksize*8;
12904 if (blocksize == 1024)
12905 superblock++;
12906 io_channel_set_blksize(io, blocksize);
12907 if (io_channel_read_blk(io, superblock,
12908 -SUPERBLOCK_SIZE, buf))
12909 continue;
Rob Landley7c94bed2006-05-03 21:58:45 +000012910#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012911 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
12912 ext2fs_swap_super(sb);
12913#endif
12914 if (sb->s_magic == EXT2_SUPER_MAGIC) {
12915 ret_sb = superblock;
12916 if (ctx) {
12917 ctx->superblock = superblock;
12918 ctx->blocksize = blocksize;
12919 }
12920 break;
12921 }
12922 }
12923
12924cleanup:
12925 if (io)
12926 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000012927 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012928 return (ret_sb);
12929}
12930
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012931
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012932/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012933 * This function runs through the e2fsck passes and calls them all,
12934 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012935 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012936typedef void (*pass_t)(e2fsck_t ctx);
12937
12938static const pass_t e2fsck_passes[] = {
12939 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
12940 e2fsck_pass5, 0 };
12941
12942#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
12943
12944static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012945{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012946 int i;
12947 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012948
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012949 if (setjmp(ctx->abort_loc)) {
12950 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
12951 return (ctx->flags & E2F_FLAG_RUN_RETURN);
12952 }
12953 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012954
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012955 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
12956 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12957 break;
12958 e2fsck_pass(ctx);
12959 if (ctx->progress)
12960 (void) (ctx->progress)(ctx, 0, 0, 0);
12961 }
12962 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012963
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012964 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12965 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012966 return 0;
12967}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012968
12969
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012970/*
12971 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012972 */
12973
12974
Mike Frysinger51a43b42005-09-24 07:11:16 +000012975/* Command line options */
12976static int swapfs;
12977#ifdef ENABLE_SWAPFS
12978static int normalize_swapfs;
12979#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012980static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000012981static int show_version_only;
12982static int verbose;
12983
12984static int replace_bad_blocks;
12985static int keep_bad_blocks;
12986static char *bad_blocks_file;
12987
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012988#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
12989
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012990static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012991{
12992 ext2_filsys fs = ctx->fs;
12993 int inodes, inodes_used, blocks, blocks_used;
12994 int dir_links;
12995 int num_files, num_links;
12996 int frag_percent;
12997
12998 dir_links = 2 * ctx->fs_directory_count - 1;
12999 num_files = ctx->fs_total_count - dir_links;
13000 num_links = ctx->fs_links_count - dir_links;
13001 inodes = fs->super->s_inodes_count;
13002 inodes_used = (fs->super->s_inodes_count -
13003 fs->super->s_free_inodes_count);
13004 blocks = fs->super->s_blocks_count;
13005 blocks_used = (fs->super->s_blocks_count -
13006 fs->super->s_free_blocks_count);
13007
13008 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
13009 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013010
Mike Frysinger51a43b42005-09-24 07:11:16 +000013011 if (!verbose) {
13012 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
13013 ctx->device_name, inodes_used, inodes,
13014 frag_percent / 10, frag_percent % 10,
13015 blocks_used, blocks);
13016 return;
13017 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013018 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
13019 100 * inodes_used / inodes);
13020 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
13021 P_E2("", "s", ctx->fs_fragmented),
13022 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013023 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
13024 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013025 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
13026 (int) ((long long) 100 * blocks_used / blocks));
13027 printf ("%8d bad block%s\n", P_E2("", "s", ctx->fs_badblocks_count));
13028 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
13029 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
13030 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
13031 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
13032 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
13033 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
13034 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
13035 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
13036 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
13037 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
13038 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013039}
13040
13041static void check_mount(e2fsck_t ctx)
13042{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013043 errcode_t retval;
13044 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013045
13046 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
13047 &ctx->mount_flags);
13048 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013049 bb_error_msg(_("while determining whether %s is mounted."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013050 ctx->filesystem_name);
13051 return;
13052 }
13053
13054 /*
13055 * If the filesystem isn't mounted, or it's the root filesystem
13056 * and it's mounted read-only, then everything's fine.
13057 */
13058 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
13059 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
13060 (ctx->mount_flags & EXT2_MF_READONLY)))
13061 return;
13062
13063 if (ctx->options & E2F_OPT_READONLY) {
13064 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
13065 return;
13066 }
13067
13068 printf(_("%s is mounted. "), ctx->filesystem_name);
13069 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013070 bb_error_msg_and_die(_("Cannot continue, aborting.\n\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013071 printf(_("\n\n\007\007\007\007WARNING!!! "
13072 "Running e2fsck on a mounted filesystem may cause\n"
13073 "SEVERE filesystem damage.\007\007\007\n\n"));
13074 cont = ask_yn(_("Do you really want to continue"), -1);
13075 if (!cont) {
13076 printf (_("check aborted.\n"));
13077 exit (0);
13078 }
13079 return;
13080}
13081
13082static int is_on_batt(void)
13083{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013084 FILE *f;
13085 DIR *d;
13086 char tmp[80], tmp2[80], fname[80];
13087 unsigned int acflag;
13088 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013089
13090 f = fopen("/proc/apm", "r");
13091 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013092 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013093 acflag = 1;
13094 fclose(f);
13095 return (acflag != 1);
13096 }
13097 d = opendir("/proc/acpi/ac_adapter");
13098 if (d) {
13099 while ((de=readdir(d)) != NULL) {
13100 if (!strncmp(".", de->d_name, 1))
13101 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013102 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000013103 de->d_name);
13104 f = fopen(fname, "r");
13105 if (!f)
13106 continue;
13107 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
13108 tmp[0] = 0;
13109 fclose(f);
13110 if (strncmp(tmp, "off-line", 8) == 0) {
13111 closedir(d);
13112 return 1;
13113 }
13114 }
13115 closedir(d);
13116 }
13117 return 0;
13118}
13119
13120/*
13121 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013122 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000013123 * message explaining why a check is being forced.
13124 */
13125static void check_if_skip(e2fsck_t ctx)
13126{
13127 ext2_filsys fs = ctx->fs;
13128 const char *reason = NULL;
13129 unsigned int reason_arg = 0;
13130 long next_check;
13131 int batt = is_on_batt();
13132 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013133
Mike Frysinger51a43b42005-09-24 07:11:16 +000013134 if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
13135 cflag || swapfs)
13136 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013137
Mike Frysinger51a43b42005-09-24 07:11:16 +000013138 if ((fs->super->s_state & EXT2_ERROR_FS) ||
13139 !ext2fs_test_valid(fs))
13140 reason = _(" contains a file system with errors");
13141 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
13142 reason = _(" was not cleanly unmounted");
13143 else if ((fs->super->s_max_mnt_count > 0) &&
13144 (fs->super->s_mnt_count >=
13145 (unsigned) fs->super->s_max_mnt_count)) {
13146 reason = _(" has been mounted %u times without being checked");
13147 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013148 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000013149 (unsigned) fs->super->s_max_mnt_count*2))
13150 reason = 0;
13151 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013152 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000013153 fs->super->s_checkinterval)) {
13154 reason = _(" has gone %u days without being checked");
13155 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013156 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000013157 fs->super->s_checkinterval*2))
13158 reason = 0;
13159 }
13160 if (reason) {
13161 fputs(ctx->device_name, stdout);
13162 printf(reason, reason_arg);
13163 fputs(_(", check forced.\n"), stdout);
13164 return;
13165 }
13166 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
13167 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
13168 fs->super->s_inodes_count,
13169 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
13170 fs->super->s_blocks_count);
13171 next_check = 100000;
13172 if (fs->super->s_max_mnt_count > 0) {
13173 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013174 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013175 next_check = 1;
13176 }
13177 if (fs->super->s_checkinterval &&
13178 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
13179 next_check = 1;
13180 if (next_check <= 5) {
13181 if (next_check == 1)
13182 fputs(_(" (check after next mount)"), stdout);
13183 else
13184 printf(_(" (check in %ld mounts)"), next_check);
13185 }
13186 fputc('\n', stdout);
13187 ext2fs_close(fs);
13188 ctx->fs = NULL;
13189 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013190 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013191}
13192
13193/*
13194 * For completion notice
13195 */
13196struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013197 int max_pass;
13198 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000013199};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013200static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013201 5, { 0, 70, 90, 92, 95, 100 }
13202};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013203
Mike Frysinger51a43b42005-09-24 07:11:16 +000013204static char bar[128], spaces[128];
13205
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013206static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013207 int max)
13208{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013209 float percent;
13210
Mike Frysinger51a43b42005-09-24 07:11:16 +000013211 if (pass <= 0)
13212 return 0.0;
13213 if (pass > tbl->max_pass || max == 0)
13214 return 100.0;
13215 percent = ((float) curr) / ((float) max);
13216 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
13217 + tbl->table[pass-1]);
13218}
13219
Rob Landleydfba7412006-03-06 20:47:33 +000013220void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013221{
13222 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
13223 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013224
Mike Frysinger51a43b42005-09-24 07:11:16 +000013225 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
13226 ctx->stop_meta);
13227 fflush(stdout);
13228 ctx->flags &= ~E2F_FLAG_PROG_BAR;
13229}
13230
13231int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
13232 unsigned int dpynum)
13233{
13234 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013235 int i;
13236 unsigned int tick;
13237 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013238 int dpywidth;
13239 int fixed_percent;
13240
13241 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
13242 return 0;
13243
13244 /*
13245 * Calculate the new progress position. If the
13246 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013247 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000013248 */
13249 fixed_percent = (int) ((10 * percent) + 0.5);
13250 if (ctx->progress_last_percent == fixed_percent)
13251 return 0;
13252 ctx->progress_last_percent = fixed_percent;
13253
13254 /*
13255 * If we've already updated the spinner once within
13256 * the last 1/8th of a second, no point doing it
13257 * again.
13258 */
13259 gettimeofday(&tv, NULL);
13260 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
13261 if ((tick == ctx->progress_last_time) &&
13262 (fixed_percent != 0) && (fixed_percent != 1000))
13263 return 0;
13264 ctx->progress_last_time = tick;
13265
13266 /*
13267 * Advance the spinner, and note that the progress bar
13268 * will be on the screen
13269 */
13270 ctx->progress_pos = (ctx->progress_pos+1) & 3;
13271 ctx->flags |= E2F_FLAG_PROG_BAR;
13272
13273 dpywidth = 66 - strlen(label);
13274 dpywidth = 8 * (dpywidth / 8);
13275 if (dpynum)
13276 dpywidth -= 8;
13277
13278 i = ((percent * dpywidth) + 50) / 100;
13279 printf("%s%s: |%s%s", ctx->start_meta, label,
13280 bar + (sizeof(bar) - (i+1)),
13281 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
13282 if (fixed_percent == 1000)
13283 fputc('|', stdout);
13284 else
13285 fputc(spinner[ctx->progress_pos & 3], stdout);
13286 printf(" %4.1f%% ", percent);
13287 if (dpynum)
13288 printf("%u\r", dpynum);
13289 else
13290 fputs(" \r", stdout);
13291 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013292
Mike Frysinger51a43b42005-09-24 07:11:16 +000013293 if (fixed_percent == 1000)
13294 e2fsck_clear_progbar(ctx);
13295 fflush(stdout);
13296
13297 return 0;
13298}
13299
13300static int e2fsck_update_progress(e2fsck_t ctx, int pass,
13301 unsigned long cur, unsigned long max)
13302{
13303 char buf[80];
13304 float percent;
13305
13306 if (pass == 0)
13307 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013308
Mike Frysinger51a43b42005-09-24 07:11:16 +000013309 if (ctx->progress_fd) {
13310 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
13311 write(ctx->progress_fd, buf, strlen(buf));
13312 } else {
13313 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
13314 e2fsck_simple_progress(ctx, ctx->device_name,
13315 percent, 0);
13316 }
13317 return 0;
13318}
13319
Mike Frysinger51a43b42005-09-24 07:11:16 +000013320static void reserve_stdio_fds(void)
13321{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013322 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013323
13324 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000013325 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013326 if (fd > 2)
13327 break;
13328 if (fd < 0) {
13329 fprintf(stderr, _("ERROR: Couldn't open "
13330 "/dev/null (%s)\n"),
13331 strerror(errno));
13332 break;
13333 }
13334 }
13335 close(fd);
13336}
13337
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000013338static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000013339{
13340 e2fsck_t ctx = e2fsck_global_ctx;
13341
13342 if (!ctx)
13343 return;
13344
13345 ctx->progress = e2fsck_update_progress;
13346 ctx->progress_fd = 0;
13347}
13348
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000013349static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000013350{
13351 e2fsck_t ctx = e2fsck_global_ctx;
13352
13353 if (!ctx)
13354 return;
13355
13356 e2fsck_clear_progbar(ctx);
13357 ctx->progress = 0;
13358}
13359
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000013360static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000013361{
13362 e2fsck_t ctx = e2fsck_global_ctx;
13363
13364 if (!ctx)
13365 exit(FSCK_CANCELED);
13366
13367 ctx->flags |= E2F_FLAG_CANCEL;
13368}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013369
13370static void parse_extended_opts(e2fsck_t ctx, const char *opts)
13371{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013372 char *buf, *token, *next, *p, *arg;
13373 int ea_ver;
13374 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013375
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013376 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013377 for (token = buf; token && *token; token = next) {
13378 p = strchr(token, ',');
13379 next = 0;
13380 if (p) {
13381 *p = 0;
13382 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013383 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000013384 arg = strchr(token, '=');
13385 if (arg) {
13386 *arg = 0;
13387 arg++;
13388 }
13389 if (strcmp(token, "ea_ver") == 0) {
13390 if (!arg) {
13391 extended_usage++;
13392 continue;
13393 }
13394 ea_ver = strtoul(arg, &p, 0);
13395 if (*p ||
13396 ((ea_ver != 1) && (ea_ver != 2))) {
13397 fprintf(stderr,
13398 _("Invalid EA version.\n"));
13399 extended_usage++;
13400 continue;
13401 }
13402 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000013403 } else {
13404 fprintf(stderr, _("Unknown extended option: %s\n"),
13405 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013406 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000013407 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000013408 }
13409 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013410 bb_error_msg_and_die(
13411 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000013412 "and may take an argument which\n"
13413 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000013414 "Valid extended options are:\n"
13415 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000013416 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013417}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013418
13419
13420static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
13421{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013422 int flush = 0;
13423 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013424 e2fsck_t ctx;
13425 errcode_t retval;
13426 struct sigaction sa;
13427 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013428
13429 retval = e2fsck_allocate_context(&ctx);
13430 if (retval)
13431 return retval;
13432
13433 *ret_ctx = ctx;
13434
13435 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
13436 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
13437 if (isatty(0) && isatty(1)) {
13438 ctx->interactive = 1;
13439 } else {
13440 ctx->start_meta[0] = '\001';
13441 ctx->stop_meta[0] = '\002';
13442 }
13443 memset(bar, '=', sizeof(bar)-1);
13444 memset(spaces, ' ', sizeof(spaces)-1);
13445 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013446
Mike Frysinger51a43b42005-09-24 07:11:16 +000013447 if (argc && *argv)
13448 ctx->program_name = *argv;
13449 else
13450 ctx->program_name = "e2fsck";
13451 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
13452 switch (c) {
13453 case 'C':
13454 ctx->progress = e2fsck_update_progress;
13455 ctx->progress_fd = atoi(optarg);
13456 if (!ctx->progress_fd)
13457 break;
13458 /* Validate the file descriptor to avoid disasters */
13459 fd = dup(ctx->progress_fd);
13460 if (fd < 0) {
13461 fprintf(stderr,
13462 _("Error validating file descriptor %d: %s\n"),
13463 ctx->progress_fd,
13464 error_message(errno));
Rob Landley7c94bed2006-05-03 21:58:45 +000013465 bb_error_msg_and_die(_("Invalid completion information file descriptor"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013466 } else
13467 close(fd);
13468 break;
13469 case 'D':
13470 ctx->options |= E2F_OPT_COMPRESS_DIRS;
13471 break;
13472 case 'E':
13473 extended_opts = optarg;
13474 break;
13475 case 'p':
13476 case 'a':
13477 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
13478 conflict_opt:
Rob Landley7c94bed2006-05-03 21:58:45 +000013479 bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013480 }
13481 ctx->options |= E2F_OPT_PREEN;
13482 break;
13483 case 'n':
13484 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
13485 goto conflict_opt;
13486 ctx->options |= E2F_OPT_NO;
13487 break;
13488 case 'y':
13489 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
13490 goto conflict_opt;
13491 ctx->options |= E2F_OPT_YES;
13492 break;
13493 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000013494 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013495 fprintf(stderr, _("The -t option is not "
13496 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013497 break;
13498 case 'c':
13499 if (cflag++)
13500 ctx->options |= E2F_OPT_WRITECHECK;
13501 ctx->options |= E2F_OPT_CHECKBLOCKS;
13502 break;
13503 case 'r':
13504 /* What we do by default, anyway! */
13505 break;
13506 case 'b':
13507 ctx->use_superblock = atoi(optarg);
13508 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
13509 break;
13510 case 'B':
13511 ctx->blocksize = atoi(optarg);
13512 break;
13513 case 'I':
13514 ctx->inode_buffer_blocks = atoi(optarg);
13515 break;
13516 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013517 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013518 break;
13519 case 'P':
13520 ctx->process_inode_size = atoi(optarg);
13521 break;
13522 case 'L':
13523 replace_bad_blocks++;
13524 case 'l':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013525 bad_blocks_file = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013526 break;
13527 case 'd':
13528 ctx->options |= E2F_OPT_DEBUG;
13529 break;
13530 case 'f':
13531 ctx->options |= E2F_OPT_FORCE;
13532 break;
13533 case 'F':
13534 flush = 1;
13535 break;
13536 case 'v':
13537 verbose = 1;
13538 break;
13539 case 'V':
13540 show_version_only = 1;
13541 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013542 case 'N':
13543 ctx->device_name = optarg;
13544 break;
13545#ifdef ENABLE_SWAPFS
13546 case 's':
13547 normalize_swapfs = 1;
13548 case 'S':
13549 swapfs = 1;
13550 break;
13551#else
13552 case 's':
13553 case 'S':
13554 fprintf(stderr, _("Byte-swapping filesystems "
13555 "not compiled in this version "
13556 "of e2fsck\n"));
13557 exit(1);
13558#endif
13559 case 'k':
13560 keep_bad_blocks++;
13561 break;
13562 default:
Rob Landley7c94bed2006-05-03 21:58:45 +000013563 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013564 }
13565 if (show_version_only)
13566 return 0;
13567 if (optind != argc - 1)
Rob Landley7c94bed2006-05-03 21:58:45 +000013568 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013569 if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
13570 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
13571 ctx->options |= E2F_OPT_READONLY;
13572 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013573 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013574 *ctx->io_options++ = 0;
13575 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
13576 if (!ctx->filesystem_name) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013577 bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
13578 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013579 }
13580 if (extended_opts)
13581 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013582
Mike Frysinger51a43b42005-09-24 07:11:16 +000013583 if (flush) {
13584 fd = open(ctx->filesystem_name, O_RDONLY, 0);
13585 if (fd < 0) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013586 bb_error_msg(_("while opening %s for flushing"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013587 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013588 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013589 }
13590 if ((retval = ext2fs_sync_device(fd, 1))) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013591 bb_error_msg(_("while trying to flush %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013592 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013593 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013594 }
13595 close(fd);
13596 }
13597#ifdef ENABLE_SWAPFS
13598 if (swapfs) {
13599 if (cflag || bad_blocks_file) {
13600 fprintf(stderr, _("Incompatible options not "
13601 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013602 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013603 }
13604 }
13605#endif
13606 if (cflag && bad_blocks_file) {
13607 fprintf(stderr, _("The -c and the -l/-L options may "
13608 "not be both used at the same time.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013609 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013610 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000013611 /*
13612 * Set up signal action
13613 */
13614 memset(&sa, 0, sizeof(struct sigaction));
13615 sa.sa_handler = signal_cancel;
13616 sigaction(SIGINT, &sa, 0);
13617 sigaction(SIGTERM, &sa, 0);
13618#ifdef SA_RESTART
13619 sa.sa_flags = SA_RESTART;
13620#endif
13621 e2fsck_global_ctx = ctx;
13622 sa.sa_handler = signal_progress_on;
13623 sigaction(SIGUSR1, &sa, 0);
13624 sa.sa_handler = signal_progress_off;
13625 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013626
13627 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013628 if (cflag)
13629 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013630 return 0;
13631}
13632
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013633static const char my_ver_string[] = E2FSPROGS_VERSION;
13634static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013635
Mike Frysinger51a43b42005-09-24 07:11:16 +000013636int e2fsck_main (int argc, char *argv[])
13637{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013638 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013639 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013640 ext2_filsys fs = 0;
13641 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013642 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013643 const char *lib_ver_date;
13644 int my_ver, lib_ver;
13645 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013646 struct problem_context pctx;
13647 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013648
Mike Frysinger51a43b42005-09-24 07:11:16 +000013649 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000013650
Mike Frysinger51a43b42005-09-24 07:11:16 +000013651 my_ver = ext2fs_parse_version_string(my_ver_string);
13652 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
13653 if (my_ver > lib_ver) {
13654 fprintf( stderr, _("Error: ext2fs library version "
13655 "out of date!\n"));
13656 show_version_only++;
13657 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013658
Mike Frysinger51a43b42005-09-24 07:11:16 +000013659 retval = PRS(argc, argv, &ctx);
13660 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013661 bb_error_msg(_("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013662 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013663 }
13664 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013665
Mike Frysinger51a43b42005-09-24 07:11:16 +000013666 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
13667 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
13668 my_ver_date);
13669
13670 if (show_version_only) {
13671 fprintf(stderr, _("\tUsing %s, %s\n"),
13672 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013673 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013674 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013675
Mike Frysinger51a43b42005-09-24 07:11:16 +000013676 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013677
Mike Frysinger51a43b42005-09-24 07:11:16 +000013678 if (!(ctx->options & E2F_OPT_PREEN) &&
13679 !(ctx->options & E2F_OPT_NO) &&
13680 !(ctx->options & E2F_OPT_YES)) {
13681 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013682 bb_error_msg_and_die(_("need terminal for interactive repairs"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013683 }
13684 ctx->superblock = ctx->use_superblock;
13685restart:
13686#ifdef CONFIG_TESTIO_DEBUG
13687 io_ptr = test_io_manager;
13688 test_io_backing_manager = unix_io_manager;
13689#else
13690 io_ptr = unix_io_manager;
13691#endif
13692 flags = 0;
13693 if ((ctx->options & E2F_OPT_READONLY) == 0)
13694 flags |= EXT2_FLAG_RW;
13695
13696 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013697 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013698 flags, ctx->superblock, ctx->blocksize,
13699 io_ptr, &fs);
13700 } else if (ctx->superblock) {
13701 int blocksize;
13702 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13703 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013704 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013705 ctx->io_options, flags,
13706 ctx->superblock, blocksize,
13707 io_ptr, &fs);
13708 if (!retval)
13709 break;
13710 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013711 } else
13712 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013713 flags, 0, 0, io_ptr, &fs);
13714 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
13715 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
13716 ((retval == EXT2_ET_BAD_MAGIC) ||
13717 ((retval == 0) && ext2fs_check_desc(fs)))) {
13718 if (!fs || (fs->group_desc_count > 1)) {
13719 printf(_("%s trying backup blocks...\n"),
13720 retval ? _("Couldn't find ext2 superblock,") :
13721 _("Group descriptors look bad..."));
13722 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
13723 if (fs)
13724 ext2fs_close(fs);
13725 goto restart;
13726 }
13727 }
13728 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013729 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013730 ctx->filesystem_name);
13731 if (retval == EXT2_ET_REV_TOO_HIGH) {
13732 printf(_("The filesystem revision is apparently "
13733 "too high for this version of e2fsck.\n"
13734 "(Or the filesystem superblock "
13735 "is corrupt)\n\n"));
13736 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
13737 } else if (retval == EXT2_ET_SHORT_READ)
13738 printf(_("Could this be a zero-length partition?\n"));
13739 else if ((retval == EPERM) || (retval == EACCES))
13740 printf(_("You must have %s access to the "
13741 "filesystem or be root\n"),
13742 (ctx->options & E2F_OPT_READONLY) ?
13743 "r/o" : "r/w");
13744 else if (retval == ENXIO)
13745 printf(_("Possibly non-existent or swap device?\n"));
13746#ifdef EROFS
13747 else if (retval == EROFS)
13748 printf(_("Disk write-protected; use the -n option "
13749 "to do a read-only\n"
13750 "check of the device.\n"));
13751#endif
13752 else
13753 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
Rob Landley7c94bed2006-05-03 21:58:45 +000013754 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013755 }
13756 ctx->fs = fs;
13757 fs->priv_data = ctx;
13758 sb = fs->super;
13759 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013760 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013761 ctx->filesystem_name);
13762 get_newer:
Rob Landley7c94bed2006-05-03 21:58:45 +000013763 bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013764 }
13765
13766 /*
13767 * Set the device name, which is used whenever we print error
13768 * or informational messages to the user.
13769 */
13770 if (ctx->device_name == 0 &&
13771 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013772 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013773 sizeof(sb->s_volume_name));
13774 }
13775 if (ctx->device_name == 0)
13776 ctx->device_name = ctx->filesystem_name;
13777
13778 /*
13779 * Make sure the ext3 superblock fields are consistent.
13780 */
13781 retval = e2fsck_check_ext3_journal(ctx);
13782 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013783 bb_error_msg(_("while checking ext3 journal for %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013784 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013785 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013786 }
13787
13788 /*
13789 * Check to see if we need to do ext3-style recovery. If so,
13790 * do it, and then restart the fsck.
13791 */
13792 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
13793 if (ctx->options & E2F_OPT_READONLY) {
13794 printf(_("Warning: skipping journal recovery "
13795 "because doing a read-only filesystem "
13796 "check.\n"));
13797 io_channel_flush(ctx->fs->io);
13798 } else {
13799 if (ctx->flags & E2F_FLAG_RESTARTED) {
13800 /*
13801 * Whoops, we attempted to run the
13802 * journal twice. This should never
13803 * happen, unless the hardware or
13804 * device driver is being bogus.
13805 */
Rob Landley7c94bed2006-05-03 21:58:45 +000013806 bb_error_msg(_("unable to set superblock flags on %s\n"), ctx->device_name);
13807 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013808 }
13809 retval = e2fsck_run_ext3_journal(ctx);
13810 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013811 bb_error_msg(_("while recovering ext3 journal of %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013812 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013813 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013814 }
13815 ext2fs_close(ctx->fs);
13816 ctx->fs = 0;
13817 ctx->flags |= E2F_FLAG_RESTARTED;
13818 goto restart;
13819 }
13820 }
13821
13822 /*
13823 * Check for compatibility with the feature sets. We need to
13824 * be more stringent than ext2fs_open().
13825 */
13826 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
13827 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013828 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013829 goto get_newer;
13830 }
13831 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013832 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013833 goto get_newer;
13834 }
13835#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000013836 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013837 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
Rob Landley7c94bed2006-05-03 21:58:45 +000013838 bb_error_msg(_("Warning: compression support is experimental.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013839#endif
13840#ifndef ENABLE_HTREE
13841 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013842 bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
Mike Frysinger51a43b42005-09-24 07:11:16 +000013843 "but filesystem %s has HTREE directories.\n"),
13844 ctx->device_name);
13845 goto get_newer;
13846 }
13847#endif
13848
13849 /*
13850 * If the user specified a specific superblock, presumably the
13851 * master superblock has been trashed. So we mark the
13852 * superblock as dirty, so it can be written out.
13853 */
13854 if (ctx->superblock &&
13855 !(ctx->options & E2F_OPT_READONLY))
13856 ext2fs_mark_super_dirty(fs);
13857
13858 /*
13859 * We only update the master superblock because (a) paranoia;
13860 * we don't want to corrupt the backup superblocks, and (b) we
13861 * don't need to update the mount count and last checked
13862 * fields in the backup superblock (the kernel doesn't
13863 * update the backup superblocks anyway).
13864 */
13865 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13866
13867 ehandler_init(fs->io);
13868
13869 if (ctx->superblock)
13870 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
13871 ext2fs_mark_valid(fs);
13872 check_super_block(ctx);
13873 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013874 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013875 check_if_skip(ctx);
13876 if (bad_blocks_file)
13877 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
13878 else if (cflag)
13879 read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
13880 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013881 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013882#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000013883
13884#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000013885#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000013886#else
Rob Landley8b606342006-01-24 02:38:28 +000013887#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000013888#endif
13889
13890
Mike Frysinger51a43b42005-09-24 07:11:16 +000013891 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000013892 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013893 fprintf(stderr, _("%s: Filesystem byte order "
13894 "already normalized.\n"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013895 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013896 }
13897 }
13898 if (swapfs) {
13899 swap_filesys(ctx);
13900 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013901 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013902 }
13903#endif
13904
13905 /*
13906 * Mark the system as valid, 'til proven otherwise
13907 */
13908 ext2fs_mark_valid(fs);
13909
13910 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
13911 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013912 bb_error_msg(_("while reading bad blocks inode"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013913 preenhalt(ctx);
13914 printf(_("This doesn't bode well,"
13915 " but we'll try to go on...\n"));
13916 }
13917
13918 run_result = e2fsck_run(ctx);
13919 e2fsck_clear_progbar(ctx);
13920 if (run_result == E2F_FLAG_RESTART) {
13921 printf(_("Restarting e2fsck from the beginning...\n"));
13922 retval = e2fsck_reset_context(ctx);
13923 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013924 bb_error_msg(_("while resetting context"));
13925 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013926 }
13927 ext2fs_close(fs);
13928 goto restart;
13929 }
13930 if (run_result & E2F_FLAG_CANCEL) {
13931 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
13932 ctx->device_name : ctx->filesystem_name);
13933 exit_value |= FSCK_CANCELED;
13934 }
13935 if (run_result & E2F_FLAG_ABORT)
Rob Landley7c94bed2006-05-03 21:58:45 +000013936 bb_error_msg_and_die(_("aborted"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013937
Rob Landley3e72c592006-04-06 22:49:04 +000013938 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013939 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013940 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013941 if (!(ctx->options & E2F_OPT_PREEN))
13942 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
13943 ctx->device_name);
13944 if (ctx->mount_flags & EXT2_MF_ISROOT) {
13945 printf(_("%s: ***** REBOOT LINUX *****\n"),
13946 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013947 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013948 }
13949 }
13950 if (!ext2fs_test_valid(fs)) {
13951 printf(_("\n%s: ********** WARNING: Filesystem still has "
13952 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013953 exit_value |= EXIT_UNCORRECTED;
13954 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013955 }
13956 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013957 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013958 else {
13959 show_stats(ctx);
13960 if (!(ctx->options & E2F_OPT_READONLY)) {
13961 if (ext2fs_test_valid(fs)) {
13962 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013963 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013964 sb->s_state = EXT2_VALID_FS;
13965 } else
13966 sb->s_state &= ~EXT2_VALID_FS;
13967 sb->s_mnt_count = 0;
13968 sb->s_lastcheck = time(NULL);
13969 ext2fs_mark_super_dirty(fs);
13970 }
13971 }
13972
13973 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013974
Mike Frysinger51a43b42005-09-24 07:11:16 +000013975 ext2fs_close(fs);
13976 ctx->fs = NULL;
13977 free(ctx->filesystem_name);
13978 free(ctx->journal_name);
13979 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013980
Mike Frysinger51a43b42005-09-24 07:11:16 +000013981 return exit_value;
13982}