blob: eadb0f64dbd51bf45284a9753d0d33a1990382a9 [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 * Function declarations
106 */
107static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
108static int end_problem_latch(e2fsck_t ctx, int mask);
109static int set_latch_flags(int mask, int setflags, int clearflags);
110static void clear_problem_context(struct problem_context *ctx);
111
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000112/*
113 * Dictionary Abstract Data Type
114 * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
115 *
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000116 * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
117 * kazlib_1_20
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000118 */
119
120#ifndef DICT_H
121#define DICT_H
122
123/*
124 * Blurb for inclusion into C++ translation units
125 */
126
127typedef unsigned long dictcount_t;
128#define DICTCOUNT_T_MAX ULONG_MAX
129
130/*
131 * The dictionary is implemented as a red-black tree
132 */
133
134typedef enum { dnode_red, dnode_black } dnode_color_t;
135
136typedef struct dnode_t {
137 struct dnode_t *dict_left;
138 struct dnode_t *dict_right;
139 struct dnode_t *dict_parent;
140 dnode_color_t dict_color;
141 const void *dict_key;
142 void *dict_data;
143} dnode_t;
144
145typedef int (*dict_comp_t)(const void *, const void *);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000146typedef void (*dnode_free_t)(dnode_t *);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000147
148typedef struct dict_t {
149 dnode_t dict_nilnode;
150 dictcount_t dict_nodecount;
151 dictcount_t dict_maxcount;
152 dict_comp_t dict_compare;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000153 dnode_free_t dict_freenode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000154 int dict_dupes;
155} dict_t;
156
157typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
158
159typedef struct dict_load_t {
160 dict_t *dict_dictptr;
161 dnode_t dict_nilnode;
162} dict_load_t;
163
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000164#define dict_count(D) ((D)->dict_nodecount)
165#define dnode_get(N) ((N)->dict_data)
166#define dnode_getkey(N) ((N)->dict_key)
167
168#endif
169
170/*
171 * Compatibility header file for e2fsck which should be included
172 * instead of linux/jfs.h
173 *
174 * Copyright (C) 2000 Stephen C. Tweedie
175 */
176
177/*
178 * Pull in the definition of the e2fsck context structure
179 */
180
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000181struct buffer_head {
182 char b_data[8192];
183 e2fsck_t b_ctx;
184 io_channel b_io;
185 int b_size;
186 blk_t b_blocknr;
187 int b_dirty;
188 int b_uptodate;
189 int b_err;
190};
191
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000192
193#define K_DEV_FS 1
194#define K_DEV_JOURNAL 2
195
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000196#define lock_buffer(bh) do {} while(0)
197#define unlock_buffer(bh) do {} while(0)
198#define buffer_req(bh) 1
199#define do_readahead(journal, start) do {} while(0)
200
201static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
202
203typedef struct {
204 int object_length;
205} kmem_cache_t;
206
207#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000208
209/*
210 * We use the standard libext2fs portability tricks for inline
211 * functions.
212 */
213
Rob Landley7c94bed2006-05-03 21:58:45 +0000214static kmem_cache_t * do_cache_create(int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000215{
216 kmem_cache_t *new_cache;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000217
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000218 new_cache = malloc(sizeof(*new_cache));
219 if (new_cache)
220 new_cache->object_length = len;
221 return new_cache;
222}
223
Rob Landley7c94bed2006-05-03 21:58:45 +0000224static void do_cache_destroy(kmem_cache_t *cache)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000225{
226 free(cache);
227}
228
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000229
230/*
231 * Dictionary Abstract Data Type
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000232 */
233
234
235/*
236 * These macros provide short convenient names for structure members,
237 * which are embellished with dict_ prefixes so that they are
238 * properly confined to the documented namespace. It's legal for a
239 * program which uses dict to define, for instance, a macro called ``parent''.
240 * Such a macro would interfere with the dnode_t struct definition.
241 * In general, highly portable and reusable C modules which expose their
242 * structures need to confine structure member names to well-defined spaces.
243 * The resulting identifiers aren't necessarily convenient to use, nor
244 * readable, in the implementation, however!
245 */
246
247#define left dict_left
248#define right dict_right
249#define parent dict_parent
250#define color dict_color
251#define key dict_key
252#define data dict_data
253
254#define nilnode dict_nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000255#define maxcount dict_maxcount
256#define compare dict_compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000257#define dupes dict_dupes
258
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000259#define dict_root(D) ((D)->nilnode.left)
260#define dict_nil(D) (&(D)->nilnode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000261
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000262static void dnode_free(dnode_t *node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000263
264/*
265 * Perform a ``left rotation'' adjustment on the tree. The given node P and
266 * its right child C are rearranged so that the P instead becomes the left
267 * child of C. The left subtree of C is inherited as the new right subtree
268 * for P. The ordering of the keys within the tree is thus preserved.
269 */
270
271static void rotate_left(dnode_t *upper)
272{
273 dnode_t *lower, *lowleft, *upparent;
274
275 lower = upper->right;
276 upper->right = lowleft = lower->left;
277 lowleft->parent = upper;
278
279 lower->parent = upparent = upper->parent;
280
281 /* don't need to check for root node here because root->parent is
282 the sentinel nil node, and root->parent->left points back to root */
283
284 if (upper == upparent->left) {
285 upparent->left = lower;
286 } else {
287 assert (upper == upparent->right);
288 upparent->right = lower;
289 }
290
291 lower->left = upper;
292 upper->parent = lower;
293}
294
295/*
296 * This operation is the ``mirror'' image of rotate_left. It is
297 * the same procedure, but with left and right interchanged.
298 */
299
300static void rotate_right(dnode_t *upper)
301{
302 dnode_t *lower, *lowright, *upparent;
303
304 lower = upper->left;
305 upper->left = lowright = lower->right;
306 lowright->parent = upper;
307
308 lower->parent = upparent = upper->parent;
309
310 if (upper == upparent->right) {
311 upparent->right = lower;
312 } else {
313 assert (upper == upparent->left);
314 upparent->left = lower;
315 }
316
317 lower->right = upper;
318 upper->parent = lower;
319}
320
321/*
322 * Do a postorder traversal of the tree rooted at the specified
323 * node and free everything under it. Used by dict_free().
324 */
325
326static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
327{
328 if (node == nil)
329 return;
330 free_nodes(dict, node->left, nil);
331 free_nodes(dict, node->right, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000332 dict->dict_freenode(node);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000333}
334
335/*
336 * Verify that the tree contains the given node. This is done by
337 * traversing all of the nodes and comparing their pointers to the
338 * given pointer. Returns 1 if the node is found, otherwise
339 * returns zero. It is intended for debugging purposes.
340 */
341
342static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
343{
344 if (root != nil) {
345 return root == node
346 || verify_dict_has_node(nil, root->left, node)
347 || verify_dict_has_node(nil, root->right, node);
348 }
349 return 0;
350}
351
352
353/*
354 * Select a different set of node allocator routines.
355 */
356
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000357static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000358{
359 assert (dict_count(dict) == 0);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000360 dict->dict_freenode = fr;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000361}
362
363/*
364 * Free all the nodes in the dictionary by using the dictionary's
365 * installed free routine. The dictionary is emptied.
366 */
367
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000368static void dict_free_nodes(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000369{
370 dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
371 free_nodes(dict, root, nil);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000372 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000373 dict->nilnode.left = &dict->nilnode;
374 dict->nilnode.right = &dict->nilnode;
375}
376
377/*
378 * Initialize a user-supplied dictionary object.
379 */
380
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000381static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000382{
383 dict->compare = comp;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000384 dict->dict_freenode = dnode_free;
385 dict->dict_nodecount = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000386 dict->maxcount = maxcount;
387 dict->nilnode.left = &dict->nilnode;
388 dict->nilnode.right = &dict->nilnode;
389 dict->nilnode.parent = &dict->nilnode;
390 dict->nilnode.color = dnode_black;
391 dict->dupes = 0;
392 return dict;
393}
394
395/*
396 * Locate a node in the dictionary having the given key.
397 * If the node is not found, a null a pointer is returned (rather than
398 * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
399 * located node is returned.
400 */
401
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000402static dnode_t *dict_lookup(dict_t *dict, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000403{
404 dnode_t *root = dict_root(dict);
405 dnode_t *nil = dict_nil(dict);
406 dnode_t *saved;
407 int result;
408
409 /* simple binary search adapted for trees that contain duplicate keys */
410
411 while (root != nil) {
412 result = dict->compare(key, root->key);
413 if (result < 0)
414 root = root->left;
415 else if (result > 0)
416 root = root->right;
417 else {
418 if (!dict->dupes) { /* no duplicates, return match */
419 return root;
420 } else { /* could be dupes, find leftmost one */
421 do {
422 saved = root;
423 root = root->left;
424 while (root != nil && dict->compare(key, root->key))
425 root = root->right;
426 } while (root != nil);
427 return saved;
428 }
429 }
430 }
431
432 return NULL;
433}
434
435/*
436 * Insert a node into the dictionary. The node should have been
437 * initialized with a data field. All other fields are ignored.
438 * The behavior is undefined if the user attempts to insert into
439 * a dictionary that is already full (for which the dict_isfull()
440 * function returns true).
441 */
442
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000443static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000444{
445 dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
446 dnode_t *parent = nil, *uncle, *grandpa;
447 int result = -1;
448
449 node->key = key;
450
451 /* basic binary tree insert */
452
453 while (where != nil) {
454 parent = where;
455 result = dict->compare(key, where->key);
456 /* trap attempts at duplicate key insertion unless it's explicitly allowed */
457 assert (dict->dupes || result != 0);
458 if (result < 0)
459 where = where->left;
460 else
461 where = where->right;
462 }
463
464 assert (where == nil);
465
466 if (result < 0)
467 parent->left = node;
468 else
469 parent->right = node;
470
471 node->parent = parent;
472 node->left = nil;
473 node->right = nil;
474
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000475 dict->dict_nodecount++;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000476
477 /* red black adjustments */
478
479 node->color = dnode_red;
480
481 while (parent->color == dnode_red) {
482 grandpa = parent->parent;
483 if (parent == grandpa->left) {
484 uncle = grandpa->right;
485 if (uncle->color == dnode_red) { /* red parent, red uncle */
486 parent->color = dnode_black;
487 uncle->color = dnode_black;
488 grandpa->color = dnode_red;
489 node = grandpa;
490 parent = grandpa->parent;
491 } else { /* red parent, black uncle */
492 if (node == parent->right) {
493 rotate_left(parent);
494 parent = node;
495 assert (grandpa == parent->parent);
496 /* rotation between parent and child preserves grandpa */
497 }
498 parent->color = dnode_black;
499 grandpa->color = dnode_red;
500 rotate_right(grandpa);
501 break;
502 }
503 } else { /* symmetric cases: parent == parent->parent->right */
504 uncle = grandpa->left;
505 if (uncle->color == dnode_red) {
506 parent->color = dnode_black;
507 uncle->color = dnode_black;
508 grandpa->color = dnode_red;
509 node = grandpa;
510 parent = grandpa->parent;
511 } else {
512 if (node == parent->left) {
513 rotate_right(parent);
514 parent = node;
515 assert (grandpa == parent->parent);
516 }
517 parent->color = dnode_black;
518 grandpa->color = dnode_red;
519 rotate_left(grandpa);
520 break;
521 }
522 }
523 }
524
525 dict_root(dict)->color = dnode_black;
526
527}
528
529/*
530 * Allocate a node using the dictionary's allocator routine, give it
531 * the data item.
532 */
533
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000534static dnode_t *dnode_init(dnode_t *dnode, void *data)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000535{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000536 dnode->data = data;
537 dnode->parent = NULL;
538 dnode->left = NULL;
539 dnode->right = NULL;
540 return dnode;
541}
542
543static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
544{
545 dnode_t *node = malloc(sizeof(dnode_t));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000546
547 if (node) {
548 dnode_init(node, data);
549 dict_insert(dict, node, key);
550 return 1;
551 }
552 return 0;
553}
554
555/*
556 * Return the node with the lowest (leftmost) key. If the dictionary is empty
557 * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
558 */
559
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000560static dnode_t *dict_first(dict_t *dict)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000561{
562 dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
563
564 if (root != nil)
565 while ((left = root->left) != nil)
566 root = left;
567
568 return (root == nil) ? NULL : root;
569}
570
571/*
572 * Return the given node's successor node---the node which has the
573 * next key in the the left to right ordering. If the node has
574 * no successor, a null pointer is returned rather than a pointer to
575 * the nil node.
576 */
577
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000578static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000579{
580 dnode_t *nil = dict_nil(dict), *parent, *left;
581
582 if (curr->right != nil) {
583 curr = curr->right;
584 while ((left = curr->left) != nil)
585 curr = left;
586 return curr;
587 }
588
589 parent = curr->parent;
590
591 while (parent != nil && curr == parent->right) {
592 curr = parent;
593 parent = curr->parent;
594 }
595
596 return (parent == nil) ? NULL : parent;
597}
598
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000599
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000600static void dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000601{
602 free(node);
603}
604
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000605
606#undef left
607#undef right
608#undef parent
609#undef color
610#undef key
611#undef data
612
613#undef nilnode
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000614#undef maxcount
615#undef compare
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000616#undef dupes
617
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000618
619/*
620 * dirinfo.c --- maintains the directory information table for e2fsck.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000621 */
622
623/*
624 * This subroutine is called during pass1 to create a directory info
625 * entry. During pass1, the passed-in parent is 0; it will get filled
626 * in during pass2.
627 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000628static 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 +0000629{
630 struct dir_info *dir;
631 int i, j;
632 ext2_ino_t num_dirs;
633 errcode_t retval;
634 unsigned long old_size;
635
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000636 if (!ctx->dir_info) {
637 ctx->dir_info_count = 0;
638 retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
639 if (retval)
640 num_dirs = 1024; /* Guess */
641 ctx->dir_info_size = num_dirs + 10;
642 ctx->dir_info = (struct dir_info *)
643 e2fsck_allocate_memory(ctx, ctx->dir_info_size
644 * sizeof (struct dir_info),
645 "directory map");
646 }
647
648 if (ctx->dir_info_count >= ctx->dir_info_size) {
649 old_size = ctx->dir_info_size * sizeof(struct dir_info);
650 ctx->dir_info_size += 10;
651 retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
652 sizeof(struct dir_info),
653 &ctx->dir_info);
654 if (retval) {
655 ctx->dir_info_size -= 10;
656 return;
657 }
658 }
659
660 /*
661 * Normally, add_dir_info is called with each inode in
662 * sequential order; but once in a while (like when pass 3
663 * needs to recreate the root directory or lost+found
664 * directory) it is called out of order. In those cases, we
665 * need to move the dir_info entries down to make room, since
666 * the dir_info array needs to be sorted by inode number for
667 * get_dir_info()'s sake.
668 */
669 if (ctx->dir_info_count &&
670 ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
671 for (i = ctx->dir_info_count-1; i > 0; i--)
672 if (ctx->dir_info[i-1].ino < ino)
673 break;
674 dir = &ctx->dir_info[i];
675 if (dir->ino != ino)
676 for (j = ctx->dir_info_count++; j > i; j--)
677 ctx->dir_info[j] = ctx->dir_info[j-1];
678 } else
679 dir = &ctx->dir_info[ctx->dir_info_count++];
680
681 dir->ino = ino;
682 dir->dotdot = parent;
683 dir->parent = parent;
684}
685
686/*
687 * get_dir_info() --- given an inode number, try to find the directory
688 * information entry for it.
689 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000690static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000691{
692 int low, high, mid;
693
694 low = 0;
695 high = ctx->dir_info_count-1;
696 if (!ctx->dir_info)
697 return 0;
698 if (ino == ctx->dir_info[low].ino)
699 return &ctx->dir_info[low];
700 if (ino == ctx->dir_info[high].ino)
701 return &ctx->dir_info[high];
702
703 while (low < high) {
704 mid = (low+high)/2;
705 if (mid == low || mid == high)
706 break;
707 if (ino == ctx->dir_info[mid].ino)
708 return &ctx->dir_info[mid];
709 if (ino < ctx->dir_info[mid].ino)
710 high = mid;
711 else
712 low = mid;
713 }
714 return 0;
715}
716
717/*
718 * Free the dir_info structure when it isn't needed any more.
719 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000720static void e2fsck_free_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000721{
Rob Landleye7c43b62006-03-01 16:39:45 +0000722 ext2fs_free_mem(&ctx->dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000723 ctx->dir_info_size = 0;
724 ctx->dir_info_count = 0;
725}
726
727/*
728 * Return the count of number of directories in the dir_info structure
729 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000730static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000731{
732 return ctx->dir_info_count;
733}
734
735/*
736 * A simple interator function
737 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000738static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000739{
740 if (*control >= ctx->dir_info_count)
741 return 0;
742
743 return(ctx->dir_info + (*control)++);
744}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000745
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000746/*
747 * dirinfo.c --- maintains the directory information table for e2fsck.
748 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000749 */
750
751#ifdef ENABLE_HTREE
752
753/*
754 * This subroutine is called during pass1 to create a directory info
755 * entry. During pass1, the passed-in parent is 0; it will get filled
756 * in during pass2.
757 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000758static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000759{
760 struct dx_dir_info *dir;
761 int i, j;
762 errcode_t retval;
763 unsigned long old_size;
764
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000765 if (!ctx->dx_dir_info) {
766 ctx->dx_dir_info_count = 0;
767 ctx->dx_dir_info_size = 100; /* Guess */
768 ctx->dx_dir_info = (struct dx_dir_info *)
769 e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
770 * sizeof (struct dx_dir_info),
771 "directory map");
772 }
773
774 if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
775 old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
776 ctx->dx_dir_info_size += 10;
777 retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
778 sizeof(struct dx_dir_info),
779 &ctx->dx_dir_info);
780 if (retval) {
781 ctx->dx_dir_info_size -= 10;
782 return;
783 }
784 }
785
786 /*
787 * Normally, add_dx_dir_info is called with each inode in
788 * sequential order; but once in a while (like when pass 3
789 * needs to recreate the root directory or lost+found
790 * directory) it is called out of order. In those cases, we
791 * need to move the dx_dir_info entries down to make room, since
792 * the dx_dir_info array needs to be sorted by inode number for
793 * get_dx_dir_info()'s sake.
794 */
795 if (ctx->dx_dir_info_count &&
796 ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
797 for (i = ctx->dx_dir_info_count-1; i > 0; i--)
798 if (ctx->dx_dir_info[i-1].ino < ino)
799 break;
800 dir = &ctx->dx_dir_info[i];
801 if (dir->ino != ino)
802 for (j = ctx->dx_dir_info_count++; j > i; j--)
803 ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
804 } else
805 dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
806
807 dir->ino = ino;
808 dir->numblocks = num_blocks;
809 dir->hashversion = 0;
810 dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
811 * sizeof (struct dx_dirblock_info),
812 "dx_block info array");
813
814}
815
816/*
817 * get_dx_dir_info() --- given an inode number, try to find the directory
818 * information entry for it.
819 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000820static 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 +0000821{
822 int low, high, mid;
823
824 low = 0;
825 high = ctx->dx_dir_info_count-1;
826 if (!ctx->dx_dir_info)
827 return 0;
828 if (ino == ctx->dx_dir_info[low].ino)
829 return &ctx->dx_dir_info[low];
830 if (ino == ctx->dx_dir_info[high].ino)
831 return &ctx->dx_dir_info[high];
832
833 while (low < high) {
834 mid = (low+high)/2;
835 if (mid == low || mid == high)
836 break;
837 if (ino == ctx->dx_dir_info[mid].ino)
838 return &ctx->dx_dir_info[mid];
839 if (ino < ctx->dx_dir_info[mid].ino)
840 high = mid;
841 else
842 low = mid;
843 }
844 return 0;
845}
846
847/*
848 * Free the dx_dir_info structure when it isn't needed any more.
849 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000850static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000851{
852 int i;
853 struct dx_dir_info *dir;
854
855 if (ctx->dx_dir_info) {
856 dir = ctx->dx_dir_info;
857 for (i=0; i < ctx->dx_dir_info_count; i++) {
Rob Landleye7c43b62006-03-01 16:39:45 +0000858 ext2fs_free_mem(&dir->dx_block);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000859 }
860 ext2fs_free_mem(&ctx->dx_dir_info);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000861 }
862 ctx->dx_dir_info_size = 0;
863 ctx->dx_dir_info_count = 0;
864}
865
866/*
867 * A simple interator function
868 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000869static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000870{
871 if (*control >= ctx->dx_dir_info_count)
872 return 0;
873
874 return(ctx->dx_dir_info + (*control)++);
875}
876
877#endif /* ENABLE_HTREE */
878/*
879 * e2fsck.c - a consistency checker for the new extended file system.
880 *
Mike Frysinger51a43b42005-09-24 07:11:16 +0000881 */
882
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000883/*
884 * This function allocates an e2fsck context
885 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000886static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000887{
888 e2fsck_t context;
889 errcode_t retval;
890
891 retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
892 if (retval)
893 return retval;
894
895 memset(context, 0, sizeof(struct e2fsck_struct));
896
897 context->process_inode_size = 256;
898 context->ext_attr_ver = 2;
899
900 *ret = context;
901 return 0;
902}
903
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000904struct ea_refcount_el {
905 blk_t ea_blk;
906 int ea_count;
907};
908
909struct ea_refcount {
910 blk_t count;
911 blk_t size;
912 blk_t cursor;
913 struct ea_refcount_el *list;
914};
915
916static void ea_refcount_free(ext2_refcount_t refcount)
917{
918 if (!refcount)
919 return;
920
Rob Landleye7c43b62006-03-01 16:39:45 +0000921 ext2fs_free_mem(&refcount->list);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000922 ext2fs_free_mem(&refcount);
923}
924
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000925/*
926 * This function resets an e2fsck context; it is called when e2fsck
927 * needs to be restarted.
928 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +0000929static errcode_t e2fsck_reset_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000930{
931 ctx->flags = 0;
932 ctx->lost_and_found = 0;
933 ctx->bad_lost_and_found = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000934 ext2fs_free_inode_bitmap(ctx->inode_used_map);
935 ctx->inode_used_map = 0;
936 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
937 ctx->inode_dir_map = 0;
938 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
939 ctx->inode_reg_map = 0;
940 ext2fs_free_block_bitmap(ctx->block_found_map);
941 ctx->block_found_map = 0;
942 ext2fs_free_icount(ctx->inode_link_info);
943 ctx->inode_link_info = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000944 if (ctx->journal_io) {
945 if (ctx->fs && ctx->fs->io != ctx->journal_io)
946 io_channel_close(ctx->journal_io);
947 ctx->journal_io = 0;
948 }
Rob Landleye7c43b62006-03-01 16:39:45 +0000949 if (ctx->fs) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000950 ext2fs_free_dblist(ctx->fs->dblist);
951 ctx->fs->dblist = 0;
952 }
953 e2fsck_free_dir_info(ctx);
954#ifdef ENABLE_HTREE
955 e2fsck_free_dx_dir_info(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +0000956#endif
Rob Landleye7c43b62006-03-01 16:39:45 +0000957 ea_refcount_free(ctx->refcount);
958 ctx->refcount = 0;
959 ea_refcount_free(ctx->refcount_extra);
960 ctx->refcount_extra = 0;
961 ext2fs_free_block_bitmap(ctx->block_dup_map);
962 ctx->block_dup_map = 0;
963 ext2fs_free_block_bitmap(ctx->block_ea_map);
964 ctx->block_ea_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +0000965 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
966 ctx->inode_bad_map = 0;
967 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
968 ctx->inode_imagic_map = 0;
969 ext2fs_u32_list_free(ctx->dirs_to_hash);
970 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000971
972 /*
973 * Clear the array of invalid meta-data flags
974 */
Rob Landleye7c43b62006-03-01 16:39:45 +0000975 ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
976 ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
977 ext2fs_free_mem(&ctx->invalid_inode_table_flag);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000978
979 /* Clear statistic counters */
980 ctx->fs_directory_count = 0;
981 ctx->fs_regular_count = 0;
982 ctx->fs_blockdev_count = 0;
983 ctx->fs_chardev_count = 0;
984 ctx->fs_links_count = 0;
985 ctx->fs_symlinks_count = 0;
986 ctx->fs_fast_symlinks_count = 0;
987 ctx->fs_fifo_count = 0;
988 ctx->fs_total_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +0000989 ctx->fs_sockets_count = 0;
990 ctx->fs_ind_count = 0;
991 ctx->fs_dind_count = 0;
992 ctx->fs_tind_count = 0;
993 ctx->fs_fragmented = 0;
994 ctx->large_files = 0;
995
996 /* Reset the superblock to the user's requested value */
997 ctx->superblock = ctx->use_superblock;
998
999 return 0;
1000}
1001
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001002static void e2fsck_free_context(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001003{
1004 if (!ctx)
1005 return;
1006
1007 e2fsck_reset_context(ctx);
1008 if (ctx->blkid)
1009 blkid_put_cache(ctx->blkid);
1010
1011 ext2fs_free_mem(&ctx);
1012}
1013
1014/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001015 * ea_refcount.c
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001016 */
1017
1018/*
1019 * The strategy we use for keeping track of EA refcounts is as
1020 * follows. We keep a sorted array of first EA blocks and its
1021 * reference counts. Once the refcount has dropped to zero, it is
1022 * removed from the array to save memory space. Once the EA block is
1023 * checked, its bit is set in the block_ea_map bitmap.
1024 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001025
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001026
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001027static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001028{
1029 ext2_refcount_t refcount;
1030 errcode_t retval;
1031 size_t bytes;
1032
1033 retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
1034 if (retval)
1035 return retval;
1036 memset(refcount, 0, sizeof(struct ea_refcount));
1037
1038 if (!size)
1039 size = 500;
1040 refcount->size = size;
1041 bytes = (size_t) (size * sizeof(struct ea_refcount_el));
1042#ifdef DEBUG
1043 printf("Refcount allocated %d entries, %d bytes.\n",
1044 refcount->size, bytes);
1045#endif
1046 retval = ext2fs_get_mem(bytes, &refcount->list);
1047 if (retval)
1048 goto errout;
1049 memset(refcount->list, 0, bytes);
1050
1051 refcount->count = 0;
1052 refcount->cursor = 0;
1053
1054 *ret = refcount;
1055 return 0;
1056
1057errout:
1058 ea_refcount_free(refcount);
1059 return(retval);
1060}
1061
1062/*
1063 * collapse_refcount() --- go through the refcount array, and get rid
1064 * of any count == zero entries
1065 */
1066static void refcount_collapse(ext2_refcount_t refcount)
1067{
1068 unsigned int i, j;
1069 struct ea_refcount_el *list;
1070
1071 list = refcount->list;
1072 for (i = 0, j = 0; i < refcount->count; i++) {
1073 if (list[i].ea_count) {
1074 if (i != j)
1075 list[j] = list[i];
1076 j++;
1077 }
1078 }
1079#if defined(DEBUG) || defined(TEST_PROGRAM)
1080 printf("Refcount_collapse: size was %d, now %d\n",
1081 refcount->count, j);
1082#endif
1083 refcount->count = j;
1084}
1085
1086
1087/*
1088 * insert_refcount_el() --- Insert a new entry into the sorted list at a
1089 * specified position.
1090 */
1091static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
1092 blk_t blk, int pos)
1093{
1094 struct ea_refcount_el *el;
1095 errcode_t retval;
1096 blk_t new_size = 0;
1097 int num;
1098
1099 if (refcount->count >= refcount->size) {
1100 new_size = refcount->size + 100;
1101#ifdef DEBUG
1102 printf("Reallocating refcount %d entries...\n", new_size);
1103#endif
1104 retval = ext2fs_resize_mem((size_t) refcount->size *
1105 sizeof(struct ea_refcount_el),
1106 (size_t) new_size *
1107 sizeof(struct ea_refcount_el),
1108 &refcount->list);
1109 if (retval)
1110 return 0;
1111 refcount->size = new_size;
1112 }
1113 num = (int) refcount->count - pos;
1114 if (num < 0)
1115 return 0; /* should never happen */
1116 if (num) {
1117 memmove(&refcount->list[pos+1], &refcount->list[pos],
1118 sizeof(struct ea_refcount_el) * num);
1119 }
1120 refcount->count++;
1121 el = &refcount->list[pos];
1122 el->ea_count = 0;
1123 el->ea_blk = blk;
1124 return el;
1125}
1126
1127
1128/*
1129 * get_refcount_el() --- given an block number, try to find refcount
1130 * information in the sorted list. If the create flag is set,
1131 * and we can't find an entry, create one in the sorted list.
1132 */
1133static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
1134 blk_t blk, int create)
1135{
1136 float range;
1137 int low, high, mid;
1138 blk_t lowval, highval;
1139
1140 if (!refcount || !refcount->list)
1141 return 0;
1142retry:
1143 low = 0;
1144 high = (int) refcount->count-1;
1145 if (create && ((refcount->count == 0) ||
1146 (blk > refcount->list[high].ea_blk))) {
1147 if (refcount->count >= refcount->size)
1148 refcount_collapse(refcount);
1149
1150 return insert_refcount_el(refcount, blk,
1151 (unsigned) refcount->count);
1152 }
1153 if (refcount->count == 0)
1154 return 0;
1155
1156 if (refcount->cursor >= refcount->count)
1157 refcount->cursor = 0;
1158 if (blk == refcount->list[refcount->cursor].ea_blk)
1159 return &refcount->list[refcount->cursor++];
1160#ifdef DEBUG
1161 printf("Non-cursor get_refcount_el: %u\n", blk);
1162#endif
1163 while (low <= high) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001164 if (low == high)
1165 mid = low;
1166 else {
1167 /* Interpolate for efficiency */
1168 lowval = refcount->list[low].ea_blk;
1169 highval = refcount->list[high].ea_blk;
1170
1171 if (blk < lowval)
1172 range = 0;
1173 else if (blk > highval)
1174 range = 1;
1175 else
1176 range = ((float) (blk - lowval)) /
1177 (highval - lowval);
1178 mid = low + ((int) (range * (high-low)));
1179 }
Rob Landley3e72c592006-04-06 22:49:04 +00001180
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001181 if (blk == refcount->list[mid].ea_blk) {
1182 refcount->cursor = mid+1;
1183 return &refcount->list[mid];
1184 }
1185 if (blk < refcount->list[mid].ea_blk)
1186 high = mid-1;
1187 else
1188 low = mid+1;
1189 }
1190 /*
1191 * If we need to create a new entry, it should be right at
1192 * low (where high will be left at low-1).
1193 */
1194 if (create) {
1195 if (refcount->count >= refcount->size) {
1196 refcount_collapse(refcount);
1197 if (refcount->count < refcount->size)
1198 goto retry;
1199 }
1200 return insert_refcount_el(refcount, blk, low);
1201 }
1202 return 0;
1203}
1204
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001205static errcode_t
1206ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001207{
1208 struct ea_refcount_el *el;
1209
1210 el = get_refcount_el(refcount, blk, 1);
1211 if (!el)
1212 return EXT2_ET_NO_MEMORY;
1213 el->ea_count++;
1214
1215 if (ret)
1216 *ret = el->ea_count;
1217 return 0;
1218}
1219
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001220static errcode_t
1221ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001222{
1223 struct ea_refcount_el *el;
1224
1225 el = get_refcount_el(refcount, blk, 0);
1226 if (!el || el->ea_count == 0)
1227 return EXT2_ET_INVALID_ARGUMENT;
1228
1229 el->ea_count--;
1230
1231 if (ret)
1232 *ret = el->ea_count;
1233 return 0;
1234}
1235
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001236static errcode_t
1237ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001238{
1239 struct ea_refcount_el *el;
1240
1241 /*
1242 * Get the refcount element
1243 */
1244 el = get_refcount_el(refcount, blk, count ? 1 : 0);
1245 if (!el)
1246 return count ? EXT2_ET_NO_MEMORY : 0;
1247 el->ea_count = count;
1248 return 0;
1249}
1250
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001251static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001252{
1253 refcount->cursor = 0;
1254}
1255
1256
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001257static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001258{
1259 struct ea_refcount_el *list;
1260
1261 while (1) {
1262 if (refcount->cursor >= refcount->count)
1263 return 0;
1264 list = refcount->list;
1265 if (list[refcount->cursor].ea_count) {
1266 if (ret)
1267 *ret = list[refcount->cursor].ea_count;
1268 return list[refcount->cursor++].ea_blk;
1269 }
1270 refcount->cursor++;
1271 }
1272}
1273
1274
1275/*
1276 * ehandler.c --- handle bad block errors which come up during the
1277 * course of an e2fsck session.
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001278 */
1279
1280
1281static const char *operation;
1282
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001283static errcode_t
1284e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001285 void *data, size_t size FSCK_ATTR((unused)),
1286 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001287{
1288 int i;
1289 char *p;
1290 ext2_filsys fs = (ext2_filsys) channel->app_data;
1291 e2fsck_t ctx;
1292
1293 ctx = (e2fsck_t) fs->priv_data;
1294
1295 /*
1296 * If more than one block was read, try reading each block
1297 * separately. We could use the actual bytes read to figure
1298 * out where to start, but we don't bother.
1299 */
1300 if (count > 1) {
1301 p = (char *) data;
1302 for (i=0; i < count; i++, p += channel->block_size, block++) {
1303 error = io_channel_read_blk(channel, block,
1304 1, p);
1305 if (error)
1306 return error;
1307 }
1308 return 0;
1309 }
1310 if (operation)
1311 printf(_("Error reading block %lu (%s) while %s. "), block,
1312 error_message(error), operation);
1313 else
1314 printf(_("Error reading block %lu (%s). "), block,
1315 error_message(error));
1316 preenhalt(ctx);
1317 if (ask(ctx, _("Ignore error"), 1)) {
1318 if (ask(ctx, _("Force rewrite"), 1))
1319 io_channel_write_blk(channel, block, 1, data);
1320 return 0;
1321 }
1322
1323 return error;
1324}
1325
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001326static errcode_t
1327e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00001328 const void *data, size_t size FSCK_ATTR((unused)),
1329 int actual FSCK_ATTR((unused)), errcode_t error)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001330{
1331 int i;
1332 const char *p;
1333 ext2_filsys fs = (ext2_filsys) channel->app_data;
1334 e2fsck_t ctx;
1335
1336 ctx = (e2fsck_t) fs->priv_data;
1337
1338 /*
1339 * If more than one block was written, try writing each block
1340 * separately. We could use the actual bytes read to figure
1341 * out where to start, but we don't bother.
1342 */
1343 if (count > 1) {
1344 p = (const char *) data;
1345 for (i=0; i < count; i++, p += channel->block_size, block++) {
1346 error = io_channel_write_blk(channel, block,
1347 1, p);
1348 if (error)
1349 return error;
1350 }
1351 return 0;
1352 }
1353
1354 if (operation)
1355 printf(_("Error writing block %lu (%s) while %s. "), block,
1356 error_message(error), operation);
1357 else
1358 printf(_("Error writing block %lu (%s). "), block,
1359 error_message(error));
1360 preenhalt(ctx);
1361 if (ask(ctx, _("Ignore error"), 1))
1362 return 0;
1363
1364 return error;
1365}
1366
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001367static inline const char *ehandler_operation(const char *op)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001368{
1369 const char *ret = operation;
1370
1371 operation = op;
1372 return ret;
1373}
1374
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001375static void ehandler_init(io_channel channel)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001376{
1377 channel->read_error = e2fsck_handle_read_error;
1378 channel->write_error = e2fsck_handle_write_error;
1379}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001380
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001381/*
1382 * journal.c --- code for handling the "ext3" journal
1383 *
1384 * Copyright (C) 2000 Andreas Dilger
1385 * Copyright (C) 2000 Theodore Ts'o
1386 *
1387 * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
1388 * Copyright (C) 1999 Red Hat Software
1389 *
1390 * This file may be redistributed under the terms of the
1391 * GNU General Public License version 2 or at your discretion
1392 * any later version.
1393 */
1394
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001395/*
1396 * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
1397 * This creates a larger static binary, and a smaller binary using
1398 * shared libraries. It's also probably slightly less CPU-efficient,
1399 * which is why it's not on by default. But, it's a good way of
1400 * testing the functions in inode_io.c and fileio.c.
1401 */
1402#undef USE_INODE_IO
1403
1404/* Kernel compatibility functions for handling the journal. These allow us
1405 * to use the recovery.c file virtually unchanged from the kernel, so we
1406 * don't have to do much to keep kernel and user recovery in sync.
1407 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001408static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001409{
1410#ifdef USE_INODE_IO
1411 *phys = block;
1412 return 0;
1413#else
1414 struct inode *inode = journal->j_inode;
1415 errcode_t retval;
1416 blk_t pblk;
1417
1418 if (!inode) {
1419 *phys = block;
1420 return 0;
1421 }
1422
1423 retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
1424 &inode->i_ext2, NULL, 0, block, &pblk);
1425 *phys = pblk;
1426 return (retval);
1427#endif
1428}
1429
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001430static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001431{
1432 struct buffer_head *bh;
1433
1434 bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
1435 if (!bh)
1436 return NULL;
1437
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001438 bh->b_ctx = kdev->k_ctx;
1439 if (kdev->k_dev == K_DEV_FS)
1440 bh->b_io = kdev->k_ctx->fs->io;
1441 else
1442 bh->b_io = kdev->k_ctx->journal_io;
1443 bh->b_size = blocksize;
1444 bh->b_blocknr = blocknr;
1445
1446 return bh;
1447}
1448
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001449static void sync_blockdev(kdev_t kdev)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001450{
1451 io_channel io;
1452
1453 if (kdev->k_dev == K_DEV_FS)
1454 io = kdev->k_ctx->fs->io;
1455 else
1456 io = kdev->k_ctx->journal_io;
1457
1458 io_channel_flush(io);
1459}
1460
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001461static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001462{
1463 int retval;
1464 struct buffer_head *bh;
1465
1466 for (; nr > 0; --nr) {
1467 bh = *bhp++;
1468 if (rw == READ && !bh->b_uptodate) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001469 retval = io_channel_read_blk(bh->b_io,
1470 bh->b_blocknr,
1471 1, bh->b_data);
1472 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001473 bb_error_msg("while reading block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001474 (unsigned long) bh->b_blocknr);
1475 bh->b_err = retval;
1476 continue;
1477 }
1478 bh->b_uptodate = 1;
1479 } else if (rw == WRITE && bh->b_dirty) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001480 retval = io_channel_write_blk(bh->b_io,
1481 bh->b_blocknr,
1482 1, bh->b_data);
1483 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001484 bb_error_msg("while writing block %lu\n",
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001485 (unsigned long) bh->b_blocknr);
1486 bh->b_err = retval;
1487 continue;
1488 }
1489 bh->b_dirty = 0;
1490 bh->b_uptodate = 1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001491 }
1492 }
1493}
1494
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001495static inline void mark_buffer_dirty(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001496{
1497 bh->b_dirty = 1;
1498}
1499
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001500static inline void mark_buffer_clean(struct buffer_head * bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001501{
1502 bh->b_dirty = 0;
1503}
1504
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001505static void brelse(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001506{
1507 if (bh->b_dirty)
1508 ll_rw_block(WRITE, 1, &bh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001509 ext2fs_free_mem(&bh);
1510}
1511
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001512static inline int buffer_uptodate(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001513{
1514 return bh->b_uptodate;
1515}
1516
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001517static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001518{
1519 bh->b_uptodate = val;
1520}
1521
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001522static void wait_on_buffer(struct buffer_head *bh)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001523{
1524 if (!bh->b_uptodate)
1525 ll_rw_block(READ, 1, &bh);
1526}
1527
1528
1529static void e2fsck_clear_recover(e2fsck_t ctx, int error)
1530{
1531 ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
1532
1533 /* if we had an error doing journal recovery, we need a full fsck */
1534 if (error)
1535 ctx->fs->super->s_state &= ~EXT2_VALID_FS;
1536 ext2fs_mark_super_dirty(ctx->fs);
1537}
1538
1539static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
1540{
1541 struct ext2_super_block *sb = ctx->fs->super;
1542 struct ext2_super_block jsuper;
1543 struct problem_context pctx;
1544 struct buffer_head *bh;
1545 struct inode *j_inode = NULL;
1546 struct kdev_s *dev_fs = NULL, *dev_journal;
1547 const char *journal_name = 0;
1548 journal_t *journal = NULL;
1549 errcode_t retval = 0;
1550 io_manager io_ptr = 0;
1551 unsigned long start = 0;
1552 blk_t blk;
1553 int ext_journal = 0;
1554 int tried_backup_jnl = 0;
1555 int i;
1556
1557 clear_problem_context(&pctx);
1558
1559 journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
1560 if (!journal) {
1561 return EXT2_ET_NO_MEMORY;
1562 }
1563
1564 dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
1565 if (!dev_fs) {
1566 retval = EXT2_ET_NO_MEMORY;
1567 goto errout;
1568 }
1569 dev_journal = dev_fs+1;
1570
1571 dev_fs->k_ctx = dev_journal->k_ctx = ctx;
1572 dev_fs->k_dev = K_DEV_FS;
1573 dev_journal->k_dev = K_DEV_JOURNAL;
1574
1575 journal->j_dev = dev_journal;
1576 journal->j_fs_dev = dev_fs;
1577 journal->j_inode = NULL;
1578 journal->j_blocksize = ctx->fs->blocksize;
1579
1580 if (uuid_is_null(sb->s_journal_uuid)) {
1581 if (!sb->s_journal_inum)
1582 return EXT2_ET_BAD_INODE_NUM;
1583 j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
1584 "journal inode");
1585 if (!j_inode) {
1586 retval = EXT2_ET_NO_MEMORY;
1587 goto errout;
1588 }
1589
1590 j_inode->i_ctx = ctx;
1591 j_inode->i_ino = sb->s_journal_inum;
1592
1593 if ((retval = ext2fs_read_inode(ctx->fs,
1594 sb->s_journal_inum,
1595 &j_inode->i_ext2))) {
1596 try_backup_journal:
1597 if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
1598 tried_backup_jnl)
1599 goto errout;
1600 memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
1601 memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
1602 EXT2_N_BLOCKS*4);
1603 j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
1604 j_inode->i_ext2.i_links_count = 1;
1605 j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
1606 tried_backup_jnl++;
1607 }
1608 if (!j_inode->i_ext2.i_links_count ||
1609 !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
1610 retval = EXT2_ET_NO_JOURNAL;
1611 goto try_backup_journal;
1612 }
1613 if (j_inode->i_ext2.i_size / journal->j_blocksize <
1614 JFS_MIN_JOURNAL_BLOCKS) {
1615 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1616 goto try_backup_journal;
1617 }
1618 for (i=0; i < EXT2_N_BLOCKS; i++) {
1619 blk = j_inode->i_ext2.i_block[i];
1620 if (!blk) {
1621 if (i < EXT2_NDIR_BLOCKS) {
1622 retval = EXT2_ET_JOURNAL_TOO_SMALL;
1623 goto try_backup_journal;
1624 }
1625 continue;
1626 }
1627 if (blk < sb->s_first_data_block ||
1628 blk >= sb->s_blocks_count) {
1629 retval = EXT2_ET_BAD_BLOCK_NUM;
1630 goto try_backup_journal;
1631 }
1632 }
1633 journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
1634
1635#ifdef USE_INODE_IO
1636 retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
1637 &j_inode->i_ext2,
1638 &journal_name);
1639 if (retval)
1640 goto errout;
1641
1642 io_ptr = inode_io_manager;
1643#else
1644 journal->j_inode = j_inode;
1645 ctx->journal_io = ctx->fs->io;
1646 if ((retval = journal_bmap(journal, 0, &start)) != 0)
1647 goto errout;
1648#endif
1649 } else {
1650 ext_journal = 1;
1651 if (!ctx->journal_name) {
1652 char uuid[37];
1653
1654 uuid_unparse(sb->s_journal_uuid, uuid);
1655 ctx->journal_name = blkid_get_devname(ctx->blkid,
1656 "UUID", uuid);
1657 if (!ctx->journal_name)
1658 ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
1659 }
1660 journal_name = ctx->journal_name;
1661
1662 if (!journal_name) {
1663 fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
1664 return EXT2_ET_LOAD_EXT_JOURNAL;
1665 }
1666
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001667 io_ptr = unix_io_manager;
1668 }
1669
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001670#ifndef USE_INODE_IO
1671 if (ext_journal)
1672#endif
1673 retval = io_ptr->open(journal_name, IO_FLAG_RW,
1674 &ctx->journal_io);
1675 if (retval)
1676 goto errout;
1677
1678 io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
1679
1680 if (ext_journal) {
1681 if (ctx->fs->blocksize == 1024)
1682 start = 1;
1683 bh = getblk(dev_journal, start, ctx->fs->blocksize);
1684 if (!bh) {
1685 retval = EXT2_ET_NO_MEMORY;
1686 goto errout;
1687 }
1688 ll_rw_block(READ, 1, &bh);
1689 if ((retval = bh->b_err) != 0)
1690 goto errout;
1691 memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
1692 sizeof(jsuper));
1693 brelse(bh);
Rob Landley7c94bed2006-05-03 21:58:45 +00001694#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001695 if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
1696 ext2fs_swap_super(&jsuper);
1697#endif
1698 if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
1699 !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
1700 fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
1701 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1702 goto errout;
1703 }
1704 /* Make sure the journal UUID is correct */
1705 if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
1706 sizeof(jsuper.s_uuid))) {
1707 fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
1708 retval = EXT2_ET_LOAD_EXT_JOURNAL;
1709 goto errout;
1710 }
1711
1712 journal->j_maxlen = jsuper.s_blocks_count;
1713 start++;
1714 }
1715
1716 if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
1717 retval = EXT2_ET_NO_MEMORY;
1718 goto errout;
1719 }
1720
1721 journal->j_sb_buffer = bh;
1722 journal->j_superblock = (journal_superblock_t *)bh->b_data;
1723
1724#ifdef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001725 ext2fs_free_mem(&j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001726#endif
1727
1728 *ret_journal = journal;
1729 return 0;
1730
1731errout:
Rob Landleye7c43b62006-03-01 16:39:45 +00001732 ext2fs_free_mem(&dev_fs);
1733 ext2fs_free_mem(&j_inode);
1734 ext2fs_free_mem(&journal);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001735 return retval;
1736
1737}
1738
1739static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
1740 struct problem_context *pctx)
1741{
1742 struct ext2_super_block *sb = ctx->fs->super;
1743 int recover = ctx->fs->super->s_feature_incompat &
1744 EXT3_FEATURE_INCOMPAT_RECOVER;
1745 int has_journal = ctx->fs->super->s_feature_compat &
1746 EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1747
1748 if (has_journal || sb->s_journal_inum) {
1749 /* The journal inode is bogus, remove and force full fsck */
1750 pctx->ino = sb->s_journal_inum;
1751 if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
1752 if (has_journal && sb->s_journal_inum)
1753 printf("*** ext3 journal has been deleted - "
1754 "filesystem is now ext2 only ***\n\n");
1755 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
1756 sb->s_journal_inum = 0;
1757 ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
1758 e2fsck_clear_recover(ctx, 1);
1759 return 0;
1760 }
1761 return EXT2_ET_BAD_INODE_NUM;
1762 } else if (recover) {
1763 if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
1764 e2fsck_clear_recover(ctx, 1);
1765 return 0;
1766 }
1767 return EXT2_ET_UNSUPP_FEATURE;
1768 }
1769 return 0;
1770}
1771
1772#define V1_SB_SIZE 0x0024
1773static void clear_v2_journal_fields(journal_t *journal)
1774{
1775 e2fsck_t ctx = journal->j_dev->k_ctx;
1776 struct problem_context pctx;
1777
1778 clear_problem_context(&pctx);
1779
1780 if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
1781 return;
1782
1783 memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
1784 ctx->fs->blocksize-V1_SB_SIZE);
1785 mark_buffer_dirty(journal->j_sb_buffer);
1786}
1787
1788
1789static errcode_t e2fsck_journal_load(journal_t *journal)
1790{
1791 e2fsck_t ctx = journal->j_dev->k_ctx;
1792 journal_superblock_t *jsb;
1793 struct buffer_head *jbh = journal->j_sb_buffer;
1794 struct problem_context pctx;
1795
1796 clear_problem_context(&pctx);
1797
1798 ll_rw_block(READ, 1, &jbh);
1799 if (jbh->b_err) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001800 bb_error_msg(_("reading journal superblock\n"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001801 return jbh->b_err;
1802 }
1803
1804 jsb = journal->j_superblock;
1805 /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
1806 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
1807 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
1808
1809 switch (ntohl(jsb->s_header.h_blocktype)) {
1810 case JFS_SUPERBLOCK_V1:
1811 journal->j_format_version = 1;
1812 if (jsb->s_feature_compat ||
1813 jsb->s_feature_incompat ||
1814 jsb->s_feature_ro_compat ||
1815 jsb->s_nr_users)
1816 clear_v2_journal_fields(journal);
1817 break;
1818
1819 case JFS_SUPERBLOCK_V2:
1820 journal->j_format_version = 2;
1821 if (ntohl(jsb->s_nr_users) > 1 &&
1822 uuid_is_null(ctx->fs->super->s_journal_uuid))
1823 clear_v2_journal_fields(journal);
1824 if (ntohl(jsb->s_nr_users) > 1) {
1825 fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
1826 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1827 }
1828 break;
1829
1830 /*
1831 * These should never appear in a journal super block, so if
1832 * they do, the journal is badly corrupted.
1833 */
1834 case JFS_DESCRIPTOR_BLOCK:
1835 case JFS_COMMIT_BLOCK:
1836 case JFS_REVOKE_BLOCK:
1837 return EXT2_ET_CORRUPT_SUPERBLOCK;
1838
1839 /* If we don't understand the superblock major type, but there
1840 * is a magic number, then it is likely to be a new format we
1841 * just don't understand, so leave it alone. */
1842 default:
1843 return EXT2_ET_JOURNAL_UNSUPP_VERSION;
1844 }
1845
1846 if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
1847 return EXT2_ET_UNSUPP_FEATURE;
1848
1849 if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
1850 return EXT2_ET_RO_UNSUPP_FEATURE;
1851
1852 /* We have now checked whether we know enough about the journal
1853 * format to be able to proceed safely, so any other checks that
1854 * fail we should attempt to recover from. */
1855 if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001856 bb_error_msg(_("%s: no valid journal superblock found\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001857 ctx->device_name);
1858 return EXT2_ET_CORRUPT_SUPERBLOCK;
1859 }
1860
1861 if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
1862 journal->j_maxlen = ntohl(jsb->s_maxlen);
1863 else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
Rob Landley7c94bed2006-05-03 21:58:45 +00001864 bb_error_msg(_("%s: journal too short\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001865 ctx->device_name);
1866 return EXT2_ET_CORRUPT_SUPERBLOCK;
1867 }
1868
1869 journal->j_tail_sequence = ntohl(jsb->s_sequence);
1870 journal->j_transaction_sequence = journal->j_tail_sequence;
1871 journal->j_tail = ntohl(jsb->s_start);
1872 journal->j_first = ntohl(jsb->s_first);
1873 journal->j_last = ntohl(jsb->s_maxlen);
1874
1875 return 0;
1876}
1877
1878static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
1879 journal_t *journal)
1880{
1881 char *p;
1882 union {
1883 uuid_t uuid;
1884 __u32 val[4];
1885 } u;
1886 __u32 new_seq = 0;
1887 int i;
1888
1889 /* Leave a valid existing V1 superblock signature alone.
1890 * Anything unrecognisable we overwrite with a new V2
1891 * signature. */
1892
1893 if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
1894 jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
1895 jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
1896 jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
1897 }
1898
1899 /* Zero out everything else beyond the superblock header */
1900
1901 p = ((char *) jsb) + sizeof(journal_header_t);
1902 memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
1903
1904 jsb->s_blocksize = htonl(ctx->fs->blocksize);
1905 jsb->s_maxlen = htonl(journal->j_maxlen);
1906 jsb->s_first = htonl(1);
1907
1908 /* Initialize the journal sequence number so that there is "no"
1909 * chance we will find old "valid" transactions in the journal.
1910 * This avoids the need to zero the whole journal (slow to do,
1911 * and risky when we are just recovering the filesystem).
1912 */
1913 uuid_generate(u.uuid);
1914 for (i = 0; i < 4; i ++)
1915 new_seq ^= u.val[i];
1916 jsb->s_sequence = htonl(new_seq);
1917
1918 mark_buffer_dirty(journal->j_sb_buffer);
1919 ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
1920}
1921
1922static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
1923 journal_t *journal,
1924 struct problem_context *pctx)
1925{
1926 struct ext2_super_block *sb = ctx->fs->super;
1927 int recover = ctx->fs->super->s_feature_incompat &
1928 EXT3_FEATURE_INCOMPAT_RECOVER;
1929
1930 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
1931 if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
1932 e2fsck_journal_reset_super(ctx, journal->j_superblock,
1933 journal);
1934 journal->j_transaction_sequence = 1;
1935 e2fsck_clear_recover(ctx, recover);
1936 return 0;
1937 }
1938 return EXT2_ET_CORRUPT_SUPERBLOCK;
1939 } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
1940 return EXT2_ET_CORRUPT_SUPERBLOCK;
1941
1942 return 0;
1943}
1944
1945static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
1946 int reset, int drop)
1947{
1948 journal_superblock_t *jsb;
1949
1950 if (drop)
1951 mark_buffer_clean(journal->j_sb_buffer);
1952 else if (!(ctx->options & E2F_OPT_READONLY)) {
1953 jsb = journal->j_superblock;
1954 jsb->s_sequence = htonl(journal->j_transaction_sequence);
1955 if (reset)
1956 jsb->s_start = 0; /* this marks the journal as empty */
1957 mark_buffer_dirty(journal->j_sb_buffer);
1958 }
1959 brelse(journal->j_sb_buffer);
1960
1961 if (ctx->journal_io) {
1962 if (ctx->fs && ctx->fs->io != ctx->journal_io)
1963 io_channel_close(ctx->journal_io);
1964 ctx->journal_io = 0;
1965 }
1966
1967#ifndef USE_INODE_IO
Rob Landleye7c43b62006-03-01 16:39:45 +00001968 ext2fs_free_mem(&journal->j_inode);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001969#endif
Rob Landleye7c43b62006-03-01 16:39:45 +00001970 ext2fs_free_mem(&journal->j_fs_dev);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001971 ext2fs_free_mem(&journal);
1972}
1973
1974/*
1975 * This function makes sure that the superblock fields regarding the
1976 * journal are consistent.
1977 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00001978static int e2fsck_check_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00001979{
1980 struct ext2_super_block *sb = ctx->fs->super;
1981 journal_t *journal;
1982 int recover = ctx->fs->super->s_feature_incompat &
1983 EXT3_FEATURE_INCOMPAT_RECOVER;
1984 struct problem_context pctx;
1985 problem_t problem;
1986 int reset = 0, force_fsck = 0;
1987 int retval;
1988
1989 /* If we don't have any journal features, don't do anything more */
1990 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
1991 !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
1992 uuid_is_null(sb->s_journal_uuid))
1993 return 0;
1994
1995 clear_problem_context(&pctx);
1996 pctx.num = sb->s_journal_inum;
1997
1998 retval = e2fsck_get_journal(ctx, &journal);
1999 if (retval) {
2000 if ((retval == EXT2_ET_BAD_INODE_NUM) ||
2001 (retval == EXT2_ET_BAD_BLOCK_NUM) ||
2002 (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
2003 (retval == EXT2_ET_NO_JOURNAL))
2004 return e2fsck_journal_fix_bad_inode(ctx, &pctx);
2005 return retval;
2006 }
2007
2008 retval = e2fsck_journal_load(journal);
2009 if (retval) {
2010 if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
2011 ((retval == EXT2_ET_UNSUPP_FEATURE) &&
2012 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
2013 &pctx))) ||
2014 ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
2015 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
2016 &pctx))) ||
2017 ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
2018 (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
2019 retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
2020 &pctx);
2021 e2fsck_journal_release(ctx, journal, 0, 1);
2022 return retval;
2023 }
2024
2025 /*
2026 * We want to make the flags consistent here. We will not leave with
2027 * needs_recovery set but has_journal clear. We can't get in a loop
2028 * with -y, -n, or -p, only if a user isn't making up their mind.
2029 */
2030no_has_journal:
2031 if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
2032 recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
2033 pctx.str = "inode";
2034 if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
2035 if (recover &&
2036 !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
2037 goto no_has_journal;
2038 /*
2039 * Need a full fsck if we are releasing a
2040 * journal stored on a reserved inode.
2041 */
2042 force_fsck = recover ||
2043 (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
2044 /* Clear all of the journal fields */
2045 sb->s_journal_inum = 0;
2046 sb->s_journal_dev = 0;
2047 memset(sb->s_journal_uuid, 0,
2048 sizeof(sb->s_journal_uuid));
2049 e2fsck_clear_recover(ctx, force_fsck);
2050 } else if (!(ctx->options & E2F_OPT_READONLY)) {
2051 sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
2052 ext2fs_mark_super_dirty(ctx->fs);
2053 }
2054 }
2055
2056 if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
2057 !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
2058 journal->j_superblock->s_start != 0) {
2059 /* Print status information */
2060 fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
2061 if (ctx->superblock)
2062 problem = PR_0_JOURNAL_RUN_DEFAULT;
2063 else
2064 problem = PR_0_JOURNAL_RUN;
2065 if (fix_problem(ctx, problem, &pctx)) {
2066 ctx->options |= E2F_OPT_FORCE;
2067 sb->s_feature_incompat |=
2068 EXT3_FEATURE_INCOMPAT_RECOVER;
2069 ext2fs_mark_super_dirty(ctx->fs);
2070 } else if (fix_problem(ctx,
2071 PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
2072 reset = 1;
2073 sb->s_state &= ~EXT2_VALID_FS;
2074 ext2fs_mark_super_dirty(ctx->fs);
2075 }
2076 /*
2077 * If the user answers no to the above question, we
2078 * ignore the fact that journal apparently has data;
2079 * accidentally replaying over valid data would be far
2080 * worse than skipping a questionable recovery.
2081 *
2082 * XXX should we abort with a fatal error here? What
2083 * will the ext3 kernel code do if a filesystem with
2084 * !NEEDS_RECOVERY but with a non-zero
2085 * journal->j_superblock->s_start is mounted?
2086 */
2087 }
2088
2089 e2fsck_journal_release(ctx, journal, reset, 0);
2090 return retval;
2091}
2092
2093static errcode_t recover_ext3_journal(e2fsck_t ctx)
2094{
2095 journal_t *journal;
2096 int retval;
2097
2098 journal_init_revoke_caches();
2099 retval = e2fsck_get_journal(ctx, &journal);
2100 if (retval)
2101 return retval;
2102
2103 retval = e2fsck_journal_load(journal);
2104 if (retval)
2105 goto errout;
2106
2107 retval = journal_init_revoke(journal, 1024);
2108 if (retval)
2109 goto errout;
2110
2111 retval = -journal_recover(journal);
2112 if (retval)
2113 goto errout;
2114
2115 if (journal->j_superblock->s_errno) {
2116 ctx->fs->super->s_state |= EXT2_ERROR_FS;
2117 ext2fs_mark_super_dirty(ctx->fs);
2118 journal->j_superblock->s_errno = 0;
2119 mark_buffer_dirty(journal->j_sb_buffer);
2120 }
2121
2122errout:
2123 journal_destroy_revoke(journal);
2124 journal_destroy_revoke_caches();
2125 e2fsck_journal_release(ctx, journal, 1, 0);
2126 return retval;
2127}
2128
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002129static int e2fsck_run_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002130{
2131 io_manager io_ptr = ctx->fs->io->manager;
2132 int blocksize = ctx->fs->blocksize;
2133 errcode_t retval, recover_retval;
2134
2135 printf(_("%s: recovering journal\n"), ctx->device_name);
2136 if (ctx->options & E2F_OPT_READONLY) {
2137 printf(_("%s: won't do journal recovery while read-only\n"),
2138 ctx->device_name);
2139 return EXT2_ET_FILE_RO;
2140 }
2141
2142 if (ctx->fs->flags & EXT2_FLAG_DIRTY)
2143 ext2fs_flush(ctx->fs); /* Force out any modifications */
2144
2145 recover_retval = recover_ext3_journal(ctx);
2146
2147 /*
2148 * Reload the filesystem context to get up-to-date data from disk
2149 * because journal recovery will change the filesystem under us.
2150 */
2151 ext2fs_close(ctx->fs);
2152 retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
2153 ctx->superblock, blocksize, io_ptr,
2154 &ctx->fs);
2155
2156 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +00002157 bb_error_msg(_("while trying to re-open %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002158 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +00002159 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002160 }
2161 ctx->fs->priv_data = ctx;
2162
2163 /* Set the superblock flags */
2164 e2fsck_clear_recover(ctx, recover_retval);
2165 return recover_retval;
2166}
2167
2168/*
2169 * This function will move the journal inode from a visible file in
2170 * the filesystem directory hierarchy to the reserved inode if necessary.
2171 */
2172static const char * const journal_names[] = {
2173 ".journal", "journal", ".journal.dat", "journal.dat", 0 };
2174
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002175static void e2fsck_move_ext3_journal(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002176{
2177 struct ext2_super_block *sb = ctx->fs->super;
2178 struct problem_context pctx;
2179 struct ext2_inode inode;
2180 ext2_filsys fs = ctx->fs;
2181 ext2_ino_t ino;
2182 errcode_t retval;
2183 const char * const * cpp;
2184 int group, mount_flags;
2185
2186 clear_problem_context(&pctx);
2187
2188 /*
2189 * If the filesystem is opened read-only, or there is no
2190 * journal, then do nothing.
2191 */
2192 if ((ctx->options & E2F_OPT_READONLY) ||
2193 (sb->s_journal_inum == 0) ||
2194 !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
2195 return;
2196
2197 /*
2198 * Read in the journal inode
2199 */
2200 if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
2201 return;
2202
2203 /*
2204 * If it's necessary to backup the journal inode, do so.
2205 */
2206 if ((sb->s_jnl_backup_type == 0) ||
2207 ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
2208 memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
2209 if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
2210 memcpy(sb->s_jnl_blocks, inode.i_block,
2211 EXT2_N_BLOCKS*4);
2212 sb->s_jnl_blocks[16] = inode.i_size;
2213 sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
2214 ext2fs_mark_super_dirty(fs);
2215 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2216 }
2217 }
2218
2219 /*
2220 * If the journal is already the hidden inode, then do nothing
2221 */
2222 if (sb->s_journal_inum == EXT2_JOURNAL_INO)
2223 return;
2224
2225 /*
2226 * The journal inode had better have only one link and not be readable.
2227 */
2228 if (inode.i_links_count != 1)
2229 return;
2230
2231 /*
2232 * If the filesystem is mounted, or we can't tell whether
2233 * or not it's mounted, do nothing.
2234 */
2235 retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
2236 if (retval || (mount_flags & EXT2_MF_MOUNTED))
2237 return;
2238
2239 /*
2240 * If we can't find the name of the journal inode, then do
2241 * nothing.
2242 */
2243 for (cpp = journal_names; *cpp; cpp++) {
2244 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
2245 strlen(*cpp), 0, &ino);
2246 if ((retval == 0) && (ino == sb->s_journal_inum))
2247 break;
2248 }
2249 if (*cpp == 0)
2250 return;
2251
2252 /* We need the inode bitmap to be loaded */
2253 retval = ext2fs_read_bitmaps(fs);
2254 if (retval)
2255 return;
2256
2257 pctx.str = *cpp;
2258 if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
2259 return;
2260
2261 /*
2262 * OK, we've done all the checks, let's actually move the
2263 * journal inode. Errors at this point mean we need to force
2264 * an ext2 filesystem check.
2265 */
2266 if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
2267 goto err_out;
2268 if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
2269 goto err_out;
2270 sb->s_journal_inum = EXT2_JOURNAL_INO;
2271 ext2fs_mark_super_dirty(fs);
2272 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2273 inode.i_links_count = 0;
2274 inode.i_dtime = time(0);
2275 if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
2276 goto err_out;
2277
2278 group = ext2fs_group_of_ino(fs, ino);
2279 ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
2280 ext2fs_mark_ib_dirty(fs);
2281 fs->group_desc[group].bg_free_inodes_count++;
2282 fs->super->s_free_inodes_count++;
2283 return;
2284
2285err_out:
2286 pctx.errcode = retval;
2287 fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
2288 fs->super->s_state &= ~EXT2_VALID_FS;
2289 ext2fs_mark_super_dirty(fs);
2290 return;
2291}
2292
2293/*
2294 * message.c --- print e2fsck messages (with compression)
2295 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002296 * print_e2fsck_message() prints a message to the user, using
2297 * compression techniques and expansions of abbreviations.
2298 *
2299 * The following % expansions are supported:
2300 *
2301 * %b <blk> block number
2302 * %B <blkcount> integer
2303 * %c <blk2> block number
2304 * %Di <dirent>->ino inode number
2305 * %Dn <dirent>->name string
2306 * %Dr <dirent>->rec_len
2307 * %Dl <dirent>->name_len
2308 * %Dt <dirent>->filetype
2309 * %d <dir> inode number
2310 * %g <group> integer
2311 * %i <ino> inode number
2312 * %Is <inode> -> i_size
2313 * %IS <inode> -> i_extra_isize
2314 * %Ib <inode> -> i_blocks
2315 * %Il <inode> -> i_links_count
2316 * %Im <inode> -> i_mode
2317 * %IM <inode> -> i_mtime
2318 * %IF <inode> -> i_faddr
2319 * %If <inode> -> i_file_acl
2320 * %Id <inode> -> i_dir_acl
2321 * %Iu <inode> -> i_uid
2322 * %Ig <inode> -> i_gid
2323 * %j <ino2> inode number
2324 * %m <com_err error message>
2325 * %N <num>
2326 * %p ext2fs_get_pathname of directory <ino>
2327 * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
2328 * the containing directory. (If dirent is NULL
2329 * then return the pathname of directory <ino2>)
2330 * %q ext2fs_get_pathname of directory <dir>
2331 * %Q ext2fs_get_pathname of directory <ino> with <dir> as
2332 * the containing directory.
2333 * %s <str> miscellaneous string
2334 * %S backup superblock
2335 * %X <num> hexadecimal format
2336 *
2337 * The following '@' expansions are supported:
2338 *
2339 * @a extended attribute
2340 * @A error allocating
2341 * @b block
2342 * @B bitmap
2343 * @c compress
2344 * @C conflicts with some other fs block
2345 * @D deleted
2346 * @d directory
2347 * @e entry
2348 * @E Entry '%Dn' in %p (%i)
2349 * @f filesystem
2350 * @F for @i %i (%Q) is
2351 * @g group
2352 * @h HTREE directory inode
2353 * @i inode
2354 * @I illegal
2355 * @j journal
2356 * @l lost+found
2357 * @L is a link
Mike Frysinger874af852006-03-08 07:03:27 +00002358 * @m multiply-claimed
2359 * @n invalid
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002360 * @o orphaned
2361 * @p problem in
2362 * @r root inode
2363 * @s should be
2364 * @S superblock
2365 * @u unattached
2366 * @v device
2367 * @z zero-length
2368 */
2369
2370
2371/*
2372 * This structure defines the abbreviations used by the text strings
2373 * below. The first character in the string is the index letter. An
2374 * abbreviation of the form '@<i>' is expanded by looking up the index
2375 * letter <i> in the table below.
2376 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002377static const char * const abbrevs[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002378 N_("aextended attribute"),
2379 N_("Aerror allocating"),
2380 N_("bblock"),
2381 N_("Bbitmap"),
2382 N_("ccompress"),
2383 N_("Cconflicts with some other fs @b"),
2384 N_("iinode"),
2385 N_("Iillegal"),
2386 N_("jjournal"),
2387 N_("Ddeleted"),
2388 N_("ddirectory"),
2389 N_("eentry"),
2390 N_("E@e '%Dn' in %p (%i)"),
2391 N_("ffilesystem"),
2392 N_("Ffor @i %i (%Q) is"),
2393 N_("ggroup"),
2394 N_("hHTREE @d @i"),
2395 N_("llost+found"),
2396 N_("Lis a link"),
Mike Frysinger874af852006-03-08 07:03:27 +00002397 N_("mmultiply-claimed"),
2398 N_("ninvalid"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002399 N_("oorphaned"),
2400 N_("pproblem in"),
2401 N_("rroot @i"),
2402 N_("sshould be"),
2403 N_("Ssuper@b"),
2404 N_("uunattached"),
2405 N_("vdevice"),
2406 N_("zzero-length"),
2407 "@@",
2408 0
2409 };
2410
2411/*
2412 * Give more user friendly names to the "special" inodes.
2413 */
2414#define num_special_inodes 11
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002415static const char * const special_inode_name[] =
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002416{
2417 N_("<The NULL inode>"), /* 0 */
2418 N_("<The bad blocks inode>"), /* 1 */
2419 "/", /* 2 */
2420 N_("<The ACL index inode>"), /* 3 */
2421 N_("<The ACL data inode>"), /* 4 */
2422 N_("<The boot loader inode>"), /* 5 */
2423 N_("<The undelete directory inode>"), /* 6 */
2424 N_("<The group descriptor inode>"), /* 7 */
2425 N_("<The journal inode>"), /* 8 */
2426 N_("<Reserved inode 9>"), /* 9 */
2427 N_("<Reserved inode 10>"), /* 10 */
2428};
2429
2430/*
2431 * This function does "safe" printing. It will convert non-printable
2432 * ASCII characters using '^' and M- notation.
2433 */
2434static void safe_print(const char *cp, int len)
2435{
2436 unsigned char ch;
2437
2438 if (len < 0)
2439 len = strlen(cp);
2440
2441 while (len--) {
2442 ch = *cp++;
2443 if (ch > 128) {
2444 fputs("M-", stdout);
2445 ch -= 128;
2446 }
2447 if ((ch < 32) || (ch == 0x7f)) {
2448 fputc('^', stdout);
2449 ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
2450 }
2451 fputc(ch, stdout);
2452 }
2453}
2454
2455
2456/*
2457 * This function prints a pathname, using the ext2fs_get_pathname
2458 * function
2459 */
2460static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
2461{
2462 errcode_t retval;
2463 char *path;
2464
2465 if (!dir && (ino < num_special_inodes)) {
2466 fputs(_(special_inode_name[ino]), stdout);
2467 return;
2468 }
2469
2470 retval = ext2fs_get_pathname(fs, dir, ino, &path);
2471 if (retval)
2472 fputs("???", stdout);
2473 else {
2474 safe_print(path, -1);
2475 ext2fs_free_mem(&path);
2476 }
2477}
2478
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002479static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
2480 struct problem_context *pctx, int first);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002481/*
2482 * This function handles the '@' expansion. We allow recursive
2483 * expansion; an @ expression can contain further '@' and '%'
2484 * expressions.
2485 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002486static void expand_at_expression(e2fsck_t ctx, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002487 struct problem_context *pctx,
2488 int *first)
2489{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002490 const char * const *cpp;
2491 const char *str;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002492
2493 /* Search for the abbreviation */
2494 for (cpp = abbrevs; *cpp; cpp++) {
2495 if (ch == *cpp[0])
2496 break;
2497 }
2498 if (*cpp) {
2499 str = _(*cpp) + 1;
2500 if (*first && islower(*str)) {
2501 *first = 0;
2502 fputc(toupper(*str++), stdout);
2503 }
2504 print_e2fsck_message(ctx, str, pctx, *first);
2505 } else
2506 printf("@%c", ch);
2507}
2508
2509/*
2510 * This function expands '%IX' expressions
2511 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002512static void expand_inode_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002513 struct problem_context *ctx)
2514{
2515 struct ext2_inode *inode;
2516 struct ext2_inode_large *large_inode;
2517 char * time_str;
2518 time_t t;
2519 int do_gmt = -1;
2520
2521 if (!ctx || !ctx->inode)
2522 goto no_inode;
2523
2524 inode = ctx->inode;
2525 large_inode = (struct ext2_inode_large *) inode;
2526
2527 switch (ch) {
2528 case 's':
2529 if (LINUX_S_ISDIR(inode->i_mode))
2530 printf("%u", inode->i_size);
2531 else {
2532#ifdef EXT2_NO_64_TYPE
2533 if (inode->i_size_high)
2534 printf("0x%x%08x", inode->i_size_high,
2535 inode->i_size);
2536 else
2537 printf("%u", inode->i_size);
2538#else
2539 printf("%llu", (inode->i_size |
2540 ((__u64) inode->i_size_high << 32)));
2541#endif
2542 }
2543 break;
2544 case 'S':
2545 printf("%u", large_inode->i_extra_isize);
2546 break;
2547 case 'b':
2548 printf("%u", inode->i_blocks);
2549 break;
2550 case 'l':
2551 printf("%d", inode->i_links_count);
2552 break;
2553 case 'm':
2554 printf("0%o", inode->i_mode);
2555 break;
2556 case 'M':
2557 /* The diet libc doesn't respect the TZ environemnt variable */
2558 if (do_gmt == -1) {
2559 time_str = getenv("TZ");
2560 if (!time_str)
2561 time_str = "";
2562 do_gmt = !strcmp(time_str, "GMT");
2563 }
2564 t = inode->i_mtime;
2565 time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
2566 printf("%.24s", time_str);
2567 break;
2568 case 'F':
2569 printf("%u", inode->i_faddr);
2570 break;
2571 case 'f':
2572 printf("%u", inode->i_file_acl);
2573 break;
2574 case 'd':
2575 printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
2576 inode->i_dir_acl : 0));
2577 break;
2578 case 'u':
2579 printf("%d", (inode->i_uid |
2580 (inode->osd2.linux2.l_i_uid_high << 16)));
2581 break;
2582 case 'g':
2583 printf("%d", (inode->i_gid |
2584 (inode->osd2.linux2.l_i_gid_high << 16)));
2585 break;
2586 default:
2587 no_inode:
2588 printf("%%I%c", ch);
2589 break;
2590 }
2591}
2592
2593/*
2594 * This function expands '%dX' expressions
2595 */
Rob Landley7c94bed2006-05-03 21:58:45 +00002596static void expand_dirent_expression(char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002597 struct problem_context *ctx)
2598{
2599 struct ext2_dir_entry *dirent;
2600 int len;
2601
2602 if (!ctx || !ctx->dirent)
2603 goto no_dirent;
2604
2605 dirent = ctx->dirent;
2606
2607 switch (ch) {
2608 case 'i':
2609 printf("%u", dirent->inode);
2610 break;
2611 case 'n':
2612 len = dirent->name_len & 0xFF;
2613 if (len > EXT2_NAME_LEN)
2614 len = EXT2_NAME_LEN;
2615 if (len > dirent->rec_len)
2616 len = dirent->rec_len;
2617 safe_print(dirent->name, len);
2618 break;
2619 case 'r':
2620 printf("%u", dirent->rec_len);
2621 break;
2622 case 'l':
2623 printf("%u", dirent->name_len & 0xFF);
2624 break;
2625 case 't':
2626 printf("%u", dirent->name_len >> 8);
2627 break;
2628 default:
2629 no_dirent:
2630 printf("%%D%c", ch);
2631 break;
2632 }
2633}
2634
Rob Landley7c94bed2006-05-03 21:58:45 +00002635static void expand_percent_expression(ext2_filsys fs, char ch,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002636 struct problem_context *ctx)
2637{
2638 if (!ctx)
2639 goto no_context;
2640
2641 switch (ch) {
2642 case '%':
2643 fputc('%', stdout);
2644 break;
2645 case 'b':
2646 printf("%u", ctx->blk);
2647 break;
2648 case 'B':
2649#ifdef EXT2_NO_64_TYPE
2650 printf("%d", ctx->blkcount);
2651#else
2652 printf("%lld", ctx->blkcount);
2653#endif
2654 break;
2655 case 'c':
2656 printf("%u", ctx->blk2);
2657 break;
2658 case 'd':
2659 printf("%u", ctx->dir);
2660 break;
2661 case 'g':
2662 printf("%d", ctx->group);
2663 break;
2664 case 'i':
2665 printf("%u", ctx->ino);
2666 break;
2667 case 'j':
2668 printf("%u", ctx->ino2);
2669 break;
2670 case 'm':
2671 printf("%s", error_message(ctx->errcode));
2672 break;
2673 case 'N':
2674#ifdef EXT2_NO_64_TYPE
2675 printf("%u", ctx->num);
2676#else
2677 printf("%llu", ctx->num);
2678#endif
2679 break;
2680 case 'p':
2681 print_pathname(fs, ctx->ino, 0);
2682 break;
2683 case 'P':
2684 print_pathname(fs, ctx->ino2,
2685 ctx->dirent ? ctx->dirent->inode : 0);
2686 break;
2687 case 'q':
2688 print_pathname(fs, ctx->dir, 0);
2689 break;
2690 case 'Q':
2691 print_pathname(fs, ctx->dir, ctx->ino);
2692 break;
2693 case 'S':
2694 printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
2695 break;
2696 case 's':
2697 printf("%s", ctx->str ? ctx->str : "NULL");
2698 break;
2699 case 'X':
2700#ifdef EXT2_NO_64_TYPE
2701 printf("0x%x", ctx->num);
2702#else
2703 printf("0x%llx", ctx->num);
2704#endif
2705 break;
2706 default:
2707 no_context:
2708 printf("%%%c", ch);
2709 break;
2710 }
2711}
2712
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002713
2714static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002715 struct problem_context *pctx, int first)
2716{
2717 ext2_filsys fs = ctx->fs;
2718 const char * cp;
2719 int i;
2720
2721 e2fsck_clear_progbar(ctx);
2722 for (cp = msg; *cp; cp++) {
2723 if (cp[0] == '@') {
2724 cp++;
2725 expand_at_expression(ctx, *cp, pctx, &first);
2726 } else if (cp[0] == '%' && cp[1] == 'I') {
2727 cp += 2;
2728 expand_inode_expression(*cp, pctx);
2729 } else if (cp[0] == '%' && cp[1] == 'D') {
2730 cp += 2;
2731 expand_dirent_expression(*cp, pctx);
2732 } else if ((cp[0] == '%')) {
2733 cp++;
2734 expand_percent_expression(fs, *cp, pctx);
2735 } else {
2736 for (i=0; cp[i]; i++)
2737 if ((cp[i] == '@') || cp[i] == '%')
2738 break;
2739 printf("%.*s", i, cp);
2740 cp += i-1;
2741 }
2742 first = 0;
2743 }
2744}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002745
2746
2747/*
2748 * region.c --- code which manages allocations within a region.
2749 */
2750
2751struct region_el {
2752 region_addr_t start;
2753 region_addr_t end;
2754 struct region_el *next;
2755};
2756
2757struct region_struct {
2758 region_addr_t min;
2759 region_addr_t max;
2760 struct region_el *allocated;
2761};
2762
2763static region_t region_create(region_addr_t min, region_addr_t max)
2764{
2765 region_t region;
2766
2767 region = malloc(sizeof(struct region_struct));
2768 if (!region)
2769 return NULL;
2770 memset(region, 0, sizeof(struct region_struct));
2771 region->min = min;
2772 region->max = max;
2773 return region;
2774}
2775
2776static void region_free(region_t region)
2777{
2778 struct region_el *r, *next;
2779
2780 for (r = region->allocated; r; r = next) {
2781 next = r->next;
2782 free(r);
2783 }
2784 memset(region, 0, sizeof(struct region_struct));
2785 free(region);
2786}
2787
2788static int region_allocate(region_t region, region_addr_t start, int n)
2789{
2790 struct region_el *r, *new_region, *prev, *next;
2791 region_addr_t end;
2792
2793 end = start+n;
2794 if ((start < region->min) || (end > region->max))
2795 return -1;
2796 if (n == 0)
2797 return 1;
2798
2799 /*
2800 * Search through the linked list. If we find that it
2801 * conflicts witih something that's already allocated, return
2802 * 1; if we can find an existing region which we can grow, do
2803 * so. Otherwise, stop when we find the appropriate place
2804 * insert a new region element into the linked list.
2805 */
2806 for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
2807 if (((start >= r->start) && (start < r->end)) ||
2808 ((end > r->start) && (end <= r->end)) ||
2809 ((start <= r->start) && (end >= r->end)))
2810 return 1;
2811 if (end == r->start) {
2812 r->start = start;
2813 return 0;
2814 }
2815 if (start == r->end) {
2816 if ((next = r->next)) {
2817 if (end > next->start)
2818 return 1;
2819 if (end == next->start) {
2820 r->end = next->end;
2821 r->next = next->next;
2822 free(next);
2823 return 0;
2824 }
2825 }
2826 r->end = end;
2827 return 0;
2828 }
2829 if (start < r->start)
2830 break;
2831 }
2832 /*
2833 * Insert a new region element structure into the linked list
2834 */
2835 new_region = malloc(sizeof(struct region_el));
2836 if (!new_region)
2837 return -1;
2838 new_region->start = start;
2839 new_region->end = start + n;
2840 new_region->next = r;
2841 if (prev)
2842 prev->next = new_region;
2843 else
2844 region->allocated = new_region;
2845 return 0;
2846}
2847
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002848/*
2849 * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
2850 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002851 * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
2852 * and applies the following tests to each inode:
2853 *
2854 * - The mode field of the inode must be legal.
2855 * - The size and block count fields of the inode are correct.
2856 * - A data block must not be used by another inode
2857 *
2858 * Pass 1 also gathers the collects the following information:
2859 *
2860 * - A bitmap of which inodes are in use. (inode_used_map)
2861 * - A bitmap of which inodes are directories. (inode_dir_map)
2862 * - A bitmap of which inodes are regular files. (inode_reg_map)
2863 * - A bitmap of which inodes have bad fields. (inode_bad_map)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002864 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
2865 * - A bitmap of which blocks are in use. (block_found_map)
2866 * - A bitmap of which blocks are in use by two inodes (block_dup_map)
2867 * - The data blocks of the directory inodes. (dir_map)
2868 *
2869 * Pass 1 is designed to stash away enough information so that the
2870 * other passes should not need to read in the inode information
2871 * during the normal course of a filesystem check. (Althogh if an
2872 * inconsistency is detected, other passes may need to read in an
2873 * inode to fix it.)
2874 *
2875 * Note that pass 1B will be invoked if there are any duplicate blocks
2876 * found.
2877 */
2878
2879
2880static int process_block(ext2_filsys fs, blk_t *blocknr,
2881 e2_blkcnt_t blockcnt, blk_t ref_blk,
2882 int ref_offset, void *priv_data);
2883static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
2884 e2_blkcnt_t blockcnt, blk_t ref_blk,
2885 int ref_offset, void *priv_data);
2886static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
2887 char *block_buf);
2888static void mark_table_blocks(e2fsck_t ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002889static void alloc_imagic_map(e2fsck_t ctx);
2890static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
2891static void handle_fs_bad_blocks(e2fsck_t ctx);
2892static void process_inodes(e2fsck_t ctx, char *block_buf);
Rob Landley7c94bed2006-05-03 21:58:45 +00002893static int process_inode_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002894static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
2895 dgrp_t group, void * priv_data);
2896static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
2897 char *block_buf, int adjust_sign);
2898/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
2899
2900static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
2901 struct ext2_inode * inode, int bufsize,
2902 const char *proc);
2903
2904struct process_block_struct_1 {
2905 ext2_ino_t ino;
2906 unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
2907 fragmented:1, compressed:1, bbcheck:1;
2908 blk_t num_blocks;
2909 blk_t max_blocks;
2910 e2_blkcnt_t last_block;
2911 int num_illegal_blocks;
2912 blk_t previous_block;
2913 struct ext2_inode *inode;
2914 struct problem_context *pctx;
2915 ext2fs_block_bitmap fs_meta_blocks;
2916 e2fsck_t ctx;
2917};
2918
2919struct process_inode_block {
2920 ext2_ino_t ino;
2921 struct ext2_inode inode;
2922};
2923
2924struct scan_callback_struct {
2925 e2fsck_t ctx;
2926 char *block_buf;
2927};
2928
2929/*
2930 * For the inodes to process list.
2931 */
2932static struct process_inode_block *inodes_to_process;
2933static int process_inode_count;
2934
2935static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
2936 EXT2_MIN_BLOCK_LOG_SIZE + 1];
2937
2938/*
2939 * Free all memory allocated by pass1 in preparation for restarting
2940 * things.
2941 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002942static void unwind_pass1(void)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002943{
2944 ext2fs_free_mem(&inodes_to_process);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002945}
2946
2947/*
2948 * Check to make sure a device inode is real. Returns 1 if the device
2949 * checks out, 0 if not.
2950 *
2951 * Note: this routine is now also used to check FIFO's and Sockets,
2952 * since they have the same requirement; the i_block fields should be
2953 * zero.
2954 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002955static int
2956e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002957{
2958 int i;
2959
2960 /*
2961 * If i_blocks is non-zero, or the index flag is set, then
2962 * this is a bogus device/fifo/socket
2963 */
2964 if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
2965 (inode->i_flags & EXT2_INDEX_FL))
2966 return 0;
2967
2968 /*
2969 * We should be able to do the test below all the time, but
2970 * because the kernel doesn't forcibly clear the device
2971 * inode's additional i_block fields, there are some rare
2972 * occasions when a legitimate device inode will have non-zero
2973 * additional i_block fields. So for now, we only complain
2974 * when the immutable flag is set, which should never happen
2975 * for devices. (And that's when the problem is caused, since
2976 * you can't set or clear immutable flags for devices.) Once
2977 * the kernel has been fixed we can change this...
2978 */
2979 if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
2980 for (i=4; i < EXT2_N_BLOCKS; i++)
2981 if (inode->i_block[i])
2982 return 0;
2983 }
2984 return 1;
2985}
2986
2987/*
2988 * Check to make sure a symlink inode is real. Returns 1 if the symlink
2989 * checks out, 0 if not.
2990 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00002991static int
2992e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002993{
2994 unsigned int len;
2995 int i;
2996 blk_t blocks;
2997
2998 if ((inode->i_size_high || inode->i_size == 0) ||
2999 (inode->i_flags & EXT2_INDEX_FL))
3000 return 0;
3001
3002 blocks = ext2fs_inode_data_blocks(fs, inode);
3003 if (blocks) {
3004 if ((inode->i_size >= fs->blocksize) ||
3005 (blocks != fs->blocksize >> 9) ||
3006 (inode->i_block[0] < fs->super->s_first_data_block) ||
3007 (inode->i_block[0] >= fs->super->s_blocks_count))
3008 return 0;
3009
3010 for (i = 1; i < EXT2_N_BLOCKS; i++)
3011 if (inode->i_block[i])
3012 return 0;
3013
3014 if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
3015 return 0;
3016
3017 len = strnlen(buf, fs->blocksize);
3018 if (len == fs->blocksize)
3019 return 0;
3020 } else {
3021 if (inode->i_size >= sizeof(inode->i_block))
3022 return 0;
3023
3024 len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
3025 if (len == sizeof(inode->i_block))
3026 return 0;
3027 }
3028 if (len != inode->i_size)
3029 return 0;
3030 return 1;
3031}
3032
3033/*
3034 * If the immutable (or append-only) flag is set on the inode, offer
3035 * to clear it.
3036 */
3037#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
3038static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
3039{
3040 if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
3041 return;
3042
3043 if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
3044 return;
3045
3046 pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
3047 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3048}
3049
3050/*
3051 * If device, fifo or socket, check size is zero -- if not offer to
3052 * clear it
3053 */
3054static void check_size(e2fsck_t ctx, struct problem_context *pctx)
3055{
3056 struct ext2_inode *inode = pctx->inode;
3057
3058 if ((inode->i_size == 0) && (inode->i_size_high == 0))
3059 return;
3060
3061 if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
3062 return;
3063
3064 inode->i_size = 0;
3065 inode->i_size_high = 0;
3066 e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
3067}
3068
3069static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
3070{
3071 struct ext2_super_block *sb = ctx->fs->super;
3072 struct ext2_inode_large *inode;
3073 struct ext2_ext_attr_entry *entry;
3074 char *start, *end;
3075 int storage_size, remain, offs;
3076 int problem = 0;
3077
3078 inode = (struct ext2_inode_large *) pctx->inode;
3079 storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
3080 inode->i_extra_isize;
3081 start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3082 inode->i_extra_isize + sizeof(__u32);
3083 end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
3084 entry = (struct ext2_ext_attr_entry *) start;
3085
3086 /* scan all entry's headers first */
3087
3088 /* take finish entry 0UL into account */
3089 remain = storage_size - sizeof(__u32);
3090 offs = end - start;
3091
3092 while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
3093
3094 /* header eats this space */
3095 remain -= sizeof(struct ext2_ext_attr_entry);
3096
3097 /* is attribute name valid? */
3098 if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
3099 pctx->num = entry->e_name_len;
3100 problem = PR_1_ATTR_NAME_LEN;
3101 goto fix;
3102 }
3103
3104 /* attribute len eats this space */
3105 remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
3106
3107 /* check value size */
3108 if (entry->e_value_size == 0 || entry->e_value_size > remain) {
3109 pctx->num = entry->e_value_size;
3110 problem = PR_1_ATTR_VALUE_SIZE;
3111 goto fix;
3112 }
3113
3114 /* check value placement */
3115 if (entry->e_value_offs +
3116 EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
3117 printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
3118 pctx->num = entry->e_value_offs;
3119 problem = PR_1_ATTR_VALUE_OFFSET;
3120 goto fix;
3121 }
3122
3123 /* e_value_block must be 0 in inode's ea */
3124 if (entry->e_value_block != 0) {
3125 pctx->num = entry->e_value_block;
3126 problem = PR_1_ATTR_VALUE_BLOCK;
3127 goto fix;
3128 }
3129
3130 /* e_hash must be 0 in inode's ea */
3131 if (entry->e_hash != 0) {
3132 pctx->num = entry->e_hash;
3133 problem = PR_1_ATTR_HASH;
3134 goto fix;
3135 }
3136
3137 remain -= entry->e_value_size;
3138 offs -= EXT2_XATTR_SIZE(entry->e_value_size);
3139
3140 entry = EXT2_EXT_ATTR_NEXT(entry);
3141 }
3142fix:
3143 /*
3144 * it seems like a corruption. it's very unlikely we could repair
3145 * EA(s) in automatic fashion -bzzz
3146 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003147 if (problem == 0 || !fix_problem(ctx, problem, pctx))
3148 return;
3149
3150 /* simple remove all possible EA(s) */
3151 *((__u32 *)start) = 0UL;
3152 e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
3153 EXT2_INODE_SIZE(sb), "pass1");
3154}
3155
3156static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
3157{
3158 struct ext2_super_block *sb = ctx->fs->super;
3159 struct ext2_inode_large *inode;
3160 __u32 *eamagic;
3161 int min, max;
3162
3163 inode = (struct ext2_inode_large *) pctx->inode;
3164 if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
3165 /* this isn't large inode. so, nothing to check */
3166 return;
3167 }
3168
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003169 /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
3170 min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
3171 max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
3172 /*
3173 * For now we will allow i_extra_isize to be 0, but really
3174 * implementations should never allow i_extra_isize to be 0
3175 */
3176 if (inode->i_extra_isize &&
3177 (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
3178 if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
3179 return;
3180 inode->i_extra_isize = min;
3181 e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
3182 EXT2_INODE_SIZE(sb), "pass1");
3183 return;
3184 }
3185
3186 eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
3187 inode->i_extra_isize);
3188 if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
3189 /* it seems inode has an extended attribute(s) in body */
3190 check_ea_in_inode(ctx, pctx);
3191 }
3192}
3193
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003194static void e2fsck_pass1(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003195{
3196 int i;
3197 __u64 max_sizes;
3198 ext2_filsys fs = ctx->fs;
3199 ext2_ino_t ino;
3200 struct ext2_inode *inode;
3201 ext2_inode_scan scan;
3202 char *block_buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003203 unsigned char frag, fsize;
3204 struct problem_context pctx;
3205 struct scan_callback_struct scan_struct;
3206 struct ext2_super_block *sb = ctx->fs->super;
3207 int imagic_fs;
3208 int busted_fs_time = 0;
3209 int inode_size;
3210
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003211 clear_problem_context(&pctx);
3212
3213 if (!(ctx->options & E2F_OPT_PREEN))
3214 fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
3215
3216 if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
3217 !(ctx->options & E2F_OPT_NO)) {
3218 if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
3219 ctx->dirs_to_hash = 0;
3220 }
3221
Rob Landley3e72c592006-04-06 22:49:04 +00003222 /* Pass 1 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003223
3224#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
3225
3226 for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
3227 max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
3228 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
3229 max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
3230 max_sizes = (max_sizes * (1UL << i)) - 1;
3231 ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
3232 }
3233#undef EXT2_BPP
3234
3235 imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
3236
3237 /*
3238 * Allocate bitmaps structures
3239 */
3240 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
3241 &ctx->inode_used_map);
3242 if (pctx.errcode) {
3243 pctx.num = 1;
3244 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3245 ctx->flags |= E2F_FLAG_ABORT;
3246 return;
3247 }
3248 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3249 _("directory inode map"), &ctx->inode_dir_map);
3250 if (pctx.errcode) {
3251 pctx.num = 2;
3252 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3253 ctx->flags |= E2F_FLAG_ABORT;
3254 return;
3255 }
3256 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
3257 _("regular file inode map"), &ctx->inode_reg_map);
3258 if (pctx.errcode) {
3259 pctx.num = 6;
3260 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3261 ctx->flags |= E2F_FLAG_ABORT;
3262 return;
3263 }
3264 pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
3265 &ctx->block_found_map);
3266 if (pctx.errcode) {
3267 pctx.num = 1;
3268 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3269 ctx->flags |= E2F_FLAG_ABORT;
3270 return;
3271 }
3272 pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
3273 &ctx->inode_link_info);
3274 if (pctx.errcode) {
3275 fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
3276 ctx->flags |= E2F_FLAG_ABORT;
3277 return;
3278 }
3279 inode_size = EXT2_INODE_SIZE(fs->super);
3280 inode = (struct ext2_inode *)
3281 e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
3282
3283 inodes_to_process = (struct process_inode_block *)
3284 e2fsck_allocate_memory(ctx,
3285 (ctx->process_inode_size *
3286 sizeof(struct process_inode_block)),
3287 "array of inodes to process");
3288 process_inode_count = 0;
3289
3290 pctx.errcode = ext2fs_init_dblist(fs, 0);
3291 if (pctx.errcode) {
3292 fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
3293 ctx->flags |= E2F_FLAG_ABORT;
3294 return;
3295 }
3296
3297 /*
3298 * If the last orphan field is set, clear it, since the pass1
3299 * processing will automatically find and clear the orphans.
3300 * In the future, we may want to try using the last_orphan
3301 * linked list ourselves, but for now, we clear it so that the
3302 * ext3 mount code won't get confused.
3303 */
3304 if (!(ctx->options & E2F_OPT_READONLY)) {
3305 if (fs->super->s_last_orphan) {
3306 fs->super->s_last_orphan = 0;
3307 ext2fs_mark_super_dirty(fs);
3308 }
3309 }
3310
3311 mark_table_blocks(ctx);
3312 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
3313 "block interate buffer");
3314 e2fsck_use_inode_shortcuts(ctx, 1);
3315 ehandler_operation(_("doing inode scan"));
3316 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
3317 &scan);
3318 if (pctx.errcode) {
3319 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3320 ctx->flags |= E2F_FLAG_ABORT;
3321 return;
3322 }
3323 ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
3324 ctx->stashed_inode = inode;
3325 scan_struct.ctx = ctx;
3326 scan_struct.block_buf = block_buf;
3327 ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
3328 if (ctx->progress)
3329 if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
3330 return;
Mike Frysinger874af852006-03-08 07:03:27 +00003331 if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
3332 (fs->super->s_mtime < fs->super->s_inodes_count))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003333 busted_fs_time = 1;
3334
3335 while (1) {
3336 pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
3337 inode, inode_size);
3338 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3339 return;
3340 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003341 continue;
3342 }
3343 if (pctx.errcode) {
3344 fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
3345 ctx->flags |= E2F_FLAG_ABORT;
3346 return;
3347 }
3348 if (!ino)
3349 break;
3350 pctx.ino = ino;
3351 pctx.inode = inode;
3352 ctx->stashed_ino = ino;
3353 if (inode->i_links_count) {
3354 pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
3355 ino, inode->i_links_count);
3356 if (pctx.errcode) {
3357 pctx.num = inode->i_links_count;
3358 fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
3359 ctx->flags |= E2F_FLAG_ABORT;
3360 return;
3361 }
3362 }
3363 if (ino == EXT2_BAD_INO) {
3364 struct process_block_struct_1 pb;
3365
3366 pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
3367 &pb.fs_meta_blocks);
3368 if (pctx.errcode) {
3369 pctx.num = 4;
3370 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
3371 ctx->flags |= E2F_FLAG_ABORT;
3372 return;
3373 }
3374 pb.ino = EXT2_BAD_INO;
3375 pb.num_blocks = pb.last_block = 0;
3376 pb.num_illegal_blocks = 0;
3377 pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
3378 pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
3379 pb.inode = inode;
3380 pb.pctx = &pctx;
3381 pb.ctx = ctx;
3382 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
3383 block_buf, process_bad_block, &pb);
3384 ext2fs_free_block_bitmap(pb.fs_meta_blocks);
3385 if (pctx.errcode) {
3386 fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
3387 ctx->flags |= E2F_FLAG_ABORT;
3388 return;
3389 }
3390 if (pb.bbcheck)
3391 if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
3392 ctx->flags |= E2F_FLAG_ABORT;
3393 return;
3394 }
3395 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3396 clear_problem_context(&pctx);
3397 continue;
3398 } else if (ino == EXT2_ROOT_INO) {
3399 /*
3400 * Make sure the root inode is a directory; if
3401 * not, offer to clear it. It will be
3402 * regnerated in pass #3.
3403 */
3404 if (!LINUX_S_ISDIR(inode->i_mode)) {
3405 if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
3406 inode->i_dtime = time(0);
3407 inode->i_links_count = 0;
3408 ext2fs_icount_store(ctx->inode_link_info,
3409 ino, 0);
3410 e2fsck_write_inode(ctx, ino, inode,
3411 "pass1");
3412 }
3413
3414 }
3415 /*
3416 * If dtime is set, offer to clear it. mke2fs
3417 * version 0.2b created filesystems with the
3418 * dtime field set for the root and lost+found
3419 * directories. We won't worry about
3420 * /lost+found, since that can be regenerated
3421 * easily. But we will fix the root directory
3422 * as a special case.
3423 */
3424 if (inode->i_dtime && inode->i_links_count) {
3425 if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
3426 inode->i_dtime = 0;
3427 e2fsck_write_inode(ctx, ino, inode,
3428 "pass1");
3429 }
3430 }
3431 } else if (ino == EXT2_JOURNAL_INO) {
3432 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3433 if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
3434 if (!LINUX_S_ISREG(inode->i_mode) &&
3435 fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
3436 &pctx)) {
3437 inode->i_mode = LINUX_S_IFREG;
3438 e2fsck_write_inode(ctx, ino, inode,
3439 "pass1");
3440 }
3441 check_blocks(ctx, &pctx, block_buf);
3442 continue;
3443 }
3444 if ((inode->i_links_count || inode->i_blocks ||
3445 inode->i_blocks || inode->i_block[0]) &&
3446 fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
3447 &pctx)) {
3448 memset(inode, 0, inode_size);
3449 ext2fs_icount_store(ctx->inode_link_info,
3450 ino, 0);
3451 e2fsck_write_inode_full(ctx, ino, inode,
3452 inode_size, "pass1");
3453 }
3454 } else if (ino < EXT2_FIRST_INODE(fs->super)) {
3455 int problem = 0;
3456
3457 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3458 if (ino == EXT2_BOOT_LOADER_INO) {
3459 if (LINUX_S_ISDIR(inode->i_mode))
3460 problem = PR_1_RESERVED_BAD_MODE;
3461 } else if (ino == EXT2_RESIZE_INO) {
3462 if (inode->i_mode &&
3463 !LINUX_S_ISREG(inode->i_mode))
3464 problem = PR_1_RESERVED_BAD_MODE;
3465 } else {
3466 if (inode->i_mode != 0)
3467 problem = PR_1_RESERVED_BAD_MODE;
3468 }
3469 if (problem) {
3470 if (fix_problem(ctx, problem, &pctx)) {
3471 inode->i_mode = 0;
3472 e2fsck_write_inode(ctx, ino, inode,
3473 "pass1");
3474 }
3475 }
3476 check_blocks(ctx, &pctx, block_buf);
3477 continue;
3478 }
3479 /*
3480 * Check for inodes who might have been part of the
3481 * orphaned list linked list. They should have gotten
3482 * dealt with by now, unless the list had somehow been
3483 * corrupted.
3484 *
3485 * FIXME: In the future, inodes which are still in use
3486 * (and which are therefore) pending truncation should
3487 * be handled specially. Right now we just clear the
3488 * dtime field, and the normal e2fsck handling of
3489 * inodes where i_size and the inode blocks are
3490 * inconsistent is to fix i_size, instead of releasing
3491 * the extra blocks. This won't catch the inodes that
3492 * was at the end of the orphan list, but it's better
3493 * than nothing. The right answer is that there
3494 * shouldn't be any bugs in the orphan list handling. :-)
3495 */
3496 if (inode->i_dtime && !busted_fs_time &&
3497 inode->i_dtime < ctx->fs->super->s_inodes_count) {
3498 if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
3499 inode->i_dtime = inode->i_links_count ?
3500 0 : time(0);
3501 e2fsck_write_inode(ctx, ino, inode,
3502 "pass1");
3503 }
3504 }
3505
3506 /*
3507 * This code assumes that deleted inodes have
3508 * i_links_count set to 0.
3509 */
3510 if (!inode->i_links_count) {
3511 if (!inode->i_dtime && inode->i_mode) {
3512 if (fix_problem(ctx,
3513 PR_1_ZERO_DTIME, &pctx)) {
3514 inode->i_dtime = time(0);
3515 e2fsck_write_inode(ctx, ino, inode,
3516 "pass1");
3517 }
3518 }
3519 continue;
3520 }
3521 /*
3522 * n.b. 0.3c ext2fs code didn't clear i_links_count for
3523 * deleted files. Oops.
3524 *
3525 * Since all new ext2 implementations get this right,
3526 * we now assume that the case of non-zero
3527 * i_links_count and non-zero dtime means that we
3528 * should keep the file, not delete it.
3529 *
3530 */
3531 if (inode->i_dtime) {
3532 if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
3533 inode->i_dtime = 0;
3534 e2fsck_write_inode(ctx, ino, inode, "pass1");
3535 }
3536 }
3537
3538 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
3539 switch (fs->super->s_creator_os) {
3540 case EXT2_OS_LINUX:
3541 frag = inode->osd2.linux2.l_i_frag;
3542 fsize = inode->osd2.linux2.l_i_fsize;
3543 break;
3544 case EXT2_OS_HURD:
3545 frag = inode->osd2.hurd2.h_i_frag;
3546 fsize = inode->osd2.hurd2.h_i_fsize;
3547 break;
3548 case EXT2_OS_MASIX:
3549 frag = inode->osd2.masix2.m_i_frag;
3550 fsize = inode->osd2.masix2.m_i_fsize;
3551 break;
3552 default:
3553 frag = fsize = 0;
3554 }
3555
3556 if (inode->i_faddr || frag || fsize ||
3557 (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
3558 mark_inode_bad(ctx, ino);
3559 if (inode->i_flags & EXT2_IMAGIC_FL) {
3560 if (imagic_fs) {
3561 if (!ctx->inode_imagic_map)
3562 alloc_imagic_map(ctx);
3563 ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
3564 ino);
3565 } else {
3566 if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
3567 inode->i_flags &= ~EXT2_IMAGIC_FL;
3568 e2fsck_write_inode(ctx, ino,
3569 inode, "pass1");
3570 }
3571 }
3572 }
3573
3574 check_inode_extra_space(ctx, &pctx);
3575
3576 if (LINUX_S_ISDIR(inode->i_mode)) {
3577 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
3578 e2fsck_add_dir_info(ctx, ino, 0);
3579 ctx->fs_directory_count++;
3580 } else if (LINUX_S_ISREG (inode->i_mode)) {
3581 ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
3582 ctx->fs_regular_count++;
3583 } else if (LINUX_S_ISCHR (inode->i_mode) &&
3584 e2fsck_pass1_check_device_inode(fs, inode)) {
3585 check_immutable(ctx, &pctx);
3586 check_size(ctx, &pctx);
3587 ctx->fs_chardev_count++;
3588 } else if (LINUX_S_ISBLK (inode->i_mode) &&
3589 e2fsck_pass1_check_device_inode(fs, inode)) {
3590 check_immutable(ctx, &pctx);
3591 check_size(ctx, &pctx);
3592 ctx->fs_blockdev_count++;
3593 } else if (LINUX_S_ISLNK (inode->i_mode) &&
3594 e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
3595 check_immutable(ctx, &pctx);
3596 ctx->fs_symlinks_count++;
3597 if (ext2fs_inode_data_blocks(fs, inode) == 0) {
3598 ctx->fs_fast_symlinks_count++;
3599 check_blocks(ctx, &pctx, block_buf);
3600 continue;
3601 }
3602 }
3603 else if (LINUX_S_ISFIFO (inode->i_mode) &&
3604 e2fsck_pass1_check_device_inode(fs, inode)) {
3605 check_immutable(ctx, &pctx);
3606 check_size(ctx, &pctx);
3607 ctx->fs_fifo_count++;
3608 } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
3609 e2fsck_pass1_check_device_inode(fs, inode)) {
3610 check_immutable(ctx, &pctx);
3611 check_size(ctx, &pctx);
3612 ctx->fs_sockets_count++;
3613 } else
3614 mark_inode_bad(ctx, ino);
3615 if (inode->i_block[EXT2_IND_BLOCK])
3616 ctx->fs_ind_count++;
3617 if (inode->i_block[EXT2_DIND_BLOCK])
3618 ctx->fs_dind_count++;
3619 if (inode->i_block[EXT2_TIND_BLOCK])
3620 ctx->fs_tind_count++;
3621 if (inode->i_block[EXT2_IND_BLOCK] ||
3622 inode->i_block[EXT2_DIND_BLOCK] ||
3623 inode->i_block[EXT2_TIND_BLOCK] ||
3624 inode->i_file_acl) {
3625 inodes_to_process[process_inode_count].ino = ino;
3626 inodes_to_process[process_inode_count].inode = *inode;
3627 process_inode_count++;
3628 } else
3629 check_blocks(ctx, &pctx, block_buf);
3630
3631 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3632 return;
3633
3634 if (process_inode_count >= ctx->process_inode_size) {
3635 process_inodes(ctx, block_buf);
3636
3637 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3638 return;
3639 }
3640 }
3641 process_inodes(ctx, block_buf);
3642 ext2fs_close_inode_scan(scan);
3643 ehandler_operation(0);
3644
3645 /*
3646 * If any extended attribute blocks' reference counts need to
3647 * be adjusted, either up (ctx->refcount_extra), or down
3648 * (ctx->refcount), then fix them.
3649 */
3650 if (ctx->refcount) {
3651 adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
3652 ea_refcount_free(ctx->refcount);
3653 ctx->refcount = 0;
3654 }
3655 if (ctx->refcount_extra) {
3656 adjust_extattr_refcount(ctx, ctx->refcount_extra,
3657 block_buf, +1);
3658 ea_refcount_free(ctx->refcount_extra);
3659 ctx->refcount_extra = 0;
3660 }
3661
3662 if (ctx->invalid_bitmaps)
3663 handle_fs_bad_blocks(ctx);
3664
3665 /* We don't need the block_ea_map any more */
Rob Landleye7c43b62006-03-01 16:39:45 +00003666 ext2fs_free_block_bitmap(ctx->block_ea_map);
3667 ctx->block_ea_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003668
3669 if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
3670 ext2fs_block_bitmap save_bmap;
3671
3672 save_bmap = fs->block_map;
3673 fs->block_map = ctx->block_found_map;
3674 clear_problem_context(&pctx);
3675 pctx.errcode = ext2fs_create_resize_inode(fs);
3676 if (pctx.errcode) {
3677 fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
3678 /* Should never get here */
3679 ctx->flags |= E2F_FLAG_ABORT;
3680 return;
3681 }
Mike Frysinger874af852006-03-08 07:03:27 +00003682 e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
3683 "recreate inode");
3684 inode->i_mtime = time(0);
3685 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
3686 "recreate inode");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003687 fs->block_map = save_bmap;
3688 ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
3689 }
3690
3691 if (ctx->flags & E2F_FLAG_RESTART) {
3692 /*
3693 * Only the master copy of the superblock and block
3694 * group descriptors are going to be written during a
3695 * restart, so set the superblock to be used to be the
3696 * master superblock.
3697 */
3698 ctx->use_superblock = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00003699 unwind_pass1();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003700 goto endit;
3701 }
3702
3703 if (ctx->block_dup_map) {
3704 if (ctx->options & E2F_OPT_PREEN) {
3705 clear_problem_context(&pctx);
3706 fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
3707 }
3708 e2fsck_pass1_dupblocks(ctx, block_buf);
3709 }
3710 ext2fs_free_mem(&inodes_to_process);
3711endit:
3712 e2fsck_use_inode_shortcuts(ctx, 0);
3713
3714 ext2fs_free_mem(&block_buf);
3715 ext2fs_free_mem(&inode);
3716
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003717}
3718
3719/*
3720 * When the inode_scan routines call this callback at the end of the
3721 * glock group, call process_inodes.
3722 */
3723static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00003724 ext2_inode_scan scan FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003725 dgrp_t group, void * priv_data)
3726{
3727 struct scan_callback_struct *scan_struct;
3728 e2fsck_t ctx;
3729
3730 scan_struct = (struct scan_callback_struct *) priv_data;
3731 ctx = scan_struct->ctx;
3732
3733 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
3734
3735 if (ctx->progress)
3736 if ((ctx->progress)(ctx, 1, group+1,
3737 ctx->fs->group_desc_count))
3738 return EXT2_ET_CANCEL_REQUESTED;
3739
3740 return 0;
3741}
3742
3743/*
3744 * Process the inodes in the "inodes to process" list.
3745 */
3746static void process_inodes(e2fsck_t ctx, char *block_buf)
3747{
3748 int i;
3749 struct ext2_inode *old_stashed_inode;
3750 ext2_ino_t old_stashed_ino;
3751 const char *old_operation;
3752 char buf[80];
3753 struct problem_context pctx;
3754
Rob Landley3e72c592006-04-06 22:49:04 +00003755 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003756 if (process_inode_count == 0)
3757 return;
3758 old_operation = ehandler_operation(0);
3759 old_stashed_inode = ctx->stashed_inode;
3760 old_stashed_ino = ctx->stashed_ino;
3761 qsort(inodes_to_process, process_inode_count,
3762 sizeof(struct process_inode_block), process_inode_cmp);
3763 clear_problem_context(&pctx);
3764 for (i=0; i < process_inode_count; i++) {
3765 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
3766 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003767 sprintf(buf, _("reading indirect blocks of inode %u"),
3768 pctx.ino);
3769 ehandler_operation(buf);
3770 check_blocks(ctx, &pctx, block_buf);
3771 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3772 break;
3773 }
3774 ctx->stashed_inode = old_stashed_inode;
3775 ctx->stashed_ino = old_stashed_ino;
3776 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00003777 /* end process inodes */
3778
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003779 ehandler_operation(old_operation);
3780}
3781
Rob Landley7c94bed2006-05-03 21:58:45 +00003782static int process_inode_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003783{
3784 const struct process_inode_block *ib_a =
3785 (const struct process_inode_block *) a;
3786 const struct process_inode_block *ib_b =
3787 (const struct process_inode_block *) b;
3788 int ret;
3789
3790 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
3791 ib_b->inode.i_block[EXT2_IND_BLOCK]);
3792 if (ret == 0)
3793 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
3794 return ret;
3795}
3796
3797/*
3798 * Mark an inode as being bad in some what
3799 */
3800static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
3801{
3802 struct problem_context pctx;
3803
3804 if (!ctx->inode_bad_map) {
3805 clear_problem_context(&pctx);
3806
3807 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3808 _("bad inode map"), &ctx->inode_bad_map);
3809 if (pctx.errcode) {
3810 pctx.num = 3;
3811 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3812 /* Should never get here */
3813 ctx->flags |= E2F_FLAG_ABORT;
3814 return;
3815 }
3816 }
3817 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
3818}
3819
3820
3821/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003822 * This procedure will allocate the inode imagic table
3823 */
3824static void alloc_imagic_map(e2fsck_t ctx)
3825{
3826 struct problem_context pctx;
3827
3828 clear_problem_context(&pctx);
3829 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3830 _("imagic inode map"),
3831 &ctx->inode_imagic_map);
3832 if (pctx.errcode) {
3833 pctx.num = 5;
3834 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3835 /* Should never get here */
3836 ctx->flags |= E2F_FLAG_ABORT;
3837 return;
3838 }
3839}
3840
3841/*
3842 * Marks a block as in use, setting the dup_map if it's been set
3843 * already. Called by process_block and process_bad_block.
3844 *
3845 * WARNING: Assumes checks have already been done to make sure block
3846 * is valid. This is true in both process_block and process_bad_block.
3847 */
Rob Landley7c94bed2006-05-03 21:58:45 +00003848static void mark_block_used(e2fsck_t ctx, blk_t block)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003849{
3850 struct problem_context pctx;
3851
3852 clear_problem_context(&pctx);
3853
3854 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
3855 if (!ctx->block_dup_map) {
3856 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
3857 _("multiply claimed block map"),
3858 &ctx->block_dup_map);
3859 if (pctx.errcode) {
3860 pctx.num = 3;
3861 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
3862 &pctx);
3863 /* Should never get here */
3864 ctx->flags |= E2F_FLAG_ABORT;
3865 return;
3866 }
3867 }
3868 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
3869 } else {
3870 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
3871 }
3872}
3873
3874/*
3875 * Adjust the extended attribute block's reference counts at the end
3876 * of pass 1, either by subtracting out references for EA blocks that
3877 * are still referenced in ctx->refcount, or by adding references for
3878 * EA blocks that had extra references as accounted for in
3879 * ctx->refcount_extra.
3880 */
3881static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3882 char *block_buf, int adjust_sign)
3883{
3884 struct ext2_ext_attr_header *header;
3885 struct problem_context pctx;
3886 ext2_filsys fs = ctx->fs;
3887 blk_t blk;
3888 __u32 should_be;
3889 int count;
3890
3891 clear_problem_context(&pctx);
3892
3893 ea_refcount_intr_begin(refcount);
3894 while (1) {
3895 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
3896 break;
3897 pctx.blk = blk;
3898 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3899 if (pctx.errcode) {
3900 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
3901 return;
3902 }
3903 header = (struct ext2_ext_attr_header *) block_buf;
3904 pctx.blkcount = header->h_refcount;
3905 should_be = header->h_refcount + adjust_sign * count;
3906 pctx.num = should_be;
3907 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
3908 header->h_refcount = should_be;
3909 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
3910 block_buf);
3911 if (pctx.errcode) {
3912 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
3913 continue;
3914 }
3915 }
3916 }
3917}
3918
3919/*
3920 * Handle processing the extended attribute blocks
3921 */
3922static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
3923 char *block_buf)
3924{
3925 ext2_filsys fs = ctx->fs;
3926 ext2_ino_t ino = pctx->ino;
3927 struct ext2_inode *inode = pctx->inode;
3928 blk_t blk;
3929 char * end;
3930 struct ext2_ext_attr_header *header;
3931 struct ext2_ext_attr_entry *entry;
3932 int count;
3933 region_t region;
3934
3935 blk = inode->i_file_acl;
3936 if (blk == 0)
3937 return 0;
3938
3939 /*
3940 * If the Extended attribute flag isn't set, then a non-zero
3941 * file acl means that the inode is corrupted.
3942 *
3943 * Or if the extended attribute block is an invalid block,
3944 * then the inode is also corrupted.
3945 */
3946 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
3947 (blk < fs->super->s_first_data_block) ||
3948 (blk >= fs->super->s_blocks_count)) {
3949 mark_inode_bad(ctx, ino);
3950 return 0;
3951 }
3952
3953 /* If ea bitmap hasn't been allocated, create it */
3954 if (!ctx->block_ea_map) {
3955 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
3956 _("ext attr block map"),
3957 &ctx->block_ea_map);
3958 if (pctx->errcode) {
3959 pctx->num = 2;
3960 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
3961 ctx->flags |= E2F_FLAG_ABORT;
3962 return 0;
3963 }
3964 }
3965
3966 /* Create the EA refcount structure if necessary */
3967 if (!ctx->refcount) {
3968 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
3969 if (pctx->errcode) {
3970 pctx->num = 1;
3971 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3972 ctx->flags |= E2F_FLAG_ABORT;
3973 return 0;
3974 }
3975 }
3976
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003977 /* Have we seen this EA block before? */
3978 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
3979 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
3980 return 1;
3981 /* Ooops, this EA was referenced more than it stated */
3982 if (!ctx->refcount_extra) {
3983 pctx->errcode = ea_refcount_create(0,
3984 &ctx->refcount_extra);
3985 if (pctx->errcode) {
3986 pctx->num = 2;
3987 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3988 ctx->flags |= E2F_FLAG_ABORT;
3989 return 0;
3990 }
3991 }
3992 ea_refcount_increment(ctx->refcount_extra, blk, 0);
3993 return 1;
3994 }
3995
3996 /*
3997 * OK, we haven't seen this EA block yet. So we need to
3998 * validate it
3999 */
4000 pctx->blk = blk;
4001 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4002 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
4003 goto clear_extattr;
4004 header = (struct ext2_ext_attr_header *) block_buf;
4005 pctx->blk = inode->i_file_acl;
4006 if (((ctx->ext_attr_ver == 1) &&
4007 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
4008 ((ctx->ext_attr_ver == 2) &&
4009 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
4010 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
4011 goto clear_extattr;
4012 }
4013
4014 if (header->h_blocks != 1) {
4015 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
4016 goto clear_extattr;
4017 }
4018
4019 region = region_create(0, fs->blocksize);
4020 if (!region) {
4021 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
4022 ctx->flags |= E2F_FLAG_ABORT;
4023 return 0;
4024 }
4025 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
4026 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4027 goto clear_extattr;
4028 }
4029
4030 entry = (struct ext2_ext_attr_entry *)(header+1);
4031 end = block_buf + fs->blocksize;
4032 while ((char *)entry < end && *(__u32 *)entry) {
4033 if (region_allocate(region, (char *)entry - (char *)header,
4034 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
4035 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4036 goto clear_extattr;
4037 }
4038 if ((ctx->ext_attr_ver == 1 &&
4039 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
4040 (ctx->ext_attr_ver == 2 &&
4041 entry->e_name_index == 0)) {
4042 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
4043 goto clear_extattr;
4044 }
4045 if (entry->e_value_block != 0) {
4046 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
4047 goto clear_extattr;
4048 }
4049 if (entry->e_value_size &&
4050 region_allocate(region, entry->e_value_offs,
4051 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
4052 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4053 goto clear_extattr;
4054 }
4055 entry = EXT2_EXT_ATTR_NEXT(entry);
4056 }
4057 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
4058 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4059 goto clear_extattr;
4060 }
4061 region_free(region);
4062
4063 count = header->h_refcount - 1;
4064 if (count)
4065 ea_refcount_store(ctx->refcount, blk, count);
4066 mark_block_used(ctx, blk);
4067 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
4068
4069 return 1;
4070
4071clear_extattr:
4072 inode->i_file_acl = 0;
4073 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
4074 return 0;
4075}
4076
4077/* Returns 1 if bad htree, 0 if OK */
4078static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004079 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004080 struct ext2_inode *inode,
4081 char *block_buf)
4082{
4083 struct ext2_dx_root_info *root;
4084 ext2_filsys fs = ctx->fs;
4085 errcode_t retval;
4086 blk_t blk;
4087
4088 if ((!LINUX_S_ISDIR(inode->i_mode) &&
4089 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
4090 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4091 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
4092 return 1;
4093
4094 blk = inode->i_block[0];
4095 if (((blk == 0) ||
4096 (blk < fs->super->s_first_data_block) ||
4097 (blk >= fs->super->s_blocks_count)) &&
4098 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4099 return 1;
4100
4101 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
4102 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4103 return 1;
4104
4105 /* XXX should check that beginning matches a directory */
4106 root = (struct ext2_dx_root_info *) (block_buf + 24);
4107
4108 if ((root->reserved_zero || root->info_length < 8) &&
4109 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4110 return 1;
4111
4112 pctx->num = root->hash_version;
4113 if ((root->hash_version != EXT2_HASH_LEGACY) &&
4114 (root->hash_version != EXT2_HASH_HALF_MD4) &&
4115 (root->hash_version != EXT2_HASH_TEA) &&
4116 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
4117 return 1;
4118
4119 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
4120 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
4121 return 1;
4122
4123 pctx->num = root->indirect_levels;
4124 if ((root->indirect_levels > 1) &&
4125 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
4126 return 1;
4127
4128 return 0;
4129}
4130
4131/*
4132 * This subroutine is called on each inode to account for all of the
4133 * blocks used by that inode.
4134 */
4135static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
4136 char *block_buf)
4137{
4138 ext2_filsys fs = ctx->fs;
4139 struct process_block_struct_1 pb;
4140 ext2_ino_t ino = pctx->ino;
4141 struct ext2_inode *inode = pctx->inode;
4142 int bad_size = 0;
4143 int dirty_inode = 0;
4144 __u64 size;
4145
4146 pb.ino = ino;
4147 pb.num_blocks = 0;
4148 pb.last_block = -1;
4149 pb.num_illegal_blocks = 0;
4150 pb.suppress = 0; pb.clear = 0;
4151 pb.fragmented = 0;
4152 pb.compressed = 0;
4153 pb.previous_block = 0;
4154 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
4155 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
4156 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
4157 pb.inode = inode;
4158 pb.pctx = pctx;
4159 pb.ctx = ctx;
4160 pctx->ino = ino;
4161 pctx->errcode = 0;
4162
4163 if (inode->i_flags & EXT2_COMPRBLK_FL) {
4164 if (fs->super->s_feature_incompat &
4165 EXT2_FEATURE_INCOMPAT_COMPRESSION)
4166 pb.compressed = 1;
4167 else {
4168 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
4169 inode->i_flags &= ~EXT2_COMPRBLK_FL;
4170 dirty_inode++;
4171 }
4172 }
4173 }
4174
4175 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
4176 pb.num_blocks++;
4177
4178 if (ext2fs_inode_has_valid_blocks(inode))
4179 pctx->errcode = ext2fs_block_iterate2(fs, ino,
4180 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
4181 block_buf, process_block, &pb);
4182 end_problem_latch(ctx, PR_LATCH_BLOCK);
4183 end_problem_latch(ctx, PR_LATCH_TOOBIG);
4184 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4185 goto out;
4186 if (pctx->errcode)
4187 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
4188
4189 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
4190 ctx->fs_fragmented++;
4191
4192 if (pb.clear) {
4193 inode->i_links_count = 0;
4194 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4195 inode->i_dtime = time(0);
4196 dirty_inode++;
4197 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4198 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4199 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4200 /*
4201 * The inode was probably partially accounted for
4202 * before processing was aborted, so we need to
4203 * restart the pass 1 scan.
4204 */
4205 ctx->flags |= E2F_FLAG_RESTART;
4206 goto out;
4207 }
4208
4209 if (inode->i_flags & EXT2_INDEX_FL) {
4210 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
4211 inode->i_flags &= ~EXT2_INDEX_FL;
4212 dirty_inode++;
4213 } else {
4214#ifdef ENABLE_HTREE
4215 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
4216#endif
4217 }
4218 }
4219 if (ctx->dirs_to_hash && pb.is_dir &&
4220 !(inode->i_flags & EXT2_INDEX_FL) &&
4221 ((inode->i_size / fs->blocksize) >= 3))
4222 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
4223
4224 if (!pb.num_blocks && pb.is_dir) {
4225 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
4226 inode->i_links_count = 0;
4227 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4228 inode->i_dtime = time(0);
4229 dirty_inode++;
4230 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4231 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4232 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4233 ctx->fs_directory_count--;
4234 goto out;
4235 }
4236 }
4237
4238 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00004239
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004240 if (pb.is_dir) {
4241 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
4242 if (nblock > (pb.last_block + 1))
4243 bad_size = 1;
4244 else if (nblock < (pb.last_block + 1)) {
4245 if (((pb.last_block + 1) - nblock) >
4246 fs->super->s_prealloc_dir_blocks)
4247 bad_size = 2;
4248 }
4249 } else {
4250 size = EXT2_I_SIZE(inode);
4251 if ((pb.last_block >= 0) &&
4252 (size < (__u64) pb.last_block * fs->blocksize))
4253 bad_size = 3;
4254 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
4255 bad_size = 4;
4256 }
4257 /* i_size for symlinks is checked elsewhere */
4258 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
4259 pctx->num = (pb.last_block+1) * fs->blocksize;
4260 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
4261 inode->i_size = pctx->num;
4262 if (!LINUX_S_ISDIR(inode->i_mode))
4263 inode->i_size_high = pctx->num >> 32;
4264 dirty_inode++;
4265 }
4266 pctx->num = 0;
4267 }
4268 if (LINUX_S_ISREG(inode->i_mode) &&
4269 (inode->i_size_high || inode->i_size & 0x80000000UL))
4270 ctx->large_files++;
4271 if (pb.num_blocks != inode->i_blocks) {
4272 pctx->num = pb.num_blocks;
4273 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
4274 inode->i_blocks = pb.num_blocks;
4275 dirty_inode++;
4276 }
4277 pctx->num = 0;
4278 }
4279out:
4280 if (dirty_inode)
4281 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
4282}
4283
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004284
4285/*
4286 * This is a helper function for check_blocks().
4287 */
4288static int process_block(ext2_filsys fs,
4289 blk_t *block_nr,
4290 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004291 blk_t ref_block FSCK_ATTR((unused)),
4292 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004293 void *priv_data)
4294{
4295 struct process_block_struct_1 *p;
4296 struct problem_context *pctx;
4297 blk_t blk = *block_nr;
4298 int ret_code = 0;
4299 int problem = 0;
4300 e2fsck_t ctx;
4301
4302 p = (struct process_block_struct_1 *) priv_data;
4303 pctx = p->pctx;
4304 ctx = p->ctx;
4305
4306 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
4307 /* todo: Check that the comprblk_fl is high, that the
4308 blkaddr pattern looks right (all non-holes up to
4309 first EXT2FS_COMPRESSED_BLKADDR, then all
4310 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
4311 that the feature_incompat bit is high, and that the
4312 inode is a regular file. If we're doing a "full
4313 check" (a concept introduced to e2fsck by e2compr,
4314 meaning that we look at data blocks as well as
4315 metadata) then call some library routine that
4316 checks the compressed data. I'll have to think
4317 about this, because one particularly important
4318 problem to be able to fix is to recalculate the
4319 cluster size if necessary. I think that perhaps
4320 we'd better do most/all e2compr-specific checks
4321 separately, after the non-e2compr checks. If not
4322 doing a full check, it may be useful to test that
4323 the personality is linux; e.g. if it isn't then
4324 perhaps this really is just an illegal block. */
4325 return 0;
4326 }
4327
4328 if (blk == 0) {
4329 if (p->is_dir == 0) {
4330 /*
4331 * Should never happen, since only directories
4332 * get called with BLOCK_FLAG_HOLE
4333 */
4334#if DEBUG_E2FSCK
4335 printf("process_block() called with blk == 0, "
4336 "blockcnt=%d, inode %lu???\n",
4337 blockcnt, p->ino);
4338#endif
4339 return 0;
4340 }
4341 if (blockcnt < 0)
4342 return 0;
4343 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004344 goto mark_dir;
4345 }
4346 return 0;
4347 }
4348
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004349 /*
4350 * Simplistic fragmentation check. We merely require that the
4351 * file be contiguous. (Which can never be true for really
4352 * big files that are greater than a block group.)
4353 */
4354 if (!HOLE_BLKADDR(p->previous_block)) {
4355 if (p->previous_block+1 != blk)
4356 p->fragmented = 1;
4357 }
4358 p->previous_block = blk;
4359
4360 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
4361 problem = PR_1_TOOBIG_DIR;
4362 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
4363 problem = PR_1_TOOBIG_REG;
4364 if (!p->is_dir && !p->is_reg && blockcnt > 0)
4365 problem = PR_1_TOOBIG_SYMLINK;
4366
4367 if (blk < fs->super->s_first_data_block ||
4368 blk >= fs->super->s_blocks_count)
4369 problem = PR_1_ILLEGAL_BLOCK_NUM;
4370
4371 if (problem) {
4372 p->num_illegal_blocks++;
4373 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
4374 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
4375 p->clear = 1;
4376 return BLOCK_ABORT;
4377 }
4378 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
4379 p->suppress = 1;
4380 set_latch_flags(PR_LATCH_BLOCK,
4381 PRL_SUPPRESS, 0);
4382 }
4383 }
4384 pctx->blk = blk;
4385 pctx->blkcount = blockcnt;
4386 if (fix_problem(ctx, problem, pctx)) {
4387 blk = *block_nr = 0;
4388 ret_code = BLOCK_CHANGED;
4389 goto mark_dir;
4390 } else
4391 return 0;
4392 }
4393
4394 if (p->ino == EXT2_RESIZE_INO) {
4395 /*
4396 * The resize inode has already be sanity checked
4397 * during pass #0 (the superblock checks). All we
4398 * have to do is mark the double indirect block as
4399 * being in use; all of the other blocks are handled
4400 * by mark_table_blocks()).
4401 */
4402 if (blockcnt == BLOCK_COUNT_DIND)
4403 mark_block_used(ctx, blk);
4404 } else
4405 mark_block_used(ctx, blk);
4406 p->num_blocks++;
4407 if (blockcnt >= 0)
4408 p->last_block = blockcnt;
4409mark_dir:
4410 if (p->is_dir && (blockcnt >= 0)) {
4411 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
4412 blk, blockcnt);
4413 if (pctx->errcode) {
4414 pctx->blk = blk;
4415 pctx->num = blockcnt;
4416 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
4417 /* Should never get here */
4418 ctx->flags |= E2F_FLAG_ABORT;
4419 return BLOCK_ABORT;
4420 }
4421 }
4422 return ret_code;
4423}
4424
Rob Landleyd8f66012006-05-05 17:29:09 +00004425static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004426 blk_t *block_nr,
4427 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004428 blk_t ref_block FSCK_ATTR((unused)),
4429 int ref_offset FSCK_ATTR((unused)),
Rob Landleyd8f66012006-05-05 17:29:09 +00004430 void *priv_data EXT2FS_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004431{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004432 /*
4433 * Note: This function processes blocks for the bad blocks
4434 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
4435 */
4436
Rob Landleyd8f66012006-05-05 17:29:09 +00004437 printf("Unrecoverable Error: Found %lli bad blocks starting at block number: %u\n", blockcnt, *block_nr);
4438 return BLOCK_ERROR;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004439}
4440
4441/*
4442 * This routine gets called at the end of pass 1 if bad blocks are
4443 * detected in the superblock, group descriptors, inode_bitmaps, or
4444 * block bitmaps. At this point, all of the blocks have been mapped
4445 * out, so we can try to allocate new block(s) to replace the bad
4446 * blocks.
4447 */
4448static void handle_fs_bad_blocks(e2fsck_t ctx)
4449{
Rob Landleyd8f66012006-05-05 17:29:09 +00004450 printf("Bad blocks detected on your filesystem\n"
4451 "You should get your data off as the device will soon die\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004452}
4453
4454/*
4455 * This routine marks all blocks which are used by the superblock,
4456 * group descriptors, inode bitmaps, and block bitmaps.
4457 */
4458static void mark_table_blocks(e2fsck_t ctx)
4459{
4460 ext2_filsys fs = ctx->fs;
4461 blk_t block, b;
4462 dgrp_t i;
4463 int j;
4464 struct problem_context pctx;
4465
4466 clear_problem_context(&pctx);
4467
4468 block = fs->super->s_first_data_block;
4469 for (i = 0; i < fs->group_desc_count; i++) {
4470 pctx.group = i;
4471
4472 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
4473
4474 /*
4475 * Mark the blocks used for the inode table
4476 */
4477 if (fs->group_desc[i].bg_inode_table) {
4478 for (j = 0, b = fs->group_desc[i].bg_inode_table;
4479 j < fs->inode_blocks_per_group;
4480 j++, b++) {
4481 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4482 b)) {
4483 pctx.blk = b;
4484 if (fix_problem(ctx,
4485 PR_1_ITABLE_CONFLICT, &pctx)) {
4486 ctx->invalid_inode_table_flag[i]++;
4487 ctx->invalid_bitmaps++;
4488 }
4489 } else {
4490 ext2fs_mark_block_bitmap(ctx->block_found_map,
4491 b);
4492 }
4493 }
4494 }
4495
4496 /*
4497 * Mark block used for the block bitmap
4498 */
4499 if (fs->group_desc[i].bg_block_bitmap) {
4500 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4501 fs->group_desc[i].bg_block_bitmap)) {
4502 pctx.blk = fs->group_desc[i].bg_block_bitmap;
4503 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
4504 ctx->invalid_block_bitmap_flag[i]++;
4505 ctx->invalid_bitmaps++;
4506 }
4507 } else {
4508 ext2fs_mark_block_bitmap(ctx->block_found_map,
4509 fs->group_desc[i].bg_block_bitmap);
4510 }
4511
4512 }
4513 /*
4514 * Mark block used for the inode bitmap
4515 */
4516 if (fs->group_desc[i].bg_inode_bitmap) {
4517 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4518 fs->group_desc[i].bg_inode_bitmap)) {
4519 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
4520 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
4521 ctx->invalid_inode_bitmap_flag[i]++;
4522 ctx->invalid_bitmaps++;
4523 }
4524 } else {
4525 ext2fs_mark_block_bitmap(ctx->block_found_map,
4526 fs->group_desc[i].bg_inode_bitmap);
4527 }
4528 }
4529 block += fs->super->s_blocks_per_group;
4530 }
4531}
4532
4533/*
4534 * Thes subroutines short circuits ext2fs_get_blocks and
4535 * ext2fs_check_directory; we use them since we already have the inode
4536 * structure, so there's no point in letting the ext2fs library read
4537 * the inode again.
4538 */
4539static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
4540 blk_t *blocks)
4541{
4542 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4543 int i;
4544
4545 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4546 return EXT2_ET_CALLBACK_NOTHANDLED;
4547
4548 for (i=0; i < EXT2_N_BLOCKS; i++)
4549 blocks[i] = ctx->stashed_inode->i_block[i];
4550 return 0;
4551}
4552
4553static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
4554 struct ext2_inode *inode)
4555{
4556 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4557
4558 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4559 return EXT2_ET_CALLBACK_NOTHANDLED;
4560 *inode = *ctx->stashed_inode;
4561 return 0;
4562}
4563
4564static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
4565 struct ext2_inode *inode)
4566{
4567 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4568
4569 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
4570 *ctx->stashed_inode = *inode;
4571 return EXT2_ET_CALLBACK_NOTHANDLED;
4572}
4573
4574static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
4575{
4576 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4577
4578 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4579 return EXT2_ET_CALLBACK_NOTHANDLED;
4580
4581 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
4582 return EXT2_ET_NO_DIRECTORY;
4583 return 0;
4584}
4585
4586void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
4587{
4588 ext2_filsys fs = ctx->fs;
4589
4590 if (bool) {
4591 fs->get_blocks = pass1_get_blocks;
4592 fs->check_directory = pass1_check_directory;
4593 fs->read_inode = pass1_read_inode;
4594 fs->write_inode = pass1_write_inode;
4595 ctx->stashed_ino = 0;
4596 } else {
4597 fs->get_blocks = 0;
4598 fs->check_directory = 0;
4599 fs->read_inode = 0;
4600 fs->write_inode = 0;
4601 }
4602}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004603
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004604/*
4605 * pass1b.c --- Pass #1b of e2fsck
4606 *
4607 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
4608 * only invoked if pass 1 discovered blocks which are in use by more
4609 * than one inode.
4610 *
4611 * Pass1B scans the data blocks of all the inodes again, generating a
4612 * complete list of duplicate blocks and which inodes have claimed
4613 * them.
4614 *
4615 * Pass1C does a tree-traversal of the filesystem, to determine the
4616 * parent directories of these inodes. This step is necessary so that
4617 * e2fsck can print out the pathnames of affected inodes.
4618 *
4619 * Pass1D is a reconciliation pass. For each inode with duplicate
4620 * blocks, the user is prompted if s/he would like to clone the file
4621 * (so that the file gets a fresh copy of the duplicated blocks) or
4622 * simply to delete the file.
4623 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004624 */
4625
4626
4627/* Needed for architectures where sizeof(int) != sizeof(void *) */
4628#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
4629#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
4630
4631/* Define an extension to the ext2 library's block count information */
4632#define BLOCK_COUNT_EXTATTR (-5)
4633
4634struct block_el {
4635 blk_t block;
4636 struct block_el *next;
4637};
4638
4639struct inode_el {
4640 ext2_ino_t inode;
4641 struct inode_el *next;
4642};
4643
4644struct dup_block {
4645 int num_bad;
4646 struct inode_el *inode_list;
4647};
4648
4649/*
4650 * This structure stores information about a particular inode which
4651 * is sharing blocks with other inodes. This information is collected
4652 * to display to the user, so that the user knows what files he or she
4653 * is dealing with, when trying to decide how to resolve the conflict
4654 * of multiply-claimed blocks.
4655 */
4656struct dup_inode {
4657 ext2_ino_t dir;
4658 int num_dupblocks;
4659 struct ext2_inode inode;
4660 struct block_el *block_list;
4661};
4662
4663static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
4664 e2_blkcnt_t blockcnt, blk_t ref_blk,
4665 int ref_offset, void *priv_data);
4666static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
4667 struct dup_inode *dp, char *block_buf);
4668static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
4669 struct dup_inode *dp, char* block_buf);
4670static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
4671
4672static void pass1b(e2fsck_t ctx, char *block_buf);
4673static void pass1c(e2fsck_t ctx, char *block_buf);
4674static void pass1d(e2fsck_t ctx, char *block_buf);
4675
4676static int dup_inode_count = 0;
4677
4678static dict_t blk_dict, ino_dict;
4679
4680static ext2fs_inode_bitmap inode_dup_map;
4681
4682static int dict_int_cmp(const void *a, const void *b)
4683{
4684 intptr_t ia, ib;
4685
4686 ia = (intptr_t)a;
4687 ib = (intptr_t)b;
4688
4689 return (ia-ib);
4690}
4691
4692/*
4693 * Add a duplicate block record
4694 */
4695static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
4696 struct ext2_inode *inode)
4697{
4698 dnode_t *n;
4699 struct dup_block *db;
4700 struct dup_inode *di;
4701 struct block_el *blk_el;
4702 struct inode_el *ino_el;
4703
4704 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
4705 if (n)
4706 db = (struct dup_block *) dnode_get(n);
4707 else {
4708 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
4709 sizeof(struct dup_block), "duplicate block header");
4710 db->num_bad = 0;
4711 db->inode_list = 0;
4712 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
4713 }
4714 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
4715 sizeof(struct inode_el), "inode element");
4716 ino_el->inode = ino;
4717 ino_el->next = db->inode_list;
4718 db->inode_list = ino_el;
4719 db->num_bad++;
4720
4721 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
4722 if (n)
4723 di = (struct dup_inode *) dnode_get(n);
4724 else {
4725 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
4726 sizeof(struct dup_inode), "duplicate inode header");
4727 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
4728 di->num_dupblocks = 0;
4729 di->block_list = 0;
4730 di->inode = *inode;
4731 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
4732 }
4733 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
4734 sizeof(struct block_el), "block element");
4735 blk_el->block = blk;
4736 blk_el->next = di->block_list;
4737 di->block_list = blk_el;
4738 di->num_dupblocks++;
4739}
4740
4741/*
4742 * Free a duplicate inode record
4743 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004744static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004745{
4746 struct dup_inode *di;
4747 struct block_el *p, *next;
4748
4749 di = (struct dup_inode *) dnode_get(node);
4750 for (p = di->block_list; p; p = next) {
4751 next = p->next;
4752 free(p);
4753 }
4754 free(node);
4755}
4756
4757/*
4758 * Free a duplicate block record
4759 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004760static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004761{
4762 struct dup_block *db;
4763 struct inode_el *p, *next;
4764
4765 db = (struct dup_block *) dnode_get(node);
4766 for (p = db->inode_list; p; p = next) {
4767 next = p->next;
4768 free(p);
4769 }
4770 free(node);
4771}
4772
4773
4774/*
4775 * Main procedure for handling duplicate blocks
4776 */
4777void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
4778{
4779 ext2_filsys fs = ctx->fs;
4780 struct problem_context pctx;
4781
4782 clear_problem_context(&pctx);
4783
4784 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4785 _("multiply claimed inode map"), &inode_dup_map);
4786 if (pctx.errcode) {
4787 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
4788 ctx->flags |= E2F_FLAG_ABORT;
4789 return;
4790 }
4791
4792 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
4793 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004794 dict_set_allocator(&ino_dict, inode_dnode_free);
4795 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004796
4797 pass1b(ctx, block_buf);
4798 pass1c(ctx, block_buf);
4799 pass1d(ctx, block_buf);
4800
4801 /*
4802 * Time to free all of the accumulated data structures that we
4803 * don't need anymore.
4804 */
4805 dict_free_nodes(&ino_dict);
4806 dict_free_nodes(&blk_dict);
4807}
4808
4809/*
4810 * Scan the inodes looking for inodes that contain duplicate blocks.
4811 */
4812struct process_block_struct_1b {
4813 e2fsck_t ctx;
4814 ext2_ino_t ino;
4815 int dup_blocks;
4816 struct ext2_inode *inode;
4817 struct problem_context *pctx;
4818};
4819
4820static void pass1b(e2fsck_t ctx, char *block_buf)
4821{
4822 ext2_filsys fs = ctx->fs;
4823 ext2_ino_t ino;
4824 struct ext2_inode inode;
4825 ext2_inode_scan scan;
4826 struct process_block_struct_1b pb;
4827 struct problem_context pctx;
4828
4829 clear_problem_context(&pctx);
4830
4831 if (!(ctx->options & E2F_OPT_PREEN))
4832 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
4833 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4834 &scan);
4835 if (pctx.errcode) {
4836 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4837 ctx->flags |= E2F_FLAG_ABORT;
4838 return;
4839 }
4840 ctx->stashed_inode = &inode;
4841 pb.ctx = ctx;
4842 pb.pctx = &pctx;
4843 pctx.str = "pass1b";
4844 while (1) {
4845 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
4846 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
4847 continue;
4848 if (pctx.errcode) {
4849 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4850 ctx->flags |= E2F_FLAG_ABORT;
4851 return;
4852 }
4853 if (!ino)
4854 break;
4855 pctx.ino = ctx->stashed_ino = ino;
4856 if ((ino != EXT2_BAD_INO) &&
4857 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
4858 continue;
4859
4860 pb.ino = ino;
4861 pb.dup_blocks = 0;
4862 pb.inode = &inode;
4863
4864 if (ext2fs_inode_has_valid_blocks(&inode) ||
4865 (ino == EXT2_BAD_INO))
4866 pctx.errcode = ext2fs_block_iterate2(fs, ino,
4867 0, block_buf, process_pass1b_block, &pb);
4868 if (inode.i_file_acl)
4869 process_pass1b_block(fs, &inode.i_file_acl,
4870 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
4871 if (pb.dup_blocks) {
4872 end_problem_latch(ctx, PR_LATCH_DBLOCK);
4873 if (ino >= EXT2_FIRST_INODE(fs->super) ||
4874 ino == EXT2_ROOT_INO)
4875 dup_inode_count++;
4876 }
4877 if (pctx.errcode)
4878 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
4879 }
4880 ext2fs_close_inode_scan(scan);
4881 e2fsck_use_inode_shortcuts(ctx, 0);
4882}
4883
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004884static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004885 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004886 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
4887 blk_t ref_blk FSCK_ATTR((unused)),
4888 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004889 void *priv_data)
4890{
4891 struct process_block_struct_1b *p;
4892 e2fsck_t ctx;
4893
4894 if (HOLE_BLKADDR(*block_nr))
4895 return 0;
4896 p = (struct process_block_struct_1b *) priv_data;
4897 ctx = p->ctx;
4898
4899 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
4900 return 0;
4901
4902 /* OK, this is a duplicate block */
4903 if (p->ino != EXT2_BAD_INO) {
4904 p->pctx->blk = *block_nr;
4905 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
4906 }
4907 p->dup_blocks++;
4908 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
4909
4910 add_dupe(ctx, p->ino, *block_nr, p->inode);
4911
4912 return 0;
4913}
4914
4915/*
4916 * Pass 1c: Scan directories for inodes with duplicate blocks. This
4917 * is used so that we can print pathnames when prompting the user for
4918 * what to do.
4919 */
4920struct search_dir_struct {
4921 int count;
4922 ext2_ino_t first_inode;
4923 ext2_ino_t max_inode;
4924};
4925
4926static int search_dirent_proc(ext2_ino_t dir, int entry,
4927 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004928 int offset FSCK_ATTR((unused)),
4929 int blocksize FSCK_ATTR((unused)),
4930 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004931 void *priv_data)
4932{
4933 struct search_dir_struct *sd;
4934 struct dup_inode *p;
4935 dnode_t *n;
4936
4937 sd = (struct search_dir_struct *) priv_data;
4938
4939 if (dirent->inode > sd->max_inode)
4940 /* Should abort this inode, but not everything */
4941 return 0;
4942
4943 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
4944 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
4945 return 0;
4946
4947 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
4948 if (!n)
4949 return 0;
4950 p = (struct dup_inode *) dnode_get(n);
4951 p->dir = dir;
4952 sd->count--;
4953
4954 return(sd->count ? 0 : DIRENT_ABORT);
4955}
4956
4957
4958static void pass1c(e2fsck_t ctx, char *block_buf)
4959{
4960 ext2_filsys fs = ctx->fs;
4961 struct search_dir_struct sd;
4962 struct problem_context pctx;
4963
4964 clear_problem_context(&pctx);
4965
4966 if (!(ctx->options & E2F_OPT_PREEN))
4967 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
4968
4969 /*
4970 * Search through all directories to translate inodes to names
4971 * (by searching for the containing directory for that inode.)
4972 */
4973 sd.count = dup_inode_count;
4974 sd.first_inode = EXT2_FIRST_INODE(fs->super);
4975 sd.max_inode = fs->super->s_inodes_count;
4976 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
4977 search_dirent_proc, &sd);
4978}
4979
4980static void pass1d(e2fsck_t ctx, char *block_buf)
4981{
4982 ext2_filsys fs = ctx->fs;
4983 struct dup_inode *p, *t;
4984 struct dup_block *q;
4985 ext2_ino_t *shared, ino;
4986 int shared_len;
4987 int i;
4988 int file_ok;
4989 int meta_data = 0;
4990 struct problem_context pctx;
4991 dnode_t *n, *m;
4992 struct block_el *s;
4993 struct inode_el *r;
4994
4995 clear_problem_context(&pctx);
4996
4997 if (!(ctx->options & E2F_OPT_PREEN))
4998 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
4999 e2fsck_read_bitmaps(ctx);
5000
5001 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
5002 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
5003 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
5004 sizeof(ext2_ino_t) * dict_count(&ino_dict),
5005 "Shared inode list");
5006 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
5007 p = (struct dup_inode *) dnode_get(n);
5008 shared_len = 0;
5009 file_ok = 1;
5010 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00005011 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005012 continue;
5013
5014 /*
5015 * Find all of the inodes which share blocks with this
5016 * one. First we find all of the duplicate blocks
5017 * belonging to this inode, and then search each block
5018 * get the list of inodes, and merge them together.
5019 */
5020 for (s = p->block_list; s; s = s->next) {
5021 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
5022 if (!m)
5023 continue; /* Should never happen... */
5024 q = (struct dup_block *) dnode_get(m);
5025 if (q->num_bad > 1)
5026 file_ok = 0;
5027 if (check_if_fs_block(ctx, s->block)) {
5028 file_ok = 0;
5029 meta_data = 1;
5030 }
5031
5032 /*
5033 * Add all inodes used by this block to the
5034 * shared[] --- which is a unique list, so
5035 * if an inode is already in shared[], don't
5036 * add it again.
5037 */
5038 for (r = q->inode_list; r; r = r->next) {
5039 if (r->inode == ino)
5040 continue;
5041 for (i = 0; i < shared_len; i++)
5042 if (shared[i] == r->inode)
5043 break;
5044 if (i == shared_len) {
5045 shared[shared_len++] = r->inode;
5046 }
5047 }
5048 }
5049
5050 /*
5051 * Report the inode that we are working on
5052 */
5053 pctx.inode = &p->inode;
5054 pctx.ino = ino;
5055 pctx.dir = p->dir;
5056 pctx.blkcount = p->num_dupblocks;
5057 pctx.num = meta_data ? shared_len+1 : shared_len;
5058 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
5059 pctx.blkcount = 0;
5060 pctx.num = 0;
5061
5062 if (meta_data)
5063 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
5064
5065 for (i = 0; i < shared_len; i++) {
5066 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
5067 if (!m)
5068 continue; /* should never happen */
5069 t = (struct dup_inode *) dnode_get(m);
5070 /*
5071 * Report the inode that we are sharing with
5072 */
5073 pctx.inode = &t->inode;
5074 pctx.ino = shared[i];
5075 pctx.dir = t->dir;
5076 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
5077 }
5078 if (file_ok) {
5079 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
5080 continue;
5081 }
5082 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
5083 pctx.errcode = clone_file(ctx, ino, p, block_buf);
5084 if (pctx.errcode)
5085 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
5086 else
5087 continue;
5088 }
5089 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
5090 delete_file(ctx, ino, p, block_buf);
5091 else
5092 ext2fs_unmark_valid(fs);
5093 }
5094 ext2fs_free_mem(&shared);
5095}
5096
5097/*
5098 * Drop the refcount on the dup_block structure, and clear the entry
5099 * in the block_dup_map if appropriate.
5100 */
5101static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
5102{
5103 p->num_bad--;
5104 if (p->num_bad <= 0 ||
5105 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
5106 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
5107}
5108
5109static int delete_file_block(ext2_filsys fs,
5110 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005111 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5112 blk_t ref_block FSCK_ATTR((unused)),
5113 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005114 void *priv_data)
5115{
5116 struct process_block_struct_1b *pb;
5117 struct dup_block *p;
5118 dnode_t *n;
5119 e2fsck_t ctx;
5120
5121 pb = (struct process_block_struct_1b *) priv_data;
5122 ctx = pb->ctx;
5123
5124 if (HOLE_BLKADDR(*block_nr))
5125 return 0;
5126
5127 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5128 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5129 if (n) {
5130 p = (struct dup_block *) dnode_get(n);
5131 decrement_badcount(ctx, *block_nr, p);
5132 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005133 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005134 *block_nr);
5135 } else {
5136 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
5137 ext2fs_block_alloc_stats(fs, *block_nr, -1);
5138 }
5139
5140 return 0;
5141}
5142
5143static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5144 struct dup_inode *dp, char* block_buf)
5145{
5146 ext2_filsys fs = ctx->fs;
5147 struct process_block_struct_1b pb;
5148 struct ext2_inode inode;
5149 struct problem_context pctx;
5150 unsigned int count;
5151
5152 clear_problem_context(&pctx);
5153 pctx.ino = pb.ino = ino;
5154 pb.dup_blocks = dp->num_dupblocks;
5155 pb.ctx = ctx;
5156 pctx.str = "delete_file";
5157
5158 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5159 if (ext2fs_inode_has_valid_blocks(&inode))
5160 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5161 delete_file_block, &pb);
5162 if (pctx.errcode)
5163 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5164 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5165 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5166 if (ctx->inode_bad_map)
5167 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
5168 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
5169
5170 /* Inode may have changed by block_iterate, so reread it */
5171 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5172 inode.i_links_count = 0;
5173 inode.i_dtime = time(0);
5174 if (inode.i_file_acl &&
5175 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
5176 count = 1;
5177 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
5178 block_buf, -1, &count);
5179 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
5180 pctx.errcode = 0;
5181 count = 1;
5182 }
5183 if (pctx.errcode) {
5184 pctx.blk = inode.i_file_acl;
5185 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
5186 }
5187 /*
5188 * If the count is zero, then arrange to have the
5189 * block deleted. If the block is in the block_dup_map,
5190 * also call delete_file_block since it will take care
5191 * of keeping the accounting straight.
5192 */
5193 if ((count == 0) ||
5194 ext2fs_test_block_bitmap(ctx->block_dup_map,
5195 inode.i_file_acl))
5196 delete_file_block(fs, &inode.i_file_acl,
5197 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5198 }
5199 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
5200}
5201
5202struct clone_struct {
5203 errcode_t errcode;
5204 ext2_ino_t dir;
5205 char *buf;
5206 e2fsck_t ctx;
5207};
5208
5209static int clone_file_block(ext2_filsys fs,
5210 blk_t *block_nr,
5211 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005212 blk_t ref_block FSCK_ATTR((unused)),
5213 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005214 void *priv_data)
5215{
5216 struct dup_block *p;
5217 blk_t new_block;
5218 errcode_t retval;
5219 struct clone_struct *cs = (struct clone_struct *) priv_data;
5220 dnode_t *n;
5221 e2fsck_t ctx;
5222
5223 ctx = cs->ctx;
5224
5225 if (HOLE_BLKADDR(*block_nr))
5226 return 0;
5227
5228 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5229 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5230 if (n) {
5231 p = (struct dup_block *) dnode_get(n);
5232 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
5233 &new_block);
5234 if (retval) {
5235 cs->errcode = retval;
5236 return BLOCK_ABORT;
5237 }
5238 if (cs->dir && (blockcnt >= 0)) {
5239 retval = ext2fs_set_dir_block(fs->dblist,
5240 cs->dir, new_block, blockcnt);
5241 if (retval) {
5242 cs->errcode = retval;
5243 return BLOCK_ABORT;
5244 }
5245 }
Rob Landley3e72c592006-04-06 22:49:04 +00005246
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005247 retval = io_channel_read_blk(fs->io, *block_nr, 1,
5248 cs->buf);
5249 if (retval) {
5250 cs->errcode = retval;
5251 return BLOCK_ABORT;
5252 }
5253 retval = io_channel_write_blk(fs->io, new_block, 1,
5254 cs->buf);
5255 if (retval) {
5256 cs->errcode = retval;
5257 return BLOCK_ABORT;
5258 }
5259 decrement_badcount(ctx, *block_nr, p);
5260 *block_nr = new_block;
5261 ext2fs_mark_block_bitmap(ctx->block_found_map,
5262 new_block);
5263 ext2fs_mark_block_bitmap(fs->block_map, new_block);
5264 return BLOCK_CHANGED;
5265 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005266 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005267 *block_nr);
5268 }
5269 return 0;
5270}
5271
5272static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5273 struct dup_inode *dp, char* block_buf)
5274{
5275 ext2_filsys fs = ctx->fs;
5276 errcode_t retval;
5277 struct clone_struct cs;
5278 struct problem_context pctx;
5279 blk_t blk;
5280 dnode_t *n;
5281 struct inode_el *ino_el;
5282 struct dup_block *db;
5283 struct dup_inode *di;
5284
5285 clear_problem_context(&pctx);
5286 cs.errcode = 0;
5287 cs.dir = 0;
5288 cs.ctx = ctx;
5289 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
5290 if (retval)
5291 return retval;
5292
5293 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
5294 cs.dir = ino;
5295
5296 pctx.ino = ino;
5297 pctx.str = "clone_file";
5298 if (ext2fs_inode_has_valid_blocks(&dp->inode))
5299 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5300 clone_file_block, &cs);
5301 ext2fs_mark_bb_dirty(fs);
5302 if (pctx.errcode) {
5303 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5304 retval = pctx.errcode;
5305 goto errout;
5306 }
5307 if (cs.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +00005308 bb_error_msg(_("returned from clone_file_block"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005309 retval = cs.errcode;
5310 goto errout;
5311 }
5312 /* The inode may have changed on disk, so we have to re-read it */
5313 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
5314 blk = dp->inode.i_file_acl;
5315 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
5316 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
5317 BLOCK_CHANGED)) {
5318 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
5319 /*
5320 * If we cloned the EA block, find all other inodes
5321 * which refered to that EA block, and modify
5322 * them to point to the new EA block.
5323 */
5324 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5325 db = (struct dup_block *) dnode_get(n);
5326 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
5327 if (ino_el->inode == ino)
5328 continue;
5329 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
5330 di = (struct dup_inode *) dnode_get(n);
5331 if (di->inode.i_file_acl == blk) {
5332 di->inode.i_file_acl = dp->inode.i_file_acl;
5333 e2fsck_write_inode(ctx, ino_el->inode,
5334 &di->inode, "clone file EA");
5335 decrement_badcount(ctx, blk, db);
5336 }
5337 }
5338 }
5339 retval = 0;
5340errout:
5341 ext2fs_free_mem(&cs.buf);
5342 return retval;
5343}
5344
5345/*
5346 * This routine returns 1 if a block overlaps with one of the superblocks,
5347 * group descriptors, inode bitmaps, or block bitmaps.
5348 */
5349static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
5350{
5351 ext2_filsys fs = ctx->fs;
5352 blk_t block;
5353 dgrp_t i;
5354
5355 block = fs->super->s_first_data_block;
5356 for (i = 0; i < fs->group_desc_count; i++) {
5357
5358 /* Check superblocks/block group descriptros */
5359 if (ext2fs_bg_has_super(fs, i)) {
5360 if (test_block >= block &&
5361 (test_block <= block + fs->desc_blocks))
5362 return 1;
5363 }
5364
5365 /* Check the inode table */
5366 if ((fs->group_desc[i].bg_inode_table) &&
5367 (test_block >= fs->group_desc[i].bg_inode_table) &&
5368 (test_block < (fs->group_desc[i].bg_inode_table +
5369 fs->inode_blocks_per_group)))
5370 return 1;
5371
5372 /* Check the bitmap blocks */
5373 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
5374 (test_block == fs->group_desc[i].bg_inode_bitmap))
5375 return 1;
5376
5377 block += fs->super->s_blocks_per_group;
5378 }
5379 return 0;
5380}
5381/*
5382 * pass2.c --- check directory structure
5383 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005384 * Pass 2 of e2fsck iterates through all active directory inodes, and
5385 * applies to following tests to each directory entry in the directory
5386 * blocks in the inodes:
5387 *
5388 * - The length of the directory entry (rec_len) should be at
5389 * least 8 bytes, and no more than the remaining space
5390 * left in the directory block.
5391 * - The length of the name in the directory entry (name_len)
5392 * should be less than (rec_len - 8).
5393 * - The inode number in the directory entry should be within
5394 * legal bounds.
5395 * - The inode number should refer to a in-use inode.
5396 * - The first entry should be '.', and its inode should be
5397 * the inode of the directory.
5398 * - The second entry should be '..'.
5399 *
5400 * To minimize disk seek time, the directory blocks are processed in
5401 * sorted order of block numbers.
5402 *
5403 * Pass 2 also collects the following information:
5404 * - The inode numbers of the subdirectories for each directory.
5405 *
5406 * Pass 2 relies on the following information from previous passes:
5407 * - The directory information collected in pass 1.
5408 * - The inode_used_map bitmap
5409 * - The inode_bad_map bitmap
5410 * - The inode_dir_map bitmap
5411 *
5412 * Pass 2 frees the following data structures
5413 * - The inode_bad_map bitmap
5414 * - The inode_reg_map bitmap
5415 */
5416
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005417/*
5418 * Keeps track of how many times an inode is referenced.
5419 */
5420static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
5421static int check_dir_block(ext2_filsys fs,
5422 struct ext2_db_entry *dir_blocks_info,
5423 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005424static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
5425 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005426static int update_dir_block(ext2_filsys fs,
5427 blk_t *block_nr,
5428 e2_blkcnt_t blockcnt,
5429 blk_t ref_block,
5430 int ref_offset,
5431 void *priv_data);
5432static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
5433static int htree_depth(struct dx_dir_info *dx_dir,
5434 struct dx_dirblock_info *dx_db);
Rob Landley7c94bed2006-05-03 21:58:45 +00005435static int special_dir_block_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005436
5437struct check_dir_struct {
5438 char *buf;
5439 struct problem_context pctx;
5440 int count, max;
5441 e2fsck_t ctx;
5442};
5443
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005444static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005445{
5446 struct ext2_super_block *sb = ctx->fs->super;
5447 struct problem_context pctx;
5448 ext2_filsys fs = ctx->fs;
5449 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005450 struct dir_info *dir;
5451 struct check_dir_struct cd;
5452 struct dx_dir_info *dx_dir;
5453 struct dx_dirblock_info *dx_db, *dx_parent;
5454 int b;
5455 int i, depth;
5456 problem_t code;
5457 int bad_dir;
5458
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005459 clear_problem_context(&cd.pctx);
5460
Rob Landley3e72c592006-04-06 22:49:04 +00005461 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005462
5463 if (!(ctx->options & E2F_OPT_PREEN))
5464 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
5465
5466 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
5467 0, ctx->inode_link_info,
5468 &ctx->inode_count);
5469 if (cd.pctx.errcode) {
5470 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
5471 ctx->flags |= E2F_FLAG_ABORT;
5472 return;
5473 }
5474 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
5475 "directory scan buffer");
5476
5477 /*
5478 * Set up the parent pointer for the root directory, if
5479 * present. (If the root directory is not present, we will
5480 * create it in pass 3.)
5481 */
5482 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
5483 if (dir)
5484 dir->parent = EXT2_ROOT_INO;
5485
5486 cd.buf = buf;
5487 cd.ctx = ctx;
5488 cd.count = 1;
5489 cd.max = ext2fs_dblist_count(fs->dblist);
5490
5491 if (ctx->progress)
5492 (void) (ctx->progress)(ctx, 2, 0, cd.max);
5493
5494 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
5495 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
5496
5497 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
5498 &cd);
5499 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5500 return;
5501 if (cd.pctx.errcode) {
5502 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
5503 ctx->flags |= E2F_FLAG_ABORT;
5504 return;
5505 }
5506
5507#ifdef ENABLE_HTREE
5508 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
5509 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5510 return;
5511 if (dx_dir->numblocks == 0)
5512 continue;
5513 clear_problem_context(&pctx);
5514 bad_dir = 0;
5515 pctx.dir = dx_dir->ino;
5516 dx_db = dx_dir->dx_block;
5517 if (dx_db->flags & DX_FLAG_REFERENCED)
5518 dx_db->flags |= DX_FLAG_DUP_REF;
5519 else
5520 dx_db->flags |= DX_FLAG_REFERENCED;
5521 /*
5522 * Find all of the first and last leaf blocks, and
5523 * update their parent's min and max hash values
5524 */
5525 for (b=0, dx_db = dx_dir->dx_block;
5526 b < dx_dir->numblocks;
5527 b++, dx_db++) {
5528 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
5529 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
5530 continue;
5531 dx_parent = &dx_dir->dx_block[dx_db->parent];
5532 /*
5533 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
5534 */
5535 if (dx_db->flags & DX_FLAG_FIRST)
5536 dx_parent->min_hash = dx_db->min_hash;
5537 /*
5538 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
5539 */
5540 if (dx_db->flags & DX_FLAG_LAST)
5541 dx_parent->max_hash = dx_db->max_hash;
5542 }
5543
5544 for (b=0, dx_db = dx_dir->dx_block;
5545 b < dx_dir->numblocks;
5546 b++, dx_db++) {
5547 pctx.blkcount = b;
5548 pctx.group = dx_db->parent;
5549 code = 0;
5550 if (!(dx_db->flags & DX_FLAG_FIRST) &&
5551 (dx_db->min_hash < dx_db->node_min_hash)) {
5552 pctx.blk = dx_db->min_hash;
5553 pctx.blk2 = dx_db->node_min_hash;
5554 code = PR_2_HTREE_MIN_HASH;
5555 fix_problem(ctx, code, &pctx);
5556 bad_dir++;
5557 }
5558 if (dx_db->type == DX_DIRBLOCK_LEAF) {
5559 depth = htree_depth(dx_dir, dx_db);
5560 if (depth != dx_dir->depth) {
5561 code = PR_2_HTREE_BAD_DEPTH;
5562 fix_problem(ctx, code, &pctx);
5563 bad_dir++;
5564 }
5565 }
5566 /*
5567 * This test doesn't apply for the root block
5568 * at block #0
5569 */
5570 if (b &&
5571 (dx_db->max_hash > dx_db->node_max_hash)) {
5572 pctx.blk = dx_db->max_hash;
5573 pctx.blk2 = dx_db->node_max_hash;
5574 code = PR_2_HTREE_MAX_HASH;
5575 fix_problem(ctx, code, &pctx);
5576 bad_dir++;
5577 }
5578 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
5579 code = PR_2_HTREE_NOTREF;
5580 fix_problem(ctx, code, &pctx);
5581 bad_dir++;
5582 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
5583 code = PR_2_HTREE_DUPREF;
5584 fix_problem(ctx, code, &pctx);
5585 bad_dir++;
5586 }
5587 if (code == 0)
5588 continue;
5589 }
5590 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
5591 clear_htree(ctx, dx_dir->ino);
5592 dx_dir->numblocks = 0;
5593 }
5594 }
5595#endif
5596 ext2fs_free_mem(&buf);
5597 ext2fs_free_dblist(fs->dblist);
5598
Rob Landleye7c43b62006-03-01 16:39:45 +00005599 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
5600 ctx->inode_bad_map = 0;
5601 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
5602 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005603
5604 clear_problem_context(&pctx);
5605 if (ctx->large_files) {
5606 if (!(sb->s_feature_ro_compat &
5607 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
5608 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
5609 sb->s_feature_ro_compat |=
5610 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5611 ext2fs_mark_super_dirty(fs);
5612 }
5613 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
5614 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
5615 ext2fs_update_dynamic_rev(fs);
5616 ext2fs_mark_super_dirty(fs);
5617 }
5618 } else if (!ctx->large_files &&
5619 (sb->s_feature_ro_compat &
5620 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
5621 if (fs->flags & EXT2_FLAG_RW) {
5622 sb->s_feature_ro_compat &=
5623 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5624 ext2fs_mark_super_dirty(fs);
5625 }
5626 }
5627
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005628}
5629
5630#define MAX_DEPTH 32000
5631static int htree_depth(struct dx_dir_info *dx_dir,
5632 struct dx_dirblock_info *dx_db)
5633{
5634 int depth = 0;
5635
5636 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
5637 dx_db = &dx_dir->dx_block[dx_db->parent];
5638 depth++;
5639 }
5640 return depth;
5641}
5642
5643static int dict_de_cmp(const void *a, const void *b)
5644{
5645 const struct ext2_dir_entry *de_a, *de_b;
5646 int a_len, b_len;
5647
5648 de_a = (const struct ext2_dir_entry *) a;
5649 a_len = de_a->name_len & 0xFF;
5650 de_b = (const struct ext2_dir_entry *) b;
5651 b_len = de_b->name_len & 0xFF;
5652
5653 if (a_len != b_len)
5654 return (a_len - b_len);
5655
5656 return strncmp(de_a->name, de_b->name, a_len);
5657}
5658
5659/*
5660 * This is special sort function that makes sure that directory blocks
5661 * with a dirblock of zero are sorted to the beginning of the list.
5662 * This guarantees that the root node of the htree directories are
5663 * processed first, so we know what hash version to use.
5664 */
Rob Landley7c94bed2006-05-03 21:58:45 +00005665static int special_dir_block_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005666{
5667 const struct ext2_db_entry *db_a =
5668 (const struct ext2_db_entry *) a;
5669 const struct ext2_db_entry *db_b =
5670 (const struct ext2_db_entry *) b;
5671
5672 if (db_a->blockcnt && !db_b->blockcnt)
5673 return 1;
5674
5675 if (!db_a->blockcnt && db_b->blockcnt)
5676 return -1;
5677
5678 if (db_a->blk != db_b->blk)
5679 return (int) (db_a->blk - db_b->blk);
5680
5681 if (db_a->ino != db_b->ino)
5682 return (int) (db_a->ino - db_b->ino);
5683
5684 return (int) (db_a->blockcnt - db_b->blockcnt);
5685}
5686
5687
5688/*
5689 * Make sure the first entry in the directory is '.', and that the
5690 * directory entry is sane.
5691 */
5692static int check_dot(e2fsck_t ctx,
5693 struct ext2_dir_entry *dirent,
5694 ext2_ino_t ino, struct problem_context *pctx)
5695{
5696 struct ext2_dir_entry *nextdir;
5697 int status = 0;
5698 int created = 0;
5699 int new_len;
5700 int problem = 0;
5701
5702 if (!dirent->inode)
5703 problem = PR_2_MISSING_DOT;
5704 else if (((dirent->name_len & 0xFF) != 1) ||
5705 (dirent->name[0] != '.'))
5706 problem = PR_2_1ST_NOT_DOT;
5707 else if (dirent->name[1] != '\0')
5708 problem = PR_2_DOT_NULL_TERM;
5709
5710 if (problem) {
5711 if (fix_problem(ctx, problem, pctx)) {
5712 if (dirent->rec_len < 12)
5713 dirent->rec_len = 12;
5714 dirent->inode = ino;
5715 dirent->name_len = 1;
5716 dirent->name[0] = '.';
5717 dirent->name[1] = '\0';
5718 status = 1;
5719 created = 1;
5720 }
5721 }
5722 if (dirent->inode != ino) {
5723 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
5724 dirent->inode = ino;
5725 status = 1;
5726 }
5727 }
5728 if (dirent->rec_len > 12) {
5729 new_len = dirent->rec_len - 12;
5730 if (new_len > 12) {
5731 if (created ||
5732 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
5733 nextdir = (struct ext2_dir_entry *)
5734 ((char *) dirent + 12);
5735 dirent->rec_len = 12;
5736 nextdir->rec_len = new_len;
5737 nextdir->inode = 0;
5738 nextdir->name_len = 0;
5739 status = 1;
5740 }
5741 }
5742 }
5743 return status;
5744}
5745
5746/*
5747 * Make sure the second entry in the directory is '..', and that the
5748 * directory entry is sane. We do not check the inode number of '..'
5749 * here; this gets done in pass 3.
5750 */
5751static int check_dotdot(e2fsck_t ctx,
5752 struct ext2_dir_entry *dirent,
5753 struct dir_info *dir, struct problem_context *pctx)
5754{
5755 int problem = 0;
5756
5757 if (!dirent->inode)
5758 problem = PR_2_MISSING_DOT_DOT;
5759 else if (((dirent->name_len & 0xFF) != 2) ||
5760 (dirent->name[0] != '.') ||
5761 (dirent->name[1] != '.'))
5762 problem = PR_2_2ND_NOT_DOT_DOT;
5763 else if (dirent->name[2] != '\0')
5764 problem = PR_2_DOT_DOT_NULL_TERM;
5765
5766 if (problem) {
5767 if (fix_problem(ctx, problem, pctx)) {
5768 if (dirent->rec_len < 12)
5769 dirent->rec_len = 12;
5770 /*
5771 * Note: we don't have the parent inode just
5772 * yet, so we will fill it in with the root
5773 * inode. This will get fixed in pass 3.
5774 */
5775 dirent->inode = EXT2_ROOT_INO;
5776 dirent->name_len = 2;
5777 dirent->name[0] = '.';
5778 dirent->name[1] = '.';
5779 dirent->name[2] = '\0';
5780 return 1;
5781 }
5782 return 0;
5783 }
5784 dir->dotdot = dirent->inode;
5785 return 0;
5786}
5787
5788/*
5789 * Check to make sure a directory entry doesn't contain any illegal
5790 * characters.
5791 */
5792static int check_name(e2fsck_t ctx,
5793 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005794 struct problem_context *pctx)
5795{
5796 int i;
5797 int fixup = -1;
5798 int ret = 0;
5799
5800 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
5801 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
5802 if (fixup < 0) {
5803 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
5804 }
5805 if (fixup) {
5806 dirent->name[i] = '.';
5807 ret = 1;
5808 }
5809 }
5810 }
5811 return ret;
5812}
5813
5814/*
5815 * Check the directory filetype (if present)
5816 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005817
5818/*
5819 * Given a mode, return the ext2 file type
5820 */
5821static int ext2_file_type(unsigned int mode)
5822{
5823 if (LINUX_S_ISREG(mode))
5824 return EXT2_FT_REG_FILE;
5825
5826 if (LINUX_S_ISDIR(mode))
5827 return EXT2_FT_DIR;
5828
5829 if (LINUX_S_ISCHR(mode))
5830 return EXT2_FT_CHRDEV;
5831
5832 if (LINUX_S_ISBLK(mode))
5833 return EXT2_FT_BLKDEV;
5834
5835 if (LINUX_S_ISLNK(mode))
5836 return EXT2_FT_SYMLINK;
5837
5838 if (LINUX_S_ISFIFO(mode))
5839 return EXT2_FT_FIFO;
5840
5841 if (LINUX_S_ISSOCK(mode))
5842 return EXT2_FT_SOCK;
5843
5844 return 0;
5845}
5846
Rob Landley7c94bed2006-05-03 21:58:45 +00005847static int check_filetype(e2fsck_t ctx,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005848 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005849 struct problem_context *pctx)
5850{
5851 int filetype = dirent->name_len >> 8;
5852 int should_be = EXT2_FT_UNKNOWN;
5853 struct ext2_inode inode;
5854
5855 if (!(ctx->fs->super->s_feature_incompat &
5856 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
5857 if (filetype == 0 ||
5858 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
5859 return 0;
5860 dirent->name_len = dirent->name_len & 0xFF;
5861 return 1;
5862 }
5863
5864 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
5865 should_be = EXT2_FT_DIR;
5866 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
5867 dirent->inode)) {
5868 should_be = EXT2_FT_REG_FILE;
5869 } else if (ctx->inode_bad_map &&
5870 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
5871 dirent->inode))
5872 should_be = 0;
5873 else {
5874 e2fsck_read_inode(ctx, dirent->inode, &inode,
5875 "check_filetype");
5876 should_be = ext2_file_type(inode.i_mode);
5877 }
5878 if (filetype == should_be)
5879 return 0;
5880 pctx->num = should_be;
5881
5882 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
5883 pctx) == 0)
5884 return 0;
5885
5886 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
5887 return 1;
5888}
5889
5890#ifdef ENABLE_HTREE
5891static void parse_int_node(ext2_filsys fs,
5892 struct ext2_db_entry *db,
5893 struct check_dir_struct *cd,
5894 struct dx_dir_info *dx_dir,
5895 char *block_buf)
5896{
5897 struct ext2_dx_root_info *root;
5898 struct ext2_dx_entry *ent;
5899 struct ext2_dx_countlimit *limit;
5900 struct dx_dirblock_info *dx_db;
5901 int i, expect_limit, count;
5902 blk_t blk;
5903 ext2_dirhash_t min_hash = 0xffffffff;
5904 ext2_dirhash_t max_hash = 0;
5905 ext2_dirhash_t hash = 0, prev_hash;
5906
5907 if (db->blockcnt == 0) {
5908 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005909 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
5910 } else {
5911 ent = (struct ext2_dx_entry *) (block_buf+8);
5912 }
5913 limit = (struct ext2_dx_countlimit *) ent;
5914
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005915 count = ext2fs_le16_to_cpu(limit->count);
5916 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
5917 sizeof(struct ext2_dx_entry);
5918 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
5919 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
5920 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
5921 goto clear_and_exit;
5922 }
5923 if (count > expect_limit) {
5924 cd->pctx.num = count;
5925 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
5926 goto clear_and_exit;
5927 count = expect_limit;
5928 }
5929
5930 for (i=0; i < count; i++) {
5931 prev_hash = hash;
5932 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005933 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
5934 /* Check to make sure the block is valid */
5935 if (blk > (blk_t) dx_dir->numblocks) {
5936 cd->pctx.blk = blk;
5937 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
5938 &cd->pctx))
5939 goto clear_and_exit;
5940 }
5941 if (hash < prev_hash &&
5942 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
5943 goto clear_and_exit;
5944 dx_db = &dx_dir->dx_block[blk];
5945 if (dx_db->flags & DX_FLAG_REFERENCED) {
5946 dx_db->flags |= DX_FLAG_DUP_REF;
5947 } else {
5948 dx_db->flags |= DX_FLAG_REFERENCED;
5949 dx_db->parent = db->blockcnt;
5950 }
5951 if (hash < min_hash)
5952 min_hash = hash;
5953 if (hash > max_hash)
5954 max_hash = hash;
5955 dx_db->node_min_hash = hash;
5956 if ((i+1) < count)
5957 dx_db->node_max_hash =
5958 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
5959 else {
5960 dx_db->node_max_hash = 0xfffffffe;
5961 dx_db->flags |= DX_FLAG_LAST;
5962 }
5963 if (i == 0)
5964 dx_db->flags |= DX_FLAG_FIRST;
5965 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005966 dx_db = &dx_dir->dx_block[db->blockcnt];
5967 dx_db->min_hash = min_hash;
5968 dx_db->max_hash = max_hash;
5969 return;
5970
5971clear_and_exit:
5972 clear_htree(cd->ctx, cd->pctx.ino);
5973 dx_dir->numblocks = 0;
5974}
5975#endif /* ENABLE_HTREE */
5976
5977/*
5978 * Given a busted directory, try to salvage it somehow.
5979 *
5980 */
5981static void salvage_directory(ext2_filsys fs,
5982 struct ext2_dir_entry *dirent,
5983 struct ext2_dir_entry *prev,
5984 unsigned int *offset)
5985{
5986 char *cp = (char *) dirent;
5987 int left = fs->blocksize - *offset - dirent->rec_len;
5988 int name_len = dirent->name_len & 0xFF;
5989
5990 /*
5991 * Special case of directory entry of size 8: copy what's left
5992 * of the directory block up to cover up the invalid hole.
5993 */
5994 if ((left >= 12) && (dirent->rec_len == 8)) {
5995 memmove(cp, cp+8, left);
5996 memset(cp + left, 0, 8);
5997 return;
5998 }
5999 /*
6000 * If the directory entry overruns the end of the directory
6001 * block, and the name is small enough to fit, then adjust the
6002 * record length.
6003 */
6004 if ((left < 0) &&
6005 (name_len + 8 <= dirent->rec_len + left) &&
6006 dirent->inode <= fs->super->s_inodes_count &&
6007 strnlen(dirent->name, name_len) == name_len) {
6008 dirent->rec_len += left;
6009 return;
6010 }
6011 /*
6012 * If the directory entry is a multiple of four, so it is
6013 * valid, let the previous directory entry absorb the invalid
6014 * one.
6015 */
6016 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
6017 prev->rec_len += dirent->rec_len;
6018 *offset += dirent->rec_len;
6019 return;
6020 }
6021 /*
6022 * Default salvage method --- kill all of the directory
6023 * entries for the rest of the block. We will either try to
6024 * absorb it into the previous directory entry, or create a
6025 * new empty directory entry the rest of the directory block.
6026 */
6027 if (prev) {
6028 prev->rec_len += fs->blocksize - *offset;
6029 *offset = fs->blocksize;
6030 } else {
6031 dirent->rec_len = fs->blocksize - *offset;
6032 dirent->name_len = 0;
6033 dirent->inode = 0;
6034 }
6035}
6036
6037static int check_dir_block(ext2_filsys fs,
6038 struct ext2_db_entry *db,
6039 void *priv_data)
6040{
6041 struct dir_info *subdir, *dir;
6042 struct dx_dir_info *dx_dir;
6043#ifdef ENABLE_HTREE
6044 struct dx_dirblock_info *dx_db = 0;
6045#endif /* ENABLE_HTREE */
6046 struct ext2_dir_entry *dirent, *prev;
6047 ext2_dirhash_t hash;
6048 unsigned int offset = 0;
6049 int dir_modified = 0;
6050 int dot_state;
6051 blk_t block_nr = db->blk;
6052 ext2_ino_t ino = db->ino;
6053 __u16 links;
6054 struct check_dir_struct *cd;
6055 char *buf;
6056 e2fsck_t ctx;
6057 int problem;
6058 struct ext2_dx_root_info *root;
6059 struct ext2_dx_countlimit *limit;
6060 static dict_t de_dict;
6061 struct problem_context pctx;
6062 int dups_found = 0;
6063
6064 cd = (struct check_dir_struct *) priv_data;
6065 buf = cd->buf;
6066 ctx = cd->ctx;
6067
6068 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6069 return DIRENT_ABORT;
6070
6071 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
6072 return DIRENT_ABORT;
6073
6074 /*
6075 * Make sure the inode is still in use (could have been
6076 * deleted in the duplicate/bad blocks pass.
6077 */
6078 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
6079 return 0;
6080
6081 cd->pctx.ino = ino;
6082 cd->pctx.blk = block_nr;
6083 cd->pctx.blkcount = db->blockcnt;
6084 cd->pctx.ino2 = 0;
6085 cd->pctx.dirent = 0;
6086 cd->pctx.num = 0;
6087
6088 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006089 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006090 return 0;
6091 block_nr = db->blk;
6092 }
6093
6094 if (db->blockcnt)
6095 dot_state = 2;
6096 else
6097 dot_state = 0;
6098
6099 if (ctx->dirs_to_hash &&
6100 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
6101 dups_found++;
6102
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006103 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
6104 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
6105 cd->pctx.errcode = 0; /* We'll handle this ourselves */
6106 if (cd->pctx.errcode) {
6107 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
6108 ctx->flags |= E2F_FLAG_ABORT;
6109 return DIRENT_ABORT;
6110 }
6111 memset(buf, 0, fs->blocksize);
6112 }
6113#ifdef ENABLE_HTREE
6114 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
6115 if (dx_dir && dx_dir->numblocks) {
6116 if (db->blockcnt >= dx_dir->numblocks) {
6117 printf("XXX should never happen!!!\n");
6118 abort();
6119 }
6120 dx_db = &dx_dir->dx_block[db->blockcnt];
6121 dx_db->type = DX_DIRBLOCK_LEAF;
6122 dx_db->phys = block_nr;
6123 dx_db->min_hash = ~0;
6124 dx_db->max_hash = 0;
6125
6126 dirent = (struct ext2_dir_entry *) buf;
6127 limit = (struct ext2_dx_countlimit *) (buf+8);
6128 if (db->blockcnt == 0) {
6129 root = (struct ext2_dx_root_info *) (buf + 24);
6130 dx_db->type = DX_DIRBLOCK_ROOT;
6131 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
6132 if ((root->reserved_zero ||
6133 root->info_length < 8 ||
6134 root->indirect_levels > 1) &&
6135 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
6136 clear_htree(ctx, ino);
6137 dx_dir->numblocks = 0;
6138 dx_db = 0;
6139 }
6140 dx_dir->hashversion = root->hash_version;
6141 dx_dir->depth = root->indirect_levels + 1;
6142 } else if ((dirent->inode == 0) &&
6143 (dirent->rec_len == fs->blocksize) &&
6144 (dirent->name_len == 0) &&
6145 (ext2fs_le16_to_cpu(limit->limit) ==
6146 ((fs->blocksize-8) /
6147 sizeof(struct ext2_dx_entry))))
6148 dx_db->type = DX_DIRBLOCK_NODE;
6149 }
6150#endif /* ENABLE_HTREE */
6151
6152 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
6153 prev = 0;
6154 do {
6155 problem = 0;
6156 dirent = (struct ext2_dir_entry *) (buf + offset);
6157 cd->pctx.dirent = dirent;
6158 cd->pctx.num = offset;
6159 if (((offset + dirent->rec_len) > fs->blocksize) ||
6160 (dirent->rec_len < 12) ||
6161 ((dirent->rec_len % 4) != 0) ||
6162 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
6163 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
6164 salvage_directory(fs, dirent, prev, &offset);
6165 dir_modified++;
6166 continue;
6167 } else
6168 goto abort_free_dict;
6169 }
6170 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
6171 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
6172 dirent->name_len = EXT2_NAME_LEN;
6173 dir_modified++;
6174 }
6175 }
6176
6177 if (dot_state == 0) {
6178 if (check_dot(ctx, dirent, ino, &cd->pctx))
6179 dir_modified++;
6180 } else if (dot_state == 1) {
6181 dir = e2fsck_get_dir_info(ctx, ino);
6182 if (!dir) {
6183 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6184 goto abort_free_dict;
6185 }
6186 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
6187 dir_modified++;
6188 } else if (dirent->inode == ino) {
6189 problem = PR_2_LINK_DOT;
6190 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
6191 dirent->inode = 0;
6192 dir_modified++;
6193 goto next;
6194 }
6195 }
6196 if (!dirent->inode)
6197 goto next;
6198
6199 /*
6200 * Make sure the inode listed is a legal one.
6201 */
6202 if (((dirent->inode != EXT2_ROOT_INO) &&
6203 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
6204 (dirent->inode > fs->super->s_inodes_count)) {
6205 problem = PR_2_BAD_INO;
6206 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
6207 dirent->inode))) {
6208 /*
6209 * If the inode is unused, offer to clear it.
6210 */
6211 problem = PR_2_UNUSED_INODE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006212 } else if ((dot_state > 1) &&
6213 ((dirent->name_len & 0xFF) == 1) &&
6214 (dirent->name[0] == '.')) {
6215 /*
6216 * If there's a '.' entry in anything other
6217 * than the first directory entry, it's a
6218 * duplicate entry that should be removed.
6219 */
6220 problem = PR_2_DUP_DOT;
6221 } else if ((dot_state > 1) &&
6222 ((dirent->name_len & 0xFF) == 2) &&
6223 (dirent->name[0] == '.') &&
6224 (dirent->name[1] == '.')) {
6225 /*
6226 * If there's a '..' entry in anything other
6227 * than the second directory entry, it's a
6228 * duplicate entry that should be removed.
6229 */
6230 problem = PR_2_DUP_DOT_DOT;
6231 } else if ((dot_state > 1) &&
6232 (dirent->inode == EXT2_ROOT_INO)) {
6233 /*
6234 * Don't allow links to the root directory.
6235 * We check this specially to make sure we
6236 * catch this error case even if the root
6237 * directory hasn't been created yet.
6238 */
6239 problem = PR_2_LINK_ROOT;
6240 } else if ((dot_state > 1) &&
6241 (dirent->name_len & 0xFF) == 0) {
6242 /*
6243 * Don't allow zero-length directory names.
6244 */
6245 problem = PR_2_NULL_NAME;
6246 }
6247
6248 if (problem) {
6249 if (fix_problem(ctx, problem, &cd->pctx)) {
6250 dirent->inode = 0;
6251 dir_modified++;
6252 goto next;
6253 } else {
6254 ext2fs_unmark_valid(fs);
6255 if (problem == PR_2_BAD_INO)
6256 goto next;
6257 }
6258 }
6259
6260 /*
6261 * If the inode was marked as having bad fields in
6262 * pass1, process it and offer to fix/clear it.
6263 * (We wait until now so that we can display the
6264 * pathname to the user.)
6265 */
6266 if (ctx->inode_bad_map &&
6267 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6268 dirent->inode)) {
6269 if (e2fsck_process_bad_inode(ctx, ino,
6270 dirent->inode,
6271 buf + fs->blocksize)) {
6272 dirent->inode = 0;
6273 dir_modified++;
6274 goto next;
6275 }
6276 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6277 return DIRENT_ABORT;
6278 }
6279
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006280 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006281 dir_modified++;
6282
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006283 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006284 dir_modified++;
6285
6286#ifdef ENABLE_HTREE
6287 if (dx_db) {
6288 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
6289 (dirent->name_len & 0xFF),
6290 fs->super->s_hash_seed, &hash, 0);
6291 if (hash < dx_db->min_hash)
6292 dx_db->min_hash = hash;
6293 if (hash > dx_db->max_hash)
6294 dx_db->max_hash = hash;
6295 }
6296#endif
6297
6298 /*
6299 * If this is a directory, then mark its parent in its
6300 * dir_info structure. If the parent field is already
6301 * filled in, then this directory has more than one
6302 * hard link. We assume the first link is correct,
6303 * and ask the user if he/she wants to clear this one.
6304 */
6305 if ((dot_state > 1) &&
6306 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6307 dirent->inode))) {
6308 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
6309 if (!subdir) {
6310 cd->pctx.ino = dirent->inode;
6311 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6312 goto abort_free_dict;
6313 }
6314 if (subdir->parent) {
6315 cd->pctx.ino2 = subdir->parent;
6316 if (fix_problem(ctx, PR_2_LINK_DIR,
6317 &cd->pctx)) {
6318 dirent->inode = 0;
6319 dir_modified++;
6320 goto next;
6321 }
6322 cd->pctx.ino2 = 0;
6323 } else
6324 subdir->parent = ino;
6325 }
6326
6327 if (dups_found) {
6328 ;
6329 } else if (dict_lookup(&de_dict, dirent)) {
6330 clear_problem_context(&pctx);
6331 pctx.ino = ino;
6332 pctx.dirent = dirent;
6333 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
6334 if (!ctx->dirs_to_hash)
6335 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
6336 if (ctx->dirs_to_hash)
6337 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6338 dups_found++;
6339 } else
6340 dict_alloc_insert(&de_dict, dirent, dirent);
6341
6342 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
6343 &links);
6344 if (links > 1)
6345 ctx->fs_links_count++;
6346 ctx->fs_total_count++;
6347 next:
6348 prev = dirent;
6349 offset += dirent->rec_len;
6350 dot_state++;
6351 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006352#ifdef ENABLE_HTREE
6353 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006354 cd->pctx.dir = cd->pctx.ino;
6355 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
6356 (dx_db->type == DX_DIRBLOCK_NODE))
6357 parse_int_node(fs, db, cd, dx_dir, buf);
6358 }
6359#endif /* ENABLE_HTREE */
6360 if (offset != fs->blocksize) {
6361 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
6362 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
6363 dirent->rec_len = cd->pctx.num;
6364 dir_modified++;
6365 }
6366 }
6367 if (dir_modified) {
6368 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
6369 if (cd->pctx.errcode) {
6370 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
6371 &cd->pctx))
6372 goto abort_free_dict;
6373 }
6374 ext2fs_mark_changed(fs);
6375 }
6376 dict_free_nodes(&de_dict);
6377 return 0;
6378abort_free_dict:
6379 dict_free_nodes(&de_dict);
6380 ctx->flags |= E2F_FLAG_ABORT;
6381 return DIRENT_ABORT;
6382}
6383
6384/*
6385 * This function is called to deallocate a block, and is an interator
6386 * functioned called by deallocate inode via ext2fs_iterate_block().
6387 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006388static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006389 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6390 blk_t ref_block FSCK_ATTR((unused)),
6391 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006392 void *priv_data)
6393{
6394 e2fsck_t ctx = (e2fsck_t) priv_data;
6395
6396 if (HOLE_BLKADDR(*block_nr))
6397 return 0;
6398 if ((*block_nr < fs->super->s_first_data_block) ||
6399 (*block_nr >= fs->super->s_blocks_count))
6400 return 0;
6401 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6402 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6403 return 0;
6404}
6405
6406/*
6407 * This fuction deallocates an inode
6408 */
6409static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
6410{
6411 ext2_filsys fs = ctx->fs;
6412 struct ext2_inode inode;
6413 struct problem_context pctx;
6414 __u32 count;
6415
6416 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
6417 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
6418 inode.i_links_count = 0;
6419 inode.i_dtime = time(0);
6420 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
6421 clear_problem_context(&pctx);
6422 pctx.ino = ino;
6423
6424 /*
6425 * Fix up the bitmaps...
6426 */
6427 e2fsck_read_bitmaps(ctx);
6428 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6429 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6430 if (ctx->inode_bad_map)
6431 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6432 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6433
6434 if (inode.i_file_acl &&
6435 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6436 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6437 block_buf, -1, &count);
6438 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6439 pctx.errcode = 0;
6440 count = 1;
6441 }
6442 if (pctx.errcode) {
6443 pctx.blk = inode.i_file_acl;
6444 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
6445 ctx->flags |= E2F_FLAG_ABORT;
6446 return;
6447 }
6448 if (count == 0) {
6449 ext2fs_unmark_block_bitmap(ctx->block_found_map,
6450 inode.i_file_acl);
6451 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
6452 }
6453 inode.i_file_acl = 0;
6454 }
6455
6456 if (!ext2fs_inode_has_valid_blocks(&inode))
6457 return;
6458
6459 if (LINUX_S_ISREG(inode.i_mode) &&
6460 (inode.i_size_high || inode.i_size & 0x80000000UL))
6461 ctx->large_files--;
6462
6463 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6464 deallocate_inode_block, ctx);
6465 if (pctx.errcode) {
6466 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
6467 ctx->flags |= E2F_FLAG_ABORT;
6468 return;
6469 }
6470}
6471
6472/*
6473 * This fuction clears the htree flag on an inode
6474 */
6475static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
6476{
6477 struct ext2_inode inode;
6478
6479 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
6480 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
6481 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
6482 if (ctx->dirs_to_hash)
6483 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6484}
6485
6486
6487static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
6488 ext2_ino_t ino, char *buf)
6489{
6490 ext2_filsys fs = ctx->fs;
6491 struct ext2_inode inode;
6492 int inode_modified = 0;
6493 int not_fixed = 0;
6494 unsigned char *frag, *fsize;
6495 struct problem_context pctx;
6496 int problem = 0;
6497
6498 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
6499
6500 clear_problem_context(&pctx);
6501 pctx.ino = ino;
6502 pctx.dir = dir;
6503 pctx.inode = &inode;
6504
6505 if (inode.i_file_acl &&
6506 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
6507 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
6508 inode.i_file_acl = 0;
Rob Landley7c94bed2006-05-03 21:58:45 +00006509#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006510 /*
6511 * This is a special kludge to deal with long symlinks
6512 * on big endian systems. i_blocks had already been
6513 * decremented earlier in pass 1, but since i_file_acl
6514 * hadn't yet been cleared, ext2fs_read_inode()
6515 * assumed that the file was short symlink and would
6516 * not have byte swapped i_block[0]. Hence, we have
6517 * to byte-swap it here.
6518 */
6519 if (LINUX_S_ISLNK(inode.i_mode) &&
6520 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
6521 (inode.i_blocks == fs->blocksize >> 9))
6522 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
6523#endif
6524 inode_modified++;
6525 } else
6526 not_fixed++;
6527
6528 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
6529 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
6530 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
6531 !(LINUX_S_ISSOCK(inode.i_mode)))
6532 problem = PR_2_BAD_MODE;
6533 else if (LINUX_S_ISCHR(inode.i_mode)
6534 && !e2fsck_pass1_check_device_inode(fs, &inode))
6535 problem = PR_2_BAD_CHAR_DEV;
6536 else if (LINUX_S_ISBLK(inode.i_mode)
6537 && !e2fsck_pass1_check_device_inode(fs, &inode))
6538 problem = PR_2_BAD_BLOCK_DEV;
6539 else if (LINUX_S_ISFIFO(inode.i_mode)
6540 && !e2fsck_pass1_check_device_inode(fs, &inode))
6541 problem = PR_2_BAD_FIFO;
6542 else if (LINUX_S_ISSOCK(inode.i_mode)
6543 && !e2fsck_pass1_check_device_inode(fs, &inode))
6544 problem = PR_2_BAD_SOCKET;
6545 else if (LINUX_S_ISLNK(inode.i_mode)
6546 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
6547 problem = PR_2_INVALID_SYMLINK;
6548 }
6549
6550 if (problem) {
6551 if (fix_problem(ctx, problem, &pctx)) {
6552 deallocate_inode(ctx, ino, 0);
6553 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6554 return 0;
6555 return 1;
6556 } else
6557 not_fixed++;
6558 problem = 0;
6559 }
6560
6561 if (inode.i_faddr) {
6562 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
6563 inode.i_faddr = 0;
6564 inode_modified++;
6565 } else
6566 not_fixed++;
6567 }
6568
6569 switch (fs->super->s_creator_os) {
6570 case EXT2_OS_LINUX:
6571 frag = &inode.osd2.linux2.l_i_frag;
6572 fsize = &inode.osd2.linux2.l_i_fsize;
6573 break;
6574 case EXT2_OS_HURD:
6575 frag = &inode.osd2.hurd2.h_i_frag;
6576 fsize = &inode.osd2.hurd2.h_i_fsize;
6577 break;
6578 case EXT2_OS_MASIX:
6579 frag = &inode.osd2.masix2.m_i_frag;
6580 fsize = &inode.osd2.masix2.m_i_fsize;
6581 break;
6582 default:
6583 frag = fsize = 0;
6584 }
6585 if (frag && *frag) {
6586 pctx.num = *frag;
6587 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
6588 *frag = 0;
6589 inode_modified++;
6590 } else
6591 not_fixed++;
6592 pctx.num = 0;
6593 }
6594 if (fsize && *fsize) {
6595 pctx.num = *fsize;
6596 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
6597 *fsize = 0;
6598 inode_modified++;
6599 } else
6600 not_fixed++;
6601 pctx.num = 0;
6602 }
6603
6604 if (inode.i_file_acl &&
6605 ((inode.i_file_acl < fs->super->s_first_data_block) ||
6606 (inode.i_file_acl >= fs->super->s_blocks_count))) {
6607 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
6608 inode.i_file_acl = 0;
6609 inode_modified++;
6610 } else
6611 not_fixed++;
6612 }
6613 if (inode.i_dir_acl &&
6614 LINUX_S_ISDIR(inode.i_mode)) {
6615 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
6616 inode.i_dir_acl = 0;
6617 inode_modified++;
6618 } else
6619 not_fixed++;
6620 }
6621
6622 if (inode_modified)
6623 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
6624 if (!not_fixed)
6625 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6626 return 0;
6627}
6628
6629
6630/*
6631 * allocate_dir_block --- this function allocates a new directory
6632 * block for a particular inode; this is done if a directory has
6633 * a "hole" in it, or if a directory has a illegal block number
6634 * that was zeroed out and now needs to be replaced.
6635 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006636static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006637 struct problem_context *pctx)
6638{
6639 ext2_filsys fs = ctx->fs;
6640 blk_t blk;
6641 char *block;
6642 struct ext2_inode inode;
6643
6644 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
6645 return 1;
6646
6647 /*
6648 * Read the inode and block bitmaps in; we'll be messing with
6649 * them.
6650 */
6651 e2fsck_read_bitmaps(ctx);
6652
6653 /*
6654 * First, find a free block
6655 */
6656 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6657 if (pctx->errcode) {
6658 pctx->str = "ext2fs_new_block";
6659 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6660 return 1;
6661 }
6662 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6663 ext2fs_mark_block_bitmap(fs->block_map, blk);
6664 ext2fs_mark_bb_dirty(fs);
6665
6666 /*
6667 * Now let's create the actual data block for the inode
6668 */
6669 if (db->blockcnt)
6670 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
6671 else
6672 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
6673 EXT2_ROOT_INO, &block);
6674
6675 if (pctx->errcode) {
6676 pctx->str = "ext2fs_new_dir_block";
6677 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6678 return 1;
6679 }
6680
6681 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
6682 ext2fs_free_mem(&block);
6683 if (pctx->errcode) {
6684 pctx->str = "ext2fs_write_dir_block";
6685 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6686 return 1;
6687 }
6688
6689 /*
6690 * Update the inode block count
6691 */
6692 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
6693 inode.i_blocks += fs->blocksize / 512;
6694 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
6695 inode.i_size = (db->blockcnt+1) * fs->blocksize;
6696 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
6697
6698 /*
6699 * Finally, update the block pointers for the inode
6700 */
6701 db->blk = blk;
6702 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
6703 0, update_dir_block, db);
6704 if (pctx->errcode) {
6705 pctx->str = "ext2fs_block_iterate";
6706 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6707 return 1;
6708 }
6709
6710 return 0;
6711}
6712
6713/*
6714 * This is a helper function for allocate_dir_block().
6715 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006716static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006717 blk_t *block_nr,
6718 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006719 blk_t ref_block FSCK_ATTR((unused)),
6720 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006721 void *priv_data)
6722{
6723 struct ext2_db_entry *db;
6724
6725 db = (struct ext2_db_entry *) priv_data;
6726 if (db->blockcnt == (int) blockcnt) {
6727 *block_nr = db->blk;
6728 return BLOCK_CHANGED;
6729 }
6730 return 0;
6731}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006732
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006733/*
6734 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
6735 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006736 * Pass #3 assures that all directories are connected to the
6737 * filesystem tree, using the following algorithm:
6738 *
6739 * First, the root directory is checked to make sure it exists; if
6740 * not, e2fsck will offer to create a new one. It is then marked as
6741 * "done".
6742 *
6743 * Then, pass3 interates over all directory inodes; for each directory
6744 * it attempts to trace up the filesystem tree, using dirinfo.parent
6745 * until it reaches a directory which has been marked "done". If it
6746 * can not do so, then the directory must be disconnected, and e2fsck
6747 * will offer to reconnect it to /lost+found. While it is chasing
6748 * parent pointers up the filesystem tree, if pass3 sees a directory
6749 * twice, then it has detected a filesystem loop, and it will again
6750 * offer to reconnect the directory to /lost+found in to break the
6751 * filesystem loop.
6752 *
6753 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
6754 * reconnect inodes to /lost+found; this subroutine is also used by
6755 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
6756 * is responsible for creating /lost+found if it does not exist.
6757 *
6758 * Pass 3 frees the following data structures:
6759 * - The dirinfo directory information cache.
6760 */
6761
6762static void check_root(e2fsck_t ctx);
6763static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6764 struct problem_context *pctx);
6765static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
6766
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006767static ext2fs_inode_bitmap inode_loop_detect;
6768static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006769
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006770static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006771{
6772 ext2_filsys fs = ctx->fs;
6773 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006774 struct problem_context pctx;
6775 struct dir_info *dir;
6776 unsigned long maxdirs, count;
6777
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006778 clear_problem_context(&pctx);
6779
Rob Landley3e72c592006-04-06 22:49:04 +00006780 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006781
6782 if (!(ctx->options & E2F_OPT_PREEN))
6783 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
6784
6785 /*
6786 * Allocate some bitmaps to do loop detection.
6787 */
6788 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
6789 &inode_done_map);
6790 if (pctx.errcode) {
6791 pctx.num = 2;
6792 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
6793 ctx->flags |= E2F_FLAG_ABORT;
6794 goto abort_exit;
6795 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006796 check_root(ctx);
6797 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6798 goto abort_exit;
6799
6800 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
6801
6802 maxdirs = e2fsck_get_num_dirinfo(ctx);
6803 count = 1;
6804
6805 if (ctx->progress)
6806 if ((ctx->progress)(ctx, 3, 0, maxdirs))
6807 goto abort_exit;
6808
6809 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
6810 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6811 goto abort_exit;
6812 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
6813 goto abort_exit;
6814 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
6815 if (check_directory(ctx, dir, &pctx))
6816 goto abort_exit;
6817 }
6818
6819 /*
6820 * Force the creation of /lost+found if not present
6821 */
6822 if ((ctx->flags & E2F_OPT_READONLY) == 0)
6823 e2fsck_get_lost_and_found(ctx, 1);
6824
6825 /*
6826 * If there are any directories that need to be indexed or
6827 * optimized, do it here.
6828 */
6829 e2fsck_rehash_directories(ctx);
6830
6831abort_exit:
6832 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00006833 ext2fs_free_inode_bitmap(inode_loop_detect);
6834 inode_loop_detect = 0;
6835 ext2fs_free_inode_bitmap(inode_done_map);
6836 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006837}
6838
6839/*
6840 * This makes sure the root inode is present; if not, we ask if the
6841 * user wants us to create it. Not creating it is a fatal error.
6842 */
6843static void check_root(e2fsck_t ctx)
6844{
6845 ext2_filsys fs = ctx->fs;
6846 blk_t blk;
6847 struct ext2_inode inode;
6848 char * block;
6849 struct problem_context pctx;
6850
6851 clear_problem_context(&pctx);
6852
6853 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
6854 /*
6855 * If the root inode is not a directory, die here. The
6856 * user must have answered 'no' in pass1 when we
6857 * offered to clear it.
6858 */
6859 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6860 EXT2_ROOT_INO))) {
6861 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
6862 ctx->flags |= E2F_FLAG_ABORT;
6863 }
6864 return;
6865 }
6866
6867 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
6868 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
6869 ctx->flags |= E2F_FLAG_ABORT;
6870 return;
6871 }
6872
6873 e2fsck_read_bitmaps(ctx);
6874
6875 /*
6876 * First, find a free block
6877 */
6878 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6879 if (pctx.errcode) {
6880 pctx.str = "ext2fs_new_block";
6881 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6882 ctx->flags |= E2F_FLAG_ABORT;
6883 return;
6884 }
6885 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6886 ext2fs_mark_block_bitmap(fs->block_map, blk);
6887 ext2fs_mark_bb_dirty(fs);
6888
6889 /*
6890 * Now let's create the actual data block for the inode
6891 */
6892 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
6893 &block);
6894 if (pctx.errcode) {
6895 pctx.str = "ext2fs_new_dir_block";
6896 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6897 ctx->flags |= E2F_FLAG_ABORT;
6898 return;
6899 }
6900
6901 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
6902 if (pctx.errcode) {
6903 pctx.str = "ext2fs_write_dir_block";
6904 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6905 ctx->flags |= E2F_FLAG_ABORT;
6906 return;
6907 }
6908 ext2fs_free_mem(&block);
6909
6910 /*
6911 * Set up the inode structure
6912 */
6913 memset(&inode, 0, sizeof(inode));
6914 inode.i_mode = 040755;
6915 inode.i_size = fs->blocksize;
6916 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
6917 inode.i_links_count = 2;
6918 inode.i_blocks = fs->blocksize / 512;
6919 inode.i_block[0] = blk;
6920
6921 /*
6922 * Write out the inode.
6923 */
6924 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
6925 if (pctx.errcode) {
6926 pctx.str = "ext2fs_write_inode";
6927 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6928 ctx->flags |= E2F_FLAG_ABORT;
6929 return;
6930 }
6931
6932 /*
6933 * Miscellaneous bookkeeping...
6934 */
6935 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
6936 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
6937 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
6938
6939 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
6940 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
6941 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
6942 ext2fs_mark_ib_dirty(fs);
6943}
6944
6945/*
6946 * This subroutine is responsible for making sure that a particular
6947 * directory is connected to the root; if it isn't we trace it up as
6948 * far as we can go, and then offer to connect the resulting parent to
6949 * the lost+found. We have to do loop detection; if we ever discover
6950 * a loop, we treat that as a disconnected directory and offer to
6951 * reparent it to lost+found.
6952 *
6953 * However, loop detection is expensive, because for very large
6954 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
6955 * is non-trivial. Loops in filesystems are also a rare error case,
6956 * and we shouldn't optimize for error cases. So we try two passes of
6957 * the algorithm. The first time, we ignore loop detection and merely
6958 * increment a counter; if the counter exceeds some extreme threshold,
6959 * then we try again with the loop detection bitmap enabled.
6960 */
6961static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6962 struct problem_context *pctx)
6963{
6964 ext2_filsys fs = ctx->fs;
6965 struct dir_info *p = dir;
6966 int loop_pass = 0, parent_count = 0;
6967
6968 if (!p)
6969 return 0;
6970
6971 while (1) {
6972 /*
6973 * Mark this inode as being "done"; by the time we
6974 * return from this function, the inode we either be
6975 * verified as being connected to the directory tree,
6976 * or we will have offered to reconnect this to
6977 * lost+found.
6978 *
6979 * If it was marked done already, then we've reached a
6980 * parent we've already checked.
6981 */
6982 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
6983 break;
6984
6985 /*
6986 * If this directory doesn't have a parent, or we've
6987 * seen the parent once already, then offer to
6988 * reparent it to lost+found
6989 */
6990 if (!p->parent ||
6991 (loop_pass &&
6992 (ext2fs_test_inode_bitmap(inode_loop_detect,
6993 p->parent)))) {
6994 pctx->ino = p->ino;
6995 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
6996 if (e2fsck_reconnect_file(ctx, pctx->ino))
6997 ext2fs_unmark_valid(fs);
6998 else {
6999 p = e2fsck_get_dir_info(ctx, pctx->ino);
7000 p->parent = ctx->lost_and_found;
7001 fix_dotdot(ctx, p, ctx->lost_and_found);
7002 }
7003 }
7004 break;
7005 }
7006 p = e2fsck_get_dir_info(ctx, p->parent);
7007 if (!p) {
7008 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
7009 return 0;
7010 }
7011 if (loop_pass) {
7012 ext2fs_mark_inode_bitmap(inode_loop_detect,
7013 p->ino);
7014 } else if (parent_count++ > 2048) {
7015 /*
7016 * If we've run into a path depth that's
7017 * greater than 2048, try again with the inode
7018 * loop bitmap turned on and start from the
7019 * top.
7020 */
7021 loop_pass = 1;
7022 if (inode_loop_detect)
7023 ext2fs_clear_inode_bitmap(inode_loop_detect);
7024 else {
7025 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
7026 if (pctx->errcode) {
7027 pctx->num = 1;
7028 fix_problem(ctx,
7029 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
7030 ctx->flags |= E2F_FLAG_ABORT;
7031 return -1;
7032 }
7033 }
7034 p = dir;
7035 }
7036 }
7037
7038 /*
7039 * Make sure that .. and the parent directory are the same;
7040 * offer to fix it if not.
7041 */
7042 if (dir->parent != dir->dotdot) {
7043 pctx->ino = dir->ino;
7044 pctx->ino2 = dir->dotdot;
7045 pctx->dir = dir->parent;
7046 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
7047 fix_dotdot(ctx, dir, dir->parent);
7048 }
7049 return 0;
7050}
7051
7052/*
7053 * This routine gets the lost_and_found inode, making it a directory
7054 * if necessary
7055 */
7056ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
7057{
7058 ext2_filsys fs = ctx->fs;
7059 ext2_ino_t ino;
7060 blk_t blk;
7061 errcode_t retval;
7062 struct ext2_inode inode;
7063 char * block;
7064 static const char name[] = "lost+found";
7065 struct problem_context pctx;
7066 struct dir_info *dirinfo;
7067
7068 if (ctx->lost_and_found)
7069 return ctx->lost_and_found;
7070
7071 clear_problem_context(&pctx);
7072
7073 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
7074 sizeof(name)-1, 0, &ino);
7075 if (retval && !fix)
7076 return 0;
7077 if (!retval) {
7078 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
7079 ctx->lost_and_found = ino;
7080 return ino;
7081 }
7082
7083 /* Lost+found isn't a directory! */
7084 if (!fix)
7085 return 0;
7086 pctx.ino = ino;
7087 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
7088 return 0;
7089
7090 /* OK, unlink the old /lost+found file. */
7091 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
7092 if (pctx.errcode) {
7093 pctx.str = "ext2fs_unlink";
7094 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7095 return 0;
7096 }
7097 dirinfo = e2fsck_get_dir_info(ctx, ino);
7098 if (dirinfo)
7099 dirinfo->parent = 0;
7100 e2fsck_adjust_inode_count(ctx, ino, -1);
7101 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
7102 pctx.errcode = retval;
7103 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
7104 }
7105 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
7106 return 0;
7107
7108 /*
7109 * Read the inode and block bitmaps in; we'll be messing with
7110 * them.
7111 */
7112 e2fsck_read_bitmaps(ctx);
7113
7114 /*
7115 * First, find a free block
7116 */
7117 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7118 if (retval) {
7119 pctx.errcode = retval;
7120 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
7121 return 0;
7122 }
7123 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7124 ext2fs_block_alloc_stats(fs, blk, +1);
7125
7126 /*
7127 * Next find a free inode.
7128 */
7129 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
7130 ctx->inode_used_map, &ino);
7131 if (retval) {
7132 pctx.errcode = retval;
7133 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
7134 return 0;
7135 }
7136 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
7137 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
7138 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
7139
7140 /*
7141 * Now let's create the actual data block for the inode
7142 */
7143 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
7144 if (retval) {
7145 pctx.errcode = retval;
7146 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
7147 return 0;
7148 }
7149
7150 retval = ext2fs_write_dir_block(fs, blk, block);
7151 ext2fs_free_mem(&block);
7152 if (retval) {
7153 pctx.errcode = retval;
7154 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
7155 return 0;
7156 }
7157
7158 /*
7159 * Set up the inode structure
7160 */
7161 memset(&inode, 0, sizeof(inode));
7162 inode.i_mode = 040700;
7163 inode.i_size = fs->blocksize;
7164 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7165 inode.i_links_count = 2;
7166 inode.i_blocks = fs->blocksize / 512;
7167 inode.i_block[0] = blk;
7168
7169 /*
7170 * Next, write out the inode.
7171 */
7172 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
7173 if (pctx.errcode) {
7174 pctx.str = "ext2fs_write_inode";
7175 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7176 return 0;
7177 }
7178 /*
7179 * Finally, create the directory link
7180 */
7181 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
7182 if (pctx.errcode) {
7183 pctx.str = "ext2fs_link";
7184 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7185 return 0;
7186 }
7187
7188 /*
7189 * Miscellaneous bookkeeping that needs to be kept straight.
7190 */
7191 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
7192 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
7193 ext2fs_icount_store(ctx->inode_count, ino, 2);
7194 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
7195 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007196 return ino;
7197}
7198
7199/*
7200 * This routine will connect a file to lost+found
7201 */
7202int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
7203{
7204 ext2_filsys fs = ctx->fs;
7205 errcode_t retval;
7206 char name[80];
7207 struct problem_context pctx;
7208 struct ext2_inode inode;
7209 int file_type = 0;
7210
7211 clear_problem_context(&pctx);
7212 pctx.ino = ino;
7213
7214 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
7215 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
7216 ctx->bad_lost_and_found++;
7217 }
7218 if (ctx->bad_lost_and_found) {
7219 fix_problem(ctx, PR_3_NO_LPF, &pctx);
7220 return 1;
7221 }
7222
7223 sprintf(name, "#%u", ino);
7224 if (ext2fs_read_inode(fs, ino, &inode) == 0)
7225 file_type = ext2_file_type(inode.i_mode);
7226 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
7227 if (retval == EXT2_ET_DIR_NO_SPACE) {
7228 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
7229 return 1;
7230 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
7231 1, 0);
7232 if (retval) {
7233 pctx.errcode = retval;
7234 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
7235 return 1;
7236 }
7237 retval = ext2fs_link(fs, ctx->lost_and_found, name,
7238 ino, file_type);
7239 }
7240 if (retval) {
7241 pctx.errcode = retval;
7242 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
7243 return 1;
7244 }
7245 e2fsck_adjust_inode_count(ctx, ino, 1);
7246
7247 return 0;
7248}
7249
7250/*
7251 * Utility routine to adjust the inode counts on an inode.
7252 */
7253errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
7254{
7255 ext2_filsys fs = ctx->fs;
7256 errcode_t retval;
7257 struct ext2_inode inode;
7258
7259 if (!ino)
7260 return 0;
7261
7262 retval = ext2fs_read_inode(fs, ino, &inode);
7263 if (retval)
7264 return retval;
7265
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007266 if (adj == 1) {
7267 ext2fs_icount_increment(ctx->inode_count, ino, 0);
7268 if (inode.i_links_count == (__u16) ~0)
7269 return 0;
7270 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
7271 inode.i_links_count++;
7272 } else if (adj == -1) {
7273 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
7274 if (inode.i_links_count == 0)
7275 return 0;
7276 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
7277 inode.i_links_count--;
7278 }
7279
7280 retval = ext2fs_write_inode(fs, ino, &inode);
7281 if (retval)
7282 return retval;
7283
7284 return 0;
7285}
7286
7287/*
7288 * Fix parent --- this routine fixes up the parent of a directory.
7289 */
7290struct fix_dotdot_struct {
7291 ext2_filsys fs;
7292 ext2_ino_t parent;
7293 int done;
7294 e2fsck_t ctx;
7295};
7296
7297static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007298 int offset FSCK_ATTR((unused)),
7299 int blocksize FSCK_ATTR((unused)),
7300 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007301 void *priv_data)
7302{
7303 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
7304 errcode_t retval;
7305 struct problem_context pctx;
7306
7307 if ((dirent->name_len & 0xFF) != 2)
7308 return 0;
7309 if (strncmp(dirent->name, "..", 2))
7310 return 0;
7311
7312 clear_problem_context(&pctx);
7313
7314 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
7315 if (retval) {
7316 pctx.errcode = retval;
7317 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7318 }
7319 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
7320 if (retval) {
7321 pctx.errcode = retval;
7322 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7323 }
7324 dirent->inode = fp->parent;
7325
7326 fp->done++;
7327 return DIRENT_ABORT | DIRENT_CHANGED;
7328}
7329
7330static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
7331{
7332 ext2_filsys fs = ctx->fs;
7333 errcode_t retval;
7334 struct fix_dotdot_struct fp;
7335 struct problem_context pctx;
7336
7337 fp.fs = fs;
7338 fp.parent = parent;
7339 fp.done = 0;
7340 fp.ctx = ctx;
7341
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007342 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
7343 0, fix_dotdot_proc, &fp);
7344 if (retval || !fp.done) {
7345 clear_problem_context(&pctx);
7346 pctx.ino = dir->ino;
7347 pctx.errcode = retval;
7348 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
7349 PR_3_FIX_PARENT_NOFIND, &pctx);
7350 ext2fs_unmark_valid(fs);
7351 }
7352 dir->dotdot = parent;
7353
7354 return;
7355}
7356
7357/*
7358 * These routines are responsible for expanding a /lost+found if it is
7359 * too small.
7360 */
7361
7362struct expand_dir_struct {
7363 int num;
7364 int guaranteed_size;
7365 int newblocks;
7366 int last_block;
7367 errcode_t err;
7368 e2fsck_t ctx;
7369};
7370
7371static int expand_dir_proc(ext2_filsys fs,
7372 blk_t *blocknr,
7373 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007374 blk_t ref_block FSCK_ATTR((unused)),
7375 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007376 void *priv_data)
7377{
7378 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
7379 blk_t new_blk;
7380 static blk_t last_blk = 0;
7381 char *block;
7382 errcode_t retval;
7383 e2fsck_t ctx;
7384
7385 ctx = es->ctx;
7386
7387 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
7388 return BLOCK_ABORT;
7389
7390 if (blockcnt > 0)
7391 es->last_block = blockcnt;
7392 if (*blocknr) {
7393 last_blk = *blocknr;
7394 return 0;
7395 }
7396 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
7397 &new_blk);
7398 if (retval) {
7399 es->err = retval;
7400 return BLOCK_ABORT;
7401 }
7402 if (blockcnt > 0) {
7403 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
7404 if (retval) {
7405 es->err = retval;
7406 return BLOCK_ABORT;
7407 }
7408 es->num--;
7409 retval = ext2fs_write_dir_block(fs, new_blk, block);
7410 } else {
7411 retval = ext2fs_get_mem(fs->blocksize, &block);
7412 if (retval) {
7413 es->err = retval;
7414 return BLOCK_ABORT;
7415 }
7416 memset(block, 0, fs->blocksize);
7417 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
7418 }
7419 if (retval) {
7420 es->err = retval;
7421 return BLOCK_ABORT;
7422 }
7423 ext2fs_free_mem(&block);
7424 *blocknr = new_blk;
7425 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
7426 ext2fs_block_alloc_stats(fs, new_blk, +1);
7427 es->newblocks++;
7428
7429 if (es->num == 0)
7430 return (BLOCK_CHANGED | BLOCK_ABORT);
7431 else
7432 return BLOCK_CHANGED;
7433}
7434
7435errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
7436 int num, int guaranteed_size)
7437{
7438 ext2_filsys fs = ctx->fs;
7439 errcode_t retval;
7440 struct expand_dir_struct es;
7441 struct ext2_inode inode;
7442
7443 if (!(fs->flags & EXT2_FLAG_RW))
7444 return EXT2_ET_RO_FILSYS;
7445
7446 /*
7447 * Read the inode and block bitmaps in; we'll be messing with
7448 * them.
7449 */
7450 e2fsck_read_bitmaps(ctx);
7451
7452 retval = ext2fs_check_directory(fs, dir);
7453 if (retval)
7454 return retval;
7455
7456 es.num = num;
7457 es.guaranteed_size = guaranteed_size;
7458 es.last_block = 0;
7459 es.err = 0;
7460 es.newblocks = 0;
7461 es.ctx = ctx;
7462
7463 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
7464 0, expand_dir_proc, &es);
7465
7466 if (es.err)
7467 return es.err;
7468
7469 /*
7470 * Update the size and block count fields in the inode.
7471 */
7472 retval = ext2fs_read_inode(fs, dir, &inode);
7473 if (retval)
7474 return retval;
7475
7476 inode.i_size = (es.last_block + 1) * fs->blocksize;
7477 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
7478
7479 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
7480
7481 return 0;
7482}
7483
7484/*
7485 * pass4.c -- pass #4 of e2fsck: Check reference counts
7486 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007487 * Pass 4 frees the following data structures:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007488 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
7489 */
7490
7491/*
7492 * This routine is called when an inode is not connected to the
7493 * directory tree.
7494 *
7495 * This subroutine returns 1 then the caller shouldn't bother with the
7496 * rest of the pass 4 tests.
7497 */
7498static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
7499{
7500 ext2_filsys fs = ctx->fs;
7501 struct ext2_inode inode;
7502 struct problem_context pctx;
7503
7504 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
7505 clear_problem_context(&pctx);
7506 pctx.ino = i;
7507 pctx.inode = &inode;
7508
7509 /*
7510 * Offer to delete any zero-length files that does not have
7511 * blocks. If there is an EA block, it might have useful
7512 * information, so we won't prompt to delete it, but let it be
7513 * reconnected to lost+found.
7514 */
7515 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
7516 LINUX_S_ISDIR(inode.i_mode))) {
7517 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
7518 ext2fs_icount_store(ctx->inode_link_info, i, 0);
7519 inode.i_links_count = 0;
7520 inode.i_dtime = time(0);
7521 e2fsck_write_inode(ctx, i, &inode,
7522 "disconnect_inode");
7523 /*
7524 * Fix up the bitmaps...
7525 */
7526 e2fsck_read_bitmaps(ctx);
7527 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
7528 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
7529 ext2fs_inode_alloc_stats2(fs, i, -1,
7530 LINUX_S_ISDIR(inode.i_mode));
7531 return 0;
7532 }
7533 }
7534
7535 /*
7536 * Prompt to reconnect.
7537 */
7538 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
7539 if (e2fsck_reconnect_file(ctx, i))
7540 ext2fs_unmark_valid(fs);
7541 } else {
7542 /*
7543 * If we don't attach the inode, then skip the
7544 * i_links_test since there's no point in trying to
7545 * force i_links_count to zero.
7546 */
7547 ext2fs_unmark_valid(fs);
7548 return 1;
7549 }
7550 return 0;
7551}
7552
7553
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007554static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007555{
7556 ext2_filsys fs = ctx->fs;
7557 ext2_ino_t i;
7558 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007559 struct problem_context pctx;
7560 __u16 link_count, link_counted;
7561 char *buf = 0;
7562 int group, maxgroup;
7563
Rob Landley3e72c592006-04-06 22:49:04 +00007564 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007565
7566 clear_problem_context(&pctx);
7567
7568 if (!(ctx->options & E2F_OPT_PREEN))
7569 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
7570
7571 group = 0;
7572 maxgroup = fs->group_desc_count;
7573 if (ctx->progress)
7574 if ((ctx->progress)(ctx, 4, 0, maxgroup))
7575 return;
7576
7577 for (i=1; i <= fs->super->s_inodes_count; i++) {
7578 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7579 return;
7580 if ((i % fs->super->s_inodes_per_group) == 0) {
7581 group++;
7582 if (ctx->progress)
7583 if ((ctx->progress)(ctx, 4, group, maxgroup))
7584 return;
7585 }
7586 if (i == EXT2_BAD_INO ||
7587 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
7588 continue;
7589 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
7590 (ctx->inode_imagic_map &&
Rob Landleyd8f66012006-05-05 17:29:09 +00007591 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007592 continue;
7593 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
7594 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
7595 if (link_counted == 0) {
7596 if (!buf)
7597 buf = e2fsck_allocate_memory(ctx,
7598 fs->blocksize, "bad_inode buffer");
7599 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
7600 continue;
7601 if (disconnect_inode(ctx, i))
7602 continue;
7603 ext2fs_icount_fetch(ctx->inode_link_info, i,
7604 &link_count);
7605 ext2fs_icount_fetch(ctx->inode_count, i,
7606 &link_counted);
7607 }
7608 if (link_counted != link_count) {
7609 e2fsck_read_inode(ctx, i, &inode, "pass4");
7610 pctx.ino = i;
7611 pctx.inode = &inode;
7612 if (link_count != inode.i_links_count) {
7613 pctx.num = link_count;
7614 fix_problem(ctx,
7615 PR_4_INCONSISTENT_COUNT, &pctx);
7616 }
7617 pctx.num = link_counted;
7618 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
7619 inode.i_links_count = link_counted;
7620 e2fsck_write_inode(ctx, i, &inode, "pass4");
7621 }
7622 }
7623 }
7624 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
7625 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007626 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
7627 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00007628 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007629}
7630
7631/*
7632 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007633 */
7634
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007635#define NO_BLK ((blk_t) -1)
7636
7637static void print_bitmap_problem(e2fsck_t ctx, int problem,
7638 struct problem_context *pctx)
7639{
7640 switch (problem) {
7641 case PR_5_BLOCK_UNUSED:
7642 if (pctx->blk == pctx->blk2)
7643 pctx->blk2 = 0;
7644 else
7645 problem = PR_5_BLOCK_RANGE_UNUSED;
7646 break;
7647 case PR_5_BLOCK_USED:
7648 if (pctx->blk == pctx->blk2)
7649 pctx->blk2 = 0;
7650 else
7651 problem = PR_5_BLOCK_RANGE_USED;
7652 break;
7653 case PR_5_INODE_UNUSED:
7654 if (pctx->ino == pctx->ino2)
7655 pctx->ino2 = 0;
7656 else
7657 problem = PR_5_INODE_RANGE_UNUSED;
7658 break;
7659 case PR_5_INODE_USED:
7660 if (pctx->ino == pctx->ino2)
7661 pctx->ino2 = 0;
7662 else
7663 problem = PR_5_INODE_RANGE_USED;
7664 break;
7665 }
7666 fix_problem(ctx, problem, pctx);
7667 pctx->blk = pctx->blk2 = NO_BLK;
7668 pctx->ino = pctx->ino2 = 0;
7669}
7670
7671static void check_block_bitmaps(e2fsck_t ctx)
7672{
7673 ext2_filsys fs = ctx->fs;
7674 blk_t i;
7675 int *free_array;
7676 int group = 0;
7677 unsigned int blocks = 0;
7678 unsigned int free_blocks = 0;
7679 int group_free = 0;
7680 int actual, bitmap;
7681 struct problem_context pctx;
7682 int problem, save_problem, fixit, had_problem;
7683 errcode_t retval;
7684
7685 clear_problem_context(&pctx);
7686 free_array = (int *) e2fsck_allocate_memory(ctx,
7687 fs->group_desc_count * sizeof(int), "free block count array");
7688
7689 if ((fs->super->s_first_data_block <
7690 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
7691 (fs->super->s_blocks_count-1 >
7692 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
7693 pctx.num = 1;
7694 pctx.blk = fs->super->s_first_data_block;
7695 pctx.blk2 = fs->super->s_blocks_count -1;
7696 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
7697 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
7698 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7699
7700 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7701 return;
7702 }
7703
7704 if ((fs->super->s_first_data_block <
7705 ext2fs_get_block_bitmap_start(fs->block_map)) ||
7706 (fs->super->s_blocks_count-1 >
7707 ext2fs_get_block_bitmap_end(fs->block_map))) {
7708 pctx.num = 2;
7709 pctx.blk = fs->super->s_first_data_block;
7710 pctx.blk2 = fs->super->s_blocks_count -1;
7711 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
7712 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
7713 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7714
7715 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7716 return;
7717 }
7718
7719redo_counts:
7720 had_problem = 0;
7721 save_problem = 0;
7722 pctx.blk = pctx.blk2 = NO_BLK;
7723 for (i = fs->super->s_first_data_block;
7724 i < fs->super->s_blocks_count;
7725 i++) {
7726 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
7727 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
7728
7729 if (actual == bitmap)
7730 goto do_counts;
7731
7732 if (!actual && bitmap) {
7733 /*
7734 * Block not used, but marked in use in the bitmap.
7735 */
7736 problem = PR_5_BLOCK_UNUSED;
7737 } else {
7738 /*
7739 * Block used, but not marked in use in the bitmap.
7740 */
7741 problem = PR_5_BLOCK_USED;
7742 }
7743 if (pctx.blk == NO_BLK) {
7744 pctx.blk = pctx.blk2 = i;
7745 save_problem = problem;
7746 } else {
7747 if ((problem == save_problem) &&
7748 (pctx.blk2 == i-1))
7749 pctx.blk2++;
7750 else {
7751 print_bitmap_problem(ctx, save_problem, &pctx);
7752 pctx.blk = pctx.blk2 = i;
7753 save_problem = problem;
7754 }
7755 }
7756 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7757 had_problem++;
7758
7759 do_counts:
7760 if (!bitmap) {
7761 group_free++;
7762 free_blocks++;
7763 }
7764 blocks ++;
7765 if ((blocks == fs->super->s_blocks_per_group) ||
7766 (i == fs->super->s_blocks_count-1)) {
7767 free_array[group] = group_free;
7768 group ++;
7769 blocks = 0;
7770 group_free = 0;
7771 if (ctx->progress)
7772 if ((ctx->progress)(ctx, 5, group,
7773 fs->group_desc_count*2))
7774 return;
7775 }
7776 }
7777 if (pctx.blk != NO_BLK)
7778 print_bitmap_problem(ctx, save_problem, &pctx);
7779 if (had_problem)
7780 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
7781 else
7782 fixit = -1;
7783 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7784
7785 if (fixit == 1) {
7786 ext2fs_free_block_bitmap(fs->block_map);
7787 retval = ext2fs_copy_bitmap(ctx->block_found_map,
7788 &fs->block_map);
7789 if (retval) {
7790 clear_problem_context(&pctx);
7791 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
7792 ctx->flags |= E2F_FLAG_ABORT;
7793 return;
7794 }
7795 ext2fs_set_bitmap_padding(fs->block_map);
7796 ext2fs_mark_bb_dirty(fs);
7797
7798 /* Redo the counts */
7799 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
7800 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7801 goto redo_counts;
7802 } else if (fixit == 0)
7803 ext2fs_unmark_valid(fs);
7804
7805 for (i = 0; i < fs->group_desc_count; i++) {
7806 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
7807 pctx.group = i;
7808 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
7809 pctx.blk2 = free_array[i];
7810
7811 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
7812 &pctx)) {
7813 fs->group_desc[i].bg_free_blocks_count =
7814 free_array[i];
7815 ext2fs_mark_super_dirty(fs);
7816 } else
7817 ext2fs_unmark_valid(fs);
7818 }
7819 }
7820 if (free_blocks != fs->super->s_free_blocks_count) {
7821 pctx.group = 0;
7822 pctx.blk = fs->super->s_free_blocks_count;
7823 pctx.blk2 = free_blocks;
7824
7825 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
7826 fs->super->s_free_blocks_count = free_blocks;
7827 ext2fs_mark_super_dirty(fs);
7828 } else
7829 ext2fs_unmark_valid(fs);
7830 }
7831 ext2fs_free_mem(&free_array);
7832}
7833
7834static void check_inode_bitmaps(e2fsck_t ctx)
7835{
7836 ext2_filsys fs = ctx->fs;
7837 ext2_ino_t i;
7838 unsigned int free_inodes = 0;
7839 int group_free = 0;
7840 int dirs_count = 0;
7841 int group = 0;
7842 unsigned int inodes = 0;
7843 int *free_array;
7844 int *dir_array;
7845 int actual, bitmap;
7846 errcode_t retval;
7847 struct problem_context pctx;
7848 int problem, save_problem, fixit, had_problem;
7849
7850 clear_problem_context(&pctx);
7851 free_array = (int *) e2fsck_allocate_memory(ctx,
7852 fs->group_desc_count * sizeof(int), "free inode count array");
7853
7854 dir_array = (int *) e2fsck_allocate_memory(ctx,
7855 fs->group_desc_count * sizeof(int), "directory count array");
7856
7857 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
7858 (fs->super->s_inodes_count >
7859 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
7860 pctx.num = 3;
7861 pctx.blk = 1;
7862 pctx.blk2 = fs->super->s_inodes_count;
7863 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
7864 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
7865 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7866
7867 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7868 return;
7869 }
7870 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
7871 (fs->super->s_inodes_count >
7872 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
7873 pctx.num = 4;
7874 pctx.blk = 1;
7875 pctx.blk2 = fs->super->s_inodes_count;
7876 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
7877 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
7878 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7879
7880 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7881 return;
7882 }
7883
7884redo_counts:
7885 had_problem = 0;
7886 save_problem = 0;
7887 pctx.ino = pctx.ino2 = 0;
7888 for (i = 1; i <= fs->super->s_inodes_count; i++) {
7889 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
7890 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
7891
7892 if (actual == bitmap)
7893 goto do_counts;
7894
7895 if (!actual && bitmap) {
7896 /*
7897 * Inode wasn't used, but marked in bitmap
7898 */
7899 problem = PR_5_INODE_UNUSED;
7900 } else /* if (actual && !bitmap) */ {
7901 /*
7902 * Inode used, but not in bitmap
7903 */
7904 problem = PR_5_INODE_USED;
7905 }
7906 if (pctx.ino == 0) {
7907 pctx.ino = pctx.ino2 = i;
7908 save_problem = problem;
7909 } else {
7910 if ((problem == save_problem) &&
7911 (pctx.ino2 == i-1))
7912 pctx.ino2++;
7913 else {
7914 print_bitmap_problem(ctx, save_problem, &pctx);
7915 pctx.ino = pctx.ino2 = i;
7916 save_problem = problem;
7917 }
7918 }
7919 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7920 had_problem++;
7921
7922do_counts:
7923 if (!bitmap) {
7924 group_free++;
7925 free_inodes++;
7926 } else {
7927 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
7928 dirs_count++;
7929 }
7930 inodes++;
7931 if ((inodes == fs->super->s_inodes_per_group) ||
7932 (i == fs->super->s_inodes_count)) {
7933 free_array[group] = group_free;
7934 dir_array[group] = dirs_count;
7935 group ++;
7936 inodes = 0;
7937 group_free = 0;
7938 dirs_count = 0;
7939 if (ctx->progress)
7940 if ((ctx->progress)(ctx, 5,
7941 group + fs->group_desc_count,
7942 fs->group_desc_count*2))
7943 return;
7944 }
7945 }
7946 if (pctx.ino)
7947 print_bitmap_problem(ctx, save_problem, &pctx);
7948
7949 if (had_problem)
7950 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
7951 else
7952 fixit = -1;
7953 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7954
7955 if (fixit == 1) {
7956 ext2fs_free_inode_bitmap(fs->inode_map);
7957 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
7958 &fs->inode_map);
7959 if (retval) {
7960 clear_problem_context(&pctx);
7961 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
7962 ctx->flags |= E2F_FLAG_ABORT;
7963 return;
7964 }
7965 ext2fs_set_bitmap_padding(fs->inode_map);
7966 ext2fs_mark_ib_dirty(fs);
7967
7968 /* redo counts */
7969 inodes = 0; free_inodes = 0; group_free = 0;
7970 dirs_count = 0; group = 0;
7971 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7972 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
7973 goto redo_counts;
7974 } else if (fixit == 0)
7975 ext2fs_unmark_valid(fs);
7976
7977 for (i = 0; i < fs->group_desc_count; i++) {
7978 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
7979 pctx.group = i;
7980 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
7981 pctx.ino2 = free_array[i];
7982 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
7983 &pctx)) {
7984 fs->group_desc[i].bg_free_inodes_count =
7985 free_array[i];
7986 ext2fs_mark_super_dirty(fs);
7987 } else
7988 ext2fs_unmark_valid(fs);
7989 }
7990 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
7991 pctx.group = i;
7992 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
7993 pctx.ino2 = dir_array[i];
7994
7995 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
7996 &pctx)) {
7997 fs->group_desc[i].bg_used_dirs_count =
7998 dir_array[i];
7999 ext2fs_mark_super_dirty(fs);
8000 } else
8001 ext2fs_unmark_valid(fs);
8002 }
8003 }
8004 if (free_inodes != fs->super->s_free_inodes_count) {
8005 pctx.group = -1;
8006 pctx.ino = fs->super->s_free_inodes_count;
8007 pctx.ino2 = free_inodes;
8008
8009 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
8010 fs->super->s_free_inodes_count = free_inodes;
8011 ext2fs_mark_super_dirty(fs);
8012 } else
8013 ext2fs_unmark_valid(fs);
8014 }
8015 ext2fs_free_mem(&free_array);
8016 ext2fs_free_mem(&dir_array);
8017}
8018
8019static void check_inode_end(e2fsck_t ctx)
8020{
8021 ext2_filsys fs = ctx->fs;
8022 ext2_ino_t end, save_inodes_count, i;
8023 struct problem_context pctx;
8024
8025 clear_problem_context(&pctx);
8026
8027 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
8028 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
8029 &save_inodes_count);
8030 if (pctx.errcode) {
8031 pctx.num = 1;
8032 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8033 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8034 return;
8035 }
8036 if (save_inodes_count == end)
8037 return;
8038
8039 for (i = save_inodes_count + 1; i <= end; i++) {
8040 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
8041 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
8042 for (i = save_inodes_count + 1; i <= end; i++)
8043 ext2fs_mark_inode_bitmap(fs->inode_map,
8044 i);
8045 ext2fs_mark_ib_dirty(fs);
8046 } else
8047 ext2fs_unmark_valid(fs);
8048 break;
8049 }
8050 }
8051
8052 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
8053 save_inodes_count, 0);
8054 if (pctx.errcode) {
8055 pctx.num = 2;
8056 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8057 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8058 return;
8059 }
8060}
8061
8062static void check_block_end(e2fsck_t ctx)
8063{
8064 ext2_filsys fs = ctx->fs;
8065 blk_t end, save_blocks_count, i;
8066 struct problem_context pctx;
8067
8068 clear_problem_context(&pctx);
8069
8070 end = fs->block_map->start +
8071 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
8072 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
8073 &save_blocks_count);
8074 if (pctx.errcode) {
8075 pctx.num = 3;
8076 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8077 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8078 return;
8079 }
8080 if (save_blocks_count == end)
8081 return;
8082
8083 for (i = save_blocks_count + 1; i <= end; i++) {
8084 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
8085 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
8086 for (i = save_blocks_count + 1; i <= end; i++)
8087 ext2fs_mark_block_bitmap(fs->block_map,
8088 i);
8089 ext2fs_mark_bb_dirty(fs);
8090 } else
8091 ext2fs_unmark_valid(fs);
8092 break;
8093 }
8094 }
8095
8096 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
8097 save_blocks_count, 0);
8098 if (pctx.errcode) {
8099 pctx.num = 4;
8100 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8101 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8102 return;
8103 }
8104}
8105
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008106static void e2fsck_pass5(e2fsck_t ctx)
8107{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008108 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008109
Rob Landley3e72c592006-04-06 22:49:04 +00008110 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008111
8112 clear_problem_context(&pctx);
8113
8114 if (!(ctx->options & E2F_OPT_PREEN))
8115 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
8116
8117 if (ctx->progress)
8118 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
8119 return;
8120
8121 e2fsck_read_bitmaps(ctx);
8122
8123 check_block_bitmaps(ctx);
8124 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8125 return;
8126 check_inode_bitmaps(ctx);
8127 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8128 return;
8129 check_inode_end(ctx);
8130 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8131 return;
8132 check_block_end(ctx);
8133 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8134 return;
8135
8136 ext2fs_free_inode_bitmap(ctx->inode_used_map);
8137 ctx->inode_used_map = 0;
8138 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
8139 ctx->inode_dir_map = 0;
8140 ext2fs_free_block_bitmap(ctx->block_found_map);
8141 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008142}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008143
8144/*
8145 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008146 */
8147
8148#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
8149#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
8150#define PR_NO_DEFAULT 0x000004 /* Default to no */
8151#define PR_MSG_ONLY 0x000008 /* Print message only */
8152
8153/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
8154
8155#define PR_FATAL 0x001000 /* Fatal error */
8156#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
8157 /* ask another */
8158#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
8159#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
8160#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
8161#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
8162#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
8163
8164
8165#define PROMPT_NONE 0
8166#define PROMPT_FIX 1
8167#define PROMPT_CLEAR 2
8168#define PROMPT_RELOCATE 3
8169#define PROMPT_ALLOCATE 4
8170#define PROMPT_EXPAND 5
8171#define PROMPT_CONNECT 6
8172#define PROMPT_CREATE 7
8173#define PROMPT_SALVAGE 8
8174#define PROMPT_TRUNCATE 9
8175#define PROMPT_CLEAR_INODE 10
8176#define PROMPT_ABORT 11
8177#define PROMPT_SPLIT 12
8178#define PROMPT_CONTINUE 13
8179#define PROMPT_CLONE 14
8180#define PROMPT_DELETE 15
8181#define PROMPT_SUPPRESS 16
8182#define PROMPT_UNLINK 17
8183#define PROMPT_CLEAR_HTREE 18
8184#define PROMPT_RECREATE 19
8185#define PROMPT_NULL 20
8186
8187struct e2fsck_problem {
8188 problem_t e2p_code;
8189 const char * e2p_description;
8190 char prompt;
8191 int flags;
8192 problem_t second_code;
8193};
8194
8195struct latch_descr {
8196 int latch_code;
8197 problem_t question;
8198 problem_t end_message;
8199 int flags;
8200};
8201
8202/*
8203 * These are the prompts which are used to ask the user if they want
8204 * to fix a problem.
8205 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008206static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008207 N_("(no prompt)"), /* 0 */
8208 N_("Fix"), /* 1 */
8209 N_("Clear"), /* 2 */
8210 N_("Relocate"), /* 3 */
8211 N_("Allocate"), /* 4 */
8212 N_("Expand"), /* 5 */
8213 N_("Connect to /lost+found"), /* 6 */
8214 N_("Create"), /* 7 */
8215 N_("Salvage"), /* 8 */
8216 N_("Truncate"), /* 9 */
8217 N_("Clear inode"), /* 10 */
8218 N_("Abort"), /* 11 */
8219 N_("Split"), /* 12 */
8220 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008221 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008222 N_("Delete file"), /* 15 */
8223 N_("Suppress messages"),/* 16 */
8224 N_("Unlink"), /* 17 */
8225 N_("Clear HTree index"),/* 18 */
8226 N_("Recreate"), /* 19 */
8227 "", /* 20 */
8228};
8229
8230/*
8231 * These messages are printed when we are preen mode and we will be
8232 * automatically fixing the problem.
8233 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008234static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008235 N_("(NONE)"), /* 0 */
8236 N_("FIXED"), /* 1 */
8237 N_("CLEARED"), /* 2 */
8238 N_("RELOCATED"), /* 3 */
8239 N_("ALLOCATED"), /* 4 */
8240 N_("EXPANDED"), /* 5 */
8241 N_("RECONNECTED"), /* 6 */
8242 N_("CREATED"), /* 7 */
8243 N_("SALVAGED"), /* 8 */
8244 N_("TRUNCATED"), /* 9 */
8245 N_("INODE CLEARED"), /* 10 */
8246 N_("ABORTED"), /* 11 */
8247 N_("SPLIT"), /* 12 */
8248 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008249 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008250 N_("FILE DELETED"), /* 15 */
8251 N_("SUPPRESSED"), /* 16 */
8252 N_("UNLINKED"), /* 17 */
8253 N_("HTREE INDEX CLEARED"),/* 18 */
8254 N_("WILL RECREATE"), /* 19 */
8255 "", /* 20 */
8256};
8257
8258static const struct e2fsck_problem problem_table[] = {
8259
8260 /* Pre-Pass 1 errors */
8261
8262 /* Block bitmap not in group */
8263 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
8264 PROMPT_RELOCATE, PR_LATCH_RELOC },
8265
8266 /* Inode bitmap not in group */
8267 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
8268 PROMPT_RELOCATE, PR_LATCH_RELOC },
8269
8270 /* Inode table not in group */
8271 { PR_0_ITABLE_NOT_GROUP,
8272 N_("@i table for @g %g is not in @g. (@b %b)\n"
8273 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
8274 PROMPT_RELOCATE, PR_LATCH_RELOC },
8275
8276 /* Superblock corrupt */
8277 { PR_0_SB_CORRUPT,
8278 N_("\nThe @S could not be read or does not describe a correct ext2\n"
8279 "@f. If the @v is valid and it really contains an ext2\n"
8280 "@f (and not swap or ufs or something else), then the @S\n"
8281 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
8282 " e2fsck -b %S <@v>\n\n"),
8283 PROMPT_NONE, PR_FATAL },
8284
8285 /* Filesystem size is wrong */
8286 { PR_0_FS_SIZE_WRONG,
8287 N_("The @f size (according to the @S) is %b @bs\n"
8288 "The physical size of the @v is %c @bs\n"
8289 "Either the @S or the partition table is likely to be corrupt!\n"),
8290 PROMPT_ABORT, 0 },
8291
8292 /* Fragments not supported */
8293 { PR_0_NO_FRAGMENTS,
8294 N_("@S @b_size = %b, fragsize = %c.\n"
8295 "This version of e2fsck does not support fragment sizes different\n"
8296 "from the @b size.\n"),
8297 PROMPT_NONE, PR_FATAL },
8298
8299 /* Bad blocks_per_group */
8300 { PR_0_BLOCKS_PER_GROUP,
8301 N_("@S @bs_per_group = %b, should have been %c\n"),
8302 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8303
8304 /* Bad first_data_block */
8305 { PR_0_FIRST_DATA_BLOCK,
8306 N_("@S first_data_@b = %b, should have been %c\n"),
8307 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8308
8309 /* Adding UUID to filesystem */
8310 { PR_0_ADD_UUID,
8311 N_("@f did not have a UUID; generating one.\n\n"),
8312 PROMPT_NONE, 0 },
8313
8314 /* Relocate hint */
8315 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00008316 N_("Note: if several inode or block bitmap blocks or part\n"
8317 "of the inode table require relocation, you may wish to try\n"
8318 "running e2fsck with the '-b %S' option first. The problem\n"
8319 "may lie only with the primary block group descriptors, and\n"
8320 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008321 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
8322
8323 /* Miscellaneous superblock corruption */
8324 { PR_0_MISC_CORRUPT_SUPER,
8325 N_("Corruption found in @S. (%s = %N).\n"),
8326 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8327
8328 /* Error determing physical device size of filesystem */
8329 { PR_0_GETSIZE_ERROR,
8330 N_("Error determining size of the physical @v: %m\n"),
8331 PROMPT_NONE, PR_FATAL },
8332
8333 /* Inode count in superblock is incorrect */
8334 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00008335 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008336 PROMPT_FIX, 0 },
8337
8338 { PR_0_HURD_CLEAR_FILETYPE,
8339 N_("The Hurd does not support the filetype feature.\n"),
8340 PROMPT_CLEAR, 0 },
8341
8342 /* Journal inode is invalid */
8343 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008344 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008345 PROMPT_CLEAR, PR_PREEN_OK },
8346
8347 /* The external journal has (unsupported) multiple filesystems */
8348 { PR_0_JOURNAL_UNSUPP_MULTIFS,
8349 N_("External @j has multiple @f users (unsupported).\n"),
8350 PROMPT_NONE, PR_FATAL },
8351
8352 /* Can't find external journal */
8353 { PR_0_CANT_FIND_JOURNAL,
8354 N_("Can't find external @j\n"),
8355 PROMPT_NONE, PR_FATAL },
8356
8357 /* External journal has bad superblock */
8358 { PR_0_EXT_JOURNAL_BAD_SUPER,
8359 N_("External @j has bad @S\n"),
8360 PROMPT_NONE, PR_FATAL },
8361
8362 /* Superblock has a bad journal UUID */
8363 { PR_0_JOURNAL_BAD_UUID,
8364 N_("External @j does not support this @f\n"),
8365 PROMPT_NONE, PR_FATAL },
8366
8367 /* Journal has an unknown superblock type */
8368 { PR_0_JOURNAL_UNSUPP_SUPER,
8369 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
8370 "It is likely that your copy of e2fsck is old and/or doesn't "
8371 "support this @j format.\n"
8372 "It is also possible the @j @S is corrupt.\n"),
8373 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
8374
8375 /* Journal superblock is corrupt */
8376 { PR_0_JOURNAL_BAD_SUPER,
8377 N_("Ext3 @j @S is corrupt.\n"),
8378 PROMPT_FIX, PR_PREEN_OK },
8379
8380 /* Superblock flag should be cleared */
8381 { PR_0_JOURNAL_HAS_JOURNAL,
8382 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
8383 PROMPT_CLEAR, PR_PREEN_OK },
8384
8385 /* Superblock flag is incorrect */
8386 { PR_0_JOURNAL_RECOVER_SET,
8387 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
8388 PROMPT_CLEAR, PR_PREEN_OK },
8389
8390 /* Journal has data, but recovery flag is clear */
8391 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008392 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008393 PROMPT_NONE, 0 },
8394
8395 /* Ask if we should clear the journal */
8396 { PR_0_JOURNAL_RESET_JOURNAL,
8397 N_("Clear @j"),
8398 PROMPT_NULL, PR_PREEN_NOMSG },
8399
8400 /* Ask if we should run the journal anyway */
8401 { PR_0_JOURNAL_RUN,
8402 N_("Run @j anyway"),
8403 PROMPT_NULL, 0 },
8404
8405 /* Run the journal by default */
8406 { PR_0_JOURNAL_RUN_DEFAULT,
8407 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
8408 PROMPT_NONE, 0 },
8409
8410 /* Clearing orphan inode */
8411 { PR_0_ORPHAN_CLEAR_INODE,
8412 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
8413 PROMPT_NONE, 0 },
8414
8415 /* Illegal block found in orphaned inode */
8416 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
8417 N_("@I @b #%B (%b) found in @o @i %i.\n"),
8418 PROMPT_NONE, 0 },
8419
8420 /* Already cleared block found in orphaned inode */
8421 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
8422 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
8423 PROMPT_NONE, 0 },
8424
8425 /* Illegal orphan inode in superblock */
8426 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
8427 N_("@I @o @i %i in @S.\n"),
8428 PROMPT_NONE, 0 },
8429
8430 /* Illegal inode in orphaned inode list */
8431 { PR_0_ORPHAN_ILLEGAL_INODE,
8432 N_("@I @i %i in @o @i list.\n"),
8433 PROMPT_NONE, 0 },
8434
8435 /* Filesystem revision is 0, but feature flags are set */
8436 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008437 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008438 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8439
8440 /* Journal superblock has an unknown read-only feature flag set */
8441 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
8442 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
8443 PROMPT_ABORT, 0 },
8444
8445 /* Journal superblock has an unknown incompatible feature flag set */
8446 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
8447 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
8448 PROMPT_ABORT, 0 },
8449
8450 /* Journal has unsupported version number */
8451 { PR_0_JOURNAL_UNSUPP_VERSION,
8452 N_("@j version not supported by this e2fsck.\n"),
8453 PROMPT_ABORT, 0 },
8454
8455 /* Moving journal to hidden file */
8456 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008457 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008458 PROMPT_NONE, 0 },
8459
8460 /* Error moving journal to hidden file */
8461 { PR_0_ERR_MOVE_JOURNAL,
8462 N_("Error moving @j: %m\n\n"),
8463 PROMPT_NONE, 0 },
8464
8465 /* Clearing V2 journal superblock */
8466 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008467 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008468 "Clearing fields beyond the V1 @j @S...\n\n"),
8469 PROMPT_NONE, 0 },
8470
8471 /* Backup journal inode blocks */
8472 { PR_0_BACKUP_JNL,
8473 N_("Backing up @j @i @b information.\n\n"),
8474 PROMPT_NONE, 0 },
8475
8476 /* Reserved blocks w/o resize_inode */
8477 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
8478 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
8479 "is %N; @s zero. "),
8480 PROMPT_FIX, 0 },
8481
8482 /* Resize_inode not enabled, but resize inode is non-zero */
8483 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008484 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008485 PROMPT_CLEAR, 0 },
8486
8487 /* Resize inode invalid */
8488 { PR_0_RESIZE_INODE_INVALID,
8489 N_("Resize @i not valid. "),
8490 PROMPT_RECREATE, 0 },
8491
8492 /* Pass 1 errors */
8493
8494 /* Pass 1: Checking inodes, blocks, and sizes */
8495 { PR_1_PASS_HEADER,
8496 N_("Pass 1: Checking @is, @bs, and sizes\n"),
8497 PROMPT_NONE, 0 },
8498
8499 /* Root directory is not an inode */
8500 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
8501 PROMPT_CLEAR, 0 },
8502
8503 /* Root directory has dtime set */
8504 { PR_1_ROOT_DTIME,
8505 N_("@r has dtime set (probably due to old mke2fs). "),
8506 PROMPT_FIX, PR_PREEN_OK },
8507
8508 /* Reserved inode has bad mode */
8509 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008510 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008511 PROMPT_CLEAR, PR_PREEN_OK },
8512
8513 /* Deleted inode has zero dtime */
8514 { PR_1_ZERO_DTIME,
8515 N_("@D @i %i has zero dtime. "),
8516 PROMPT_FIX, PR_PREEN_OK },
8517
8518 /* Inode in use, but dtime set */
8519 { PR_1_SET_DTIME,
8520 N_("@i %i is in use, but has dtime set. "),
8521 PROMPT_FIX, PR_PREEN_OK },
8522
8523 /* Zero-length directory */
8524 { PR_1_ZERO_LENGTH_DIR,
8525 N_("@i %i is a @z @d. "),
8526 PROMPT_CLEAR, PR_PREEN_OK },
8527
8528 /* Block bitmap conflicts with some other fs block */
8529 { PR_1_BB_CONFLICT,
8530 N_("@g %g's @b @B at %b @C.\n"),
8531 PROMPT_RELOCATE, 0 },
8532
8533 /* Inode bitmap conflicts with some other fs block */
8534 { PR_1_IB_CONFLICT,
8535 N_("@g %g's @i @B at %b @C.\n"),
8536 PROMPT_RELOCATE, 0 },
8537
8538 /* Inode table conflicts with some other fs block */
8539 { PR_1_ITABLE_CONFLICT,
8540 N_("@g %g's @i table at %b @C.\n"),
8541 PROMPT_RELOCATE, 0 },
8542
8543 /* Block bitmap is on a bad block */
8544 { PR_1_BB_BAD_BLOCK,
8545 N_("@g %g's @b @B (%b) is bad. "),
8546 PROMPT_RELOCATE, 0 },
8547
8548 /* Inode bitmap is on a bad block */
8549 { PR_1_IB_BAD_BLOCK,
8550 N_("@g %g's @i @B (%b) is bad. "),
8551 PROMPT_RELOCATE, 0 },
8552
8553 /* Inode has incorrect i_size */
8554 { PR_1_BAD_I_SIZE,
8555 N_("@i %i, i_size is %Is, @s %N. "),
8556 PROMPT_FIX, PR_PREEN_OK },
8557
8558 /* Inode has incorrect i_blocks */
8559 { PR_1_BAD_I_BLOCKS,
8560 N_("@i %i, i_@bs is %Ib, @s %N. "),
8561 PROMPT_FIX, PR_PREEN_OK },
8562
8563 /* Illegal blocknumber in inode */
8564 { PR_1_ILLEGAL_BLOCK_NUM,
8565 N_("@I @b #%B (%b) in @i %i. "),
8566 PROMPT_CLEAR, PR_LATCH_BLOCK },
8567
8568 /* Block number overlaps fs metadata */
8569 { PR_1_BLOCK_OVERLAPS_METADATA,
8570 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
8571 PROMPT_CLEAR, PR_LATCH_BLOCK },
8572
8573 /* Inode has illegal blocks (latch question) */
8574 { PR_1_INODE_BLOCK_LATCH,
8575 N_("@i %i has illegal @b(s). "),
8576 PROMPT_CLEAR, 0 },
8577
8578 /* Too many bad blocks in inode */
8579 { PR_1_TOO_MANY_BAD_BLOCKS,
8580 N_("Too many illegal @bs in @i %i.\n"),
8581 PROMPT_CLEAR_INODE, PR_NO_OK },
8582
8583 /* Illegal block number in bad block inode */
8584 { PR_1_BB_ILLEGAL_BLOCK_NUM,
8585 N_("@I @b #%B (%b) in bad @b @i. "),
8586 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8587
8588 /* Bad block inode has illegal blocks (latch question) */
8589 { PR_1_INODE_BBLOCK_LATCH,
8590 N_("Bad @b @i has illegal @b(s). "),
8591 PROMPT_CLEAR, 0 },
8592
8593 /* Duplicate or bad blocks in use! */
8594 { PR_1_DUP_BLOCKS_PREENSTOP,
8595 N_("Duplicate or bad @b in use!\n"),
8596 PROMPT_NONE, 0 },
8597
8598 /* Bad block used as bad block indirect block */
8599 { PR_1_BBINODE_BAD_METABLOCK,
8600 N_("Bad @b %b used as bad @b @i indirect @b. "),
8601 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8602
8603 /* Inconsistency can't be fixed prompt */
8604 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
8605 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
8606 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
8607 "in the @f.\n"),
8608 PROMPT_CONTINUE, PR_PREEN_NOMSG },
8609
8610 /* Bad primary block */
8611 { PR_1_BAD_PRIMARY_BLOCK,
8612 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
8613 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
8614
8615 /* Bad primary block prompt */
8616 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00008617 N_("You can remove this @b from the bad @b list and hope\n"
8618 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008619 PROMPT_CLEAR, PR_PREEN_NOMSG },
8620
8621 /* Bad primary superblock */
8622 { PR_1_BAD_PRIMARY_SUPERBLOCK,
8623 N_("The primary @S (%b) is on the bad @b list.\n"),
8624 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8625
8626 /* Bad primary block group descriptors */
8627 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
8628 N_("Block %b in the primary @g descriptors "
8629 "is on the bad @b list\n"),
8630 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8631
8632 /* Bad superblock in group */
8633 { PR_1_BAD_SUPERBLOCK,
8634 N_("Warning: Group %g's @S (%b) is bad.\n"),
8635 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8636
8637 /* Bad block group descriptors in group */
8638 { PR_1_BAD_GROUP_DESCRIPTORS,
8639 N_("Warning: Group %g's copy of the @g descriptors has a bad "
8640 "@b (%b).\n"),
8641 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8642
8643 /* Block claimed for no reason */
8644 { PR_1_PROGERR_CLAIMED_BLOCK,
8645 N_("Programming error? @b #%b claimed for no reason in "
8646 "process_bad_@b.\n"),
8647 PROMPT_NONE, PR_PREEN_OK },
8648
8649 /* Error allocating blocks for relocating metadata */
8650 { PR_1_RELOC_BLOCK_ALLOCATE,
8651 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
8652 PROMPT_NONE, PR_PREEN_OK },
8653
8654 /* Error allocating block buffer during relocation process */
8655 { PR_1_RELOC_MEMORY_ALLOCATE,
8656 N_("@A @b buffer for relocating %s\n"),
8657 PROMPT_NONE, PR_PREEN_OK },
8658
8659 /* Relocating metadata group information from X to Y */
8660 { PR_1_RELOC_FROM_TO,
8661 N_("Relocating @g %g's %s from %b to %c...\n"),
8662 PROMPT_NONE, PR_PREEN_OK },
8663
8664 /* Relocating metatdata group information to X */
8665 { PR_1_RELOC_TO,
8666 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
8667 PROMPT_NONE, PR_PREEN_OK },
8668
8669 /* Block read error during relocation process */
8670 { PR_1_RELOC_READ_ERR,
8671 N_("Warning: could not read @b %b of %s: %m\n"),
8672 PROMPT_NONE, PR_PREEN_OK },
8673
8674 /* Block write error during relocation process */
8675 { PR_1_RELOC_WRITE_ERR,
8676 N_("Warning: could not write @b %b for %s: %m\n"),
8677 PROMPT_NONE, PR_PREEN_OK },
8678
8679 /* Error allocating inode bitmap */
8680 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008681 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008682 PROMPT_NONE, PR_FATAL },
8683
8684 /* Error allocating block bitmap */
8685 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008686 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008687 PROMPT_NONE, PR_FATAL },
8688
8689 /* Error allocating icount structure */
8690 { PR_1_ALLOCATE_ICOUNT,
8691 N_("@A icount link information: %m\n"),
8692 PROMPT_NONE, PR_FATAL },
8693
8694 /* Error allocating dbcount */
8695 { PR_1_ALLOCATE_DBCOUNT,
8696 N_("@A @d @b array: %m\n"),
8697 PROMPT_NONE, PR_FATAL },
8698
8699 /* Error while scanning inodes */
8700 { PR_1_ISCAN_ERROR,
8701 N_("Error while scanning @is (%i): %m\n"),
8702 PROMPT_NONE, PR_FATAL },
8703
8704 /* Error while iterating over blocks */
8705 { PR_1_BLOCK_ITERATE,
8706 N_("Error while iterating over @bs in @i %i: %m\n"),
8707 PROMPT_NONE, PR_FATAL },
8708
8709 /* Error while storing inode count information */
8710 { PR_1_ICOUNT_STORE,
8711 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
8712 PROMPT_NONE, PR_FATAL },
8713
8714 /* Error while storing directory block information */
8715 { PR_1_ADD_DBLOCK,
8716 N_("Error storing @d @b information "
8717 "(@i=%i, @b=%b, num=%N): %m\n"),
8718 PROMPT_NONE, PR_FATAL },
8719
8720 /* Error while reading inode (for clearing) */
8721 { PR_1_READ_INODE,
8722 N_("Error reading @i %i: %m\n"),
8723 PROMPT_NONE, PR_FATAL },
8724
8725 /* Suppress messages prompt */
8726 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
8727
8728 /* Imagic flag set on an inode when filesystem doesn't support it */
8729 { PR_1_SET_IMAGIC,
8730 N_("@i %i has imagic flag set. "),
8731 PROMPT_CLEAR, 0 },
8732
8733 /* Immutable flag set on a device or socket inode */
8734 { PR_1_SET_IMMUTABLE,
8735 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
8736 "or append-only flag set. "),
8737 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
8738
8739 /* Compression flag set on an inode when filesystem doesn't support it */
8740 { PR_1_COMPR_SET,
8741 N_("@i %i has @cion flag set on @f without @cion support. "),
8742 PROMPT_CLEAR, 0 },
8743
8744 /* Non-zero size for device, fifo or socket inode */
8745 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008746 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008747 PROMPT_FIX, PR_PREEN_OK },
8748
8749 /* Filesystem revision is 0, but feature flags are set */
8750 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008751 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008752 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8753
8754 /* Journal inode is not in use, but contains data */
8755 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008756 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008757 PROMPT_CLEAR, PR_PREEN_OK },
8758
8759 /* Journal has bad mode */
8760 { PR_1_JOURNAL_BAD_MODE,
8761 N_("@j is not regular file. "),
8762 PROMPT_FIX, PR_PREEN_OK },
8763
8764 /* Deal with inodes that were part of orphan linked list */
8765 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +00008766 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008767 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
8768
8769 /* Deal with inodes that were part of corrupted orphan linked
8770 list (latch question) */
8771 { PR_1_ORPHAN_LIST_REFUGEES,
8772 N_("@is that were part of a corrupted orphan linked list found. "),
8773 PROMPT_FIX, 0 },
8774
8775 /* Error allocating refcount structure */
8776 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008777 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008778 PROMPT_NONE, PR_FATAL },
8779
8780 /* Error reading extended attribute block */
8781 { PR_1_READ_EA_BLOCK,
8782 N_("Error reading @a @b %b for @i %i. "),
8783 PROMPT_CLEAR, 0 },
8784
8785 /* Invalid extended attribute block */
8786 { PR_1_BAD_EA_BLOCK,
8787 N_("@i %i has a bad @a @b %b. "),
8788 PROMPT_CLEAR, 0 },
8789
8790 /* Error reading Extended Attribute block while fixing refcount */
8791 { PR_1_EXTATTR_READ_ABORT,
8792 N_("Error reading @a @b %b (%m). "),
8793 PROMPT_ABORT, 0 },
8794
8795 /* Extended attribute reference count incorrect */
8796 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008797 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008798 PROMPT_FIX, 0 },
8799
8800 /* Error writing Extended Attribute block while fixing refcount */
8801 { PR_1_EXTATTR_WRITE,
8802 N_("Error writing @a @b %b (%m). "),
8803 PROMPT_ABORT, 0 },
8804
8805 /* Multiple EA blocks not supported */
8806 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008807 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008808 PROMPT_CLEAR, 0},
8809
8810 /* Error allocating EA region allocation structure */
8811 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +00008812 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008813 PROMPT_ABORT, 0},
8814
8815 /* Error EA allocation collision */
8816 { PR_1_EA_ALLOC_COLLISION,
8817 N_("@a @b %b is corrupt (allocation collision). "),
8818 PROMPT_CLEAR, 0},
8819
8820 /* Bad extended attribute name */
8821 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00008822 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008823 PROMPT_CLEAR, 0},
8824
8825 /* Bad extended attribute value */
8826 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +00008827 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008828 PROMPT_CLEAR, 0},
8829
8830 /* Inode too big (latch question) */
8831 { PR_1_INODE_TOOBIG,
8832 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
8833
8834 /* Directory too big */
8835 { PR_1_TOOBIG_DIR,
8836 N_("@b #%B (%b) causes @d to be too big. "),
8837 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8838
8839 /* Regular file too big */
8840 { PR_1_TOOBIG_REG,
8841 N_("@b #%B (%b) causes file to be too big. "),
8842 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8843
8844 /* Symlink too big */
8845 { PR_1_TOOBIG_SYMLINK,
8846 N_("@b #%B (%b) causes symlink to be too big. "),
8847 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8848
8849 /* INDEX_FL flag set on a non-HTREE filesystem */
8850 { PR_1_HTREE_SET,
8851 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
8852 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8853
8854 /* INDEX_FL flag set on a non-directory */
8855 { PR_1_HTREE_NODIR,
8856 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
8857 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8858
8859 /* Invalid root node in HTREE directory */
8860 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00008861 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008862 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8863
8864 /* Unsupported hash version in HTREE directory */
8865 { PR_1_HTREE_HASHV,
8866 N_("@h %i has an unsupported hash version (%N)\n"),
8867 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8868
8869 /* Incompatible flag in HTREE root node */
8870 { PR_1_HTREE_INCOMPAT,
8871 N_("@h %i uses an incompatible htree root node flag.\n"),
8872 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8873
8874 /* HTREE too deep */
8875 { PR_1_HTREE_DEPTH,
8876 N_("@h %i has a tree depth (%N) which is too big\n"),
8877 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8878
8879 /* Bad block has indirect block that conflicts with filesystem block */
8880 { PR_1_BB_FS_BLOCK,
8881 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
8882 "@f metadata. "),
8883 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8884
8885 /* Resize inode failed */
8886 { PR_1_RESIZE_INODE_CREATE,
8887 N_("Resize @i (re)creation failed: %m."),
8888 PROMPT_ABORT, 0 },
8889
8890 /* invalid inode->i_extra_isize */
8891 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008892 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008893 PROMPT_FIX, PR_PREEN_OK },
8894
8895 /* invalid ea entry->e_name_len */
8896 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +00008897 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008898 PROMPT_CLEAR, PR_PREEN_OK },
8899
8900 /* invalid ea entry->e_value_size */
8901 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008902 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008903 PROMPT_CLEAR, PR_PREEN_OK },
8904
8905 /* invalid ea entry->e_value_offs */
8906 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +00008907 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008908 PROMPT_CLEAR, PR_PREEN_OK },
8909
8910 /* invalid ea entry->e_value_block */
8911 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008912 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 +00008913 PROMPT_CLEAR, PR_PREEN_OK },
8914
8915 /* invalid ea entry->e_hash */
8916 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +00008917 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 +00008918 PROMPT_CLEAR, PR_PREEN_OK },
8919
8920 /* Pass 1b errors */
8921
8922 /* Pass 1B: Rescan for duplicate/bad blocks */
8923 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008924 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
8925 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008926 PROMPT_NONE, 0 },
8927
8928 /* Duplicate/bad block(s) header */
8929 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008930 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008931 PROMPT_NONE, 0 },
8932
8933 /* Duplicate/bad block(s) in inode */
8934 { PR_1B_DUP_BLOCK,
8935 " %b",
8936 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
8937
8938 /* Duplicate/bad block(s) end */
8939 { PR_1B_DUP_BLOCK_END,
8940 "\n",
8941 PROMPT_NONE, PR_PREEN_NOHDR },
8942
8943 /* Error while scanning inodes */
8944 { PR_1B_ISCAN_ERROR,
8945 N_("Error while scanning inodes (%i): %m\n"),
8946 PROMPT_NONE, PR_FATAL },
8947
8948 /* Error allocating inode bitmap */
8949 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008950 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008951 PROMPT_NONE, PR_FATAL },
8952
8953 /* Error while iterating over blocks */
8954 { PR_1B_BLOCK_ITERATE,
8955 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
8956 PROMPT_NONE, 0 },
8957
8958 /* Error adjusting EA refcount */
8959 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008960 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008961 PROMPT_NONE, 0 },
8962
8963
Mike Frysinger874af852006-03-08 07:03:27 +00008964 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008965 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008966 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008967 PROMPT_NONE, 0 },
8968
8969
Mike Frysinger874af852006-03-08 07:03:27 +00008970 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008971 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008972 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008973 PROMPT_NONE, 0 },
8974
8975 /* File has duplicate blocks */
8976 { PR_1D_DUP_FILE,
8977 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +00008978 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008979 PROMPT_NONE, 0 },
8980
8981 /* List of files sharing duplicate blocks */
8982 { PR_1D_DUP_FILE_LIST,
8983 N_("\t%Q (@i #%i, mod time %IM)\n"),
8984 PROMPT_NONE, 0 },
8985
8986 /* File sharing blocks with filesystem metadata */
8987 { PR_1D_SHARE_METADATA,
8988 N_("\t<@f metadata>\n"),
8989 PROMPT_NONE, 0 },
8990
8991 /* Report of how many duplicate/bad inodes */
8992 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +00008993 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008994 PROMPT_NONE, 0 },
8995
8996 /* Duplicated blocks already reassigned or cloned. */
8997 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +00008998 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008999 PROMPT_NONE, 0 },
9000
9001 /* Clone duplicate/bad blocks? */
9002 { PR_1D_CLONE_QUESTION,
9003 "", PROMPT_CLONE, PR_NO_OK },
9004
9005 /* Delete file? */
9006 { PR_1D_DELETE_QUESTION,
9007 "", PROMPT_DELETE, 0 },
9008
9009 /* Couldn't clone file (error) */
9010 { PR_1D_CLONE_ERROR,
9011 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
9012
9013 /* Pass 2 errors */
9014
9015 /* Pass 2: Checking directory structure */
9016 { PR_2_PASS_HEADER,
9017 N_("Pass 2: Checking @d structure\n"),
9018 PROMPT_NONE, 0 },
9019
9020 /* Bad inode number for '.' */
9021 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009022 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009023 PROMPT_FIX, 0 },
9024
9025 /* Directory entry has bad inode number */
9026 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +00009027 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009028 PROMPT_CLEAR, 0 },
9029
9030 /* Directory entry has deleted or unused inode */
9031 { PR_2_UNUSED_INODE,
9032 N_("@E has @D/unused @i %Di. "),
9033 PROMPT_CLEAR, PR_PREEN_OK },
9034
9035 /* Directry entry is link to '.' */
9036 { PR_2_LINK_DOT,
9037 N_("@E @L to '.' "),
9038 PROMPT_CLEAR, 0 },
9039
9040 /* Directory entry points to inode now located in a bad block */
9041 { PR_2_BB_INODE,
9042 N_("@E points to @i (%Di) located in a bad @b.\n"),
9043 PROMPT_CLEAR, 0 },
9044
9045 /* Directory entry contains a link to a directory */
9046 { PR_2_LINK_DIR,
9047 N_("@E @L to @d %P (%Di).\n"),
9048 PROMPT_CLEAR, 0 },
9049
9050 /* Directory entry contains a link to the root directry */
9051 { PR_2_LINK_ROOT,
9052 N_("@E @L to the @r.\n"),
9053 PROMPT_CLEAR, 0 },
9054
9055 /* Directory entry has illegal characters in its name */
9056 { PR_2_BAD_NAME,
9057 N_("@E has illegal characters in its name.\n"),
9058 PROMPT_FIX, 0 },
9059
9060 /* Missing '.' in directory inode */
9061 { PR_2_MISSING_DOT,
9062 N_("Missing '.' in @d @i %i.\n"),
9063 PROMPT_FIX, 0 },
9064
9065 /* Missing '..' in directory inode */
9066 { PR_2_MISSING_DOT_DOT,
9067 N_("Missing '..' in @d @i %i.\n"),
9068 PROMPT_FIX, 0 },
9069
9070 /* First entry in directory inode doesn't contain '.' */
9071 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009072 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009073 PROMPT_FIX, 0 },
9074
9075 /* Second entry in directory inode doesn't contain '..' */
9076 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009077 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009078 PROMPT_FIX, 0 },
9079
9080 /* i_faddr should be zero */
9081 { PR_2_FADDR_ZERO,
9082 N_("i_faddr @F %IF, @s zero.\n"),
9083 PROMPT_CLEAR, 0 },
9084
9085 /* i_file_acl should be zero */
9086 { PR_2_FILE_ACL_ZERO,
9087 N_("i_file_acl @F %If, @s zero.\n"),
9088 PROMPT_CLEAR, 0 },
9089
9090 /* i_dir_acl should be zero */
9091 { PR_2_DIR_ACL_ZERO,
9092 N_("i_dir_acl @F %Id, @s zero.\n"),
9093 PROMPT_CLEAR, 0 },
9094
9095 /* i_frag should be zero */
9096 { PR_2_FRAG_ZERO,
9097 N_("i_frag @F %N, @s zero.\n"),
9098 PROMPT_CLEAR, 0 },
9099
9100 /* i_fsize should be zero */
9101 { PR_2_FSIZE_ZERO,
9102 N_("i_fsize @F %N, @s zero.\n"),
9103 PROMPT_CLEAR, 0 },
9104
9105 /* inode has bad mode */
9106 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009107 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009108 PROMPT_CLEAR, 0 },
9109
9110 /* directory corrupted */
9111 { PR_2_DIR_CORRUPTED,
9112 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
9113 PROMPT_SALVAGE, 0 },
9114
9115 /* filename too long */
9116 { PR_2_FILENAME_LONG,
9117 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
9118 PROMPT_TRUNCATE, 0 },
9119
9120 /* Directory inode has a missing block (hole) */
9121 { PR_2_DIRECTORY_HOLE,
9122 N_("@d @i %i has an unallocated @b #%B. "),
9123 PROMPT_ALLOCATE, 0 },
9124
9125 /* '.' is not NULL terminated */
9126 { PR_2_DOT_NULL_TERM,
9127 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
9128 PROMPT_FIX, 0 },
9129
9130 /* '..' is not NULL terminated */
9131 { PR_2_DOT_DOT_NULL_TERM,
9132 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
9133 PROMPT_FIX, 0 },
9134
9135 /* Illegal character device inode */
9136 { PR_2_BAD_CHAR_DEV,
9137 N_("@i %i (%Q) is an @I character @v.\n"),
9138 PROMPT_CLEAR, 0 },
9139
9140 /* Illegal block device inode */
9141 { PR_2_BAD_BLOCK_DEV,
9142 N_("@i %i (%Q) is an @I @b @v.\n"),
9143 PROMPT_CLEAR, 0 },
9144
9145 /* Duplicate '.' entry */
9146 { PR_2_DUP_DOT,
9147 N_("@E is duplicate '.' @e.\n"),
9148 PROMPT_FIX, 0 },
9149
9150 /* Duplicate '..' entry */
9151 { PR_2_DUP_DOT_DOT,
9152 N_("@E is duplicate '..' @e.\n"),
9153 PROMPT_FIX, 0 },
9154
9155 /* Internal error: couldn't find dir_info */
9156 { PR_2_NO_DIRINFO,
9157 N_("Internal error: couldn't find dir_info for %i.\n"),
9158 PROMPT_NONE, PR_FATAL },
9159
9160 /* Final rec_len is wrong */
9161 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009162 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009163 PROMPT_FIX, 0 },
9164
9165 /* Error allocating icount structure */
9166 { PR_2_ALLOCATE_ICOUNT,
9167 N_("@A icount structure: %m\n"),
9168 PROMPT_NONE, PR_FATAL },
9169
9170 /* Error iterating over directory blocks */
9171 { PR_2_DBLIST_ITERATE,
9172 N_("Error iterating over @d @bs: %m\n"),
9173 PROMPT_NONE, PR_FATAL },
9174
9175 /* Error reading directory block */
9176 { PR_2_READ_DIRBLOCK,
9177 N_("Error reading @d @b %b (@i %i): %m\n"),
9178 PROMPT_CONTINUE, 0 },
9179
9180 /* Error writing directory block */
9181 { PR_2_WRITE_DIRBLOCK,
9182 N_("Error writing @d @b %b (@i %i): %m\n"),
9183 PROMPT_CONTINUE, 0 },
9184
9185 /* Error allocating new directory block */
9186 { PR_2_ALLOC_DIRBOCK,
9187 N_("@A new @d @b for @i %i (%s): %m\n"),
9188 PROMPT_NONE, 0 },
9189
9190 /* Error deallocating inode */
9191 { PR_2_DEALLOC_INODE,
9192 N_("Error deallocating @i %i: %m\n"),
9193 PROMPT_NONE, PR_FATAL },
9194
9195 /* Directory entry for '.' is big. Split? */
9196 { PR_2_SPLIT_DOT,
9197 N_("@d @e for '.' is big. "),
9198 PROMPT_SPLIT, PR_NO_OK },
9199
9200 /* Illegal FIFO inode */
9201 { PR_2_BAD_FIFO,
9202 N_("@i %i (%Q) is an @I FIFO.\n"),
9203 PROMPT_CLEAR, 0 },
9204
9205 /* Illegal socket inode */
9206 { PR_2_BAD_SOCKET,
9207 N_("@i %i (%Q) is an @I socket.\n"),
9208 PROMPT_CLEAR, 0 },
9209
9210 /* Directory filetype not set */
9211 { PR_2_SET_FILETYPE,
9212 N_("Setting filetype for @E to %N.\n"),
9213 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
9214
9215 /* Directory filetype incorrect */
9216 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +00009217 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009218 PROMPT_FIX, 0 },
9219
9220 /* Directory filetype set on filesystem */
9221 { PR_2_CLEAR_FILETYPE,
9222 N_("@E has filetype set.\n"),
9223 PROMPT_CLEAR, PR_PREEN_OK },
9224
9225 /* Directory filename is null */
9226 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009227 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009228 PROMPT_CLEAR, 0 },
9229
9230 /* Invalid symlink */
9231 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +00009232 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009233 PROMPT_CLEAR, 0 },
9234
9235 /* i_file_acl (extended attribute block) is bad */
9236 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +00009237 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009238 PROMPT_CLEAR, 0 },
9239
9240 /* Filesystem contains large files, but has no such flag in sb */
9241 { PR_2_FEATURE_LARGE_FILES,
9242 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
9243 PROMPT_FIX, 0 },
9244
9245 /* Node in HTREE directory not referenced */
9246 { PR_2_HTREE_NOTREF,
9247 N_("@p @h %d: node (%B) not referenced\n"),
9248 PROMPT_NONE, 0 },
9249
9250 /* Node in HTREE directory referenced twice */
9251 { PR_2_HTREE_DUPREF,
9252 N_("@p @h %d: node (%B) referenced twice\n"),
9253 PROMPT_NONE, 0 },
9254
9255 /* Node in HTREE directory has bad min hash */
9256 { PR_2_HTREE_MIN_HASH,
9257 N_("@p @h %d: node (%B) has bad min hash\n"),
9258 PROMPT_NONE, 0 },
9259
9260 /* Node in HTREE directory has bad max hash */
9261 { PR_2_HTREE_MAX_HASH,
9262 N_("@p @h %d: node (%B) has bad max hash\n"),
9263 PROMPT_NONE, 0 },
9264
9265 /* Clear invalid HTREE directory */
9266 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009267 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009268
9269 /* Bad block in htree interior node */
9270 { PR_2_HTREE_BADBLK,
9271 N_("@p @h %d (%q): bad @b number %b.\n"),
9272 PROMPT_CLEAR_HTREE, 0 },
9273
9274 /* Error adjusting EA refcount */
9275 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009276 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009277 PROMPT_NONE, PR_FATAL },
9278
9279 /* Invalid HTREE root node */
9280 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009281 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009282 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9283
9284 /* Invalid HTREE limit */
9285 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +00009286 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009287 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9288
9289 /* Invalid HTREE count */
9290 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009291 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009292 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9293
9294 /* HTREE interior node has out-of-order hashes in table */
9295 { PR_2_HTREE_HASH_ORDER,
9296 N_("@p @h %d: node (%B) has an unordered hash table\n"),
9297 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9298
Mike Frysinger874af852006-03-08 07:03:27 +00009299 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009300 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +00009301 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009302 PROMPT_NONE, 0 },
9303
9304 /* Duplicate directory entry found */
9305 { PR_2_DUPLICATE_DIRENT,
9306 N_("Duplicate @E found. "),
9307 PROMPT_CLEAR, 0 },
9308
9309 /* Non-unique filename found */
9310 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
9311 N_("@E has a non-unique filename.\nRename to %s"),
9312 PROMPT_NULL, 0 },
9313
9314 /* Duplicate directory entry found */
9315 { PR_2_REPORT_DUP_DIRENT,
9316 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
9317 PROMPT_NONE, 0 },
9318
9319 /* Pass 3 errors */
9320
9321 /* Pass 3: Checking directory connectivity */
9322 { PR_3_PASS_HEADER,
9323 N_("Pass 3: Checking @d connectivity\n"),
9324 PROMPT_NONE, 0 },
9325
9326 /* Root inode not allocated */
9327 { PR_3_NO_ROOT_INODE,
9328 N_("@r not allocated. "),
9329 PROMPT_ALLOCATE, 0 },
9330
9331 /* No room in lost+found */
9332 { PR_3_EXPAND_LF_DIR,
9333 N_("No room in @l @d. "),
9334 PROMPT_EXPAND, 0 },
9335
9336 /* Unconnected directory inode */
9337 { PR_3_UNCONNECTED_DIR,
9338 N_("Unconnected @d @i %i (%p)\n"),
9339 PROMPT_CONNECT, 0 },
9340
9341 /* /lost+found not found */
9342 { PR_3_NO_LF_DIR,
9343 N_("/@l not found. "),
9344 PROMPT_CREATE, PR_PREEN_OK },
9345
9346 /* .. entry is incorrect */
9347 { PR_3_BAD_DOT_DOT,
9348 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
9349 PROMPT_FIX, 0 },
9350
9351 /* Bad or non-existent /lost+found. Cannot reconnect */
9352 { PR_3_NO_LPF,
9353 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
9354 PROMPT_NONE, 0 },
9355
9356 /* Could not expand /lost+found */
9357 { PR_3_CANT_EXPAND_LPF,
9358 N_("Could not expand /@l: %m\n"),
9359 PROMPT_NONE, 0 },
9360
9361 /* Could not reconnect inode */
9362 { PR_3_CANT_RECONNECT,
9363 N_("Could not reconnect %i: %m\n"),
9364 PROMPT_NONE, 0 },
9365
9366 /* Error while trying to find /lost+found */
9367 { PR_3_ERR_FIND_LPF,
9368 N_("Error while trying to find /@l: %m\n"),
9369 PROMPT_NONE, 0 },
9370
9371 /* Error in ext2fs_new_block while creating /lost+found */
9372 { PR_3_ERR_LPF_NEW_BLOCK,
9373 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
9374 PROMPT_NONE, 0 },
9375
9376 /* Error in ext2fs_new_inode while creating /lost+found */
9377 { PR_3_ERR_LPF_NEW_INODE,
9378 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
9379 PROMPT_NONE, 0 },
9380
9381 /* Error in ext2fs_new_dir_block while creating /lost+found */
9382 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
9383 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
9384 PROMPT_NONE, 0 },
9385
9386 /* Error while writing directory block for /lost+found */
9387 { PR_3_ERR_LPF_WRITE_BLOCK,
9388 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
9389 PROMPT_NONE, 0 },
9390
9391 /* Error while adjusting inode count */
9392 { PR_3_ADJUST_INODE,
9393 N_("Error while adjusting @i count on @i %i\n"),
9394 PROMPT_NONE, 0 },
9395
9396 /* Couldn't fix parent directory -- error */
9397 { PR_3_FIX_PARENT_ERR,
9398 N_("Couldn't fix parent of @i %i: %m\n\n"),
9399 PROMPT_NONE, 0 },
9400
9401 /* Couldn't fix parent directory -- couldn't find it */
9402 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +00009403 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 +00009404 PROMPT_NONE, 0 },
9405
9406 /* Error allocating inode bitmap */
9407 { PR_3_ALLOCATE_IBITMAP_ERROR,
9408 N_("@A @i @B (%N): %m\n"),
9409 PROMPT_NONE, PR_FATAL },
9410
9411 /* Error creating root directory */
9412 { PR_3_CREATE_ROOT_ERROR,
9413 N_("Error creating root @d (%s): %m\n"),
9414 PROMPT_NONE, PR_FATAL },
9415
9416 /* Error creating lost and found directory */
9417 { PR_3_CREATE_LPF_ERROR,
9418 N_("Error creating /@l @d (%s): %m\n"),
9419 PROMPT_NONE, PR_FATAL },
9420
9421 /* Root inode is not directory; aborting */
9422 { PR_3_ROOT_NOT_DIR_ABORT,
9423 N_("@r is not a @d; aborting.\n"),
9424 PROMPT_NONE, PR_FATAL },
9425
9426 /* Cannot proceed without a root inode. */
9427 { PR_3_NO_ROOT_INODE_ABORT,
9428 N_("Cannot proceed without a @r.\n"),
9429 PROMPT_NONE, PR_FATAL },
9430
9431 /* Internal error: couldn't find dir_info */
9432 { PR_3_NO_DIRINFO,
9433 N_("Internal error: couldn't find dir_info for %i.\n"),
9434 PROMPT_NONE, PR_FATAL },
9435
9436 /* Lost+found not a directory */
9437 { PR_3_LPF_NOTDIR,
9438 N_("/@l is not a @d (ino=%i)\n"),
9439 PROMPT_UNLINK, 0 },
9440
9441 /* Pass 3A Directory Optimization */
9442
9443 /* Pass 3A: Optimizing directories */
9444 { PR_3A_PASS_HEADER,
9445 N_("Pass 3A: Optimizing directories\n"),
9446 PROMPT_NONE, PR_PREEN_NOMSG },
9447
9448 /* Error iterating over directories */
9449 { PR_3A_OPTIMIZE_ITER,
9450 N_("Failed to create dirs_to_hash iterator: %m"),
9451 PROMPT_NONE, 0 },
9452
9453 /* Error rehash directory */
9454 { PR_3A_OPTIMIZE_DIR_ERR,
9455 N_("Failed to optimize directory %q (%d): %m"),
9456 PROMPT_NONE, 0 },
9457
9458 /* Rehashing dir header */
9459 { PR_3A_OPTIMIZE_DIR_HEADER,
9460 N_("Optimizing directories: "),
9461 PROMPT_NONE, PR_MSG_ONLY },
9462
9463 /* Rehashing directory %d */
9464 { PR_3A_OPTIMIZE_DIR,
9465 " %d",
9466 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
9467
9468 /* Rehashing dir end */
9469 { PR_3A_OPTIMIZE_DIR_END,
9470 "\n",
9471 PROMPT_NONE, PR_PREEN_NOHDR },
9472
9473 /* Pass 4 errors */
9474
9475 /* Pass 4: Checking reference counts */
9476 { PR_4_PASS_HEADER,
9477 N_("Pass 4: Checking reference counts\n"),
9478 PROMPT_NONE, 0 },
9479
9480 /* Unattached zero-length inode */
9481 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009482 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009483 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
9484
9485 /* Unattached inode */
9486 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009487 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009488 PROMPT_CONNECT, 0 },
9489
9490 /* Inode ref count wrong */
9491 { PR_4_BAD_REF_COUNT,
9492 N_("@i %i ref count is %Il, @s %N. "),
9493 PROMPT_FIX, PR_PREEN_OK },
9494
9495 { PR_4_INCONSISTENT_COUNT,
9496 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
9497 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
9498 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +00009499 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009500 PROMPT_NONE, 0 },
9501
9502 /* Pass 5 errors */
9503
9504 /* Pass 5: Checking group summary information */
9505 { PR_5_PASS_HEADER,
9506 N_("Pass 5: Checking @g summary information\n"),
9507 PROMPT_NONE, 0 },
9508
9509 /* Padding at end of inode bitmap is not set. */
9510 { PR_5_INODE_BMAP_PADDING,
9511 N_("Padding at end of @i @B is not set. "),
9512 PROMPT_FIX, PR_PREEN_OK },
9513
9514 /* Padding at end of block bitmap is not set. */
9515 { PR_5_BLOCK_BMAP_PADDING,
9516 N_("Padding at end of @b @B is not set. "),
9517 PROMPT_FIX, PR_PREEN_OK },
9518
9519 /* Block bitmap differences header */
9520 { PR_5_BLOCK_BITMAP_HEADER,
9521 N_("@b @B differences: "),
9522 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
9523
9524 /* Block not used, but marked in bitmap */
9525 { PR_5_BLOCK_UNUSED,
9526 " -%b",
9527 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9528
9529 /* Block used, but not marked used in bitmap */
9530 { PR_5_BLOCK_USED,
9531 " +%b",
9532 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9533
9534 /* Block bitmap differences end */
9535 { PR_5_BLOCK_BITMAP_END,
9536 "\n",
9537 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9538
9539 /* Inode bitmap differences header */
9540 { PR_5_INODE_BITMAP_HEADER,
9541 N_("@i @B differences: "),
9542 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9543
9544 /* Inode not used, but marked in bitmap */
9545 { PR_5_INODE_UNUSED,
9546 " -%i",
9547 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9548
9549 /* Inode used, but not marked used in bitmap */
9550 { PR_5_INODE_USED,
9551 " +%i",
9552 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9553
9554 /* Inode bitmap differences end */
9555 { PR_5_INODE_BITMAP_END,
9556 "\n",
9557 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9558
9559 /* Free inodes count for group wrong */
9560 { PR_5_FREE_INODE_COUNT_GROUP,
9561 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
9562 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9563
9564 /* Directories count for group wrong */
9565 { PR_5_FREE_DIR_COUNT_GROUP,
9566 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
9567 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9568
9569 /* Free inodes count wrong */
9570 { PR_5_FREE_INODE_COUNT,
9571 N_("Free @is count wrong (%i, counted=%j).\n"),
9572 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9573
9574 /* Free blocks count for group wrong */
9575 { PR_5_FREE_BLOCK_COUNT_GROUP,
9576 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
9577 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9578
9579 /* Free blocks count wrong */
9580 { PR_5_FREE_BLOCK_COUNT,
9581 N_("Free @bs count wrong (%b, counted=%c).\n"),
9582 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9583
9584 /* Programming error: bitmap endpoints don't match */
9585 { PR_5_BMAP_ENDPOINTS,
9586 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
9587 "match calculated @B endpoints (%i, %j)\n"),
9588 PROMPT_NONE, PR_FATAL },
9589
9590 /* Internal error: fudging end of bitmap */
9591 { PR_5_FUDGE_BITMAP_ERROR,
9592 N_("Internal error: fudging end of bitmap (%N)\n"),
9593 PROMPT_NONE, PR_FATAL },
9594
9595 /* Error copying in replacement inode bitmap */
9596 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009597 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009598 PROMPT_NONE, PR_FATAL },
9599
9600 /* Error copying in replacement block bitmap */
9601 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009602 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009603 PROMPT_NONE, PR_FATAL },
9604
9605 /* Block range not used, but marked in bitmap */
9606 { PR_5_BLOCK_RANGE_UNUSED,
9607 " -(%b--%c)",
9608 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9609
9610 /* Block range used, but not marked used in bitmap */
9611 { PR_5_BLOCK_RANGE_USED,
9612 " +(%b--%c)",
9613 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9614
9615 /* Inode range not used, but marked in bitmap */
9616 { PR_5_INODE_RANGE_UNUSED,
9617 " -(%i--%j)",
9618 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9619
9620 /* Inode range used, but not marked used in bitmap */
9621 { PR_5_INODE_RANGE_USED,
9622 " +(%i--%j)",
9623 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9624
9625 { 0 }
9626};
9627
9628/*
9629 * This is the latch flags register. It allows several problems to be
9630 * "latched" together. This means that the user has to answer but one
9631 * question for the set of problems, and all of the associated
9632 * problems will be either fixed or not fixed.
9633 */
9634static struct latch_descr pr_latch_info[] = {
9635 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
9636 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
9637 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
9638 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
9639 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
9640 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
9641 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
9642 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
9643 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
9644 { -1, 0, 0 },
9645};
9646
9647static const struct e2fsck_problem *find_problem(problem_t code)
9648{
9649 int i;
9650
9651 for (i=0; problem_table[i].e2p_code; i++) {
9652 if (problem_table[i].e2p_code == code)
9653 return &problem_table[i];
9654 }
9655 return 0;
9656}
9657
9658static struct latch_descr *find_latch(int code)
9659{
9660 int i;
9661
9662 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
9663 if (pr_latch_info[i].latch_code == code)
9664 return &pr_latch_info[i];
9665 }
9666 return 0;
9667}
9668
9669int end_problem_latch(e2fsck_t ctx, int mask)
9670{
9671 struct latch_descr *ldesc;
9672 struct problem_context pctx;
9673 int answer = -1;
9674
9675 ldesc = find_latch(mask);
9676 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
9677 clear_problem_context(&pctx);
9678 answer = fix_problem(ctx, ldesc->end_message, &pctx);
9679 }
9680 ldesc->flags &= ~(PRL_VARIABLE);
9681 return answer;
9682}
9683
9684int set_latch_flags(int mask, int setflags, int clearflags)
9685{
9686 struct latch_descr *ldesc;
9687
9688 ldesc = find_latch(mask);
9689 if (!ldesc)
9690 return -1;
9691 ldesc->flags |= setflags;
9692 ldesc->flags &= ~clearflags;
9693 return 0;
9694}
9695
9696void clear_problem_context(struct problem_context *ctx)
9697{
9698 memset(ctx, 0, sizeof(struct problem_context));
9699 ctx->blkcount = -1;
9700 ctx->group = -1;
9701}
9702
9703int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
9704{
9705 ext2_filsys fs = ctx->fs;
9706 const struct e2fsck_problem *ptr;
9707 struct latch_descr *ldesc = 0;
9708 const char *message;
9709 int def_yn, answer, ans;
9710 int print_answer = 0;
9711 int suppress = 0;
9712
9713 ptr = find_problem(code);
9714 if (!ptr) {
9715 printf(_("Unhandled error code (0x%x)!\n"), code);
9716 return 0;
9717 }
9718 def_yn = 1;
9719 if ((ptr->flags & PR_NO_DEFAULT) ||
9720 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
9721 (ctx->options & E2F_OPT_NO))
9722 def_yn= 0;
9723
9724 /*
9725 * Do special latch processing. This is where we ask the
9726 * latch question, if it exists
9727 */
9728 if (ptr->flags & PR_LATCH_MASK) {
9729 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
9730 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
9731 ans = fix_problem(ctx, ldesc->question, pctx);
9732 if (ans == 1)
9733 ldesc->flags |= PRL_YES;
9734 if (ans == 0)
9735 ldesc->flags |= PRL_NO;
9736 ldesc->flags |= PRL_LATCHED;
9737 }
9738 if (ldesc->flags & PRL_SUPPRESS)
9739 suppress++;
9740 }
9741 if ((ptr->flags & PR_PREEN_NOMSG) &&
9742 (ctx->options & E2F_OPT_PREEN))
9743 suppress++;
9744 if ((ptr->flags & PR_NO_NOMSG) &&
9745 (ctx->options & E2F_OPT_NO))
9746 suppress++;
9747 if (!suppress) {
9748 message = ptr->e2p_description;
9749 if ((ctx->options & E2F_OPT_PREEN) &&
9750 !(ptr->flags & PR_PREEN_NOHDR)) {
9751 printf("%s: ", ctx->device_name ?
9752 ctx->device_name : ctx->filesystem_name);
9753 }
9754 if (*message)
9755 print_e2fsck_message(ctx, _(message), pctx, 1);
9756 }
9757 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
9758 preenhalt(ctx);
9759
9760 if (ptr->flags & PR_FATAL)
Rob Landley7c94bed2006-05-03 21:58:45 +00009761 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009762
9763 if (ptr->prompt == PROMPT_NONE) {
9764 if (ptr->flags & PR_NOCOLLATE)
9765 answer = -1;
9766 else
9767 answer = def_yn;
9768 } else {
9769 if (ctx->options & E2F_OPT_PREEN) {
9770 answer = def_yn;
9771 if (!(ptr->flags & PR_PREEN_NOMSG))
9772 print_answer = 1;
9773 } else if ((ptr->flags & PR_LATCH_MASK) &&
9774 (ldesc->flags & (PRL_YES | PRL_NO))) {
9775 if (!suppress)
9776 print_answer = 1;
9777 if (ldesc->flags & PRL_YES)
9778 answer = 1;
9779 else
9780 answer = 0;
9781 } else
9782 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
9783 if (!answer && !(ptr->flags & PR_NO_OK))
9784 ext2fs_unmark_valid(fs);
9785
9786 if (print_answer)
9787 printf("%s.\n", answer ?
9788 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
9789
9790 }
9791
9792 if ((ptr->prompt == PROMPT_ABORT) && answer)
Rob Landley7c94bed2006-05-03 21:58:45 +00009793 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009794
9795 if (ptr->flags & PR_AFTER_CODE)
9796 answer = fix_problem(ctx, ptr->second_code, pctx);
9797
9798 return answer;
9799}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009800
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009801/*
9802 * linux/fs/recovery.c
9803 *
9804 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009805 */
9806
9807/*
9808 * Maintain information about the progress of the recovery job, so that
9809 * the different passes can carry information between them.
9810 */
9811struct recovery_info
9812{
9813 tid_t start_transaction;
9814 tid_t end_transaction;
9815
9816 int nr_replays;
9817 int nr_revokes;
9818 int nr_revoke_hits;
9819};
9820
9821enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
9822static int do_one_pass(journal_t *journal,
9823 struct recovery_info *info, enum passtype pass);
9824static int scan_revoke_records(journal_t *, struct buffer_head *,
9825 tid_t, struct recovery_info *);
9826
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009827/*
9828 * Read a block from the journal
9829 */
9830
9831static int jread(struct buffer_head **bhp, journal_t *journal,
9832 unsigned int offset)
9833{
9834 int err;
9835 unsigned long blocknr;
9836 struct buffer_head *bh;
9837
9838 *bhp = NULL;
9839
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009840 err = journal_bmap(journal, offset, &blocknr);
9841
9842 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +00009843 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009844 return err;
9845 }
9846
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009847 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009848 if (!bh)
9849 return -ENOMEM;
9850
9851 if (!buffer_uptodate(bh)) {
9852 /* If this is a brand new buffer, start readahead.
9853 Otherwise, we assume we are already reading it. */
9854 if (!buffer_req(bh))
9855 do_readahead(journal, offset);
9856 wait_on_buffer(bh);
9857 }
9858
9859 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +00009860 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009861 brelse(bh);
9862 return -EIO;
9863 }
9864
9865 *bhp = bh;
9866 return 0;
9867}
9868
9869
9870/*
9871 * Count the number of in-use tags in a journal descriptor block.
9872 */
9873
9874static int count_tags(struct buffer_head *bh, int size)
9875{
9876 char * tagp;
9877 journal_block_tag_t * tag;
9878 int nr = 0;
9879
9880 tagp = &bh->b_data[sizeof(journal_header_t)];
9881
9882 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
9883 tag = (journal_block_tag_t *) tagp;
9884
9885 nr++;
9886 tagp += sizeof(journal_block_tag_t);
9887 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
9888 tagp += 16;
9889
9890 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
9891 break;
9892 }
9893
9894 return nr;
9895}
9896
9897
9898/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00009899#define wrap(journal, var) \
9900do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009901 if (var >= (journal)->j_last) \
9902 var -= ((journal)->j_last - (journal)->j_first); \
9903} while (0)
9904
9905/**
9906 * int journal_recover(journal_t *journal) - recovers a on-disk journal
9907 * @journal: the journal to recover
9908 *
9909 * The primary function for recovering the log contents when mounting a
9910 * journaled device.
9911 *
9912 * Recovery is done in three passes. In the first pass, we look for the
9913 * end of the log. In the second, we assemble the list of revoke
9914 * blocks. In the third and final pass, we replay any un-revoked blocks
9915 * in the log.
9916 */
9917int journal_recover(journal_t *journal)
9918{
9919 int err;
9920 journal_superblock_t * sb;
9921
9922 struct recovery_info info;
9923
9924 memset(&info, 0, sizeof(info));
9925 sb = journal->j_superblock;
9926
9927 /*
9928 * The journal superblock's s_start field (the current log head)
9929 * is always zero if, and only if, the journal was cleanly
9930 * unmounted.
9931 */
9932
9933 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009934 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
9935 return 0;
9936 }
9937
9938 err = do_one_pass(journal, &info, PASS_SCAN);
9939 if (!err)
9940 err = do_one_pass(journal, &info, PASS_REVOKE);
9941 if (!err)
9942 err = do_one_pass(journal, &info, PASS_REPLAY);
9943
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009944 /* Restart the log at the next transaction ID, thus invalidating
9945 * any existing commit records in the log. */
9946 journal->j_transaction_sequence = ++info.end_transaction;
9947
9948 journal_clear_revoke(journal);
9949 sync_blockdev(journal->j_fs_dev);
9950 return err;
9951}
9952
9953static int do_one_pass(journal_t *journal,
9954 struct recovery_info *info, enum passtype pass)
9955{
9956 unsigned int first_commit_ID, next_commit_ID;
9957 unsigned long next_log_block;
9958 int err, success = 0;
9959 journal_superblock_t * sb;
9960 journal_header_t * tmp;
9961 struct buffer_head * bh;
9962 unsigned int sequence;
9963 int blocktype;
9964
9965 /* Precompute the maximum metadata descriptors in a descriptor block */
9966 int MAX_BLOCKS_PER_DESC;
9967 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
9968 / sizeof(journal_block_tag_t));
9969
9970 /*
9971 * First thing is to establish what we expect to find in the log
9972 * (in terms of transaction IDs), and where (in terms of log
9973 * block offsets): query the superblock.
9974 */
9975
9976 sb = journal->j_superblock;
9977 next_commit_ID = ntohl(sb->s_sequence);
9978 next_log_block = ntohl(sb->s_start);
9979
9980 first_commit_ID = next_commit_ID;
9981 if (pass == PASS_SCAN)
9982 info->start_transaction = first_commit_ID;
9983
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009984 /*
9985 * Now we walk through the log, transaction by transaction,
9986 * making sure that each transaction has a commit block in the
9987 * expected place. Each complete transaction gets replayed back
9988 * into the main filesystem.
9989 */
9990
9991 while (1) {
9992 int flags;
9993 char * tagp;
9994 journal_block_tag_t * tag;
9995 struct buffer_head * obh;
9996 struct buffer_head * nbh;
9997
9998 /* If we already know where to stop the log traversal,
9999 * check right now that we haven't gone past the end of
10000 * the log. */
10001
10002 if (pass != PASS_SCAN)
10003 if (tid_geq(next_commit_ID, info->end_transaction))
10004 break;
10005
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010006 /* Skip over each chunk of the transaction looking
10007 * either the next descriptor block or the final commit
10008 * record. */
10009
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010010 err = jread(&bh, journal, next_log_block);
10011 if (err)
10012 goto failed;
10013
10014 next_log_block++;
10015 wrap(journal, next_log_block);
10016
10017 /* What kind of buffer is it?
10018 *
10019 * If it is a descriptor block, check that it has the
10020 * expected sequence number. Otherwise, we're all done
10021 * here. */
10022
10023 tmp = (journal_header_t *)bh->b_data;
10024
10025 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
10026 brelse(bh);
10027 break;
10028 }
10029
10030 blocktype = ntohl(tmp->h_blocktype);
10031 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010032
10033 if (sequence != next_commit_ID) {
10034 brelse(bh);
10035 break;
10036 }
10037
10038 /* OK, we have a valid descriptor block which matches
10039 * all of the sequence number checks. What are we going
10040 * to do with it? That depends on the pass... */
10041
10042 switch(blocktype) {
10043 case JFS_DESCRIPTOR_BLOCK:
10044 /* If it is a valid descriptor block, replay it
10045 * in pass REPLAY; otherwise, just skip over the
10046 * blocks it describes. */
10047 if (pass != PASS_REPLAY) {
10048 next_log_block +=
10049 count_tags(bh, journal->j_blocksize);
10050 wrap(journal, next_log_block);
10051 brelse(bh);
10052 continue;
10053 }
10054
10055 /* A descriptor block: we can now write all of
10056 * the data blocks. Yay, useful work is finally
10057 * getting done here! */
10058
10059 tagp = &bh->b_data[sizeof(journal_header_t)];
10060 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
10061 <= journal->j_blocksize) {
10062 unsigned long io_block;
10063
10064 tag = (journal_block_tag_t *) tagp;
10065 flags = ntohl(tag->t_flags);
10066
10067 io_block = next_log_block++;
10068 wrap(journal, next_log_block);
10069 err = jread(&obh, journal, io_block);
10070 if (err) {
10071 /* Recover what we can, but
10072 * report failure at the end. */
10073 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000010074 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010075 "block %ld in log\n",
10076 err, io_block);
10077 } else {
10078 unsigned long blocknr;
10079
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010080 blocknr = ntohl(tag->t_blocknr);
10081
10082 /* If the block has been
10083 * revoked, then we're all done
10084 * here. */
10085 if (journal_test_revoke
10086 (journal, blocknr,
10087 next_commit_ID)) {
10088 brelse(obh);
10089 ++info->nr_revoke_hits;
10090 goto skip_write;
10091 }
10092
10093 /* Find a buffer for the new
10094 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010095 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010096 blocknr,
10097 journal->j_blocksize);
10098 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000010099 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010100 "during recovery.\n");
10101 err = -ENOMEM;
10102 brelse(bh);
10103 brelse(obh);
10104 goto failed;
10105 }
10106
10107 lock_buffer(nbh);
10108 memcpy(nbh->b_data, obh->b_data,
10109 journal->j_blocksize);
10110 if (flags & JFS_FLAG_ESCAPE) {
10111 *((unsigned int *)bh->b_data) =
10112 htonl(JFS_MAGIC_NUMBER);
10113 }
10114
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010115 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010116 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010117 ++info->nr_replays;
10118 /* ll_rw_block(WRITE, 1, &nbh); */
10119 unlock_buffer(nbh);
10120 brelse(obh);
10121 brelse(nbh);
10122 }
10123
10124 skip_write:
10125 tagp += sizeof(journal_block_tag_t);
10126 if (!(flags & JFS_FLAG_SAME_UUID))
10127 tagp += 16;
10128
10129 if (flags & JFS_FLAG_LAST_TAG)
10130 break;
10131 }
10132
10133 brelse(bh);
10134 continue;
10135
10136 case JFS_COMMIT_BLOCK:
10137 /* Found an expected commit block: not much to
10138 * do other than move on to the next sequence
10139 * number. */
10140 brelse(bh);
10141 next_commit_ID++;
10142 continue;
10143
10144 case JFS_REVOKE_BLOCK:
10145 /* If we aren't in the REVOKE pass, then we can
10146 * just skip over this block. */
10147 if (pass != PASS_REVOKE) {
10148 brelse(bh);
10149 continue;
10150 }
10151
10152 err = scan_revoke_records(journal, bh,
10153 next_commit_ID, info);
10154 brelse(bh);
10155 if (err)
10156 goto failed;
10157 continue;
10158
10159 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010160 goto done;
10161 }
10162 }
10163
10164 done:
10165 /*
10166 * We broke out of the log scan loop: either we came to the
10167 * known end of the log or we found an unexpected block in the
10168 * log. If the latter happened, then we know that the "current"
10169 * transaction marks the end of the valid log.
10170 */
10171
10172 if (pass == PASS_SCAN)
10173 info->end_transaction = next_commit_ID;
10174 else {
10175 /* It's really bad news if different passes end up at
10176 * different places (but possible due to IO errors). */
10177 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000010178 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010179 "transaction %u, expected %u\n",
10180 pass, next_commit_ID, info->end_transaction);
10181 if (!success)
10182 success = -EIO;
10183 }
10184 }
10185
10186 return success;
10187
10188 failed:
10189 return err;
10190}
10191
10192
10193/* Scan a revoke record, marking all blocks mentioned as revoked. */
10194
10195static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
10196 tid_t sequence, struct recovery_info *info)
10197{
10198 journal_revoke_header_t *header;
10199 int offset, max;
10200
10201 header = (journal_revoke_header_t *) bh->b_data;
10202 offset = sizeof(journal_revoke_header_t);
10203 max = ntohl(header->r_count);
10204
10205 while (offset < max) {
10206 unsigned long blocknr;
10207 int err;
10208
10209 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
10210 offset += 4;
10211 err = journal_set_revoke(journal, blocknr, sequence);
10212 if (err)
10213 return err;
10214 ++info->nr_revokes;
10215 }
10216 return 0;
10217}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010218
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010219
10220/*
10221 * rehash.c --- rebuild hash tree directories
10222 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010223 * This algorithm is designed for simplicity of implementation and to
10224 * pack the directory as much as possible. It however requires twice
10225 * as much memory as the size of the directory. The maximum size
10226 * directory supported using a 4k blocksize is roughly a gigabyte, and
10227 * so there may very well be problems with machines that don't have
10228 * virtual memory, and obscenely large directories.
10229 *
10230 * An alternate algorithm which is much more disk intensive could be
10231 * written, and probably will need to be written in the future. The
10232 * design goals of such an algorithm are: (a) use (roughly) constant
10233 * amounts of memory, no matter how large the directory, (b) the
10234 * directory must be safe at all times, even if e2fsck is interrupted
10235 * in the middle, (c) we must use minimal amounts of extra disk
10236 * blocks. This pretty much requires an incremental approach, where
10237 * we are reading from one part of the directory, and inserting into
10238 * the front half. So the algorithm will have to keep track of a
10239 * moving block boundary between the new tree and the old tree, and
10240 * files will need to be moved from the old directory and inserted
10241 * into the new tree. If the new directory requires space which isn't
10242 * yet available, blocks from the beginning part of the old directory
10243 * may need to be moved to the end of the directory to make room for
10244 * the new tree:
10245 *
10246 * --------------------------------------------------------
10247 * | new tree | | old tree |
10248 * --------------------------------------------------------
10249 * ^ ptr ^ptr
10250 * tail new head old
10251 *
10252 * This is going to be a pain in the tuckus to implement, and will
10253 * require a lot more disk accesses. So I'm going to skip it for now;
10254 * it's only really going to be an issue for really, really big
10255 * filesystems (when we reach the level of tens of millions of files
10256 * in a single directory). It will probably be easier to simply
10257 * require that e2fsck use VM first.
10258 */
10259
10260struct fill_dir_struct {
10261 char *buf;
10262 struct ext2_inode *inode;
10263 int err;
10264 e2fsck_t ctx;
10265 struct hash_entry *harray;
10266 int max_array, num_array;
10267 int dir_size;
10268 int compress;
10269 ino_t parent;
10270};
10271
10272struct hash_entry {
10273 ext2_dirhash_t hash;
10274 ext2_dirhash_t minor_hash;
10275 struct ext2_dir_entry *dir;
10276};
10277
10278struct out_dir {
10279 int num;
10280 int max;
10281 char *buf;
10282 ext2_dirhash_t *hashes;
10283};
10284
10285static int fill_dir_block(ext2_filsys fs,
10286 blk_t *block_nr,
10287 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010288 blk_t ref_block FSCK_ATTR((unused)),
10289 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010290 void *priv_data)
10291{
10292 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
10293 struct hash_entry *new_array, *ent;
10294 struct ext2_dir_entry *dirent;
10295 char *dir;
10296 unsigned int offset, dir_offset;
10297
10298 if (blockcnt < 0)
10299 return 0;
10300
10301 offset = blockcnt * fs->blocksize;
10302 if (offset + fs->blocksize > fd->inode->i_size) {
10303 fd->err = EXT2_ET_DIR_CORRUPTED;
10304 return BLOCK_ABORT;
10305 }
10306 dir = (fd->buf+offset);
10307 if (HOLE_BLKADDR(*block_nr)) {
10308 memset(dir, 0, fs->blocksize);
10309 dirent = (struct ext2_dir_entry *) dir;
10310 dirent->rec_len = fs->blocksize;
10311 } else {
10312 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
10313 if (fd->err)
10314 return BLOCK_ABORT;
10315 }
10316 /* While the directory block is "hot", index it. */
10317 dir_offset = 0;
10318 while (dir_offset < fs->blocksize) {
10319 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
10320 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
10321 (dirent->rec_len < 8) ||
10322 ((dirent->rec_len % 4) != 0) ||
10323 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
10324 fd->err = EXT2_ET_DIR_CORRUPTED;
10325 return BLOCK_ABORT;
10326 }
10327 dir_offset += dirent->rec_len;
10328 if (dirent->inode == 0)
10329 continue;
10330 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
10331 (dirent->name[0] == '.'))
10332 continue;
10333 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
10334 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
10335 fd->parent = dirent->inode;
10336 continue;
10337 }
10338 if (fd->num_array >= fd->max_array) {
10339 new_array = realloc(fd->harray,
10340 sizeof(struct hash_entry) * (fd->max_array+500));
10341 if (!new_array) {
10342 fd->err = ENOMEM;
10343 return BLOCK_ABORT;
10344 }
10345 fd->harray = new_array;
10346 fd->max_array += 500;
10347 }
10348 ent = fd->harray + fd->num_array++;
10349 ent->dir = dirent;
10350 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
10351 if (fd->compress)
10352 ent->hash = ent->minor_hash = 0;
10353 else {
10354 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
10355 dirent->name,
10356 dirent->name_len & 0xFF,
10357 fs->super->s_hash_seed,
10358 &ent->hash, &ent->minor_hash);
10359 if (fd->err)
10360 return BLOCK_ABORT;
10361 }
10362 }
10363
10364 return 0;
10365}
10366
10367/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010368static int name_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010369{
10370 const struct hash_entry *he_a = (const struct hash_entry *) a;
10371 const struct hash_entry *he_b = (const struct hash_entry *) b;
10372 int ret;
10373 int min_len;
10374
10375 min_len = he_a->dir->name_len;
10376 if (min_len > he_b->dir->name_len)
10377 min_len = he_b->dir->name_len;
10378
10379 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
10380 if (ret == 0) {
10381 if (he_a->dir->name_len > he_b->dir->name_len)
10382 ret = 1;
10383 else if (he_a->dir->name_len < he_b->dir->name_len)
10384 ret = -1;
10385 else
10386 ret = he_b->dir->inode - he_a->dir->inode;
10387 }
10388 return ret;
10389}
10390
10391/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010392static int hash_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010393{
10394 const struct hash_entry *he_a = (const struct hash_entry *) a;
10395 const struct hash_entry *he_b = (const struct hash_entry *) b;
10396 int ret;
10397
10398 if (he_a->hash > he_b->hash)
10399 ret = 1;
10400 else if (he_a->hash < he_b->hash)
10401 ret = -1;
10402 else {
10403 if (he_a->minor_hash > he_b->minor_hash)
10404 ret = 1;
10405 else if (he_a->minor_hash < he_b->minor_hash)
10406 ret = -1;
10407 else
10408 ret = name_cmp(a, b);
10409 }
10410 return ret;
10411}
10412
10413static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
10414 int blocks)
10415{
10416 void *new_mem;
10417
10418 if (outdir->max) {
10419 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
10420 if (!new_mem)
10421 return ENOMEM;
10422 outdir->buf = new_mem;
10423 new_mem = realloc(outdir->hashes,
10424 blocks * sizeof(ext2_dirhash_t));
10425 if (!new_mem)
10426 return ENOMEM;
10427 outdir->hashes = new_mem;
10428 } else {
10429 outdir->buf = malloc(blocks * fs->blocksize);
10430 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
10431 outdir->num = 0;
10432 }
10433 outdir->max = blocks;
10434 return 0;
10435}
10436
10437static void free_out_dir(struct out_dir *outdir)
10438{
Rob Landleye7c43b62006-03-01 16:39:45 +000010439 free(outdir->buf);
10440 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010441 outdir->max = 0;
10442 outdir->num =0;
10443}
10444
10445static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
10446 char ** ret)
10447{
10448 errcode_t retval;
10449
10450 if (outdir->num >= outdir->max) {
10451 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
10452 if (retval)
10453 return retval;
10454 }
10455 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
10456 memset(*ret, 0, fs->blocksize);
10457 return 0;
10458}
10459
10460/*
10461 * This function is used to make a unique filename. We do this by
10462 * appending ~0, and then incrementing the number. However, we cannot
10463 * expand the length of the filename beyond the padding available in
10464 * the directory entry.
10465 */
10466static void mutate_name(char *str, __u16 *len)
10467{
10468 int i;
10469 __u16 l = *len & 0xFF, h = *len & 0xff00;
10470
10471 /*
10472 * First check to see if it looks the name has been mutated
10473 * already
10474 */
10475 for (i = l-1; i > 0; i--) {
10476 if (!isdigit(str[i]))
10477 break;
10478 }
10479 if ((i == l-1) || (str[i] != '~')) {
10480 if (((l-1) & 3) < 2)
10481 l += 2;
10482 else
10483 l = (l+3) & ~3;
10484 str[l-2] = '~';
10485 str[l-1] = '0';
10486 *len = l | h;
10487 return;
10488 }
10489 for (i = l-1; i >= 0; i--) {
10490 if (isdigit(str[i])) {
10491 if (str[i] == '9')
10492 str[i] = '0';
10493 else {
10494 str[i]++;
10495 return;
10496 }
10497 continue;
10498 }
10499 if (i == 1) {
10500 if (str[0] == 'z')
10501 str[0] = 'A';
10502 else if (str[0] == 'Z') {
10503 str[0] = '~';
10504 str[1] = '0';
10505 } else
10506 str[0]++;
10507 } else if (i > 0) {
10508 str[i] = '1';
10509 str[i-1] = '~';
10510 } else {
10511 if (str[0] == '~')
10512 str[0] = 'a';
10513 else
10514 str[0]++;
10515 }
10516 break;
10517 }
10518}
10519
10520static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
10521 ext2_ino_t ino,
10522 struct fill_dir_struct *fd)
10523{
10524 struct problem_context pctx;
10525 struct hash_entry *ent, *prev;
10526 int i, j;
10527 int fixed = 0;
10528 char new_name[256];
10529 __u16 new_len;
10530
10531 clear_problem_context(&pctx);
10532 pctx.ino = ino;
10533
10534 for (i=1; i < fd->num_array; i++) {
10535 ent = fd->harray + i;
10536 prev = ent - 1;
10537 if (!ent->dir->inode ||
10538 ((ent->dir->name_len & 0xFF) !=
10539 (prev->dir->name_len & 0xFF)) ||
10540 (strncmp(ent->dir->name, prev->dir->name,
10541 ent->dir->name_len & 0xFF)))
10542 continue;
10543 pctx.dirent = ent->dir;
10544 if ((ent->dir->inode == prev->dir->inode) &&
10545 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
10546 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
10547 ent->dir->inode = 0;
10548 fixed++;
10549 continue;
10550 }
10551 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
10552 new_len = ent->dir->name_len;
10553 mutate_name(new_name, &new_len);
10554 for (j=0; j < fd->num_array; j++) {
10555 if ((i==j) ||
10556 ((ent->dir->name_len & 0xFF) !=
10557 (fd->harray[j].dir->name_len & 0xFF)) ||
10558 (strncmp(new_name, fd->harray[j].dir->name,
10559 new_len & 0xFF)))
10560 continue;
10561 mutate_name(new_name, &new_len);
10562
10563 j = -1;
10564 }
10565 new_name[new_len & 0xFF] = 0;
10566 pctx.str = new_name;
10567 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
10568 memcpy(ent->dir->name, new_name, new_len & 0xFF);
10569 ent->dir->name_len = new_len;
10570 ext2fs_dirhash(fs->super->s_def_hash_version,
10571 ent->dir->name,
10572 ent->dir->name_len & 0xFF,
10573 fs->super->s_hash_seed,
10574 &ent->hash, &ent->minor_hash);
10575 fixed++;
10576 }
10577 }
10578 return fixed;
10579}
10580
10581
10582static errcode_t copy_dir_entries(ext2_filsys fs,
10583 struct fill_dir_struct *fd,
10584 struct out_dir *outdir)
10585{
10586 errcode_t retval;
10587 char *block_start;
10588 struct hash_entry *ent;
10589 struct ext2_dir_entry *dirent;
10590 int i, rec_len, left;
10591 ext2_dirhash_t prev_hash;
10592 int offset;
10593
10594 outdir->max = 0;
10595 retval = alloc_size_dir(fs, outdir,
10596 (fd->dir_size / fs->blocksize) + 2);
10597 if (retval)
10598 return retval;
10599 outdir->num = fd->compress ? 0 : 1;
10600 offset = 0;
10601 outdir->hashes[0] = 0;
10602 prev_hash = 1;
10603 if ((retval = get_next_block(fs, outdir, &block_start)))
10604 return retval;
10605 dirent = (struct ext2_dir_entry *) block_start;
10606 left = fs->blocksize;
10607 for (i=0; i < fd->num_array; i++) {
10608 ent = fd->harray + i;
10609 if (ent->dir->inode == 0)
10610 continue;
10611 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
10612 if (rec_len > left) {
10613 if (left)
10614 dirent->rec_len += left;
10615 if ((retval = get_next_block(fs, outdir,
10616 &block_start)))
10617 return retval;
10618 offset = 0;
10619 }
10620 left = fs->blocksize - offset;
10621 dirent = (struct ext2_dir_entry *) (block_start + offset);
10622 if (offset == 0) {
10623 if (ent->hash == prev_hash)
10624 outdir->hashes[outdir->num-1] = ent->hash | 1;
10625 else
10626 outdir->hashes[outdir->num-1] = ent->hash;
10627 }
10628 dirent->inode = ent->dir->inode;
10629 dirent->name_len = ent->dir->name_len;
10630 dirent->rec_len = rec_len;
10631 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
10632 offset += rec_len;
10633 left -= rec_len;
10634 if (left < 12) {
10635 dirent->rec_len += left;
10636 offset += left;
10637 left = 0;
10638 }
10639 prev_hash = ent->hash;
10640 }
10641 if (left)
10642 dirent->rec_len += left;
10643
10644 return 0;
10645}
10646
10647
10648static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
10649 ext2_ino_t ino, ext2_ino_t parent)
10650{
10651 struct ext2_dir_entry *dir;
10652 struct ext2_dx_root_info *root;
10653 struct ext2_dx_countlimit *limits;
10654 int filetype = 0;
10655
10656 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
10657 filetype = EXT2_FT_DIR << 8;
10658
10659 memset(buf, 0, fs->blocksize);
10660 dir = (struct ext2_dir_entry *) buf;
10661 dir->inode = ino;
10662 dir->name[0] = '.';
10663 dir->name_len = 1 | filetype;
10664 dir->rec_len = 12;
10665 dir = (struct ext2_dir_entry *) (buf + 12);
10666 dir->inode = parent;
10667 dir->name[0] = '.';
10668 dir->name[1] = '.';
10669 dir->name_len = 2 | filetype;
10670 dir->rec_len = fs->blocksize - 12;
10671
10672 root = (struct ext2_dx_root_info *) (buf+24);
10673 root->reserved_zero = 0;
10674 root->hash_version = fs->super->s_def_hash_version;
10675 root->info_length = 8;
10676 root->indirect_levels = 0;
10677 root->unused_flags = 0;
10678
10679 limits = (struct ext2_dx_countlimit *) (buf+32);
10680 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
10681 limits->count = 0;
10682
10683 return root;
10684}
10685
10686
10687static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
10688{
10689 struct ext2_dir_entry *dir;
10690 struct ext2_dx_countlimit *limits;
10691
10692 memset(buf, 0, fs->blocksize);
10693 dir = (struct ext2_dir_entry *) buf;
10694 dir->inode = 0;
10695 dir->rec_len = fs->blocksize;
10696
10697 limits = (struct ext2_dx_countlimit *) (buf+8);
10698 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
10699 limits->count = 0;
10700
10701 return (struct ext2_dx_entry *) limits;
10702}
10703
10704/*
10705 * This function takes the leaf nodes which have been written in
10706 * outdir, and populates the root node and any necessary interior nodes.
10707 */
10708static errcode_t calculate_tree(ext2_filsys fs,
10709 struct out_dir *outdir,
10710 ext2_ino_t ino,
10711 ext2_ino_t parent)
10712{
10713 struct ext2_dx_root_info *root_info;
10714 struct ext2_dx_entry *root, *dx_ent = 0;
10715 struct ext2_dx_countlimit *root_limit, *limit;
10716 errcode_t retval;
10717 char * block_start;
10718 int i, c1, c2, nblks;
10719 int limit_offset, root_offset;
10720
10721 root_info = set_root_node(fs, outdir->buf, ino, parent);
10722 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
10723 root_info->info_length;
10724 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10725 c1 = root_limit->limit;
10726 nblks = outdir->num;
10727
10728 /* Write out the pointer blocks */
10729 if (nblks-1 <= c1) {
10730 /* Just write out the root block, and we're done */
10731 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
10732 for (i=1; i < nblks; i++) {
10733 root->block = ext2fs_cpu_to_le32(i);
10734 if (i != 1)
10735 root->hash =
10736 ext2fs_cpu_to_le32(outdir->hashes[i]);
10737 root++;
10738 c1--;
10739 }
10740 } else {
10741 c2 = 0;
10742 limit = 0;
10743 root_info->indirect_levels = 1;
10744 for (i=1; i < nblks; i++) {
10745 if (c1 == 0)
10746 return ENOSPC;
10747 if (c2 == 0) {
10748 if (limit)
10749 limit->limit = limit->count =
10750 ext2fs_cpu_to_le16(limit->limit);
10751 root = (struct ext2_dx_entry *)
10752 (outdir->buf + root_offset);
10753 root->block = ext2fs_cpu_to_le32(outdir->num);
10754 if (i != 1)
10755 root->hash =
10756 ext2fs_cpu_to_le32(outdir->hashes[i]);
10757 if ((retval = get_next_block(fs, outdir,
10758 &block_start)))
10759 return retval;
10760 dx_ent = set_int_node(fs, block_start);
10761 limit = (struct ext2_dx_countlimit *) dx_ent;
10762 c2 = limit->limit;
10763 root_offset += sizeof(struct ext2_dx_entry);
10764 c1--;
10765 }
10766 dx_ent->block = ext2fs_cpu_to_le32(i);
10767 if (c2 != limit->limit)
10768 dx_ent->hash =
10769 ext2fs_cpu_to_le32(outdir->hashes[i]);
10770 dx_ent++;
10771 c2--;
10772 }
10773 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
10774 limit->limit = ext2fs_cpu_to_le16(limit->limit);
10775 }
10776 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10777 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
10778 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
10779
10780 return 0;
10781}
10782
10783struct write_dir_struct {
10784 struct out_dir *outdir;
10785 errcode_t err;
10786 e2fsck_t ctx;
10787 int cleared;
10788};
10789
10790/*
10791 * Helper function which writes out a directory block.
10792 */
10793static int write_dir_block(ext2_filsys fs,
10794 blk_t *block_nr,
10795 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010796 blk_t ref_block FSCK_ATTR((unused)),
10797 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010798 void *priv_data)
10799{
10800 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
10801 blk_t blk;
10802 char *dir;
10803
10804 if (*block_nr == 0)
10805 return 0;
10806 if (blockcnt >= wd->outdir->num) {
10807 e2fsck_read_bitmaps(wd->ctx);
10808 blk = *block_nr;
10809 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
10810 ext2fs_block_alloc_stats(fs, blk, -1);
10811 *block_nr = 0;
10812 wd->cleared++;
10813 return BLOCK_CHANGED;
10814 }
10815 if (blockcnt < 0)
10816 return 0;
10817
10818 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
10819 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
10820 if (wd->err)
10821 return BLOCK_ABORT;
10822 return 0;
10823}
10824
10825static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
10826 struct out_dir *outdir,
10827 ext2_ino_t ino, int compress)
10828{
10829 struct write_dir_struct wd;
10830 errcode_t retval;
10831 struct ext2_inode inode;
10832
10833 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
10834 if (retval)
10835 return retval;
10836
10837 wd.outdir = outdir;
10838 wd.err = 0;
10839 wd.ctx = ctx;
10840 wd.cleared = 0;
10841
10842 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10843 write_dir_block, &wd);
10844 if (retval)
10845 return retval;
10846 if (wd.err)
10847 return wd.err;
10848
10849 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10850 if (compress)
10851 inode.i_flags &= ~EXT2_INDEX_FL;
10852 else
10853 inode.i_flags |= EXT2_INDEX_FL;
10854 inode.i_size = outdir->num * fs->blocksize;
10855 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
10856 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
10857
10858 return 0;
10859}
10860
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010861static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010862{
10863 ext2_filsys fs = ctx->fs;
10864 errcode_t retval;
10865 struct ext2_inode inode;
10866 char *dir_buf = 0;
10867 struct fill_dir_struct fd;
10868 struct out_dir outdir;
10869
10870 outdir.max = outdir.num = 0;
10871 outdir.buf = 0;
10872 outdir.hashes = 0;
10873 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10874
10875 retval = ENOMEM;
10876 fd.harray = 0;
10877 dir_buf = malloc(inode.i_size);
10878 if (!dir_buf)
10879 goto errout;
10880
10881 fd.max_array = inode.i_size / 32;
10882 fd.num_array = 0;
10883 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
10884 if (!fd.harray)
10885 goto errout;
10886
10887 fd.ctx = ctx;
10888 fd.buf = dir_buf;
10889 fd.inode = &inode;
10890 fd.err = 0;
10891 fd.dir_size = 0;
10892 fd.compress = 0;
10893 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
10894 (inode.i_size / fs->blocksize) < 2)
10895 fd.compress = 1;
10896 fd.parent = 0;
10897
10898 /* Read in the entire directory into memory */
10899 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10900 fill_dir_block, &fd);
10901 if (fd.err) {
10902 retval = fd.err;
10903 goto errout;
10904 }
10905
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010906 /* Sort the list */
10907resort:
10908 if (fd.compress)
10909 qsort(fd.harray+2, fd.num_array-2,
10910 sizeof(struct hash_entry), name_cmp);
10911 else
10912 qsort(fd.harray, fd.num_array,
10913 sizeof(struct hash_entry), hash_cmp);
10914
10915 /*
10916 * Look for duplicates
10917 */
10918 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
10919 goto resort;
10920
10921 if (ctx->options & E2F_OPT_NO) {
10922 retval = 0;
10923 goto errout;
10924 }
10925
10926 /*
10927 * Copy the directory entries. In a htree directory these
10928 * will become the leaf nodes.
10929 */
10930 retval = copy_dir_entries(fs, &fd, &outdir);
10931 if (retval)
10932 goto errout;
10933
10934 free(dir_buf); dir_buf = 0;
10935
10936 if (!fd.compress) {
10937 /* Calculate the interior nodes */
10938 retval = calculate_tree(fs, &outdir, ino, fd.parent);
10939 if (retval)
10940 goto errout;
10941 }
10942
10943 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010944
10945errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000010946 free(dir_buf);
10947 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010948
10949 free_out_dir(&outdir);
10950 return retval;
10951}
10952
10953void e2fsck_rehash_directories(e2fsck_t ctx)
10954{
10955 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010956 struct dir_info *dir;
10957 ext2_u32_iterate iter;
10958 ext2_ino_t ino;
10959 errcode_t retval;
10960 int i, cur, max, all_dirs, dir_index, first = 1;
10961
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010962 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
10963
10964 if (!ctx->dirs_to_hash && !all_dirs)
10965 return;
10966
10967 e2fsck_get_lost_and_found(ctx, 0);
10968
10969 clear_problem_context(&pctx);
10970
10971 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
10972 cur = 0;
10973 if (all_dirs) {
10974 i = 0;
10975 max = e2fsck_get_num_dirinfo(ctx);
10976 } else {
10977 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
10978 &iter);
10979 if (retval) {
10980 pctx.errcode = retval;
10981 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
10982 return;
10983 }
10984 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
10985 }
10986 while (1) {
10987 if (all_dirs) {
10988 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
10989 break;
10990 ino = dir->ino;
10991 } else {
10992 if (!ext2fs_u32_list_iterate(iter, &ino))
10993 break;
10994 }
10995 if (ino == ctx->lost_and_found)
10996 continue;
10997 pctx.dir = ino;
10998 if (first) {
10999 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
11000 first = 0;
11001 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011002 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
11003 if (pctx.errcode) {
11004 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11005 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
11006 }
11007 if (ctx->progress && !ctx->progress_fd)
11008 e2fsck_simple_progress(ctx, "Rebuilding directory",
11009 100.0 * (float) (++cur) / (float) max, ino);
11010 }
11011 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11012 if (!all_dirs)
11013 ext2fs_u32_list_iterate_end(iter);
11014
Rob Landleye7c43b62006-03-01 16:39:45 +000011015 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011016 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011017}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011018
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011019/*
11020 * linux/fs/revoke.c
11021 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011022 * Journal revoke routines for the generic filesystem journaling code;
11023 * part of the ext2fs journaling system.
11024 *
11025 * Revoke is the mechanism used to prevent old log records for deleted
11026 * metadata from being replayed on top of newer data using the same
11027 * blocks. The revoke mechanism is used in two separate places:
11028 *
11029 * + Commit: during commit we write the entire list of the current
11030 * transaction's revoked blocks to the journal
11031 *
11032 * + Recovery: during recovery we record the transaction ID of all
11033 * revoked blocks. If there are multiple revoke records in the log
11034 * for a single block, only the last one counts, and if there is a log
11035 * entry for a block beyond the last revoke, then that log entry still
11036 * gets replayed.
11037 *
11038 * We can get interactions between revokes and new log data within a
11039 * single transaction:
11040 *
11041 * Block is revoked and then journaled:
11042 * The desired end result is the journaling of the new block, so we
11043 * cancel the revoke before the transaction commits.
11044 *
11045 * Block is journaled and then revoked:
11046 * The revoke must take precedence over the write of the block, so we
11047 * need either to cancel the journal entry or to write the revoke
11048 * later in the log than the log block. In this case, we choose the
11049 * latter: journaling a block cancels any revoke record for that block
11050 * in the current transaction, so any revoke for that block in the
11051 * transaction must have happened after the block was journaled and so
11052 * the revoke must take precedence.
11053 *
11054 * Block is revoked and then written as data:
11055 * The data write is allowed to succeed, but the revoke is _not_
11056 * cancelled. We still need to prevent old log records from
11057 * overwriting the new data. We don't even need to clear the revoke
11058 * bit here.
11059 *
11060 * Revoke information on buffers is a tri-state value:
11061 *
11062 * RevokeValid clear: no cached revoke status, need to look it up
11063 * RevokeValid set, Revoked clear:
11064 * buffer has not been revoked, and cancel_revoke
11065 * need do nothing.
11066 * RevokeValid set, Revoked set:
11067 * buffer has been revoked.
11068 */
11069
11070static kmem_cache_t *revoke_record_cache;
11071static kmem_cache_t *revoke_table_cache;
11072
11073/* Each revoke record represents one single revoked block. During
11074 journal replay, this involves recording the transaction ID of the
11075 last transaction to revoke this block. */
11076
11077struct jbd_revoke_record_s
11078{
11079 struct list_head hash;
11080 tid_t sequence; /* Used for recovery only */
11081 unsigned long blocknr;
11082};
11083
11084
11085/* The revoke table is just a simple hash table of revoke records. */
11086struct jbd_revoke_table_s
11087{
11088 /* It is conceivable that we might want a larger hash table
11089 * for recovery. Must be a power of two. */
11090 int hash_size;
11091 int hash_shift;
11092 struct list_head *hash_table;
11093};
11094
11095
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011096/* Utility functions to maintain the revoke table */
11097
11098/* Borrowed from buffer.c: this is a tried and tested block hash function */
11099static inline int hash(journal_t *journal, unsigned long block)
11100{
11101 struct jbd_revoke_table_s *table = journal->j_revoke;
11102 int hash_shift = table->hash_shift;
11103
11104 return ((block << (hash_shift - 6)) ^
11105 (block >> 13) ^
11106 (block << (hash_shift - 12))) & (table->hash_size - 1);
11107}
11108
11109static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
11110 tid_t seq)
11111{
11112 struct list_head *hash_list;
11113 struct jbd_revoke_record_s *record;
11114
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011115 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
11116 if (!record)
11117 goto oom;
11118
11119 record->sequence = seq;
11120 record->blocknr = blocknr;
11121 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11122 list_add(&record->hash, hash_list);
11123 return 0;
11124
11125oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011126 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011127}
11128
11129/* Find a revoke record in the journal's hash table. */
11130
11131static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
11132 unsigned long blocknr)
11133{
11134 struct list_head *hash_list;
11135 struct jbd_revoke_record_s *record;
11136
11137 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11138
11139 record = (struct jbd_revoke_record_s *) hash_list->next;
11140 while (&(record->hash) != hash_list) {
11141 if (record->blocknr == blocknr)
11142 return record;
11143 record = (struct jbd_revoke_record_s *) record->hash.next;
11144 }
11145 return NULL;
11146}
11147
11148int journal_init_revoke_caches(void)
11149{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011150 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011151 if (revoke_record_cache == 0)
11152 return -ENOMEM;
11153
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011154 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011155 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011156 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011157 revoke_record_cache = NULL;
11158 return -ENOMEM;
11159 }
11160 return 0;
11161}
11162
11163void journal_destroy_revoke_caches(void)
11164{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011165 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011166 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011167 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011168 revoke_table_cache = 0;
11169}
11170
11171/* Initialise the revoke table for a given journal to a given size. */
11172
11173int journal_init_revoke(journal_t *journal, int hash_size)
11174{
11175 int shift, tmp;
11176
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011177 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
11178 if (!journal->j_revoke)
11179 return -ENOMEM;
11180
11181 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011182 journal->j_revoke->hash_size = hash_size;
11183
11184 shift = 0;
11185 tmp = hash_size;
11186 while((tmp >>= 1UL) != 0UL)
11187 shift++;
11188 journal->j_revoke->hash_shift = shift;
11189
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011190 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011191 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011192 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011193 journal->j_revoke = NULL;
11194 return -ENOMEM;
11195 }
11196
11197 for (tmp = 0; tmp < hash_size; tmp++)
11198 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
11199
11200 return 0;
11201}
11202
11203/* Destoy a journal's revoke table. The table must already be empty! */
11204
11205void journal_destroy_revoke(journal_t *journal)
11206{
11207 struct jbd_revoke_table_s *table;
11208 struct list_head *hash_list;
11209 int i;
11210
11211 table = journal->j_revoke;
11212 if (!table)
11213 return;
11214
11215 for (i=0; i<table->hash_size; i++) {
11216 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011217 }
11218
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011219 free(table->hash_table);
11220 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011221 journal->j_revoke = NULL;
11222}
11223
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011224/*
11225 * Revoke support for recovery.
11226 *
11227 * Recovery needs to be able to:
11228 *
11229 * record all revoke records, including the tid of the latest instance
11230 * of each revoke in the journal
11231 *
11232 * check whether a given block in a given transaction should be replayed
11233 * (ie. has not been revoked by a revoke record in that or a subsequent
11234 * transaction)
11235 *
11236 * empty the revoke table after recovery.
11237 */
11238
11239/*
11240 * First, setting revoke records. We create a new revoke record for
11241 * every block ever revoked in the log as we scan it for recovery, and
11242 * we update the existing records if we find multiple revokes for a
11243 * single block.
11244 */
11245
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011246int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011247 tid_t sequence)
11248{
11249 struct jbd_revoke_record_s *record;
11250
11251 record = find_revoke_record(journal, blocknr);
11252 if (record) {
11253 /* If we have multiple occurences, only record the
11254 * latest sequence number in the hashed record */
11255 if (tid_gt(sequence, record->sequence))
11256 record->sequence = sequence;
11257 return 0;
11258 }
11259 return insert_revoke_hash(journal, blocknr, sequence);
11260}
11261
11262/*
11263 * Test revoke records. For a given block referenced in the log, has
11264 * that block been revoked? A revoke record with a given transaction
11265 * sequence number revokes all blocks in that transaction and earlier
11266 * ones, but later transactions still need replayed.
11267 */
11268
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011269int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011270 tid_t sequence)
11271{
11272 struct jbd_revoke_record_s *record;
11273
11274 record = find_revoke_record(journal, blocknr);
11275 if (!record)
11276 return 0;
11277 if (tid_gt(sequence, record->sequence))
11278 return 0;
11279 return 1;
11280}
11281
11282/*
11283 * Finally, once recovery is over, we need to clear the revoke table so
11284 * that it can be reused by the running filesystem.
11285 */
11286
11287void journal_clear_revoke(journal_t *journal)
11288{
11289 int i;
11290 struct list_head *hash_list;
11291 struct jbd_revoke_record_s *record;
11292 struct jbd_revoke_table_s *revoke_var;
11293
11294 revoke_var = journal->j_revoke;
11295
11296 for (i = 0; i < revoke_var->hash_size; i++) {
11297 hash_list = &revoke_var->hash_table[i];
11298 while (!list_empty(hash_list)) {
11299 record = (struct jbd_revoke_record_s*) hash_list->next;
11300 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011301 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011302 }
11303 }
11304}
11305
11306/*
11307 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011308 */
11309
11310#define MIN_CHECK 1
11311#define MAX_CHECK 2
11312
11313static void check_super_value(e2fsck_t ctx, const char *descr,
11314 unsigned long value, int flags,
11315 unsigned long min_val, unsigned long max_val)
11316{
11317 struct problem_context pctx;
11318
11319 if (((flags & MIN_CHECK) && (value < min_val)) ||
11320 ((flags & MAX_CHECK) && (value > max_val))) {
11321 clear_problem_context(&pctx);
11322 pctx.num = value;
11323 pctx.str = descr;
11324 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11325 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11326 }
11327}
11328
11329/*
11330 * This routine may get stubbed out in special compilations of the
11331 * e2fsck code..
11332 */
11333#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011334static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011335{
11336 return (ext2fs_get_device_size(ctx->filesystem_name,
11337 EXT2_BLOCK_SIZE(ctx->fs->super),
11338 &ctx->num_blocks));
11339}
11340#endif
11341
11342/*
11343 * helper function to release an inode
11344 */
11345struct process_block_struct {
11346 e2fsck_t ctx;
11347 char *buf;
11348 struct problem_context *pctx;
11349 int truncating;
11350 int truncate_offset;
11351 e2_blkcnt_t truncate_block;
11352 int truncated_blocks;
11353 int abort;
11354 errcode_t errcode;
11355};
11356
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011357static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011358 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011359 blk_t ref_blk FSCK_ATTR((unused)),
11360 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011361 void *priv_data)
11362{
11363 struct process_block_struct *pb;
11364 e2fsck_t ctx;
11365 struct problem_context *pctx;
11366 blk_t blk = *block_nr;
11367 int retval = 0;
11368
11369 pb = (struct process_block_struct *) priv_data;
11370 ctx = pb->ctx;
11371 pctx = pb->pctx;
11372
11373 pctx->blk = blk;
11374 pctx->blkcount = blockcnt;
11375
11376 if (HOLE_BLKADDR(blk))
11377 return 0;
11378
11379 if ((blk < fs->super->s_first_data_block) ||
11380 (blk >= fs->super->s_blocks_count)) {
11381 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
11382 return_abort:
11383 pb->abort = 1;
11384 return BLOCK_ABORT;
11385 }
11386
11387 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
11388 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
11389 goto return_abort;
11390 }
11391
11392 /*
11393 * If we are deleting an orphan, then we leave the fields alone.
11394 * If we are truncating an orphan, then update the inode fields
11395 * and clean up any partial block data.
11396 */
11397 if (pb->truncating) {
11398 /*
11399 * We only remove indirect blocks if they are
11400 * completely empty.
11401 */
11402 if (blockcnt < 0) {
11403 int i, limit;
11404 blk_t *bp;
11405
11406 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11407 pb->buf);
11408 if (pb->errcode)
11409 goto return_abort;
11410
11411 limit = fs->blocksize >> 2;
11412 for (i = 0, bp = (blk_t *) pb->buf;
11413 i < limit; i++, bp++)
11414 if (*bp)
11415 return 0;
11416 }
11417 /*
11418 * We don't remove direct blocks until we've reached
11419 * the truncation block.
11420 */
11421 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
11422 return 0;
11423 /*
11424 * If part of the last block needs truncating, we do
11425 * it here.
11426 */
11427 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
11428 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11429 pb->buf);
11430 if (pb->errcode)
11431 goto return_abort;
11432 memset(pb->buf + pb->truncate_offset, 0,
11433 fs->blocksize - pb->truncate_offset);
11434 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
11435 pb->buf);
11436 if (pb->errcode)
11437 goto return_abort;
11438 }
11439 pb->truncated_blocks++;
11440 *block_nr = 0;
11441 retval |= BLOCK_CHANGED;
11442 }
11443
11444 ext2fs_block_alloc_stats(fs, blk, -1);
11445 return retval;
11446}
11447
11448/*
11449 * This function releases an inode. Returns 1 if an inconsistency was
11450 * found. If the inode has a link count, then it is being truncated and
11451 * not deleted.
11452 */
11453static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
11454 struct ext2_inode *inode, char *block_buf,
11455 struct problem_context *pctx)
11456{
11457 struct process_block_struct pb;
11458 ext2_filsys fs = ctx->fs;
11459 errcode_t retval;
11460 __u32 count;
11461
11462 if (!ext2fs_inode_has_valid_blocks(inode))
11463 return 0;
11464
11465 pb.buf = block_buf + 3 * ctx->fs->blocksize;
11466 pb.ctx = ctx;
11467 pb.abort = 0;
11468 pb.errcode = 0;
11469 pb.pctx = pctx;
11470 if (inode->i_links_count) {
11471 pb.truncating = 1;
11472 pb.truncate_block = (e2_blkcnt_t)
11473 ((((long long)inode->i_size_high << 32) +
11474 inode->i_size + fs->blocksize - 1) /
11475 fs->blocksize);
11476 pb.truncate_offset = inode->i_size % fs->blocksize;
11477 } else {
11478 pb.truncating = 0;
11479 pb.truncate_block = 0;
11480 pb.truncate_offset = 0;
11481 }
11482 pb.truncated_blocks = 0;
11483 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
11484 block_buf, release_inode_block, &pb);
11485 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011486 bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011487 ino);
11488 return 1;
11489 }
11490 if (pb.abort)
11491 return 1;
11492
11493 /* Refresh the inode since ext2fs_block_iterate may have changed it */
11494 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
11495
11496 if (pb.truncated_blocks)
11497 inode->i_blocks -= pb.truncated_blocks *
11498 (fs->blocksize / 512);
11499
11500 if (inode->i_file_acl) {
11501 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
11502 block_buf, -1, &count);
11503 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
11504 retval = 0;
11505 count = 1;
11506 }
11507 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011508 bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011509 ino);
11510 return 1;
11511 }
11512 if (count == 0)
11513 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
11514 inode->i_file_acl = 0;
11515 }
11516 return 0;
11517}
11518
11519/*
11520 * This function releases all of the orphan inodes. It returns 1 if
11521 * it hit some error, and 0 on success.
11522 */
11523static int release_orphan_inodes(e2fsck_t ctx)
11524{
11525 ext2_filsys fs = ctx->fs;
11526 ext2_ino_t ino, next_ino;
11527 struct ext2_inode inode;
11528 struct problem_context pctx;
11529 char *block_buf;
11530
11531 if ((ino = fs->super->s_last_orphan) == 0)
11532 return 0;
11533
11534 /*
11535 * Win or lose, we won't be using the head of the orphan inode
11536 * list again.
11537 */
11538 fs->super->s_last_orphan = 0;
11539 ext2fs_mark_super_dirty(fs);
11540
11541 /*
11542 * If the filesystem contains errors, don't run the orphan
11543 * list, since the orphan list can't be trusted; and we're
11544 * going to be running a full e2fsck run anyway...
11545 */
11546 if (fs->super->s_state & EXT2_ERROR_FS)
11547 return 0;
11548
11549 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
11550 (ino > fs->super->s_inodes_count)) {
11551 clear_problem_context(&pctx);
11552 pctx.ino = ino;
11553 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
11554 return 1;
11555 }
11556
11557 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
11558 "block iterate buffer");
11559 e2fsck_read_bitmaps(ctx);
11560
11561 while (ino) {
11562 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
11563 clear_problem_context(&pctx);
11564 pctx.ino = ino;
11565 pctx.inode = &inode;
11566 pctx.str = inode.i_links_count ? _("Truncating") :
11567 _("Clearing");
11568
11569 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
11570
11571 next_ino = inode.i_dtime;
11572 if (next_ino &&
11573 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
11574 (next_ino > fs->super->s_inodes_count))) {
11575 pctx.ino = next_ino;
11576 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
11577 goto return_abort;
11578 }
11579
11580 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
11581 goto return_abort;
11582
11583 if (!inode.i_links_count) {
11584 ext2fs_inode_alloc_stats2(fs, ino, -1,
11585 LINUX_S_ISDIR(inode.i_mode));
11586 inode.i_dtime = time(0);
11587 } else {
11588 inode.i_dtime = 0;
11589 }
11590 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
11591 ino = next_ino;
11592 }
11593 ext2fs_free_mem(&block_buf);
11594 return 0;
11595return_abort:
11596 ext2fs_free_mem(&block_buf);
11597 return 1;
11598}
11599
11600/*
11601 * Check the resize inode to make sure it is sane. We check both for
11602 * the case where on-line resizing is not enabled (in which case the
11603 * resize inode should be cleared) as well as the case where on-line
11604 * resizing is enabled.
11605 */
11606static void check_resize_inode(e2fsck_t ctx)
11607{
11608 ext2_filsys fs = ctx->fs;
11609 struct ext2_inode inode;
11610 struct problem_context pctx;
11611 int i, j, gdt_off, ind_off;
11612 blk_t blk, pblk, expect;
11613 __u32 *dind_buf = 0, *ind_buf;
11614 errcode_t retval;
11615
11616 clear_problem_context(&pctx);
11617
11618 /*
11619 * If the resize inode feature isn't set, then
11620 * s_reserved_gdt_blocks must be zero.
11621 */
11622 if (!(fs->super->s_feature_compat &
11623 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11624 if (fs->super->s_reserved_gdt_blocks) {
11625 pctx.num = fs->super->s_reserved_gdt_blocks;
11626 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
11627 &pctx)) {
11628 fs->super->s_reserved_gdt_blocks = 0;
11629 ext2fs_mark_super_dirty(fs);
11630 }
11631 }
11632 }
11633
Mike Frysinger874af852006-03-08 07:03:27 +000011634 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011635 pctx.ino = EXT2_RESIZE_INO;
11636 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
11637 if (retval) {
11638 if (fs->super->s_feature_compat &
11639 EXT2_FEATURE_COMPAT_RESIZE_INODE)
11640 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11641 return;
11642 }
11643
11644 /*
11645 * If the resize inode feature isn't set, check to make sure
11646 * the resize inode is cleared; then we're done.
11647 */
11648 if (!(fs->super->s_feature_compat &
11649 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11650 for (i=0; i < EXT2_N_BLOCKS; i++) {
11651 if (inode.i_block[i])
11652 break;
11653 }
11654 if ((i < EXT2_N_BLOCKS) &&
11655 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
11656 memset(&inode, 0, sizeof(inode));
11657 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11658 "clear_resize");
11659 }
11660 return;
11661 }
11662
11663 /*
11664 * The resize inode feature is enabled; check to make sure the
11665 * only block in use is the double indirect block
11666 */
11667 blk = inode.i_block[EXT2_DIND_BLOCK];
11668 for (i=0; i < EXT2_N_BLOCKS; i++) {
11669 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
11670 break;
11671 }
11672 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
11673 !(inode.i_mode & LINUX_S_IFREG) ||
11674 (blk < fs->super->s_first_data_block ||
11675 blk >= fs->super->s_blocks_count)) {
11676 resize_inode_invalid:
11677 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
11678 memset(&inode, 0, sizeof(inode));
11679 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11680 "clear_resize");
11681 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11682 }
11683 if (!(ctx->options & E2F_OPT_READONLY)) {
11684 fs->super->s_state &= ~EXT2_VALID_FS;
11685 ext2fs_mark_super_dirty(fs);
11686 }
11687 goto cleanup;
11688 }
11689 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
11690 "resize dind buffer");
11691 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
11692
11693 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
11694 if (retval)
11695 goto resize_inode_invalid;
11696
11697 gdt_off = fs->desc_blocks;
11698 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
11699 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
11700 i++, gdt_off++, pblk++) {
11701 gdt_off %= fs->blocksize/4;
11702 if (dind_buf[gdt_off] != pblk)
11703 goto resize_inode_invalid;
11704 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
11705 if (retval)
11706 goto resize_inode_invalid;
11707 ind_off = 0;
11708 for (j = 1; j < fs->group_desc_count; j++) {
11709 if (!ext2fs_bg_has_super(fs, j))
11710 continue;
11711 expect = pblk + (j * fs->super->s_blocks_per_group);
11712 if (ind_buf[ind_off] != expect)
11713 goto resize_inode_invalid;
11714 ind_off++;
11715 }
11716 }
11717
11718cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000011719 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011720
11721 }
11722
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011723static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011724{
11725 ext2_filsys fs = ctx->fs;
11726 blk_t first_block, last_block;
11727 struct ext2_super_block *sb = fs->super;
11728 struct ext2_group_desc *gd;
11729 blk_t blocks_per_group = fs->super->s_blocks_per_group;
11730 blk_t bpg_max;
11731 int inodes_per_block;
11732 int ipg_max;
11733 int inode_size;
11734 dgrp_t i;
11735 blk_t should_be;
11736 struct problem_context pctx;
11737 __u32 free_blocks = 0, free_inodes = 0;
11738
11739 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
11740 ipg_max = inodes_per_block * (blocks_per_group - 4);
11741 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
11742 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
11743 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
11744 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
11745 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
11746
11747 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11748 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
11749 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11750 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
11751 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
11752 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
11753
11754 clear_problem_context(&pctx);
11755
11756 /*
11757 * Verify the super block constants...
11758 */
11759 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
11760 MIN_CHECK, 1, 0);
11761 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
11762 MIN_CHECK, 1, 0);
11763 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
11764 MAX_CHECK, 0, sb->s_blocks_count);
11765 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
11766 MIN_CHECK | MAX_CHECK, 0,
11767 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
11768 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
11769 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
11770 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
11771 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
11772 bpg_max);
11773 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
11774 MIN_CHECK | MAX_CHECK, 8, bpg_max);
11775 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
11776 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
11777 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
11778 MAX_CHECK, 0, sb->s_blocks_count / 2);
11779 check_super_value(ctx, "reserved_gdt_blocks",
11780 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
11781 fs->blocksize/4);
11782 inode_size = EXT2_INODE_SIZE(sb);
11783 check_super_value(ctx, "inode_size",
11784 inode_size, MIN_CHECK | MAX_CHECK,
11785 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
11786 if (inode_size & (inode_size - 1)) {
11787 pctx.num = inode_size;
11788 pctx.str = "inode_size";
11789 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11790 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11791 return;
11792 }
11793
11794 if (!ctx->num_blocks) {
11795 pctx.errcode = e2fsck_get_device_size(ctx);
11796 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
11797 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
11798 ctx->flags |= E2F_FLAG_ABORT;
11799 return;
11800 }
11801 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
11802 (ctx->num_blocks < sb->s_blocks_count)) {
11803 pctx.blk = sb->s_blocks_count;
11804 pctx.blk2 = ctx->num_blocks;
11805 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
11806 ctx->flags |= E2F_FLAG_ABORT;
11807 return;
11808 }
11809 }
11810 }
11811
11812 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
11813 pctx.blk = EXT2_BLOCK_SIZE(sb);
11814 pctx.blk2 = EXT2_FRAG_SIZE(sb);
11815 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
11816 ctx->flags |= E2F_FLAG_ABORT;
11817 return;
11818 }
11819
11820 should_be = sb->s_frags_per_group >>
11821 (sb->s_log_block_size - sb->s_log_frag_size);
11822 if (sb->s_blocks_per_group != should_be) {
11823 pctx.blk = sb->s_blocks_per_group;
11824 pctx.blk2 = should_be;
11825 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
11826 ctx->flags |= E2F_FLAG_ABORT;
11827 return;
11828 }
11829
11830 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
11831 if (sb->s_first_data_block != should_be) {
11832 pctx.blk = sb->s_first_data_block;
11833 pctx.blk2 = should_be;
11834 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
11835 ctx->flags |= E2F_FLAG_ABORT;
11836 return;
11837 }
11838
11839 should_be = sb->s_inodes_per_group * fs->group_desc_count;
11840 if (sb->s_inodes_count != should_be) {
11841 pctx.ino = sb->s_inodes_count;
11842 pctx.ino2 = should_be;
11843 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
11844 sb->s_inodes_count = should_be;
11845 ext2fs_mark_super_dirty(fs);
11846 }
11847 }
11848
11849 /*
11850 * Verify the group descriptors....
11851 */
11852 first_block = sb->s_first_data_block;
11853 last_block = first_block + blocks_per_group;
11854
11855 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
11856 pctx.group = i;
11857
11858 if (i == fs->group_desc_count - 1)
11859 last_block = sb->s_blocks_count;
11860 if ((gd->bg_block_bitmap < first_block) ||
11861 (gd->bg_block_bitmap >= last_block)) {
11862 pctx.blk = gd->bg_block_bitmap;
11863 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
11864 gd->bg_block_bitmap = 0;
11865 }
11866 if (gd->bg_block_bitmap == 0) {
11867 ctx->invalid_block_bitmap_flag[i]++;
11868 ctx->invalid_bitmaps++;
11869 }
11870 if ((gd->bg_inode_bitmap < first_block) ||
11871 (gd->bg_inode_bitmap >= last_block)) {
11872 pctx.blk = gd->bg_inode_bitmap;
11873 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
11874 gd->bg_inode_bitmap = 0;
11875 }
11876 if (gd->bg_inode_bitmap == 0) {
11877 ctx->invalid_inode_bitmap_flag[i]++;
11878 ctx->invalid_bitmaps++;
11879 }
11880 if ((gd->bg_inode_table < first_block) ||
11881 ((gd->bg_inode_table +
11882 fs->inode_blocks_per_group - 1) >= last_block)) {
11883 pctx.blk = gd->bg_inode_table;
11884 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
11885 gd->bg_inode_table = 0;
11886 }
11887 if (gd->bg_inode_table == 0) {
11888 ctx->invalid_inode_table_flag[i]++;
11889 ctx->invalid_bitmaps++;
11890 }
11891 free_blocks += gd->bg_free_blocks_count;
11892 free_inodes += gd->bg_free_inodes_count;
11893 first_block += sb->s_blocks_per_group;
11894 last_block += sb->s_blocks_per_group;
11895
11896 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
11897 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
11898 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
11899 ext2fs_unmark_valid(fs);
11900
11901 }
11902
11903 /*
11904 * Update the global counts from the block group counts. This
11905 * is needed for an experimental patch which eliminates
11906 * locking the entire filesystem when allocating blocks or
11907 * inodes; if the filesystem is not unmounted cleanly, the
11908 * global counts may not be accurate.
11909 */
11910 if ((free_blocks != sb->s_free_blocks_count) ||
11911 (free_inodes != sb->s_free_inodes_count)) {
11912 if (ctx->options & E2F_OPT_READONLY)
11913 ext2fs_unmark_valid(fs);
11914 else {
11915 sb->s_free_blocks_count = free_blocks;
11916 sb->s_free_inodes_count = free_inodes;
11917 ext2fs_mark_super_dirty(fs);
11918 }
11919 }
11920
11921 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
11922 (sb->s_free_inodes_count > sb->s_inodes_count))
11923 ext2fs_unmark_valid(fs);
11924
11925
11926 /*
11927 * If we have invalid bitmaps, set the error state of the
11928 * filesystem.
11929 */
11930 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
11931 sb->s_state &= ~EXT2_VALID_FS;
11932 ext2fs_mark_super_dirty(fs);
11933 }
11934
11935 clear_problem_context(&pctx);
11936
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011937 /*
11938 * If the UUID field isn't assigned, assign it.
11939 */
11940 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
11941 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
11942 uuid_generate(sb->s_uuid);
11943 ext2fs_mark_super_dirty(fs);
11944 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
11945 }
11946 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011947
Rob Landley3e72c592006-04-06 22:49:04 +000011948 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011949 * For the Hurd, check to see if the filetype option is set,
11950 * since it doesn't support it.
11951 */
11952 if (!(ctx->options & E2F_OPT_READONLY) &&
11953 fs->super->s_creator_os == EXT2_OS_HURD &&
11954 (fs->super->s_feature_incompat &
11955 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
11956 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
11957 fs->super->s_feature_incompat &=
11958 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
11959 ext2fs_mark_super_dirty(fs);
11960
11961 }
11962 }
11963
11964 /*
11965 * If we have any of the compatibility flags set, we need to have a
11966 * revision 1 filesystem. Most kernels will not check the flags on
11967 * a rev 0 filesystem and we may have corruption issues because of
11968 * the incompatible changes to the filesystem.
11969 */
11970 if (!(ctx->options & E2F_OPT_READONLY) &&
11971 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
11972 (fs->super->s_feature_compat ||
11973 fs->super->s_feature_ro_compat ||
11974 fs->super->s_feature_incompat) &&
11975 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
11976 ext2fs_update_dynamic_rev(fs);
11977 ext2fs_mark_super_dirty(fs);
11978 }
11979
11980 check_resize_inode(ctx);
11981
11982 /*
11983 * Clean up any orphan inodes, if present.
11984 */
11985 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
11986 fs->super->s_state &= ~EXT2_VALID_FS;
11987 ext2fs_mark_super_dirty(fs);
11988 }
11989
11990 /*
11991 * Move the ext3 journal file, if necessary.
11992 */
11993 e2fsck_move_ext3_journal(ctx);
11994 return;
11995}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011996
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011997/*
11998 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011999 */
12000
12001#ifdef ENABLE_SWAPFS
12002
12003struct swap_block_struct {
12004 ext2_ino_t ino;
12005 int isdir;
12006 errcode_t errcode;
12007 char *dir_buf;
12008 struct ext2_inode *inode;
12009};
12010
12011/*
12012 * This is a helper function for block_iterate. We mark all of the
12013 * indirect and direct blocks as changed, so that block_iterate will
12014 * write them out.
12015 */
12016static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
12017 void *priv_data)
12018{
12019 errcode_t retval;
12020
12021 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
12022
12023 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
12024 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
12025 if (retval) {
12026 sb->errcode = retval;
12027 return BLOCK_ABORT;
12028 }
12029 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
12030 if (retval) {
12031 sb->errcode = retval;
12032 return BLOCK_ABORT;
12033 }
12034 }
12035 if (blockcnt >= 0) {
12036 if (blockcnt < EXT2_NDIR_BLOCKS)
12037 return 0;
12038 return BLOCK_CHANGED;
12039 }
12040 if (blockcnt == BLOCK_COUNT_IND) {
12041 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
12042 return 0;
12043 return BLOCK_CHANGED;
12044 }
12045 if (blockcnt == BLOCK_COUNT_DIND) {
12046 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
12047 return 0;
12048 return BLOCK_CHANGED;
12049 }
12050 if (blockcnt == BLOCK_COUNT_TIND) {
12051 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
12052 return 0;
12053 return BLOCK_CHANGED;
12054 }
12055 return BLOCK_CHANGED;
12056}
12057
12058/*
12059 * This function is responsible for byte-swapping all of the indirect,
12060 * block pointers. It is also responsible for byte-swapping directories.
12061 */
12062static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
12063 struct ext2_inode *inode)
12064{
12065 errcode_t retval;
12066 struct swap_block_struct sb;
12067
12068 sb.ino = ino;
12069 sb.inode = inode;
12070 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
12071 sb.errcode = 0;
12072 sb.isdir = 0;
12073 if (LINUX_S_ISDIR(inode->i_mode))
12074 sb.isdir = 1;
12075
12076 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
12077 swap_block, &sb);
12078 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012079 bb_error_msg(_("while calling ext2fs_block_iterate"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012080 ctx->flags |= E2F_FLAG_ABORT;
12081 return;
12082 }
12083 if (sb.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012084 bb_error_msg(_("while calling iterator function"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012085 ctx->flags |= E2F_FLAG_ABORT;
12086 return;
12087 }
12088}
12089
12090static void swap_inodes(e2fsck_t ctx)
12091{
12092 ext2_filsys fs = ctx->fs;
12093 dgrp_t group;
12094 unsigned int i;
12095 ext2_ino_t ino = 1;
12096 char *buf, *block_buf;
12097 errcode_t retval;
12098 struct ext2_inode * inode;
12099
12100 e2fsck_use_inode_shortcuts(ctx, 1);
12101
12102 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
12103 &buf);
12104 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012105 bb_error_msg(_("while allocating inode buffer"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012106 ctx->flags |= E2F_FLAG_ABORT;
12107 return;
12108 }
12109 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12110 "block interate buffer");
12111 for (group = 0; group < fs->group_desc_count; group++) {
12112 retval = io_channel_read_blk(fs->io,
12113 fs->group_desc[group].bg_inode_table,
12114 fs->inode_blocks_per_group, buf);
12115 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012116 bb_error_msg(_("while reading inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012117 group);
12118 ctx->flags |= E2F_FLAG_ABORT;
12119 return;
12120 }
12121 inode = (struct ext2_inode *) buf;
12122 for (i=0; i < fs->super->s_inodes_per_group;
12123 i++, ino++, inode++) {
12124 ctx->stashed_ino = ino;
12125 ctx->stashed_inode = inode;
12126
12127 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
12128 ext2fs_swap_inode(fs, inode, inode, 0);
12129
12130 /*
12131 * Skip deleted files.
12132 */
12133 if (inode->i_links_count == 0)
12134 continue;
12135
12136 if (LINUX_S_ISDIR(inode->i_mode) ||
12137 ((inode->i_block[EXT2_IND_BLOCK] ||
12138 inode->i_block[EXT2_DIND_BLOCK] ||
12139 inode->i_block[EXT2_TIND_BLOCK]) &&
12140 ext2fs_inode_has_valid_blocks(inode)))
12141 swap_inode_blocks(ctx, ino, block_buf, inode);
12142
12143 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12144 return;
12145
12146 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12147 ext2fs_swap_inode(fs, inode, inode, 1);
12148 }
12149 retval = io_channel_write_blk(fs->io,
12150 fs->group_desc[group].bg_inode_table,
12151 fs->inode_blocks_per_group, buf);
12152 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012153 bb_error_msg(_("while writing inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012154 group);
12155 ctx->flags |= E2F_FLAG_ABORT;
12156 return;
12157 }
12158 }
12159 ext2fs_free_mem(&buf);
12160 ext2fs_free_mem(&block_buf);
12161 e2fsck_use_inode_shortcuts(ctx, 0);
12162 ext2fs_flush_icache(fs);
12163}
12164
Rob Landley7c94bed2006-05-03 21:58:45 +000012165#if defined(__powerpc__) && BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012166/*
12167 * On the PowerPC, the big-endian variant of the ext2 filesystem
12168 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
12169 * of each word. Thus a bitmap with only bit 0 set would be, as
12170 * a string of bytes, 00 00 00 01 00 ...
12171 * To cope with this, we byte-reverse each word of a bitmap if
12172 * we have a big-endian filesystem, that is, if we are *not*
12173 * byte-swapping other word-sized numbers.
12174 */
12175#define EXT2_BIG_ENDIAN_BITMAPS
12176#endif
12177
12178#ifdef EXT2_BIG_ENDIAN_BITMAPS
12179static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
12180{
12181 __u32 *p = (__u32 *) bmap->bitmap;
12182 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
12183
12184 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
12185 *p = ext2fs_swab32(*p);
12186}
12187#endif
12188
12189
12190#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012191static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012192{
12193 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012194 if (!(ctx->options & E2F_OPT_PREEN))
12195 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
12196
Rob Landley3e72c592006-04-06 22:49:04 +000012197 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012198
12199 if (fs->super->s_mnt_count) {
12200 fprintf(stderr, _("%s: the filesystem must be freshly "
12201 "checked using fsck\n"
12202 "and not mounted before trying to "
12203 "byte-swap it.\n"), ctx->device_name);
12204 ctx->flags |= E2F_FLAG_ABORT;
12205 return;
12206 }
12207 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
12208 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
12209 EXT2_FLAG_SWAP_BYTES_WRITE);
12210 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
12211 } else {
12212 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
12213 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
12214 }
12215 swap_inodes(ctx);
12216 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12217 return;
12218 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12219 fs->flags |= EXT2_FLAG_SWAP_BYTES;
12220 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
12221 EXT2_FLAG_SWAP_BYTES_WRITE);
12222
12223#ifdef EXT2_BIG_ENDIAN_BITMAPS
12224 e2fsck_read_bitmaps(ctx);
12225 ext2fs_swap_bitmap(fs->inode_map);
12226 ext2fs_swap_bitmap(fs->block_map);
12227 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
12228#endif
12229 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12230 ext2fs_flush(fs);
12231 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012232}
12233#endif /* ENABLE_SWAPFS */
12234
12235#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012236
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012237/*
12238 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012239 */
12240
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012241
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012242void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
12243 const char *description)
12244{
12245 void *ret;
12246 char buf[256];
12247
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012248 ret = malloc(size);
12249 if (!ret) {
12250 sprintf(buf, "Can't allocate %s\n", description);
Rob Landley7c94bed2006-05-03 21:58:45 +000012251 bb_error_msg_and_die(buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012252 }
12253 memset(ret, 0, size);
12254 return ret;
12255}
12256
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012257static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012258{
12259 char *ret;
12260
12261 if (!str)
12262 return NULL;
12263 if (!len)
12264 len = strlen(str);
12265 ret = malloc(len+1);
12266 if (ret) {
12267 strncpy(ret, str, len);
12268 ret[len] = 0;
12269 }
12270 return ret;
12271}
12272
12273#ifndef HAVE_CONIO_H
12274static int read_a_char(void)
12275{
12276 char c;
12277 int r;
12278 int fail = 0;
12279
12280 while(1) {
12281 if (e2fsck_global_ctx &&
12282 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
12283 return 3;
12284 }
12285 r = read(0, &c, 1);
12286 if (r == 1)
12287 return c;
12288 if (fail++ > 100)
12289 break;
12290 }
12291 return EOF;
12292}
12293#endif
12294
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012295static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012296{
12297 int c;
12298 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012299 static const char short_yes[] = "yY";
12300 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012301
12302#ifdef HAVE_TERMIOS_H
12303 struct termios termios, tmp;
12304
12305 tcgetattr (0, &termios);
12306 tmp = termios;
12307 tmp.c_lflag &= ~(ICANON | ECHO);
12308 tmp.c_cc[VMIN] = 1;
12309 tmp.c_cc[VTIME] = 0;
12310 tcsetattr (0, TCSANOW, &tmp);
12311#endif
12312
12313 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012314 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012315 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012316 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012317 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012318 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012319 printf("%s%s? ", string, defstr);
12320 while (1) {
12321 fflush (stdout);
12322 if ((c = read_a_char()) == EOF)
12323 break;
12324 if (c == 3) {
12325#ifdef HAVE_TERMIOS_H
12326 tcsetattr (0, TCSANOW, &termios);
12327#endif
12328 if (e2fsck_global_ctx &&
12329 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
12330 puts("\n");
12331 longjmp(e2fsck_global_ctx->abort_loc, 1);
12332 }
12333 puts(_("cancelled!\n"));
12334 return 0;
12335 }
12336 if (strchr(short_yes, (char) c)) {
12337 def = 1;
12338 break;
12339 }
12340 else if (strchr(short_no, (char) c)) {
12341 def = 0;
12342 break;
12343 }
12344 else if ((c == ' ' || c == '\n') && (def != -1))
12345 break;
12346 }
12347 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012348 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012349 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012350 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012351#ifdef HAVE_TERMIOS_H
12352 tcsetattr (0, TCSANOW, &termios);
12353#endif
12354 return def;
12355}
12356
12357int ask (e2fsck_t ctx, const char * string, int def)
12358{
12359 if (ctx->options & E2F_OPT_NO) {
12360 printf (_("%s? no\n\n"), string);
12361 return 0;
12362 }
12363 if (ctx->options & E2F_OPT_YES) {
12364 printf (_("%s? yes\n\n"), string);
12365 return 1;
12366 }
12367 if (ctx->options & E2F_OPT_PREEN) {
12368 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
12369 return def;
12370 }
12371 return ask_yn(string, def);
12372}
12373
12374void e2fsck_read_bitmaps(e2fsck_t ctx)
12375{
12376 ext2_filsys fs = ctx->fs;
12377 errcode_t retval;
12378
12379 if (ctx->invalid_bitmaps) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012380 bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012381 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012382 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012383 }
12384
12385 ehandler_operation(_("reading inode and block bitmaps"));
12386 retval = ext2fs_read_bitmaps(fs);
12387 ehandler_operation(0);
12388 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012389 bb_error_msg(_("while retrying to read bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012390 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012391 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012392 }
12393}
12394
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012395static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012396{
12397 ext2_filsys fs = ctx->fs;
12398 errcode_t retval;
12399
12400 if (ext2fs_test_bb_dirty(fs)) {
12401 ehandler_operation(_("writing block bitmaps"));
12402 retval = ext2fs_write_block_bitmap(fs);
12403 ehandler_operation(0);
12404 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012405 bb_error_msg(_("while retrying to write block bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012406 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012407 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012408 }
12409 }
12410
12411 if (ext2fs_test_ib_dirty(fs)) {
12412 ehandler_operation(_("writing inode bitmaps"));
12413 retval = ext2fs_write_inode_bitmap(fs);
12414 ehandler_operation(0);
12415 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012416 bb_error_msg(_("while retrying to write inode bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012417 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012418 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012419 }
12420 }
12421}
12422
12423void preenhalt(e2fsck_t ctx)
12424{
12425 ext2_filsys fs = ctx->fs;
12426
12427 if (!(ctx->options & E2F_OPT_PREEN))
12428 return;
12429 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
12430 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
12431 ctx->device_name);
12432 if (fs != NULL) {
12433 fs->super->s_state |= EXT2_ERROR_FS;
12434 ext2fs_mark_super_dirty(fs);
12435 ext2fs_close(fs);
12436 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012437 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012438}
12439
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012440void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
12441 struct ext2_inode * inode, const char *proc)
12442{
12443 int retval;
12444
12445 retval = ext2fs_read_inode(ctx->fs, ino, inode);
12446 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012447 bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
12448 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012449 }
12450}
12451
12452extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
12453 struct ext2_inode * inode, int bufsize,
12454 const char *proc)
12455{
12456 int retval;
12457
12458 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
12459 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012460 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12461 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012462 }
12463}
12464
12465extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
12466 struct ext2_inode * inode, const char *proc)
12467{
12468 int retval;
12469
12470 retval = ext2fs_write_inode(ctx->fs, ino, inode);
12471 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012472 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12473 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012474 }
12475}
12476
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012477blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
12478 io_manager manager)
12479{
12480 struct ext2_super_block *sb;
12481 io_channel io = NULL;
12482 void *buf = NULL;
12483 int blocksize;
12484 blk_t superblock, ret_sb = 8193;
12485
12486 if (fs && fs->super) {
12487 ret_sb = (fs->super->s_blocks_per_group +
12488 fs->super->s_first_data_block);
12489 if (ctx) {
12490 ctx->superblock = ret_sb;
12491 ctx->blocksize = fs->blocksize;
12492 }
12493 return ret_sb;
12494 }
12495
12496 if (ctx) {
12497 if (ctx->blocksize) {
12498 ret_sb = ctx->blocksize * 8;
12499 if (ctx->blocksize == 1024)
12500 ret_sb++;
12501 ctx->superblock = ret_sb;
12502 return ret_sb;
12503 }
12504 ctx->superblock = ret_sb;
12505 ctx->blocksize = 1024;
12506 }
12507
12508 if (!name || !manager)
12509 goto cleanup;
12510
12511 if (manager->open(name, 0, &io) != 0)
12512 goto cleanup;
12513
12514 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
12515 goto cleanup;
12516 sb = (struct ext2_super_block *) buf;
12517
12518 for (blocksize = EXT2_MIN_BLOCK_SIZE;
12519 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
12520 superblock = blocksize*8;
12521 if (blocksize == 1024)
12522 superblock++;
12523 io_channel_set_blksize(io, blocksize);
12524 if (io_channel_read_blk(io, superblock,
12525 -SUPERBLOCK_SIZE, buf))
12526 continue;
Rob Landley7c94bed2006-05-03 21:58:45 +000012527#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012528 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
12529 ext2fs_swap_super(sb);
12530#endif
12531 if (sb->s_magic == EXT2_SUPER_MAGIC) {
12532 ret_sb = superblock;
12533 if (ctx) {
12534 ctx->superblock = superblock;
12535 ctx->blocksize = blocksize;
12536 }
12537 break;
12538 }
12539 }
12540
12541cleanup:
12542 if (io)
12543 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000012544 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012545 return (ret_sb);
12546}
12547
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012548
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012549/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012550 * This function runs through the e2fsck passes and calls them all,
12551 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012552 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012553typedef void (*pass_t)(e2fsck_t ctx);
12554
12555static const pass_t e2fsck_passes[] = {
12556 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
12557 e2fsck_pass5, 0 };
12558
12559#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
12560
12561static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012562{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012563 int i;
12564 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012565
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012566 if (setjmp(ctx->abort_loc)) {
12567 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
12568 return (ctx->flags & E2F_FLAG_RUN_RETURN);
12569 }
12570 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012571
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012572 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
12573 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12574 break;
12575 e2fsck_pass(ctx);
12576 if (ctx->progress)
12577 (void) (ctx->progress)(ctx, 0, 0, 0);
12578 }
12579 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012580
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012581 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12582 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012583 return 0;
12584}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012585
12586
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012587/*
12588 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012589 */
12590
12591
Mike Frysinger51a43b42005-09-24 07:11:16 +000012592/* Command line options */
12593static int swapfs;
12594#ifdef ENABLE_SWAPFS
12595static int normalize_swapfs;
12596#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012597static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000012598static int show_version_only;
12599static int verbose;
12600
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012601#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
12602
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012603static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012604{
12605 ext2_filsys fs = ctx->fs;
12606 int inodes, inodes_used, blocks, blocks_used;
12607 int dir_links;
12608 int num_files, num_links;
12609 int frag_percent;
12610
12611 dir_links = 2 * ctx->fs_directory_count - 1;
12612 num_files = ctx->fs_total_count - dir_links;
12613 num_links = ctx->fs_links_count - dir_links;
12614 inodes = fs->super->s_inodes_count;
12615 inodes_used = (fs->super->s_inodes_count -
12616 fs->super->s_free_inodes_count);
12617 blocks = fs->super->s_blocks_count;
12618 blocks_used = (fs->super->s_blocks_count -
12619 fs->super->s_free_blocks_count);
12620
12621 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
12622 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012623
Mike Frysinger51a43b42005-09-24 07:11:16 +000012624 if (!verbose) {
12625 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
12626 ctx->device_name, inodes_used, inodes,
12627 frag_percent / 10, frag_percent % 10,
12628 blocks_used, blocks);
12629 return;
12630 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012631 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
12632 100 * inodes_used / inodes);
12633 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
12634 P_E2("", "s", ctx->fs_fragmented),
12635 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012636 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
12637 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012638 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
12639 (int) ((long long) 100 * blocks_used / blocks));
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012640 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
12641 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
12642 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
12643 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
12644 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
12645 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
12646 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
12647 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
12648 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
12649 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
12650 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012651}
12652
12653static void check_mount(e2fsck_t ctx)
12654{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012655 errcode_t retval;
12656 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012657
12658 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
12659 &ctx->mount_flags);
12660 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012661 bb_error_msg(_("while determining whether %s is mounted."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000012662 ctx->filesystem_name);
12663 return;
12664 }
12665
12666 /*
12667 * If the filesystem isn't mounted, or it's the root filesystem
12668 * and it's mounted read-only, then everything's fine.
12669 */
12670 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
12671 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
12672 (ctx->mount_flags & EXT2_MF_READONLY)))
12673 return;
12674
12675 if (ctx->options & E2F_OPT_READONLY) {
12676 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
12677 return;
12678 }
12679
12680 printf(_("%s is mounted. "), ctx->filesystem_name);
12681 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000012682 bb_error_msg_and_die(_("Cannot continue, aborting.\n\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012683 printf(_("\n\n\007\007\007\007WARNING!!! "
12684 "Running e2fsck on a mounted filesystem may cause\n"
12685 "SEVERE filesystem damage.\007\007\007\n\n"));
12686 cont = ask_yn(_("Do you really want to continue"), -1);
12687 if (!cont) {
12688 printf (_("check aborted.\n"));
12689 exit (0);
12690 }
12691 return;
12692}
12693
12694static int is_on_batt(void)
12695{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012696 FILE *f;
12697 DIR *d;
12698 char tmp[80], tmp2[80], fname[80];
12699 unsigned int acflag;
12700 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012701
12702 f = fopen("/proc/apm", "r");
12703 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012704 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012705 acflag = 1;
12706 fclose(f);
12707 return (acflag != 1);
12708 }
12709 d = opendir("/proc/acpi/ac_adapter");
12710 if (d) {
12711 while ((de=readdir(d)) != NULL) {
12712 if (!strncmp(".", de->d_name, 1))
12713 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012714 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000012715 de->d_name);
12716 f = fopen(fname, "r");
12717 if (!f)
12718 continue;
12719 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
12720 tmp[0] = 0;
12721 fclose(f);
12722 if (strncmp(tmp, "off-line", 8) == 0) {
12723 closedir(d);
12724 return 1;
12725 }
12726 }
12727 closedir(d);
12728 }
12729 return 0;
12730}
12731
12732/*
12733 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012734 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000012735 * message explaining why a check is being forced.
12736 */
12737static void check_if_skip(e2fsck_t ctx)
12738{
12739 ext2_filsys fs = ctx->fs;
12740 const char *reason = NULL;
12741 unsigned int reason_arg = 0;
12742 long next_check;
12743 int batt = is_on_batt();
12744 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012745
Rob Landleyd8f66012006-05-05 17:29:09 +000012746 if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012747 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012748
Mike Frysinger51a43b42005-09-24 07:11:16 +000012749 if ((fs->super->s_state & EXT2_ERROR_FS) ||
12750 !ext2fs_test_valid(fs))
12751 reason = _(" contains a file system with errors");
12752 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
12753 reason = _(" was not cleanly unmounted");
12754 else if ((fs->super->s_max_mnt_count > 0) &&
12755 (fs->super->s_mnt_count >=
12756 (unsigned) fs->super->s_max_mnt_count)) {
12757 reason = _(" has been mounted %u times without being checked");
12758 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012759 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012760 (unsigned) fs->super->s_max_mnt_count*2))
12761 reason = 0;
12762 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012763 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000012764 fs->super->s_checkinterval)) {
12765 reason = _(" has gone %u days without being checked");
12766 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012767 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012768 fs->super->s_checkinterval*2))
12769 reason = 0;
12770 }
12771 if (reason) {
12772 fputs(ctx->device_name, stdout);
12773 printf(reason, reason_arg);
12774 fputs(_(", check forced.\n"), stdout);
12775 return;
12776 }
12777 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
12778 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
12779 fs->super->s_inodes_count,
12780 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
12781 fs->super->s_blocks_count);
12782 next_check = 100000;
12783 if (fs->super->s_max_mnt_count > 0) {
12784 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012785 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012786 next_check = 1;
12787 }
12788 if (fs->super->s_checkinterval &&
12789 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
12790 next_check = 1;
12791 if (next_check <= 5) {
12792 if (next_check == 1)
12793 fputs(_(" (check after next mount)"), stdout);
12794 else
12795 printf(_(" (check in %ld mounts)"), next_check);
12796 }
12797 fputc('\n', stdout);
12798 ext2fs_close(fs);
12799 ctx->fs = NULL;
12800 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012801 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012802}
12803
12804/*
12805 * For completion notice
12806 */
12807struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012808 int max_pass;
12809 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000012810};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012811static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000012812 5, { 0, 70, 90, 92, 95, 100 }
12813};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012814
Mike Frysinger51a43b42005-09-24 07:11:16 +000012815static char bar[128], spaces[128];
12816
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012817static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000012818 int max)
12819{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012820 float percent;
12821
Mike Frysinger51a43b42005-09-24 07:11:16 +000012822 if (pass <= 0)
12823 return 0.0;
12824 if (pass > tbl->max_pass || max == 0)
12825 return 100.0;
12826 percent = ((float) curr) / ((float) max);
12827 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
12828 + tbl->table[pass-1]);
12829}
12830
Rob Landleydfba7412006-03-06 20:47:33 +000012831void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012832{
12833 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
12834 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012835
Mike Frysinger51a43b42005-09-24 07:11:16 +000012836 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
12837 ctx->stop_meta);
12838 fflush(stdout);
12839 ctx->flags &= ~E2F_FLAG_PROG_BAR;
12840}
12841
12842int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
12843 unsigned int dpynum)
12844{
12845 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012846 int i;
12847 unsigned int tick;
12848 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012849 int dpywidth;
12850 int fixed_percent;
12851
12852 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
12853 return 0;
12854
12855 /*
12856 * Calculate the new progress position. If the
12857 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012858 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000012859 */
12860 fixed_percent = (int) ((10 * percent) + 0.5);
12861 if (ctx->progress_last_percent == fixed_percent)
12862 return 0;
12863 ctx->progress_last_percent = fixed_percent;
12864
12865 /*
12866 * If we've already updated the spinner once within
12867 * the last 1/8th of a second, no point doing it
12868 * again.
12869 */
12870 gettimeofday(&tv, NULL);
12871 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
12872 if ((tick == ctx->progress_last_time) &&
12873 (fixed_percent != 0) && (fixed_percent != 1000))
12874 return 0;
12875 ctx->progress_last_time = tick;
12876
12877 /*
12878 * Advance the spinner, and note that the progress bar
12879 * will be on the screen
12880 */
12881 ctx->progress_pos = (ctx->progress_pos+1) & 3;
12882 ctx->flags |= E2F_FLAG_PROG_BAR;
12883
12884 dpywidth = 66 - strlen(label);
12885 dpywidth = 8 * (dpywidth / 8);
12886 if (dpynum)
12887 dpywidth -= 8;
12888
12889 i = ((percent * dpywidth) + 50) / 100;
12890 printf("%s%s: |%s%s", ctx->start_meta, label,
12891 bar + (sizeof(bar) - (i+1)),
12892 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
12893 if (fixed_percent == 1000)
12894 fputc('|', stdout);
12895 else
12896 fputc(spinner[ctx->progress_pos & 3], stdout);
12897 printf(" %4.1f%% ", percent);
12898 if (dpynum)
12899 printf("%u\r", dpynum);
12900 else
12901 fputs(" \r", stdout);
12902 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012903
Mike Frysinger51a43b42005-09-24 07:11:16 +000012904 if (fixed_percent == 1000)
12905 e2fsck_clear_progbar(ctx);
12906 fflush(stdout);
12907
12908 return 0;
12909}
12910
12911static int e2fsck_update_progress(e2fsck_t ctx, int pass,
12912 unsigned long cur, unsigned long max)
12913{
12914 char buf[80];
12915 float percent;
12916
12917 if (pass == 0)
12918 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012919
Mike Frysinger51a43b42005-09-24 07:11:16 +000012920 if (ctx->progress_fd) {
12921 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
12922 write(ctx->progress_fd, buf, strlen(buf));
12923 } else {
12924 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
12925 e2fsck_simple_progress(ctx, ctx->device_name,
12926 percent, 0);
12927 }
12928 return 0;
12929}
12930
Mike Frysinger51a43b42005-09-24 07:11:16 +000012931static void reserve_stdio_fds(void)
12932{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012933 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012934
12935 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000012936 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012937 if (fd > 2)
12938 break;
12939 if (fd < 0) {
12940 fprintf(stderr, _("ERROR: Couldn't open "
12941 "/dev/null (%s)\n"),
12942 strerror(errno));
12943 break;
12944 }
12945 }
12946 close(fd);
12947}
12948
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012949static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012950{
12951 e2fsck_t ctx = e2fsck_global_ctx;
12952
12953 if (!ctx)
12954 return;
12955
12956 ctx->progress = e2fsck_update_progress;
12957 ctx->progress_fd = 0;
12958}
12959
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012960static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012961{
12962 e2fsck_t ctx = e2fsck_global_ctx;
12963
12964 if (!ctx)
12965 return;
12966
12967 e2fsck_clear_progbar(ctx);
12968 ctx->progress = 0;
12969}
12970
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012971static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012972{
12973 e2fsck_t ctx = e2fsck_global_ctx;
12974
12975 if (!ctx)
12976 exit(FSCK_CANCELED);
12977
12978 ctx->flags |= E2F_FLAG_CANCEL;
12979}
Mike Frysinger51a43b42005-09-24 07:11:16 +000012980
12981static void parse_extended_opts(e2fsck_t ctx, const char *opts)
12982{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012983 char *buf, *token, *next, *p, *arg;
12984 int ea_ver;
12985 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012986
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012987 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012988 for (token = buf; token && *token; token = next) {
12989 p = strchr(token, ',');
12990 next = 0;
12991 if (p) {
12992 *p = 0;
12993 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012994 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012995 arg = strchr(token, '=');
12996 if (arg) {
12997 *arg = 0;
12998 arg++;
12999 }
13000 if (strcmp(token, "ea_ver") == 0) {
13001 if (!arg) {
13002 extended_usage++;
13003 continue;
13004 }
13005 ea_ver = strtoul(arg, &p, 0);
13006 if (*p ||
13007 ((ea_ver != 1) && (ea_ver != 2))) {
13008 fprintf(stderr,
13009 _("Invalid EA version.\n"));
13010 extended_usage++;
13011 continue;
13012 }
13013 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000013014 } else {
13015 fprintf(stderr, _("Unknown extended option: %s\n"),
13016 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013017 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000013018 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000013019 }
13020 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013021 bb_error_msg_and_die(
13022 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000013023 "and may take an argument which\n"
13024 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000013025 "Valid extended options are:\n"
13026 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000013027 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013028}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013029
13030
13031static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
13032{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013033 int flush = 0;
13034 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013035 e2fsck_t ctx;
13036 errcode_t retval;
13037 struct sigaction sa;
13038 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013039
13040 retval = e2fsck_allocate_context(&ctx);
13041 if (retval)
13042 return retval;
13043
13044 *ret_ctx = ctx;
13045
13046 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
13047 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
13048 if (isatty(0) && isatty(1)) {
13049 ctx->interactive = 1;
13050 } else {
13051 ctx->start_meta[0] = '\001';
13052 ctx->stop_meta[0] = '\002';
13053 }
13054 memset(bar, '=', sizeof(bar)-1);
13055 memset(spaces, ' ', sizeof(spaces)-1);
13056 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013057
Mike Frysinger51a43b42005-09-24 07:11:16 +000013058 if (argc && *argv)
13059 ctx->program_name = *argv;
13060 else
13061 ctx->program_name = "e2fsck";
13062 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
13063 switch (c) {
13064 case 'C':
13065 ctx->progress = e2fsck_update_progress;
13066 ctx->progress_fd = atoi(optarg);
13067 if (!ctx->progress_fd)
13068 break;
13069 /* Validate the file descriptor to avoid disasters */
13070 fd = dup(ctx->progress_fd);
13071 if (fd < 0) {
13072 fprintf(stderr,
13073 _("Error validating file descriptor %d: %s\n"),
13074 ctx->progress_fd,
13075 error_message(errno));
Rob Landley7c94bed2006-05-03 21:58:45 +000013076 bb_error_msg_and_die(_("Invalid completion information file descriptor"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013077 } else
13078 close(fd);
13079 break;
13080 case 'D':
13081 ctx->options |= E2F_OPT_COMPRESS_DIRS;
13082 break;
13083 case 'E':
13084 extended_opts = optarg;
13085 break;
13086 case 'p':
13087 case 'a':
13088 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
13089 conflict_opt:
Rob Landley7c94bed2006-05-03 21:58:45 +000013090 bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013091 }
13092 ctx->options |= E2F_OPT_PREEN;
13093 break;
13094 case 'n':
13095 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
13096 goto conflict_opt;
13097 ctx->options |= E2F_OPT_NO;
13098 break;
13099 case 'y':
13100 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
13101 goto conflict_opt;
13102 ctx->options |= E2F_OPT_YES;
13103 break;
13104 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000013105 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013106 fprintf(stderr, _("The -t option is not "
13107 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013108 break;
13109 case 'c':
13110 if (cflag++)
13111 ctx->options |= E2F_OPT_WRITECHECK;
13112 ctx->options |= E2F_OPT_CHECKBLOCKS;
13113 break;
13114 case 'r':
13115 /* What we do by default, anyway! */
13116 break;
13117 case 'b':
13118 ctx->use_superblock = atoi(optarg);
13119 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
13120 break;
13121 case 'B':
13122 ctx->blocksize = atoi(optarg);
13123 break;
13124 case 'I':
13125 ctx->inode_buffer_blocks = atoi(optarg);
13126 break;
13127 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013128 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013129 break;
13130 case 'P':
13131 ctx->process_inode_size = atoi(optarg);
13132 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013133 case 'd':
13134 ctx->options |= E2F_OPT_DEBUG;
13135 break;
13136 case 'f':
13137 ctx->options |= E2F_OPT_FORCE;
13138 break;
13139 case 'F':
13140 flush = 1;
13141 break;
13142 case 'v':
13143 verbose = 1;
13144 break;
13145 case 'V':
13146 show_version_only = 1;
13147 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013148 case 'N':
13149 ctx->device_name = optarg;
13150 break;
13151#ifdef ENABLE_SWAPFS
13152 case 's':
13153 normalize_swapfs = 1;
13154 case 'S':
13155 swapfs = 1;
13156 break;
13157#else
13158 case 's':
13159 case 'S':
13160 fprintf(stderr, _("Byte-swapping filesystems "
13161 "not compiled in this version "
13162 "of e2fsck\n"));
13163 exit(1);
13164#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013165 default:
Rob Landley7c94bed2006-05-03 21:58:45 +000013166 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013167 }
13168 if (show_version_only)
13169 return 0;
13170 if (optind != argc - 1)
Rob Landley7c94bed2006-05-03 21:58:45 +000013171 bb_show_usage();
Rob Landleyd8f66012006-05-05 17:29:09 +000013172 if ((ctx->options & E2F_OPT_NO) &&
Mike Frysinger51a43b42005-09-24 07:11:16 +000013173 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
13174 ctx->options |= E2F_OPT_READONLY;
13175 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013176 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013177 *ctx->io_options++ = 0;
13178 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
13179 if (!ctx->filesystem_name) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013180 bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
13181 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013182 }
13183 if (extended_opts)
13184 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013185
Mike Frysinger51a43b42005-09-24 07:11:16 +000013186 if (flush) {
13187 fd = open(ctx->filesystem_name, O_RDONLY, 0);
13188 if (fd < 0) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013189 bb_error_msg(_("while opening %s for flushing"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013190 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013191 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013192 }
13193 if ((retval = ext2fs_sync_device(fd, 1))) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013194 bb_error_msg(_("while trying to flush %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013195 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013196 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013197 }
13198 close(fd);
13199 }
13200#ifdef ENABLE_SWAPFS
Rob Landleyd8f66012006-05-05 17:29:09 +000013201 if (swapfs && cflag) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013202 fprintf(stderr, _("Incompatible options not "
13203 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013204 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013205 }
13206 }
13207#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013208 /*
13209 * Set up signal action
13210 */
13211 memset(&sa, 0, sizeof(struct sigaction));
13212 sa.sa_handler = signal_cancel;
13213 sigaction(SIGINT, &sa, 0);
13214 sigaction(SIGTERM, &sa, 0);
13215#ifdef SA_RESTART
13216 sa.sa_flags = SA_RESTART;
13217#endif
13218 e2fsck_global_ctx = ctx;
13219 sa.sa_handler = signal_progress_on;
13220 sigaction(SIGUSR1, &sa, 0);
13221 sa.sa_handler = signal_progress_off;
13222 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013223
13224 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013225 if (cflag)
13226 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013227 return 0;
13228}
13229
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013230static const char my_ver_string[] = E2FSPROGS_VERSION;
13231static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013232
Mike Frysinger51a43b42005-09-24 07:11:16 +000013233int e2fsck_main (int argc, char *argv[])
13234{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013235 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013236 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013237 ext2_filsys fs = 0;
13238 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013239 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013240 const char *lib_ver_date;
13241 int my_ver, lib_ver;
13242 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013243 struct problem_context pctx;
13244 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013245
Mike Frysinger51a43b42005-09-24 07:11:16 +000013246 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000013247
Mike Frysinger51a43b42005-09-24 07:11:16 +000013248 my_ver = ext2fs_parse_version_string(my_ver_string);
13249 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
13250 if (my_ver > lib_ver) {
13251 fprintf( stderr, _("Error: ext2fs library version "
13252 "out of date!\n"));
13253 show_version_only++;
13254 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013255
Mike Frysinger51a43b42005-09-24 07:11:16 +000013256 retval = PRS(argc, argv, &ctx);
13257 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013258 bb_error_msg(_("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013259 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013260 }
13261 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013262
Mike Frysinger51a43b42005-09-24 07:11:16 +000013263 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
13264 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
13265 my_ver_date);
13266
13267 if (show_version_only) {
13268 fprintf(stderr, _("\tUsing %s, %s\n"),
13269 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013270 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013271 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013272
Mike Frysinger51a43b42005-09-24 07:11:16 +000013273 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013274
Mike Frysinger51a43b42005-09-24 07:11:16 +000013275 if (!(ctx->options & E2F_OPT_PREEN) &&
13276 !(ctx->options & E2F_OPT_NO) &&
13277 !(ctx->options & E2F_OPT_YES)) {
13278 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013279 bb_error_msg_and_die(_("need terminal for interactive repairs"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013280 }
13281 ctx->superblock = ctx->use_superblock;
13282restart:
13283#ifdef CONFIG_TESTIO_DEBUG
13284 io_ptr = test_io_manager;
13285 test_io_backing_manager = unix_io_manager;
13286#else
13287 io_ptr = unix_io_manager;
13288#endif
13289 flags = 0;
13290 if ((ctx->options & E2F_OPT_READONLY) == 0)
13291 flags |= EXT2_FLAG_RW;
13292
13293 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013294 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013295 flags, ctx->superblock, ctx->blocksize,
13296 io_ptr, &fs);
13297 } else if (ctx->superblock) {
13298 int blocksize;
13299 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13300 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013301 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013302 ctx->io_options, flags,
13303 ctx->superblock, blocksize,
13304 io_ptr, &fs);
13305 if (!retval)
13306 break;
13307 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013308 } else
13309 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013310 flags, 0, 0, io_ptr, &fs);
13311 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
13312 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
13313 ((retval == EXT2_ET_BAD_MAGIC) ||
13314 ((retval == 0) && ext2fs_check_desc(fs)))) {
13315 if (!fs || (fs->group_desc_count > 1)) {
13316 printf(_("%s trying backup blocks...\n"),
13317 retval ? _("Couldn't find ext2 superblock,") :
13318 _("Group descriptors look bad..."));
13319 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
13320 if (fs)
13321 ext2fs_close(fs);
13322 goto restart;
13323 }
13324 }
13325 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013326 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013327 ctx->filesystem_name);
13328 if (retval == EXT2_ET_REV_TOO_HIGH) {
13329 printf(_("The filesystem revision is apparently "
13330 "too high for this version of e2fsck.\n"
13331 "(Or the filesystem superblock "
13332 "is corrupt)\n\n"));
13333 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
13334 } else if (retval == EXT2_ET_SHORT_READ)
13335 printf(_("Could this be a zero-length partition?\n"));
13336 else if ((retval == EPERM) || (retval == EACCES))
13337 printf(_("You must have %s access to the "
13338 "filesystem or be root\n"),
13339 (ctx->options & E2F_OPT_READONLY) ?
13340 "r/o" : "r/w");
13341 else if (retval == ENXIO)
13342 printf(_("Possibly non-existent or swap device?\n"));
13343#ifdef EROFS
13344 else if (retval == EROFS)
13345 printf(_("Disk write-protected; use the -n option "
13346 "to do a read-only\n"
13347 "check of the device.\n"));
13348#endif
13349 else
13350 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
Rob Landley7c94bed2006-05-03 21:58:45 +000013351 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013352 }
13353 ctx->fs = fs;
13354 fs->priv_data = ctx;
13355 sb = fs->super;
13356 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013357 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013358 ctx->filesystem_name);
13359 get_newer:
Rob Landley7c94bed2006-05-03 21:58:45 +000013360 bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013361 }
13362
13363 /*
13364 * Set the device name, which is used whenever we print error
13365 * or informational messages to the user.
13366 */
13367 if (ctx->device_name == 0 &&
13368 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013369 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013370 sizeof(sb->s_volume_name));
13371 }
13372 if (ctx->device_name == 0)
13373 ctx->device_name = ctx->filesystem_name;
13374
13375 /*
13376 * Make sure the ext3 superblock fields are consistent.
13377 */
13378 retval = e2fsck_check_ext3_journal(ctx);
13379 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013380 bb_error_msg(_("while checking ext3 journal for %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013381 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013382 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013383 }
13384
13385 /*
13386 * Check to see if we need to do ext3-style recovery. If so,
13387 * do it, and then restart the fsck.
13388 */
13389 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
13390 if (ctx->options & E2F_OPT_READONLY) {
13391 printf(_("Warning: skipping journal recovery "
13392 "because doing a read-only filesystem "
13393 "check.\n"));
13394 io_channel_flush(ctx->fs->io);
13395 } else {
13396 if (ctx->flags & E2F_FLAG_RESTARTED) {
13397 /*
13398 * Whoops, we attempted to run the
13399 * journal twice. This should never
13400 * happen, unless the hardware or
13401 * device driver is being bogus.
13402 */
Rob Landley7c94bed2006-05-03 21:58:45 +000013403 bb_error_msg(_("unable to set superblock flags on %s\n"), ctx->device_name);
13404 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013405 }
13406 retval = e2fsck_run_ext3_journal(ctx);
13407 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013408 bb_error_msg(_("while recovering ext3 journal of %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013409 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013410 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013411 }
13412 ext2fs_close(ctx->fs);
13413 ctx->fs = 0;
13414 ctx->flags |= E2F_FLAG_RESTARTED;
13415 goto restart;
13416 }
13417 }
13418
13419 /*
13420 * Check for compatibility with the feature sets. We need to
13421 * be more stringent than ext2fs_open().
13422 */
13423 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
13424 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013425 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013426 goto get_newer;
13427 }
13428 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013429 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013430 goto get_newer;
13431 }
13432#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000013433 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013434 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
Rob Landley7c94bed2006-05-03 21:58:45 +000013435 bb_error_msg(_("Warning: compression support is experimental.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013436#endif
13437#ifndef ENABLE_HTREE
13438 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013439 bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
Mike Frysinger51a43b42005-09-24 07:11:16 +000013440 "but filesystem %s has HTREE directories.\n"),
13441 ctx->device_name);
13442 goto get_newer;
13443 }
13444#endif
13445
13446 /*
13447 * If the user specified a specific superblock, presumably the
13448 * master superblock has been trashed. So we mark the
13449 * superblock as dirty, so it can be written out.
13450 */
13451 if (ctx->superblock &&
13452 !(ctx->options & E2F_OPT_READONLY))
13453 ext2fs_mark_super_dirty(fs);
13454
13455 /*
13456 * We only update the master superblock because (a) paranoia;
13457 * we don't want to corrupt the backup superblocks, and (b) we
13458 * don't need to update the mount count and last checked
13459 * fields in the backup superblock (the kernel doesn't
13460 * update the backup superblocks anyway).
13461 */
13462 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13463
13464 ehandler_init(fs->io);
13465
13466 if (ctx->superblock)
13467 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
13468 ext2fs_mark_valid(fs);
13469 check_super_block(ctx);
13470 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013471 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013472 check_if_skip(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013473 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013474 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013475#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000013476
13477#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000013478#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000013479#else
Rob Landley8b606342006-01-24 02:38:28 +000013480#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000013481#endif
13482
13483
Mike Frysinger51a43b42005-09-24 07:11:16 +000013484 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000013485 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013486 fprintf(stderr, _("%s: Filesystem byte order "
13487 "already normalized.\n"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013488 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013489 }
13490 }
13491 if (swapfs) {
13492 swap_filesys(ctx);
13493 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013494 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013495 }
13496#endif
13497
13498 /*
13499 * Mark the system as valid, 'til proven otherwise
13500 */
13501 ext2fs_mark_valid(fs);
13502
13503 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
13504 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013505 bb_error_msg(_("while reading bad blocks inode"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013506 preenhalt(ctx);
13507 printf(_("This doesn't bode well,"
13508 " but we'll try to go on...\n"));
13509 }
13510
13511 run_result = e2fsck_run(ctx);
13512 e2fsck_clear_progbar(ctx);
13513 if (run_result == E2F_FLAG_RESTART) {
13514 printf(_("Restarting e2fsck from the beginning...\n"));
13515 retval = e2fsck_reset_context(ctx);
13516 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013517 bb_error_msg(_("while resetting context"));
13518 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013519 }
13520 ext2fs_close(fs);
13521 goto restart;
13522 }
13523 if (run_result & E2F_FLAG_CANCEL) {
13524 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
13525 ctx->device_name : ctx->filesystem_name);
13526 exit_value |= FSCK_CANCELED;
13527 }
13528 if (run_result & E2F_FLAG_ABORT)
Rob Landley7c94bed2006-05-03 21:58:45 +000013529 bb_error_msg_and_die(_("aborted"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013530
Rob Landley3e72c592006-04-06 22:49:04 +000013531 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013532 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013533 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013534 if (!(ctx->options & E2F_OPT_PREEN))
13535 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
13536 ctx->device_name);
13537 if (ctx->mount_flags & EXT2_MF_ISROOT) {
13538 printf(_("%s: ***** REBOOT LINUX *****\n"),
13539 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013540 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013541 }
13542 }
13543 if (!ext2fs_test_valid(fs)) {
13544 printf(_("\n%s: ********** WARNING: Filesystem still has "
13545 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013546 exit_value |= EXIT_UNCORRECTED;
13547 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013548 }
13549 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013550 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013551 else {
13552 show_stats(ctx);
13553 if (!(ctx->options & E2F_OPT_READONLY)) {
13554 if (ext2fs_test_valid(fs)) {
13555 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013556 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013557 sb->s_state = EXT2_VALID_FS;
13558 } else
13559 sb->s_state &= ~EXT2_VALID_FS;
13560 sb->s_mnt_count = 0;
13561 sb->s_lastcheck = time(NULL);
13562 ext2fs_mark_super_dirty(fs);
13563 }
13564 }
13565
13566 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013567
Mike Frysinger51a43b42005-09-24 07:11:16 +000013568 ext2fs_close(fs);
13569 ctx->fs = NULL;
13570 free(ctx->filesystem_name);
13571 free(ctx->journal_name);
13572 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013573
Mike Frysinger51a43b42005-09-24 07:11:16 +000013574 return exit_value;
13575}