blob: 6f3fd7edaa4901ba166de0b6609670112d50d7d5 [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);
Rob Landley206f7572006-05-19 22:42:23 +00002894static errcode_t scan_callback(ext2_filsys fs,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00002895 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"3978e552005-09-27 11:43:29 +00003724 dgrp_t group, void * priv_data)
3725{
3726 struct scan_callback_struct *scan_struct;
3727 e2fsck_t ctx;
3728
3729 scan_struct = (struct scan_callback_struct *) priv_data;
3730 ctx = scan_struct->ctx;
3731
3732 process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
3733
3734 if (ctx->progress)
3735 if ((ctx->progress)(ctx, 1, group+1,
3736 ctx->fs->group_desc_count))
3737 return EXT2_ET_CANCEL_REQUESTED;
3738
3739 return 0;
3740}
3741
3742/*
3743 * Process the inodes in the "inodes to process" list.
3744 */
3745static void process_inodes(e2fsck_t ctx, char *block_buf)
3746{
3747 int i;
3748 struct ext2_inode *old_stashed_inode;
3749 ext2_ino_t old_stashed_ino;
3750 const char *old_operation;
3751 char buf[80];
3752 struct problem_context pctx;
3753
Rob Landley3e72c592006-04-06 22:49:04 +00003754 /* begin process_inodes */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003755 if (process_inode_count == 0)
3756 return;
3757 old_operation = ehandler_operation(0);
3758 old_stashed_inode = ctx->stashed_inode;
3759 old_stashed_ino = ctx->stashed_ino;
3760 qsort(inodes_to_process, process_inode_count,
3761 sizeof(struct process_inode_block), process_inode_cmp);
3762 clear_problem_context(&pctx);
3763 for (i=0; i < process_inode_count; i++) {
3764 pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
3765 pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003766 sprintf(buf, _("reading indirect blocks of inode %u"),
3767 pctx.ino);
3768 ehandler_operation(buf);
3769 check_blocks(ctx, &pctx, block_buf);
3770 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
3771 break;
3772 }
3773 ctx->stashed_inode = old_stashed_inode;
3774 ctx->stashed_ino = old_stashed_ino;
3775 process_inode_count = 0;
Rob Landley3e72c592006-04-06 22:49:04 +00003776 /* end process inodes */
3777
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003778 ehandler_operation(old_operation);
3779}
3780
Rob Landley7c94bed2006-05-03 21:58:45 +00003781static int process_inode_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003782{
3783 const struct process_inode_block *ib_a =
3784 (const struct process_inode_block *) a;
3785 const struct process_inode_block *ib_b =
3786 (const struct process_inode_block *) b;
3787 int ret;
3788
3789 ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
3790 ib_b->inode.i_block[EXT2_IND_BLOCK]);
3791 if (ret == 0)
3792 ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
3793 return ret;
3794}
3795
3796/*
3797 * Mark an inode as being bad in some what
3798 */
3799static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
3800{
3801 struct problem_context pctx;
3802
3803 if (!ctx->inode_bad_map) {
3804 clear_problem_context(&pctx);
3805
3806 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3807 _("bad inode map"), &ctx->inode_bad_map);
3808 if (pctx.errcode) {
3809 pctx.num = 3;
3810 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3811 /* Should never get here */
3812 ctx->flags |= E2F_FLAG_ABORT;
3813 return;
3814 }
3815 }
3816 ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
3817}
3818
3819
3820/*
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003821 * This procedure will allocate the inode imagic table
3822 */
3823static void alloc_imagic_map(e2fsck_t ctx)
3824{
3825 struct problem_context pctx;
3826
3827 clear_problem_context(&pctx);
3828 pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
3829 _("imagic inode map"),
3830 &ctx->inode_imagic_map);
3831 if (pctx.errcode) {
3832 pctx.num = 5;
3833 fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
3834 /* Should never get here */
3835 ctx->flags |= E2F_FLAG_ABORT;
3836 return;
3837 }
3838}
3839
3840/*
3841 * Marks a block as in use, setting the dup_map if it's been set
3842 * already. Called by process_block and process_bad_block.
3843 *
3844 * WARNING: Assumes checks have already been done to make sure block
3845 * is valid. This is true in both process_block and process_bad_block.
3846 */
Rob Landley7c94bed2006-05-03 21:58:45 +00003847static void mark_block_used(e2fsck_t ctx, blk_t block)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003848{
3849 struct problem_context pctx;
3850
3851 clear_problem_context(&pctx);
3852
3853 if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
3854 if (!ctx->block_dup_map) {
3855 pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
3856 _("multiply claimed block map"),
3857 &ctx->block_dup_map);
3858 if (pctx.errcode) {
3859 pctx.num = 3;
3860 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
3861 &pctx);
3862 /* Should never get here */
3863 ctx->flags |= E2F_FLAG_ABORT;
3864 return;
3865 }
3866 }
3867 ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
3868 } else {
3869 ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
3870 }
3871}
3872
3873/*
3874 * Adjust the extended attribute block's reference counts at the end
3875 * of pass 1, either by subtracting out references for EA blocks that
3876 * are still referenced in ctx->refcount, or by adding references for
3877 * EA blocks that had extra references as accounted for in
3878 * ctx->refcount_extra.
3879 */
3880static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
3881 char *block_buf, int adjust_sign)
3882{
3883 struct ext2_ext_attr_header *header;
3884 struct problem_context pctx;
3885 ext2_filsys fs = ctx->fs;
3886 blk_t blk;
3887 __u32 should_be;
3888 int count;
3889
3890 clear_problem_context(&pctx);
3891
3892 ea_refcount_intr_begin(refcount);
3893 while (1) {
3894 if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
3895 break;
3896 pctx.blk = blk;
3897 pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
3898 if (pctx.errcode) {
3899 fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
3900 return;
3901 }
3902 header = (struct ext2_ext_attr_header *) block_buf;
3903 pctx.blkcount = header->h_refcount;
3904 should_be = header->h_refcount + adjust_sign * count;
3905 pctx.num = should_be;
3906 if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
3907 header->h_refcount = should_be;
3908 pctx.errcode = ext2fs_write_ext_attr(fs, blk,
3909 block_buf);
3910 if (pctx.errcode) {
3911 fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
3912 continue;
3913 }
3914 }
3915 }
3916}
3917
3918/*
3919 * Handle processing the extended attribute blocks
3920 */
3921static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
3922 char *block_buf)
3923{
3924 ext2_filsys fs = ctx->fs;
3925 ext2_ino_t ino = pctx->ino;
3926 struct ext2_inode *inode = pctx->inode;
3927 blk_t blk;
3928 char * end;
3929 struct ext2_ext_attr_header *header;
3930 struct ext2_ext_attr_entry *entry;
3931 int count;
3932 region_t region;
3933
3934 blk = inode->i_file_acl;
3935 if (blk == 0)
3936 return 0;
3937
3938 /*
3939 * If the Extended attribute flag isn't set, then a non-zero
3940 * file acl means that the inode is corrupted.
3941 *
3942 * Or if the extended attribute block is an invalid block,
3943 * then the inode is also corrupted.
3944 */
3945 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
3946 (blk < fs->super->s_first_data_block) ||
3947 (blk >= fs->super->s_blocks_count)) {
3948 mark_inode_bad(ctx, ino);
3949 return 0;
3950 }
3951
3952 /* If ea bitmap hasn't been allocated, create it */
3953 if (!ctx->block_ea_map) {
3954 pctx->errcode = ext2fs_allocate_block_bitmap(fs,
3955 _("ext attr block map"),
3956 &ctx->block_ea_map);
3957 if (pctx->errcode) {
3958 pctx->num = 2;
3959 fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
3960 ctx->flags |= E2F_FLAG_ABORT;
3961 return 0;
3962 }
3963 }
3964
3965 /* Create the EA refcount structure if necessary */
3966 if (!ctx->refcount) {
3967 pctx->errcode = ea_refcount_create(0, &ctx->refcount);
3968 if (pctx->errcode) {
3969 pctx->num = 1;
3970 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3971 ctx->flags |= E2F_FLAG_ABORT;
3972 return 0;
3973 }
3974 }
3975
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00003976 /* Have we seen this EA block before? */
3977 if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
3978 if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
3979 return 1;
3980 /* Ooops, this EA was referenced more than it stated */
3981 if (!ctx->refcount_extra) {
3982 pctx->errcode = ea_refcount_create(0,
3983 &ctx->refcount_extra);
3984 if (pctx->errcode) {
3985 pctx->num = 2;
3986 fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
3987 ctx->flags |= E2F_FLAG_ABORT;
3988 return 0;
3989 }
3990 }
3991 ea_refcount_increment(ctx->refcount_extra, blk, 0);
3992 return 1;
3993 }
3994
3995 /*
3996 * OK, we haven't seen this EA block yet. So we need to
3997 * validate it
3998 */
3999 pctx->blk = blk;
4000 pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
4001 if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
4002 goto clear_extattr;
4003 header = (struct ext2_ext_attr_header *) block_buf;
4004 pctx->blk = inode->i_file_acl;
4005 if (((ctx->ext_attr_ver == 1) &&
4006 (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
4007 ((ctx->ext_attr_ver == 2) &&
4008 (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
4009 if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
4010 goto clear_extattr;
4011 }
4012
4013 if (header->h_blocks != 1) {
4014 if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
4015 goto clear_extattr;
4016 }
4017
4018 region = region_create(0, fs->blocksize);
4019 if (!region) {
4020 fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
4021 ctx->flags |= E2F_FLAG_ABORT;
4022 return 0;
4023 }
4024 if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
4025 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4026 goto clear_extattr;
4027 }
4028
4029 entry = (struct ext2_ext_attr_entry *)(header+1);
4030 end = block_buf + fs->blocksize;
4031 while ((char *)entry < end && *(__u32 *)entry) {
4032 if (region_allocate(region, (char *)entry - (char *)header,
4033 EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
4034 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4035 goto clear_extattr;
4036 }
4037 if ((ctx->ext_attr_ver == 1 &&
4038 (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
4039 (ctx->ext_attr_ver == 2 &&
4040 entry->e_name_index == 0)) {
4041 if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
4042 goto clear_extattr;
4043 }
4044 if (entry->e_value_block != 0) {
4045 if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
4046 goto clear_extattr;
4047 }
4048 if (entry->e_value_size &&
4049 region_allocate(region, entry->e_value_offs,
4050 EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
4051 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4052 goto clear_extattr;
4053 }
4054 entry = EXT2_EXT_ATTR_NEXT(entry);
4055 }
4056 if (region_allocate(region, (char *)entry - (char *)header, 4)) {
4057 if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
4058 goto clear_extattr;
4059 }
4060 region_free(region);
4061
4062 count = header->h_refcount - 1;
4063 if (count)
4064 ea_refcount_store(ctx->refcount, blk, count);
4065 mark_block_used(ctx, blk);
4066 ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
4067
4068 return 1;
4069
4070clear_extattr:
4071 inode->i_file_acl = 0;
4072 e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
4073 return 0;
4074}
4075
4076/* Returns 1 if bad htree, 0 if OK */
4077static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004078 ext2_ino_t ino FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004079 struct ext2_inode *inode,
4080 char *block_buf)
4081{
4082 struct ext2_dx_root_info *root;
4083 ext2_filsys fs = ctx->fs;
4084 errcode_t retval;
4085 blk_t blk;
4086
4087 if ((!LINUX_S_ISDIR(inode->i_mode) &&
4088 fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
4089 (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
4090 fix_problem(ctx, PR_1_HTREE_SET, pctx)))
4091 return 1;
4092
4093 blk = inode->i_block[0];
4094 if (((blk == 0) ||
4095 (blk < fs->super->s_first_data_block) ||
4096 (blk >= fs->super->s_blocks_count)) &&
4097 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4098 return 1;
4099
4100 retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
4101 if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4102 return 1;
4103
4104 /* XXX should check that beginning matches a directory */
4105 root = (struct ext2_dx_root_info *) (block_buf + 24);
4106
4107 if ((root->reserved_zero || root->info_length < 8) &&
4108 fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
4109 return 1;
4110
4111 pctx->num = root->hash_version;
4112 if ((root->hash_version != EXT2_HASH_LEGACY) &&
4113 (root->hash_version != EXT2_HASH_HALF_MD4) &&
4114 (root->hash_version != EXT2_HASH_TEA) &&
4115 fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
4116 return 1;
4117
4118 if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
4119 fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
4120 return 1;
4121
4122 pctx->num = root->indirect_levels;
4123 if ((root->indirect_levels > 1) &&
4124 fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
4125 return 1;
4126
4127 return 0;
4128}
4129
4130/*
4131 * This subroutine is called on each inode to account for all of the
4132 * blocks used by that inode.
4133 */
4134static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
4135 char *block_buf)
4136{
4137 ext2_filsys fs = ctx->fs;
4138 struct process_block_struct_1 pb;
4139 ext2_ino_t ino = pctx->ino;
4140 struct ext2_inode *inode = pctx->inode;
4141 int bad_size = 0;
4142 int dirty_inode = 0;
4143 __u64 size;
4144
4145 pb.ino = ino;
4146 pb.num_blocks = 0;
4147 pb.last_block = -1;
4148 pb.num_illegal_blocks = 0;
4149 pb.suppress = 0; pb.clear = 0;
4150 pb.fragmented = 0;
4151 pb.compressed = 0;
4152 pb.previous_block = 0;
4153 pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
4154 pb.is_reg = LINUX_S_ISREG(inode->i_mode);
4155 pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
4156 pb.inode = inode;
4157 pb.pctx = pctx;
4158 pb.ctx = ctx;
4159 pctx->ino = ino;
4160 pctx->errcode = 0;
4161
4162 if (inode->i_flags & EXT2_COMPRBLK_FL) {
4163 if (fs->super->s_feature_incompat &
4164 EXT2_FEATURE_INCOMPAT_COMPRESSION)
4165 pb.compressed = 1;
4166 else {
4167 if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
4168 inode->i_flags &= ~EXT2_COMPRBLK_FL;
4169 dirty_inode++;
4170 }
4171 }
4172 }
4173
4174 if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
4175 pb.num_blocks++;
4176
4177 if (ext2fs_inode_has_valid_blocks(inode))
4178 pctx->errcode = ext2fs_block_iterate2(fs, ino,
4179 pb.is_dir ? BLOCK_FLAG_HOLE : 0,
4180 block_buf, process_block, &pb);
4181 end_problem_latch(ctx, PR_LATCH_BLOCK);
4182 end_problem_latch(ctx, PR_LATCH_TOOBIG);
4183 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
4184 goto out;
4185 if (pctx->errcode)
4186 fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
4187
4188 if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
4189 ctx->fs_fragmented++;
4190
4191 if (pb.clear) {
4192 inode->i_links_count = 0;
4193 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4194 inode->i_dtime = time(0);
4195 dirty_inode++;
4196 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4197 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4198 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4199 /*
4200 * The inode was probably partially accounted for
4201 * before processing was aborted, so we need to
4202 * restart the pass 1 scan.
4203 */
4204 ctx->flags |= E2F_FLAG_RESTART;
4205 goto out;
4206 }
4207
4208 if (inode->i_flags & EXT2_INDEX_FL) {
4209 if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
4210 inode->i_flags &= ~EXT2_INDEX_FL;
4211 dirty_inode++;
4212 } else {
4213#ifdef ENABLE_HTREE
4214 e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
4215#endif
4216 }
4217 }
4218 if (ctx->dirs_to_hash && pb.is_dir &&
4219 !(inode->i_flags & EXT2_INDEX_FL) &&
4220 ((inode->i_size / fs->blocksize) >= 3))
4221 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
4222
4223 if (!pb.num_blocks && pb.is_dir) {
4224 if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
4225 inode->i_links_count = 0;
4226 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
4227 inode->i_dtime = time(0);
4228 dirty_inode++;
4229 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
4230 ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
4231 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
4232 ctx->fs_directory_count--;
4233 goto out;
4234 }
4235 }
4236
4237 pb.num_blocks *= (fs->blocksize / 512);
Rob Landley3e72c592006-04-06 22:49:04 +00004238
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004239 if (pb.is_dir) {
4240 int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
4241 if (nblock > (pb.last_block + 1))
4242 bad_size = 1;
4243 else if (nblock < (pb.last_block + 1)) {
4244 if (((pb.last_block + 1) - nblock) >
4245 fs->super->s_prealloc_dir_blocks)
4246 bad_size = 2;
4247 }
4248 } else {
4249 size = EXT2_I_SIZE(inode);
4250 if ((pb.last_block >= 0) &&
4251 (size < (__u64) pb.last_block * fs->blocksize))
4252 bad_size = 3;
4253 else if (size > ext2_max_sizes[fs->super->s_log_block_size])
4254 bad_size = 4;
4255 }
4256 /* i_size for symlinks is checked elsewhere */
4257 if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
4258 pctx->num = (pb.last_block+1) * fs->blocksize;
4259 if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
4260 inode->i_size = pctx->num;
4261 if (!LINUX_S_ISDIR(inode->i_mode))
4262 inode->i_size_high = pctx->num >> 32;
4263 dirty_inode++;
4264 }
4265 pctx->num = 0;
4266 }
4267 if (LINUX_S_ISREG(inode->i_mode) &&
4268 (inode->i_size_high || inode->i_size & 0x80000000UL))
4269 ctx->large_files++;
4270 if (pb.num_blocks != inode->i_blocks) {
4271 pctx->num = pb.num_blocks;
4272 if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
4273 inode->i_blocks = pb.num_blocks;
4274 dirty_inode++;
4275 }
4276 pctx->num = 0;
4277 }
4278out:
4279 if (dirty_inode)
4280 e2fsck_write_inode(ctx, ino, inode, "check_blocks");
4281}
4282
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004283
4284/*
4285 * This is a helper function for check_blocks().
4286 */
4287static int process_block(ext2_filsys fs,
4288 blk_t *block_nr,
4289 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004290 blk_t ref_block FSCK_ATTR((unused)),
4291 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004292 void *priv_data)
4293{
4294 struct process_block_struct_1 *p;
4295 struct problem_context *pctx;
4296 blk_t blk = *block_nr;
4297 int ret_code = 0;
4298 int problem = 0;
4299 e2fsck_t ctx;
4300
4301 p = (struct process_block_struct_1 *) priv_data;
4302 pctx = p->pctx;
4303 ctx = p->ctx;
4304
4305 if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
4306 /* todo: Check that the comprblk_fl is high, that the
4307 blkaddr pattern looks right (all non-holes up to
4308 first EXT2FS_COMPRESSED_BLKADDR, then all
4309 EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
4310 that the feature_incompat bit is high, and that the
4311 inode is a regular file. If we're doing a "full
4312 check" (a concept introduced to e2fsck by e2compr,
4313 meaning that we look at data blocks as well as
4314 metadata) then call some library routine that
4315 checks the compressed data. I'll have to think
4316 about this, because one particularly important
4317 problem to be able to fix is to recalculate the
4318 cluster size if necessary. I think that perhaps
4319 we'd better do most/all e2compr-specific checks
4320 separately, after the non-e2compr checks. If not
4321 doing a full check, it may be useful to test that
4322 the personality is linux; e.g. if it isn't then
4323 perhaps this really is just an illegal block. */
4324 return 0;
4325 }
4326
4327 if (blk == 0) {
4328 if (p->is_dir == 0) {
4329 /*
4330 * Should never happen, since only directories
4331 * get called with BLOCK_FLAG_HOLE
4332 */
4333#if DEBUG_E2FSCK
4334 printf("process_block() called with blk == 0, "
4335 "blockcnt=%d, inode %lu???\n",
4336 blockcnt, p->ino);
4337#endif
4338 return 0;
4339 }
4340 if (blockcnt < 0)
4341 return 0;
4342 if (blockcnt * fs->blocksize < p->inode->i_size) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004343 goto mark_dir;
4344 }
4345 return 0;
4346 }
4347
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004348 /*
4349 * Simplistic fragmentation check. We merely require that the
4350 * file be contiguous. (Which can never be true for really
4351 * big files that are greater than a block group.)
4352 */
4353 if (!HOLE_BLKADDR(p->previous_block)) {
4354 if (p->previous_block+1 != blk)
4355 p->fragmented = 1;
4356 }
4357 p->previous_block = blk;
4358
4359 if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
4360 problem = PR_1_TOOBIG_DIR;
4361 if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
4362 problem = PR_1_TOOBIG_REG;
4363 if (!p->is_dir && !p->is_reg && blockcnt > 0)
4364 problem = PR_1_TOOBIG_SYMLINK;
4365
4366 if (blk < fs->super->s_first_data_block ||
4367 blk >= fs->super->s_blocks_count)
4368 problem = PR_1_ILLEGAL_BLOCK_NUM;
4369
4370 if (problem) {
4371 p->num_illegal_blocks++;
4372 if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
4373 if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
4374 p->clear = 1;
4375 return BLOCK_ABORT;
4376 }
4377 if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
4378 p->suppress = 1;
4379 set_latch_flags(PR_LATCH_BLOCK,
4380 PRL_SUPPRESS, 0);
4381 }
4382 }
4383 pctx->blk = blk;
4384 pctx->blkcount = blockcnt;
4385 if (fix_problem(ctx, problem, pctx)) {
4386 blk = *block_nr = 0;
4387 ret_code = BLOCK_CHANGED;
4388 goto mark_dir;
4389 } else
4390 return 0;
4391 }
4392
4393 if (p->ino == EXT2_RESIZE_INO) {
4394 /*
4395 * The resize inode has already be sanity checked
4396 * during pass #0 (the superblock checks). All we
4397 * have to do is mark the double indirect block as
4398 * being in use; all of the other blocks are handled
4399 * by mark_table_blocks()).
4400 */
4401 if (blockcnt == BLOCK_COUNT_DIND)
4402 mark_block_used(ctx, blk);
4403 } else
4404 mark_block_used(ctx, blk);
4405 p->num_blocks++;
4406 if (blockcnt >= 0)
4407 p->last_block = blockcnt;
4408mark_dir:
4409 if (p->is_dir && (blockcnt >= 0)) {
4410 pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
4411 blk, blockcnt);
4412 if (pctx->errcode) {
4413 pctx->blk = blk;
4414 pctx->num = blockcnt;
4415 fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
4416 /* Should never get here */
4417 ctx->flags |= E2F_FLAG_ABORT;
4418 return BLOCK_ABORT;
4419 }
4420 }
4421 return ret_code;
4422}
4423
Rob Landleyd8f66012006-05-05 17:29:09 +00004424static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004425 blk_t *block_nr,
4426 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004427 blk_t ref_block FSCK_ATTR((unused)),
4428 int ref_offset FSCK_ATTR((unused)),
Rob Landleyd8f66012006-05-05 17:29:09 +00004429 void *priv_data EXT2FS_ATTR((unused)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004430{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004431 /*
4432 * Note: This function processes blocks for the bad blocks
4433 * inode, which is never compressed. So we don't use HOLE_BLKADDR().
4434 */
4435
Rob Landleyd8f66012006-05-05 17:29:09 +00004436 printf("Unrecoverable Error: Found %lli bad blocks starting at block number: %u\n", blockcnt, *block_nr);
4437 return BLOCK_ERROR;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004438}
4439
4440/*
4441 * This routine gets called at the end of pass 1 if bad blocks are
4442 * detected in the superblock, group descriptors, inode_bitmaps, or
4443 * block bitmaps. At this point, all of the blocks have been mapped
4444 * out, so we can try to allocate new block(s) to replace the bad
4445 * blocks.
4446 */
4447static void handle_fs_bad_blocks(e2fsck_t ctx)
4448{
Rob Landleyd8f66012006-05-05 17:29:09 +00004449 printf("Bad blocks detected on your filesystem\n"
4450 "You should get your data off as the device will soon die\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004451}
4452
4453/*
4454 * This routine marks all blocks which are used by the superblock,
4455 * group descriptors, inode bitmaps, and block bitmaps.
4456 */
4457static void mark_table_blocks(e2fsck_t ctx)
4458{
4459 ext2_filsys fs = ctx->fs;
4460 blk_t block, b;
4461 dgrp_t i;
4462 int j;
4463 struct problem_context pctx;
4464
4465 clear_problem_context(&pctx);
4466
4467 block = fs->super->s_first_data_block;
4468 for (i = 0; i < fs->group_desc_count; i++) {
4469 pctx.group = i;
4470
4471 ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
4472
4473 /*
4474 * Mark the blocks used for the inode table
4475 */
4476 if (fs->group_desc[i].bg_inode_table) {
4477 for (j = 0, b = fs->group_desc[i].bg_inode_table;
4478 j < fs->inode_blocks_per_group;
4479 j++, b++) {
4480 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4481 b)) {
4482 pctx.blk = b;
4483 if (fix_problem(ctx,
4484 PR_1_ITABLE_CONFLICT, &pctx)) {
4485 ctx->invalid_inode_table_flag[i]++;
4486 ctx->invalid_bitmaps++;
4487 }
4488 } else {
4489 ext2fs_mark_block_bitmap(ctx->block_found_map,
4490 b);
4491 }
4492 }
4493 }
4494
4495 /*
4496 * Mark block used for the block bitmap
4497 */
4498 if (fs->group_desc[i].bg_block_bitmap) {
4499 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4500 fs->group_desc[i].bg_block_bitmap)) {
4501 pctx.blk = fs->group_desc[i].bg_block_bitmap;
4502 if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
4503 ctx->invalid_block_bitmap_flag[i]++;
4504 ctx->invalid_bitmaps++;
4505 }
4506 } else {
4507 ext2fs_mark_block_bitmap(ctx->block_found_map,
4508 fs->group_desc[i].bg_block_bitmap);
4509 }
4510
4511 }
4512 /*
4513 * Mark block used for the inode bitmap
4514 */
4515 if (fs->group_desc[i].bg_inode_bitmap) {
4516 if (ext2fs_test_block_bitmap(ctx->block_found_map,
4517 fs->group_desc[i].bg_inode_bitmap)) {
4518 pctx.blk = fs->group_desc[i].bg_inode_bitmap;
4519 if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
4520 ctx->invalid_inode_bitmap_flag[i]++;
4521 ctx->invalid_bitmaps++;
4522 }
4523 } else {
4524 ext2fs_mark_block_bitmap(ctx->block_found_map,
4525 fs->group_desc[i].bg_inode_bitmap);
4526 }
4527 }
4528 block += fs->super->s_blocks_per_group;
4529 }
4530}
4531
4532/*
4533 * Thes subroutines short circuits ext2fs_get_blocks and
4534 * ext2fs_check_directory; we use them since we already have the inode
4535 * structure, so there's no point in letting the ext2fs library read
4536 * the inode again.
4537 */
4538static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
4539 blk_t *blocks)
4540{
4541 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4542 int i;
4543
4544 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4545 return EXT2_ET_CALLBACK_NOTHANDLED;
4546
4547 for (i=0; i < EXT2_N_BLOCKS; i++)
4548 blocks[i] = ctx->stashed_inode->i_block[i];
4549 return 0;
4550}
4551
4552static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
4553 struct ext2_inode *inode)
4554{
4555 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4556
4557 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4558 return EXT2_ET_CALLBACK_NOTHANDLED;
4559 *inode = *ctx->stashed_inode;
4560 return 0;
4561}
4562
4563static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
4564 struct ext2_inode *inode)
4565{
4566 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4567
4568 if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
4569 *ctx->stashed_inode = *inode;
4570 return EXT2_ET_CALLBACK_NOTHANDLED;
4571}
4572
4573static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
4574{
4575 e2fsck_t ctx = (e2fsck_t) fs->priv_data;
4576
4577 if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
4578 return EXT2_ET_CALLBACK_NOTHANDLED;
4579
4580 if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
4581 return EXT2_ET_NO_DIRECTORY;
4582 return 0;
4583}
4584
4585void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
4586{
4587 ext2_filsys fs = ctx->fs;
4588
4589 if (bool) {
4590 fs->get_blocks = pass1_get_blocks;
4591 fs->check_directory = pass1_check_directory;
4592 fs->read_inode = pass1_read_inode;
4593 fs->write_inode = pass1_write_inode;
4594 ctx->stashed_ino = 0;
4595 } else {
4596 fs->get_blocks = 0;
4597 fs->check_directory = 0;
4598 fs->read_inode = 0;
4599 fs->write_inode = 0;
4600 }
4601}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004602
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004603/*
4604 * pass1b.c --- Pass #1b of e2fsck
4605 *
4606 * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
4607 * only invoked if pass 1 discovered blocks which are in use by more
4608 * than one inode.
4609 *
4610 * Pass1B scans the data blocks of all the inodes again, generating a
4611 * complete list of duplicate blocks and which inodes have claimed
4612 * them.
4613 *
4614 * Pass1C does a tree-traversal of the filesystem, to determine the
4615 * parent directories of these inodes. This step is necessary so that
4616 * e2fsck can print out the pathnames of affected inodes.
4617 *
4618 * Pass1D is a reconciliation pass. For each inode with duplicate
4619 * blocks, the user is prompted if s/he would like to clone the file
4620 * (so that the file gets a fresh copy of the duplicated blocks) or
4621 * simply to delete the file.
4622 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004623 */
4624
4625
4626/* Needed for architectures where sizeof(int) != sizeof(void *) */
4627#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
4628#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
4629
4630/* Define an extension to the ext2 library's block count information */
4631#define BLOCK_COUNT_EXTATTR (-5)
4632
4633struct block_el {
4634 blk_t block;
4635 struct block_el *next;
4636};
4637
4638struct inode_el {
4639 ext2_ino_t inode;
4640 struct inode_el *next;
4641};
4642
4643struct dup_block {
4644 int num_bad;
4645 struct inode_el *inode_list;
4646};
4647
4648/*
4649 * This structure stores information about a particular inode which
4650 * is sharing blocks with other inodes. This information is collected
4651 * to display to the user, so that the user knows what files he or she
4652 * is dealing with, when trying to decide how to resolve the conflict
4653 * of multiply-claimed blocks.
4654 */
4655struct dup_inode {
4656 ext2_ino_t dir;
4657 int num_dupblocks;
4658 struct ext2_inode inode;
4659 struct block_el *block_list;
4660};
4661
4662static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
4663 e2_blkcnt_t blockcnt, blk_t ref_blk,
4664 int ref_offset, void *priv_data);
4665static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
4666 struct dup_inode *dp, char *block_buf);
4667static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
4668 struct dup_inode *dp, char* block_buf);
4669static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
4670
4671static void pass1b(e2fsck_t ctx, char *block_buf);
4672static void pass1c(e2fsck_t ctx, char *block_buf);
4673static void pass1d(e2fsck_t ctx, char *block_buf);
4674
4675static int dup_inode_count = 0;
4676
4677static dict_t blk_dict, ino_dict;
4678
4679static ext2fs_inode_bitmap inode_dup_map;
4680
4681static int dict_int_cmp(const void *a, const void *b)
4682{
4683 intptr_t ia, ib;
4684
4685 ia = (intptr_t)a;
4686 ib = (intptr_t)b;
4687
4688 return (ia-ib);
4689}
4690
4691/*
4692 * Add a duplicate block record
4693 */
4694static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
4695 struct ext2_inode *inode)
4696{
4697 dnode_t *n;
4698 struct dup_block *db;
4699 struct dup_inode *di;
4700 struct block_el *blk_el;
4701 struct inode_el *ino_el;
4702
4703 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
4704 if (n)
4705 db = (struct dup_block *) dnode_get(n);
4706 else {
4707 db = (struct dup_block *) e2fsck_allocate_memory(ctx,
4708 sizeof(struct dup_block), "duplicate block header");
4709 db->num_bad = 0;
4710 db->inode_list = 0;
4711 dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
4712 }
4713 ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
4714 sizeof(struct inode_el), "inode element");
4715 ino_el->inode = ino;
4716 ino_el->next = db->inode_list;
4717 db->inode_list = ino_el;
4718 db->num_bad++;
4719
4720 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
4721 if (n)
4722 di = (struct dup_inode *) dnode_get(n);
4723 else {
4724 di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
4725 sizeof(struct dup_inode), "duplicate inode header");
4726 di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
4727 di->num_dupblocks = 0;
4728 di->block_list = 0;
4729 di->inode = *inode;
4730 dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
4731 }
4732 blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
4733 sizeof(struct block_el), "block element");
4734 blk_el->block = blk;
4735 blk_el->next = di->block_list;
4736 di->block_list = blk_el;
4737 di->num_dupblocks++;
4738}
4739
4740/*
4741 * Free a duplicate inode record
4742 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004743static void inode_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004744{
4745 struct dup_inode *di;
4746 struct block_el *p, *next;
4747
4748 di = (struct dup_inode *) dnode_get(node);
4749 for (p = di->block_list; p; p = next) {
4750 next = p->next;
4751 free(p);
4752 }
4753 free(node);
4754}
4755
4756/*
4757 * Free a duplicate block record
4758 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004759static void block_dnode_free(dnode_t *node)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004760{
4761 struct dup_block *db;
4762 struct inode_el *p, *next;
4763
4764 db = (struct dup_block *) dnode_get(node);
4765 for (p = db->inode_list; p; p = next) {
4766 next = p->next;
4767 free(p);
4768 }
4769 free(node);
4770}
4771
4772
4773/*
4774 * Main procedure for handling duplicate blocks
4775 */
4776void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
4777{
4778 ext2_filsys fs = ctx->fs;
4779 struct problem_context pctx;
4780
4781 clear_problem_context(&pctx);
4782
4783 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
4784 _("multiply claimed inode map"), &inode_dup_map);
4785 if (pctx.errcode) {
4786 fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
4787 ctx->flags |= E2F_FLAG_ABORT;
4788 return;
4789 }
4790
4791 dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
4792 dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00004793 dict_set_allocator(&ino_dict, inode_dnode_free);
4794 dict_set_allocator(&blk_dict, block_dnode_free);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004795
4796 pass1b(ctx, block_buf);
4797 pass1c(ctx, block_buf);
4798 pass1d(ctx, block_buf);
4799
4800 /*
4801 * Time to free all of the accumulated data structures that we
4802 * don't need anymore.
4803 */
4804 dict_free_nodes(&ino_dict);
4805 dict_free_nodes(&blk_dict);
4806}
4807
4808/*
4809 * Scan the inodes looking for inodes that contain duplicate blocks.
4810 */
4811struct process_block_struct_1b {
4812 e2fsck_t ctx;
4813 ext2_ino_t ino;
4814 int dup_blocks;
4815 struct ext2_inode *inode;
4816 struct problem_context *pctx;
4817};
4818
4819static void pass1b(e2fsck_t ctx, char *block_buf)
4820{
4821 ext2_filsys fs = ctx->fs;
4822 ext2_ino_t ino;
4823 struct ext2_inode inode;
4824 ext2_inode_scan scan;
4825 struct process_block_struct_1b pb;
4826 struct problem_context pctx;
4827
4828 clear_problem_context(&pctx);
4829
4830 if (!(ctx->options & E2F_OPT_PREEN))
4831 fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
4832 pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
4833 &scan);
4834 if (pctx.errcode) {
4835 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4836 ctx->flags |= E2F_FLAG_ABORT;
4837 return;
4838 }
4839 ctx->stashed_inode = &inode;
4840 pb.ctx = ctx;
4841 pb.pctx = &pctx;
4842 pctx.str = "pass1b";
4843 while (1) {
4844 pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
4845 if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
4846 continue;
4847 if (pctx.errcode) {
4848 fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
4849 ctx->flags |= E2F_FLAG_ABORT;
4850 return;
4851 }
4852 if (!ino)
4853 break;
4854 pctx.ino = ctx->stashed_ino = ino;
4855 if ((ino != EXT2_BAD_INO) &&
4856 !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
4857 continue;
4858
4859 pb.ino = ino;
4860 pb.dup_blocks = 0;
4861 pb.inode = &inode;
4862
4863 if (ext2fs_inode_has_valid_blocks(&inode) ||
4864 (ino == EXT2_BAD_INO))
4865 pctx.errcode = ext2fs_block_iterate2(fs, ino,
4866 0, block_buf, process_pass1b_block, &pb);
4867 if (inode.i_file_acl)
4868 process_pass1b_block(fs, &inode.i_file_acl,
4869 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
4870 if (pb.dup_blocks) {
4871 end_problem_latch(ctx, PR_LATCH_DBLOCK);
4872 if (ino >= EXT2_FIRST_INODE(fs->super) ||
4873 ino == EXT2_ROOT_INO)
4874 dup_inode_count++;
4875 }
4876 if (pctx.errcode)
4877 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
4878 }
4879 ext2fs_close_inode_scan(scan);
4880 e2fsck_use_inode_shortcuts(ctx, 0);
4881}
4882
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004883static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004884 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004885 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
4886 blk_t ref_blk FSCK_ATTR((unused)),
4887 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004888 void *priv_data)
4889{
4890 struct process_block_struct_1b *p;
4891 e2fsck_t ctx;
4892
4893 if (HOLE_BLKADDR(*block_nr))
4894 return 0;
4895 p = (struct process_block_struct_1b *) priv_data;
4896 ctx = p->ctx;
4897
4898 if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
4899 return 0;
4900
4901 /* OK, this is a duplicate block */
4902 if (p->ino != EXT2_BAD_INO) {
4903 p->pctx->blk = *block_nr;
4904 fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
4905 }
4906 p->dup_blocks++;
4907 ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
4908
4909 add_dupe(ctx, p->ino, *block_nr, p->inode);
4910
4911 return 0;
4912}
4913
4914/*
4915 * Pass 1c: Scan directories for inodes with duplicate blocks. This
4916 * is used so that we can print pathnames when prompting the user for
4917 * what to do.
4918 */
4919struct search_dir_struct {
4920 int count;
4921 ext2_ino_t first_inode;
4922 ext2_ino_t max_inode;
4923};
4924
4925static int search_dirent_proc(ext2_ino_t dir, int entry,
4926 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00004927 int offset FSCK_ATTR((unused)),
4928 int blocksize FSCK_ATTR((unused)),
4929 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00004930 void *priv_data)
4931{
4932 struct search_dir_struct *sd;
4933 struct dup_inode *p;
4934 dnode_t *n;
4935
4936 sd = (struct search_dir_struct *) priv_data;
4937
4938 if (dirent->inode > sd->max_inode)
4939 /* Should abort this inode, but not everything */
4940 return 0;
4941
4942 if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
4943 !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
4944 return 0;
4945
4946 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
4947 if (!n)
4948 return 0;
4949 p = (struct dup_inode *) dnode_get(n);
4950 p->dir = dir;
4951 sd->count--;
4952
4953 return(sd->count ? 0 : DIRENT_ABORT);
4954}
4955
4956
4957static void pass1c(e2fsck_t ctx, char *block_buf)
4958{
4959 ext2_filsys fs = ctx->fs;
4960 struct search_dir_struct sd;
4961 struct problem_context pctx;
4962
4963 clear_problem_context(&pctx);
4964
4965 if (!(ctx->options & E2F_OPT_PREEN))
4966 fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
4967
4968 /*
4969 * Search through all directories to translate inodes to names
4970 * (by searching for the containing directory for that inode.)
4971 */
4972 sd.count = dup_inode_count;
4973 sd.first_inode = EXT2_FIRST_INODE(fs->super);
4974 sd.max_inode = fs->super->s_inodes_count;
4975 ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
4976 search_dirent_proc, &sd);
4977}
4978
4979static void pass1d(e2fsck_t ctx, char *block_buf)
4980{
4981 ext2_filsys fs = ctx->fs;
4982 struct dup_inode *p, *t;
4983 struct dup_block *q;
4984 ext2_ino_t *shared, ino;
4985 int shared_len;
4986 int i;
4987 int file_ok;
4988 int meta_data = 0;
4989 struct problem_context pctx;
4990 dnode_t *n, *m;
4991 struct block_el *s;
4992 struct inode_el *r;
4993
4994 clear_problem_context(&pctx);
4995
4996 if (!(ctx->options & E2F_OPT_PREEN))
4997 fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
4998 e2fsck_read_bitmaps(ctx);
4999
5000 pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
5001 fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
5002 shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
5003 sizeof(ext2_ino_t) * dict_count(&ino_dict),
5004 "Shared inode list");
5005 for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
5006 p = (struct dup_inode *) dnode_get(n);
5007 shared_len = 0;
5008 file_ok = 1;
5009 ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
Mike Frysinger874af852006-03-08 07:03:27 +00005010 if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005011 continue;
5012
5013 /*
5014 * Find all of the inodes which share blocks with this
5015 * one. First we find all of the duplicate blocks
5016 * belonging to this inode, and then search each block
5017 * get the list of inodes, and merge them together.
5018 */
5019 for (s = p->block_list; s; s = s->next) {
5020 m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
5021 if (!m)
5022 continue; /* Should never happen... */
5023 q = (struct dup_block *) dnode_get(m);
5024 if (q->num_bad > 1)
5025 file_ok = 0;
5026 if (check_if_fs_block(ctx, s->block)) {
5027 file_ok = 0;
5028 meta_data = 1;
5029 }
5030
5031 /*
5032 * Add all inodes used by this block to the
5033 * shared[] --- which is a unique list, so
5034 * if an inode is already in shared[], don't
5035 * add it again.
5036 */
5037 for (r = q->inode_list; r; r = r->next) {
5038 if (r->inode == ino)
5039 continue;
5040 for (i = 0; i < shared_len; i++)
5041 if (shared[i] == r->inode)
5042 break;
5043 if (i == shared_len) {
5044 shared[shared_len++] = r->inode;
5045 }
5046 }
5047 }
5048
5049 /*
5050 * Report the inode that we are working on
5051 */
5052 pctx.inode = &p->inode;
5053 pctx.ino = ino;
5054 pctx.dir = p->dir;
5055 pctx.blkcount = p->num_dupblocks;
5056 pctx.num = meta_data ? shared_len+1 : shared_len;
5057 fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
5058 pctx.blkcount = 0;
5059 pctx.num = 0;
5060
5061 if (meta_data)
5062 fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
5063
5064 for (i = 0; i < shared_len; i++) {
5065 m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
5066 if (!m)
5067 continue; /* should never happen */
5068 t = (struct dup_inode *) dnode_get(m);
5069 /*
5070 * Report the inode that we are sharing with
5071 */
5072 pctx.inode = &t->inode;
5073 pctx.ino = shared[i];
5074 pctx.dir = t->dir;
5075 fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
5076 }
5077 if (file_ok) {
5078 fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
5079 continue;
5080 }
5081 if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
5082 pctx.errcode = clone_file(ctx, ino, p, block_buf);
5083 if (pctx.errcode)
5084 fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
5085 else
5086 continue;
5087 }
5088 if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
5089 delete_file(ctx, ino, p, block_buf);
5090 else
5091 ext2fs_unmark_valid(fs);
5092 }
5093 ext2fs_free_mem(&shared);
5094}
5095
5096/*
5097 * Drop the refcount on the dup_block structure, and clear the entry
5098 * in the block_dup_map if appropriate.
5099 */
5100static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
5101{
5102 p->num_bad--;
5103 if (p->num_bad <= 0 ||
5104 (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
5105 ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
5106}
5107
5108static int delete_file_block(ext2_filsys fs,
5109 blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005110 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
5111 blk_t ref_block FSCK_ATTR((unused)),
5112 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005113 void *priv_data)
5114{
5115 struct process_block_struct_1b *pb;
5116 struct dup_block *p;
5117 dnode_t *n;
5118 e2fsck_t ctx;
5119
5120 pb = (struct process_block_struct_1b *) priv_data;
5121 ctx = pb->ctx;
5122
5123 if (HOLE_BLKADDR(*block_nr))
5124 return 0;
5125
5126 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5127 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5128 if (n) {
5129 p = (struct dup_block *) dnode_get(n);
5130 decrement_badcount(ctx, *block_nr, p);
5131 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005132 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005133 *block_nr);
5134 } else {
5135 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
5136 ext2fs_block_alloc_stats(fs, *block_nr, -1);
5137 }
5138
5139 return 0;
5140}
5141
5142static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
5143 struct dup_inode *dp, char* block_buf)
5144{
5145 ext2_filsys fs = ctx->fs;
5146 struct process_block_struct_1b pb;
5147 struct ext2_inode inode;
5148 struct problem_context pctx;
5149 unsigned int count;
5150
5151 clear_problem_context(&pctx);
5152 pctx.ino = pb.ino = ino;
5153 pb.dup_blocks = dp->num_dupblocks;
5154 pb.ctx = ctx;
5155 pctx.str = "delete_file";
5156
5157 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5158 if (ext2fs_inode_has_valid_blocks(&inode))
5159 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5160 delete_file_block, &pb);
5161 if (pctx.errcode)
5162 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5163 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
5164 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
5165 if (ctx->inode_bad_map)
5166 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
5167 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
5168
5169 /* Inode may have changed by block_iterate, so reread it */
5170 e2fsck_read_inode(ctx, ino, &inode, "delete_file");
5171 inode.i_links_count = 0;
5172 inode.i_dtime = time(0);
5173 if (inode.i_file_acl &&
5174 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
5175 count = 1;
5176 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
5177 block_buf, -1, &count);
5178 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
5179 pctx.errcode = 0;
5180 count = 1;
5181 }
5182 if (pctx.errcode) {
5183 pctx.blk = inode.i_file_acl;
5184 fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
5185 }
5186 /*
5187 * If the count is zero, then arrange to have the
5188 * block deleted. If the block is in the block_dup_map,
5189 * also call delete_file_block since it will take care
5190 * of keeping the accounting straight.
5191 */
5192 if ((count == 0) ||
5193 ext2fs_test_block_bitmap(ctx->block_dup_map,
5194 inode.i_file_acl))
5195 delete_file_block(fs, &inode.i_file_acl,
5196 BLOCK_COUNT_EXTATTR, 0, 0, &pb);
5197 }
5198 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
5199}
5200
5201struct clone_struct {
5202 errcode_t errcode;
5203 ext2_ino_t dir;
5204 char *buf;
5205 e2fsck_t ctx;
5206};
5207
5208static int clone_file_block(ext2_filsys fs,
5209 blk_t *block_nr,
5210 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00005211 blk_t ref_block FSCK_ATTR((unused)),
5212 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005213 void *priv_data)
5214{
5215 struct dup_block *p;
5216 blk_t new_block;
5217 errcode_t retval;
5218 struct clone_struct *cs = (struct clone_struct *) priv_data;
5219 dnode_t *n;
5220 e2fsck_t ctx;
5221
5222 ctx = cs->ctx;
5223
5224 if (HOLE_BLKADDR(*block_nr))
5225 return 0;
5226
5227 if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
5228 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
5229 if (n) {
5230 p = (struct dup_block *) dnode_get(n);
5231 retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
5232 &new_block);
5233 if (retval) {
5234 cs->errcode = retval;
5235 return BLOCK_ABORT;
5236 }
5237 if (cs->dir && (blockcnt >= 0)) {
5238 retval = ext2fs_set_dir_block(fs->dblist,
5239 cs->dir, new_block, blockcnt);
5240 if (retval) {
5241 cs->errcode = retval;
5242 return BLOCK_ABORT;
5243 }
5244 }
Rob Landley3e72c592006-04-06 22:49:04 +00005245
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005246 retval = io_channel_read_blk(fs->io, *block_nr, 1,
5247 cs->buf);
5248 if (retval) {
5249 cs->errcode = retval;
5250 return BLOCK_ABORT;
5251 }
5252 retval = io_channel_write_blk(fs->io, new_block, 1,
5253 cs->buf);
5254 if (retval) {
5255 cs->errcode = retval;
5256 return BLOCK_ABORT;
5257 }
5258 decrement_badcount(ctx, *block_nr, p);
5259 *block_nr = new_block;
5260 ext2fs_mark_block_bitmap(ctx->block_found_map,
5261 new_block);
5262 ext2fs_mark_block_bitmap(fs->block_map, new_block);
5263 return BLOCK_CHANGED;
5264 } else
Rob Landley7c94bed2006-05-03 21:58:45 +00005265 bb_error_msg(_("internal error; can't find dup_blk for %d\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005266 *block_nr);
5267 }
5268 return 0;
5269}
5270
5271static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
5272 struct dup_inode *dp, char* block_buf)
5273{
5274 ext2_filsys fs = ctx->fs;
5275 errcode_t retval;
5276 struct clone_struct cs;
5277 struct problem_context pctx;
5278 blk_t blk;
5279 dnode_t *n;
5280 struct inode_el *ino_el;
5281 struct dup_block *db;
5282 struct dup_inode *di;
5283
5284 clear_problem_context(&pctx);
5285 cs.errcode = 0;
5286 cs.dir = 0;
5287 cs.ctx = ctx;
5288 retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
5289 if (retval)
5290 return retval;
5291
5292 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
5293 cs.dir = ino;
5294
5295 pctx.ino = ino;
5296 pctx.str = "clone_file";
5297 if (ext2fs_inode_has_valid_blocks(&dp->inode))
5298 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
5299 clone_file_block, &cs);
5300 ext2fs_mark_bb_dirty(fs);
5301 if (pctx.errcode) {
5302 fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
5303 retval = pctx.errcode;
5304 goto errout;
5305 }
5306 if (cs.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +00005307 bb_error_msg(_("returned from clone_file_block"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005308 retval = cs.errcode;
5309 goto errout;
5310 }
5311 /* The inode may have changed on disk, so we have to re-read it */
5312 e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
5313 blk = dp->inode.i_file_acl;
5314 if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
5315 BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
5316 BLOCK_CHANGED)) {
5317 e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
5318 /*
5319 * If we cloned the EA block, find all other inodes
5320 * which refered to that EA block, and modify
5321 * them to point to the new EA block.
5322 */
5323 n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
5324 db = (struct dup_block *) dnode_get(n);
5325 for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
5326 if (ino_el->inode == ino)
5327 continue;
5328 n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
5329 di = (struct dup_inode *) dnode_get(n);
5330 if (di->inode.i_file_acl == blk) {
5331 di->inode.i_file_acl = dp->inode.i_file_acl;
5332 e2fsck_write_inode(ctx, ino_el->inode,
5333 &di->inode, "clone file EA");
5334 decrement_badcount(ctx, blk, db);
5335 }
5336 }
5337 }
5338 retval = 0;
5339errout:
5340 ext2fs_free_mem(&cs.buf);
5341 return retval;
5342}
5343
5344/*
5345 * This routine returns 1 if a block overlaps with one of the superblocks,
5346 * group descriptors, inode bitmaps, or block bitmaps.
5347 */
5348static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
5349{
5350 ext2_filsys fs = ctx->fs;
5351 blk_t block;
5352 dgrp_t i;
5353
5354 block = fs->super->s_first_data_block;
5355 for (i = 0; i < fs->group_desc_count; i++) {
5356
5357 /* Check superblocks/block group descriptros */
5358 if (ext2fs_bg_has_super(fs, i)) {
5359 if (test_block >= block &&
5360 (test_block <= block + fs->desc_blocks))
5361 return 1;
5362 }
5363
5364 /* Check the inode table */
5365 if ((fs->group_desc[i].bg_inode_table) &&
5366 (test_block >= fs->group_desc[i].bg_inode_table) &&
5367 (test_block < (fs->group_desc[i].bg_inode_table +
5368 fs->inode_blocks_per_group)))
5369 return 1;
5370
5371 /* Check the bitmap blocks */
5372 if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
5373 (test_block == fs->group_desc[i].bg_inode_bitmap))
5374 return 1;
5375
5376 block += fs->super->s_blocks_per_group;
5377 }
5378 return 0;
5379}
5380/*
5381 * pass2.c --- check directory structure
5382 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005383 * Pass 2 of e2fsck iterates through all active directory inodes, and
5384 * applies to following tests to each directory entry in the directory
5385 * blocks in the inodes:
5386 *
5387 * - The length of the directory entry (rec_len) should be at
5388 * least 8 bytes, and no more than the remaining space
5389 * left in the directory block.
5390 * - The length of the name in the directory entry (name_len)
5391 * should be less than (rec_len - 8).
5392 * - The inode number in the directory entry should be within
5393 * legal bounds.
5394 * - The inode number should refer to a in-use inode.
5395 * - The first entry should be '.', and its inode should be
5396 * the inode of the directory.
5397 * - The second entry should be '..'.
5398 *
5399 * To minimize disk seek time, the directory blocks are processed in
5400 * sorted order of block numbers.
5401 *
5402 * Pass 2 also collects the following information:
5403 * - The inode numbers of the subdirectories for each directory.
5404 *
5405 * Pass 2 relies on the following information from previous passes:
5406 * - The directory information collected in pass 1.
5407 * - The inode_used_map bitmap
5408 * - The inode_bad_map bitmap
5409 * - The inode_dir_map bitmap
5410 *
5411 * Pass 2 frees the following data structures
5412 * - The inode_bad_map bitmap
5413 * - The inode_reg_map bitmap
5414 */
5415
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005416/*
5417 * Keeps track of how many times an inode is referenced.
5418 */
5419static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
5420static int check_dir_block(ext2_filsys fs,
5421 struct ext2_db_entry *dir_blocks_info,
5422 void *priv_data);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005423static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
5424 struct problem_context *pctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005425static int update_dir_block(ext2_filsys fs,
5426 blk_t *block_nr,
5427 e2_blkcnt_t blockcnt,
5428 blk_t ref_block,
5429 int ref_offset,
5430 void *priv_data);
5431static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
5432static int htree_depth(struct dx_dir_info *dx_dir,
5433 struct dx_dirblock_info *dx_db);
Rob Landley7c94bed2006-05-03 21:58:45 +00005434static int special_dir_block_cmp(const void *a, const void *b);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005435
5436struct check_dir_struct {
5437 char *buf;
5438 struct problem_context pctx;
5439 int count, max;
5440 e2fsck_t ctx;
5441};
5442
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005443static void e2fsck_pass2(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005444{
5445 struct ext2_super_block *sb = ctx->fs->super;
5446 struct problem_context pctx;
5447 ext2_filsys fs = ctx->fs;
5448 char *buf;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005449 struct dir_info *dir;
5450 struct check_dir_struct cd;
5451 struct dx_dir_info *dx_dir;
5452 struct dx_dirblock_info *dx_db, *dx_parent;
5453 int b;
5454 int i, depth;
5455 problem_t code;
5456 int bad_dir;
5457
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005458 clear_problem_context(&cd.pctx);
5459
Rob Landley3e72c592006-04-06 22:49:04 +00005460 /* Pass 2 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005461
5462 if (!(ctx->options & E2F_OPT_PREEN))
5463 fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
5464
5465 cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
5466 0, ctx->inode_link_info,
5467 &ctx->inode_count);
5468 if (cd.pctx.errcode) {
5469 fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
5470 ctx->flags |= E2F_FLAG_ABORT;
5471 return;
5472 }
5473 buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
5474 "directory scan buffer");
5475
5476 /*
5477 * Set up the parent pointer for the root directory, if
5478 * present. (If the root directory is not present, we will
5479 * create it in pass 3.)
5480 */
5481 dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
5482 if (dir)
5483 dir->parent = EXT2_ROOT_INO;
5484
5485 cd.buf = buf;
5486 cd.ctx = ctx;
5487 cd.count = 1;
5488 cd.max = ext2fs_dblist_count(fs->dblist);
5489
5490 if (ctx->progress)
5491 (void) (ctx->progress)(ctx, 2, 0, cd.max);
5492
5493 if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
5494 ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
5495
5496 cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
5497 &cd);
5498 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5499 return;
5500 if (cd.pctx.errcode) {
5501 fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
5502 ctx->flags |= E2F_FLAG_ABORT;
5503 return;
5504 }
5505
5506#ifdef ENABLE_HTREE
5507 for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
5508 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
5509 return;
5510 if (dx_dir->numblocks == 0)
5511 continue;
5512 clear_problem_context(&pctx);
5513 bad_dir = 0;
5514 pctx.dir = dx_dir->ino;
5515 dx_db = dx_dir->dx_block;
5516 if (dx_db->flags & DX_FLAG_REFERENCED)
5517 dx_db->flags |= DX_FLAG_DUP_REF;
5518 else
5519 dx_db->flags |= DX_FLAG_REFERENCED;
5520 /*
5521 * Find all of the first and last leaf blocks, and
5522 * update their parent's min and max hash values
5523 */
5524 for (b=0, dx_db = dx_dir->dx_block;
5525 b < dx_dir->numblocks;
5526 b++, dx_db++) {
5527 if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
5528 !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
5529 continue;
5530 dx_parent = &dx_dir->dx_block[dx_db->parent];
5531 /*
5532 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
5533 */
5534 if (dx_db->flags & DX_FLAG_FIRST)
5535 dx_parent->min_hash = dx_db->min_hash;
5536 /*
5537 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
5538 */
5539 if (dx_db->flags & DX_FLAG_LAST)
5540 dx_parent->max_hash = dx_db->max_hash;
5541 }
5542
5543 for (b=0, dx_db = dx_dir->dx_block;
5544 b < dx_dir->numblocks;
5545 b++, dx_db++) {
5546 pctx.blkcount = b;
5547 pctx.group = dx_db->parent;
5548 code = 0;
5549 if (!(dx_db->flags & DX_FLAG_FIRST) &&
5550 (dx_db->min_hash < dx_db->node_min_hash)) {
5551 pctx.blk = dx_db->min_hash;
5552 pctx.blk2 = dx_db->node_min_hash;
5553 code = PR_2_HTREE_MIN_HASH;
5554 fix_problem(ctx, code, &pctx);
5555 bad_dir++;
5556 }
5557 if (dx_db->type == DX_DIRBLOCK_LEAF) {
5558 depth = htree_depth(dx_dir, dx_db);
5559 if (depth != dx_dir->depth) {
5560 code = PR_2_HTREE_BAD_DEPTH;
5561 fix_problem(ctx, code, &pctx);
5562 bad_dir++;
5563 }
5564 }
5565 /*
5566 * This test doesn't apply for the root block
5567 * at block #0
5568 */
5569 if (b &&
5570 (dx_db->max_hash > dx_db->node_max_hash)) {
5571 pctx.blk = dx_db->max_hash;
5572 pctx.blk2 = dx_db->node_max_hash;
5573 code = PR_2_HTREE_MAX_HASH;
5574 fix_problem(ctx, code, &pctx);
5575 bad_dir++;
5576 }
5577 if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
5578 code = PR_2_HTREE_NOTREF;
5579 fix_problem(ctx, code, &pctx);
5580 bad_dir++;
5581 } else if (dx_db->flags & DX_FLAG_DUP_REF) {
5582 code = PR_2_HTREE_DUPREF;
5583 fix_problem(ctx, code, &pctx);
5584 bad_dir++;
5585 }
5586 if (code == 0)
5587 continue;
5588 }
5589 if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
5590 clear_htree(ctx, dx_dir->ino);
5591 dx_dir->numblocks = 0;
5592 }
5593 }
5594#endif
5595 ext2fs_free_mem(&buf);
5596 ext2fs_free_dblist(fs->dblist);
5597
Rob Landleye7c43b62006-03-01 16:39:45 +00005598 ext2fs_free_inode_bitmap(ctx->inode_bad_map);
5599 ctx->inode_bad_map = 0;
5600 ext2fs_free_inode_bitmap(ctx->inode_reg_map);
5601 ctx->inode_reg_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005602
5603 clear_problem_context(&pctx);
5604 if (ctx->large_files) {
5605 if (!(sb->s_feature_ro_compat &
5606 EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
5607 fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
5608 sb->s_feature_ro_compat |=
5609 EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5610 ext2fs_mark_super_dirty(fs);
5611 }
5612 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
5613 fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
5614 ext2fs_update_dynamic_rev(fs);
5615 ext2fs_mark_super_dirty(fs);
5616 }
5617 } else if (!ctx->large_files &&
5618 (sb->s_feature_ro_compat &
5619 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
5620 if (fs->flags & EXT2_FLAG_RW) {
5621 sb->s_feature_ro_compat &=
5622 ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
5623 ext2fs_mark_super_dirty(fs);
5624 }
5625 }
5626
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005627}
5628
5629#define MAX_DEPTH 32000
5630static int htree_depth(struct dx_dir_info *dx_dir,
5631 struct dx_dirblock_info *dx_db)
5632{
5633 int depth = 0;
5634
5635 while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
5636 dx_db = &dx_dir->dx_block[dx_db->parent];
5637 depth++;
5638 }
5639 return depth;
5640}
5641
5642static int dict_de_cmp(const void *a, const void *b)
5643{
5644 const struct ext2_dir_entry *de_a, *de_b;
5645 int a_len, b_len;
5646
5647 de_a = (const struct ext2_dir_entry *) a;
5648 a_len = de_a->name_len & 0xFF;
5649 de_b = (const struct ext2_dir_entry *) b;
5650 b_len = de_b->name_len & 0xFF;
5651
5652 if (a_len != b_len)
5653 return (a_len - b_len);
5654
5655 return strncmp(de_a->name, de_b->name, a_len);
5656}
5657
5658/*
5659 * This is special sort function that makes sure that directory blocks
5660 * with a dirblock of zero are sorted to the beginning of the list.
5661 * This guarantees that the root node of the htree directories are
5662 * processed first, so we know what hash version to use.
5663 */
Rob Landley7c94bed2006-05-03 21:58:45 +00005664static int special_dir_block_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005665{
5666 const struct ext2_db_entry *db_a =
5667 (const struct ext2_db_entry *) a;
5668 const struct ext2_db_entry *db_b =
5669 (const struct ext2_db_entry *) b;
5670
5671 if (db_a->blockcnt && !db_b->blockcnt)
5672 return 1;
5673
5674 if (!db_a->blockcnt && db_b->blockcnt)
5675 return -1;
5676
5677 if (db_a->blk != db_b->blk)
5678 return (int) (db_a->blk - db_b->blk);
5679
5680 if (db_a->ino != db_b->ino)
5681 return (int) (db_a->ino - db_b->ino);
5682
5683 return (int) (db_a->blockcnt - db_b->blockcnt);
5684}
5685
5686
5687/*
5688 * Make sure the first entry in the directory is '.', and that the
5689 * directory entry is sane.
5690 */
5691static int check_dot(e2fsck_t ctx,
5692 struct ext2_dir_entry *dirent,
5693 ext2_ino_t ino, struct problem_context *pctx)
5694{
5695 struct ext2_dir_entry *nextdir;
5696 int status = 0;
5697 int created = 0;
5698 int new_len;
5699 int problem = 0;
5700
5701 if (!dirent->inode)
5702 problem = PR_2_MISSING_DOT;
5703 else if (((dirent->name_len & 0xFF) != 1) ||
5704 (dirent->name[0] != '.'))
5705 problem = PR_2_1ST_NOT_DOT;
5706 else if (dirent->name[1] != '\0')
5707 problem = PR_2_DOT_NULL_TERM;
5708
5709 if (problem) {
5710 if (fix_problem(ctx, problem, pctx)) {
5711 if (dirent->rec_len < 12)
5712 dirent->rec_len = 12;
5713 dirent->inode = ino;
5714 dirent->name_len = 1;
5715 dirent->name[0] = '.';
5716 dirent->name[1] = '\0';
5717 status = 1;
5718 created = 1;
5719 }
5720 }
5721 if (dirent->inode != ino) {
5722 if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
5723 dirent->inode = ino;
5724 status = 1;
5725 }
5726 }
5727 if (dirent->rec_len > 12) {
5728 new_len = dirent->rec_len - 12;
5729 if (new_len > 12) {
5730 if (created ||
5731 fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
5732 nextdir = (struct ext2_dir_entry *)
5733 ((char *) dirent + 12);
5734 dirent->rec_len = 12;
5735 nextdir->rec_len = new_len;
5736 nextdir->inode = 0;
5737 nextdir->name_len = 0;
5738 status = 1;
5739 }
5740 }
5741 }
5742 return status;
5743}
5744
5745/*
5746 * Make sure the second entry in the directory is '..', and that the
5747 * directory entry is sane. We do not check the inode number of '..'
5748 * here; this gets done in pass 3.
5749 */
5750static int check_dotdot(e2fsck_t ctx,
5751 struct ext2_dir_entry *dirent,
5752 struct dir_info *dir, struct problem_context *pctx)
5753{
5754 int problem = 0;
5755
5756 if (!dirent->inode)
5757 problem = PR_2_MISSING_DOT_DOT;
5758 else if (((dirent->name_len & 0xFF) != 2) ||
5759 (dirent->name[0] != '.') ||
5760 (dirent->name[1] != '.'))
5761 problem = PR_2_2ND_NOT_DOT_DOT;
5762 else if (dirent->name[2] != '\0')
5763 problem = PR_2_DOT_DOT_NULL_TERM;
5764
5765 if (problem) {
5766 if (fix_problem(ctx, problem, pctx)) {
5767 if (dirent->rec_len < 12)
5768 dirent->rec_len = 12;
5769 /*
5770 * Note: we don't have the parent inode just
5771 * yet, so we will fill it in with the root
5772 * inode. This will get fixed in pass 3.
5773 */
5774 dirent->inode = EXT2_ROOT_INO;
5775 dirent->name_len = 2;
5776 dirent->name[0] = '.';
5777 dirent->name[1] = '.';
5778 dirent->name[2] = '\0';
5779 return 1;
5780 }
5781 return 0;
5782 }
5783 dir->dotdot = dirent->inode;
5784 return 0;
5785}
5786
5787/*
5788 * Check to make sure a directory entry doesn't contain any illegal
5789 * characters.
5790 */
5791static int check_name(e2fsck_t ctx,
5792 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005793 struct problem_context *pctx)
5794{
5795 int i;
5796 int fixup = -1;
5797 int ret = 0;
5798
5799 for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
5800 if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
5801 if (fixup < 0) {
5802 fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
5803 }
5804 if (fixup) {
5805 dirent->name[i] = '.';
5806 ret = 1;
5807 }
5808 }
5809 }
5810 return ret;
5811}
5812
5813/*
5814 * Check the directory filetype (if present)
5815 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00005816
5817/*
5818 * Given a mode, return the ext2 file type
5819 */
5820static int ext2_file_type(unsigned int mode)
5821{
5822 if (LINUX_S_ISREG(mode))
5823 return EXT2_FT_REG_FILE;
5824
5825 if (LINUX_S_ISDIR(mode))
5826 return EXT2_FT_DIR;
5827
5828 if (LINUX_S_ISCHR(mode))
5829 return EXT2_FT_CHRDEV;
5830
5831 if (LINUX_S_ISBLK(mode))
5832 return EXT2_FT_BLKDEV;
5833
5834 if (LINUX_S_ISLNK(mode))
5835 return EXT2_FT_SYMLINK;
5836
5837 if (LINUX_S_ISFIFO(mode))
5838 return EXT2_FT_FIFO;
5839
5840 if (LINUX_S_ISSOCK(mode))
5841 return EXT2_FT_SOCK;
5842
5843 return 0;
5844}
5845
Rob Landley7c94bed2006-05-03 21:58:45 +00005846static int check_filetype(e2fsck_t ctx,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005847 struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005848 struct problem_context *pctx)
5849{
5850 int filetype = dirent->name_len >> 8;
5851 int should_be = EXT2_FT_UNKNOWN;
5852 struct ext2_inode inode;
5853
5854 if (!(ctx->fs->super->s_feature_incompat &
5855 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
5856 if (filetype == 0 ||
5857 !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
5858 return 0;
5859 dirent->name_len = dirent->name_len & 0xFF;
5860 return 1;
5861 }
5862
5863 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
5864 should_be = EXT2_FT_DIR;
5865 } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
5866 dirent->inode)) {
5867 should_be = EXT2_FT_REG_FILE;
5868 } else if (ctx->inode_bad_map &&
5869 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
5870 dirent->inode))
5871 should_be = 0;
5872 else {
5873 e2fsck_read_inode(ctx, dirent->inode, &inode,
5874 "check_filetype");
5875 should_be = ext2_file_type(inode.i_mode);
5876 }
5877 if (filetype == should_be)
5878 return 0;
5879 pctx->num = should_be;
5880
5881 if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
5882 pctx) == 0)
5883 return 0;
5884
5885 dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
5886 return 1;
5887}
5888
5889#ifdef ENABLE_HTREE
5890static void parse_int_node(ext2_filsys fs,
5891 struct ext2_db_entry *db,
5892 struct check_dir_struct *cd,
5893 struct dx_dir_info *dx_dir,
5894 char *block_buf)
5895{
5896 struct ext2_dx_root_info *root;
5897 struct ext2_dx_entry *ent;
5898 struct ext2_dx_countlimit *limit;
5899 struct dx_dirblock_info *dx_db;
5900 int i, expect_limit, count;
5901 blk_t blk;
5902 ext2_dirhash_t min_hash = 0xffffffff;
5903 ext2_dirhash_t max_hash = 0;
5904 ext2_dirhash_t hash = 0, prev_hash;
5905
5906 if (db->blockcnt == 0) {
5907 root = (struct ext2_dx_root_info *) (block_buf + 24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005908 ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
5909 } else {
5910 ent = (struct ext2_dx_entry *) (block_buf+8);
5911 }
5912 limit = (struct ext2_dx_countlimit *) ent;
5913
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005914 count = ext2fs_le16_to_cpu(limit->count);
5915 expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
5916 sizeof(struct ext2_dx_entry);
5917 if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
5918 cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
5919 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
5920 goto clear_and_exit;
5921 }
5922 if (count > expect_limit) {
5923 cd->pctx.num = count;
5924 if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
5925 goto clear_and_exit;
5926 count = expect_limit;
5927 }
5928
5929 for (i=0; i < count; i++) {
5930 prev_hash = hash;
5931 hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005932 blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
5933 /* Check to make sure the block is valid */
5934 if (blk > (blk_t) dx_dir->numblocks) {
5935 cd->pctx.blk = blk;
5936 if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
5937 &cd->pctx))
5938 goto clear_and_exit;
5939 }
5940 if (hash < prev_hash &&
5941 fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
5942 goto clear_and_exit;
5943 dx_db = &dx_dir->dx_block[blk];
5944 if (dx_db->flags & DX_FLAG_REFERENCED) {
5945 dx_db->flags |= DX_FLAG_DUP_REF;
5946 } else {
5947 dx_db->flags |= DX_FLAG_REFERENCED;
5948 dx_db->parent = db->blockcnt;
5949 }
5950 if (hash < min_hash)
5951 min_hash = hash;
5952 if (hash > max_hash)
5953 max_hash = hash;
5954 dx_db->node_min_hash = hash;
5955 if ((i+1) < count)
5956 dx_db->node_max_hash =
5957 ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
5958 else {
5959 dx_db->node_max_hash = 0xfffffffe;
5960 dx_db->flags |= DX_FLAG_LAST;
5961 }
5962 if (i == 0)
5963 dx_db->flags |= DX_FLAG_FIRST;
5964 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00005965 dx_db = &dx_dir->dx_block[db->blockcnt];
5966 dx_db->min_hash = min_hash;
5967 dx_db->max_hash = max_hash;
5968 return;
5969
5970clear_and_exit:
5971 clear_htree(cd->ctx, cd->pctx.ino);
5972 dx_dir->numblocks = 0;
5973}
5974#endif /* ENABLE_HTREE */
5975
5976/*
5977 * Given a busted directory, try to salvage it somehow.
5978 *
5979 */
5980static void salvage_directory(ext2_filsys fs,
5981 struct ext2_dir_entry *dirent,
5982 struct ext2_dir_entry *prev,
5983 unsigned int *offset)
5984{
5985 char *cp = (char *) dirent;
5986 int left = fs->blocksize - *offset - dirent->rec_len;
5987 int name_len = dirent->name_len & 0xFF;
5988
5989 /*
5990 * Special case of directory entry of size 8: copy what's left
5991 * of the directory block up to cover up the invalid hole.
5992 */
5993 if ((left >= 12) && (dirent->rec_len == 8)) {
5994 memmove(cp, cp+8, left);
5995 memset(cp + left, 0, 8);
5996 return;
5997 }
5998 /*
5999 * If the directory entry overruns the end of the directory
6000 * block, and the name is small enough to fit, then adjust the
6001 * record length.
6002 */
6003 if ((left < 0) &&
6004 (name_len + 8 <= dirent->rec_len + left) &&
6005 dirent->inode <= fs->super->s_inodes_count &&
6006 strnlen(dirent->name, name_len) == name_len) {
6007 dirent->rec_len += left;
6008 return;
6009 }
6010 /*
6011 * If the directory entry is a multiple of four, so it is
6012 * valid, let the previous directory entry absorb the invalid
6013 * one.
6014 */
6015 if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
6016 prev->rec_len += dirent->rec_len;
6017 *offset += dirent->rec_len;
6018 return;
6019 }
6020 /*
6021 * Default salvage method --- kill all of the directory
6022 * entries for the rest of the block. We will either try to
6023 * absorb it into the previous directory entry, or create a
6024 * new empty directory entry the rest of the directory block.
6025 */
6026 if (prev) {
6027 prev->rec_len += fs->blocksize - *offset;
6028 *offset = fs->blocksize;
6029 } else {
6030 dirent->rec_len = fs->blocksize - *offset;
6031 dirent->name_len = 0;
6032 dirent->inode = 0;
6033 }
6034}
6035
6036static int check_dir_block(ext2_filsys fs,
6037 struct ext2_db_entry *db,
6038 void *priv_data)
6039{
6040 struct dir_info *subdir, *dir;
6041 struct dx_dir_info *dx_dir;
6042#ifdef ENABLE_HTREE
6043 struct dx_dirblock_info *dx_db = 0;
6044#endif /* ENABLE_HTREE */
6045 struct ext2_dir_entry *dirent, *prev;
6046 ext2_dirhash_t hash;
6047 unsigned int offset = 0;
6048 int dir_modified = 0;
6049 int dot_state;
6050 blk_t block_nr = db->blk;
6051 ext2_ino_t ino = db->ino;
6052 __u16 links;
6053 struct check_dir_struct *cd;
6054 char *buf;
6055 e2fsck_t ctx;
6056 int problem;
6057 struct ext2_dx_root_info *root;
6058 struct ext2_dx_countlimit *limit;
6059 static dict_t de_dict;
6060 struct problem_context pctx;
6061 int dups_found = 0;
6062
6063 cd = (struct check_dir_struct *) priv_data;
6064 buf = cd->buf;
6065 ctx = cd->ctx;
6066
6067 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6068 return DIRENT_ABORT;
6069
6070 if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
6071 return DIRENT_ABORT;
6072
6073 /*
6074 * Make sure the inode is still in use (could have been
6075 * deleted in the duplicate/bad blocks pass.
6076 */
6077 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
6078 return 0;
6079
6080 cd->pctx.ino = ino;
6081 cd->pctx.blk = block_nr;
6082 cd->pctx.blkcount = db->blockcnt;
6083 cd->pctx.ino2 = 0;
6084 cd->pctx.dirent = 0;
6085 cd->pctx.num = 0;
6086
6087 if (db->blk == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006088 if (allocate_dir_block(ctx, db, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006089 return 0;
6090 block_nr = db->blk;
6091 }
6092
6093 if (db->blockcnt)
6094 dot_state = 2;
6095 else
6096 dot_state = 0;
6097
6098 if (ctx->dirs_to_hash &&
6099 ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
6100 dups_found++;
6101
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006102 cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
6103 if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
6104 cd->pctx.errcode = 0; /* We'll handle this ourselves */
6105 if (cd->pctx.errcode) {
6106 if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
6107 ctx->flags |= E2F_FLAG_ABORT;
6108 return DIRENT_ABORT;
6109 }
6110 memset(buf, 0, fs->blocksize);
6111 }
6112#ifdef ENABLE_HTREE
6113 dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
6114 if (dx_dir && dx_dir->numblocks) {
6115 if (db->blockcnt >= dx_dir->numblocks) {
6116 printf("XXX should never happen!!!\n");
6117 abort();
6118 }
6119 dx_db = &dx_dir->dx_block[db->blockcnt];
6120 dx_db->type = DX_DIRBLOCK_LEAF;
6121 dx_db->phys = block_nr;
6122 dx_db->min_hash = ~0;
6123 dx_db->max_hash = 0;
6124
6125 dirent = (struct ext2_dir_entry *) buf;
6126 limit = (struct ext2_dx_countlimit *) (buf+8);
6127 if (db->blockcnt == 0) {
6128 root = (struct ext2_dx_root_info *) (buf + 24);
6129 dx_db->type = DX_DIRBLOCK_ROOT;
6130 dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
6131 if ((root->reserved_zero ||
6132 root->info_length < 8 ||
6133 root->indirect_levels > 1) &&
6134 fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
6135 clear_htree(ctx, ino);
6136 dx_dir->numblocks = 0;
6137 dx_db = 0;
6138 }
6139 dx_dir->hashversion = root->hash_version;
6140 dx_dir->depth = root->indirect_levels + 1;
6141 } else if ((dirent->inode == 0) &&
6142 (dirent->rec_len == fs->blocksize) &&
6143 (dirent->name_len == 0) &&
6144 (ext2fs_le16_to_cpu(limit->limit) ==
6145 ((fs->blocksize-8) /
6146 sizeof(struct ext2_dx_entry))))
6147 dx_db->type = DX_DIRBLOCK_NODE;
6148 }
6149#endif /* ENABLE_HTREE */
6150
6151 dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
6152 prev = 0;
6153 do {
6154 problem = 0;
6155 dirent = (struct ext2_dir_entry *) (buf + offset);
6156 cd->pctx.dirent = dirent;
6157 cd->pctx.num = offset;
6158 if (((offset + dirent->rec_len) > fs->blocksize) ||
6159 (dirent->rec_len < 12) ||
6160 ((dirent->rec_len % 4) != 0) ||
6161 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
6162 if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
6163 salvage_directory(fs, dirent, prev, &offset);
6164 dir_modified++;
6165 continue;
6166 } else
6167 goto abort_free_dict;
6168 }
6169 if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
6170 if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
6171 dirent->name_len = EXT2_NAME_LEN;
6172 dir_modified++;
6173 }
6174 }
6175
6176 if (dot_state == 0) {
6177 if (check_dot(ctx, dirent, ino, &cd->pctx))
6178 dir_modified++;
6179 } else if (dot_state == 1) {
6180 dir = e2fsck_get_dir_info(ctx, ino);
6181 if (!dir) {
6182 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6183 goto abort_free_dict;
6184 }
6185 if (check_dotdot(ctx, dirent, dir, &cd->pctx))
6186 dir_modified++;
6187 } else if (dirent->inode == ino) {
6188 problem = PR_2_LINK_DOT;
6189 if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
6190 dirent->inode = 0;
6191 dir_modified++;
6192 goto next;
6193 }
6194 }
6195 if (!dirent->inode)
6196 goto next;
6197
6198 /*
6199 * Make sure the inode listed is a legal one.
6200 */
6201 if (((dirent->inode != EXT2_ROOT_INO) &&
6202 (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
6203 (dirent->inode > fs->super->s_inodes_count)) {
6204 problem = PR_2_BAD_INO;
6205 } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
6206 dirent->inode))) {
6207 /*
6208 * If the inode is unused, offer to clear it.
6209 */
6210 problem = PR_2_UNUSED_INODE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006211 } else if ((dot_state > 1) &&
6212 ((dirent->name_len & 0xFF) == 1) &&
6213 (dirent->name[0] == '.')) {
6214 /*
6215 * If there's a '.' entry in anything other
6216 * than the first directory entry, it's a
6217 * duplicate entry that should be removed.
6218 */
6219 problem = PR_2_DUP_DOT;
6220 } else if ((dot_state > 1) &&
6221 ((dirent->name_len & 0xFF) == 2) &&
6222 (dirent->name[0] == '.') &&
6223 (dirent->name[1] == '.')) {
6224 /*
6225 * If there's a '..' entry in anything other
6226 * than the second directory entry, it's a
6227 * duplicate entry that should be removed.
6228 */
6229 problem = PR_2_DUP_DOT_DOT;
6230 } else if ((dot_state > 1) &&
6231 (dirent->inode == EXT2_ROOT_INO)) {
6232 /*
6233 * Don't allow links to the root directory.
6234 * We check this specially to make sure we
6235 * catch this error case even if the root
6236 * directory hasn't been created yet.
6237 */
6238 problem = PR_2_LINK_ROOT;
6239 } else if ((dot_state > 1) &&
6240 (dirent->name_len & 0xFF) == 0) {
6241 /*
6242 * Don't allow zero-length directory names.
6243 */
6244 problem = PR_2_NULL_NAME;
6245 }
6246
6247 if (problem) {
6248 if (fix_problem(ctx, problem, &cd->pctx)) {
6249 dirent->inode = 0;
6250 dir_modified++;
6251 goto next;
6252 } else {
6253 ext2fs_unmark_valid(fs);
6254 if (problem == PR_2_BAD_INO)
6255 goto next;
6256 }
6257 }
6258
6259 /*
6260 * If the inode was marked as having bad fields in
6261 * pass1, process it and offer to fix/clear it.
6262 * (We wait until now so that we can display the
6263 * pathname to the user.)
6264 */
6265 if (ctx->inode_bad_map &&
6266 ext2fs_test_inode_bitmap(ctx->inode_bad_map,
6267 dirent->inode)) {
6268 if (e2fsck_process_bad_inode(ctx, ino,
6269 dirent->inode,
6270 buf + fs->blocksize)) {
6271 dirent->inode = 0;
6272 dir_modified++;
6273 goto next;
6274 }
6275 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6276 return DIRENT_ABORT;
6277 }
6278
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006279 if (check_name(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006280 dir_modified++;
6281
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006282 if (check_filetype(ctx, dirent, &cd->pctx))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006283 dir_modified++;
6284
6285#ifdef ENABLE_HTREE
6286 if (dx_db) {
6287 ext2fs_dirhash(dx_dir->hashversion, dirent->name,
6288 (dirent->name_len & 0xFF),
6289 fs->super->s_hash_seed, &hash, 0);
6290 if (hash < dx_db->min_hash)
6291 dx_db->min_hash = hash;
6292 if (hash > dx_db->max_hash)
6293 dx_db->max_hash = hash;
6294 }
6295#endif
6296
6297 /*
6298 * If this is a directory, then mark its parent in its
6299 * dir_info structure. If the parent field is already
6300 * filled in, then this directory has more than one
6301 * hard link. We assume the first link is correct,
6302 * and ask the user if he/she wants to clear this one.
6303 */
6304 if ((dot_state > 1) &&
6305 (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6306 dirent->inode))) {
6307 subdir = e2fsck_get_dir_info(ctx, dirent->inode);
6308 if (!subdir) {
6309 cd->pctx.ino = dirent->inode;
6310 fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
6311 goto abort_free_dict;
6312 }
6313 if (subdir->parent) {
6314 cd->pctx.ino2 = subdir->parent;
6315 if (fix_problem(ctx, PR_2_LINK_DIR,
6316 &cd->pctx)) {
6317 dirent->inode = 0;
6318 dir_modified++;
6319 goto next;
6320 }
6321 cd->pctx.ino2 = 0;
6322 } else
6323 subdir->parent = ino;
6324 }
6325
6326 if (dups_found) {
6327 ;
6328 } else if (dict_lookup(&de_dict, dirent)) {
6329 clear_problem_context(&pctx);
6330 pctx.ino = ino;
6331 pctx.dirent = dirent;
6332 fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
6333 if (!ctx->dirs_to_hash)
6334 ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
6335 if (ctx->dirs_to_hash)
6336 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6337 dups_found++;
6338 } else
6339 dict_alloc_insert(&de_dict, dirent, dirent);
6340
6341 ext2fs_icount_increment(ctx->inode_count, dirent->inode,
6342 &links);
6343 if (links > 1)
6344 ctx->fs_links_count++;
6345 ctx->fs_total_count++;
6346 next:
6347 prev = dirent;
6348 offset += dirent->rec_len;
6349 dot_state++;
6350 } while (offset < fs->blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006351#ifdef ENABLE_HTREE
6352 if (dx_db) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006353 cd->pctx.dir = cd->pctx.ino;
6354 if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
6355 (dx_db->type == DX_DIRBLOCK_NODE))
6356 parse_int_node(fs, db, cd, dx_dir, buf);
6357 }
6358#endif /* ENABLE_HTREE */
6359 if (offset != fs->blocksize) {
6360 cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
6361 if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
6362 dirent->rec_len = cd->pctx.num;
6363 dir_modified++;
6364 }
6365 }
6366 if (dir_modified) {
6367 cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
6368 if (cd->pctx.errcode) {
6369 if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
6370 &cd->pctx))
6371 goto abort_free_dict;
6372 }
6373 ext2fs_mark_changed(fs);
6374 }
6375 dict_free_nodes(&de_dict);
6376 return 0;
6377abort_free_dict:
6378 dict_free_nodes(&de_dict);
6379 ctx->flags |= E2F_FLAG_ABORT;
6380 return DIRENT_ABORT;
6381}
6382
6383/*
6384 * This function is called to deallocate a block, and is an interator
6385 * functioned called by deallocate inode via ext2fs_iterate_block().
6386 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006387static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006388 e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
6389 blk_t ref_block FSCK_ATTR((unused)),
6390 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006391 void *priv_data)
6392{
6393 e2fsck_t ctx = (e2fsck_t) priv_data;
6394
6395 if (HOLE_BLKADDR(*block_nr))
6396 return 0;
6397 if ((*block_nr < fs->super->s_first_data_block) ||
6398 (*block_nr >= fs->super->s_blocks_count))
6399 return 0;
6400 ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
6401 ext2fs_block_alloc_stats(fs, *block_nr, -1);
6402 return 0;
6403}
6404
6405/*
6406 * This fuction deallocates an inode
6407 */
6408static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
6409{
6410 ext2_filsys fs = ctx->fs;
6411 struct ext2_inode inode;
6412 struct problem_context pctx;
6413 __u32 count;
6414
6415 ext2fs_icount_store(ctx->inode_link_info, ino, 0);
6416 e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
6417 inode.i_links_count = 0;
6418 inode.i_dtime = time(0);
6419 e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
6420 clear_problem_context(&pctx);
6421 pctx.ino = ino;
6422
6423 /*
6424 * Fix up the bitmaps...
6425 */
6426 e2fsck_read_bitmaps(ctx);
6427 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
6428 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
6429 if (ctx->inode_bad_map)
6430 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6431 ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
6432
6433 if (inode.i_file_acl &&
6434 (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
6435 pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
6436 block_buf, -1, &count);
6437 if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
6438 pctx.errcode = 0;
6439 count = 1;
6440 }
6441 if (pctx.errcode) {
6442 pctx.blk = inode.i_file_acl;
6443 fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
6444 ctx->flags |= E2F_FLAG_ABORT;
6445 return;
6446 }
6447 if (count == 0) {
6448 ext2fs_unmark_block_bitmap(ctx->block_found_map,
6449 inode.i_file_acl);
6450 ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
6451 }
6452 inode.i_file_acl = 0;
6453 }
6454
6455 if (!ext2fs_inode_has_valid_blocks(&inode))
6456 return;
6457
6458 if (LINUX_S_ISREG(inode.i_mode) &&
6459 (inode.i_size_high || inode.i_size & 0x80000000UL))
6460 ctx->large_files--;
6461
6462 pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
6463 deallocate_inode_block, ctx);
6464 if (pctx.errcode) {
6465 fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
6466 ctx->flags |= E2F_FLAG_ABORT;
6467 return;
6468 }
6469}
6470
6471/*
6472 * This fuction clears the htree flag on an inode
6473 */
6474static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
6475{
6476 struct ext2_inode inode;
6477
6478 e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
6479 inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
6480 e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
6481 if (ctx->dirs_to_hash)
6482 ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
6483}
6484
6485
6486static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
6487 ext2_ino_t ino, char *buf)
6488{
6489 ext2_filsys fs = ctx->fs;
6490 struct ext2_inode inode;
6491 int inode_modified = 0;
6492 int not_fixed = 0;
6493 unsigned char *frag, *fsize;
6494 struct problem_context pctx;
6495 int problem = 0;
6496
6497 e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
6498
6499 clear_problem_context(&pctx);
6500 pctx.ino = ino;
6501 pctx.dir = dir;
6502 pctx.inode = &inode;
6503
6504 if (inode.i_file_acl &&
6505 !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
6506 fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
6507 inode.i_file_acl = 0;
Rob Landley7c94bed2006-05-03 21:58:45 +00006508#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006509 /*
6510 * This is a special kludge to deal with long symlinks
6511 * on big endian systems. i_blocks had already been
6512 * decremented earlier in pass 1, but since i_file_acl
6513 * hadn't yet been cleared, ext2fs_read_inode()
6514 * assumed that the file was short symlink and would
6515 * not have byte swapped i_block[0]. Hence, we have
6516 * to byte-swap it here.
6517 */
6518 if (LINUX_S_ISLNK(inode.i_mode) &&
6519 (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
6520 (inode.i_blocks == fs->blocksize >> 9))
6521 inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
6522#endif
6523 inode_modified++;
6524 } else
6525 not_fixed++;
6526
6527 if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
6528 !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
6529 !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
6530 !(LINUX_S_ISSOCK(inode.i_mode)))
6531 problem = PR_2_BAD_MODE;
6532 else if (LINUX_S_ISCHR(inode.i_mode)
6533 && !e2fsck_pass1_check_device_inode(fs, &inode))
6534 problem = PR_2_BAD_CHAR_DEV;
6535 else if (LINUX_S_ISBLK(inode.i_mode)
6536 && !e2fsck_pass1_check_device_inode(fs, &inode))
6537 problem = PR_2_BAD_BLOCK_DEV;
6538 else if (LINUX_S_ISFIFO(inode.i_mode)
6539 && !e2fsck_pass1_check_device_inode(fs, &inode))
6540 problem = PR_2_BAD_FIFO;
6541 else if (LINUX_S_ISSOCK(inode.i_mode)
6542 && !e2fsck_pass1_check_device_inode(fs, &inode))
6543 problem = PR_2_BAD_SOCKET;
6544 else if (LINUX_S_ISLNK(inode.i_mode)
6545 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
6546 problem = PR_2_INVALID_SYMLINK;
6547 }
6548
6549 if (problem) {
6550 if (fix_problem(ctx, problem, &pctx)) {
6551 deallocate_inode(ctx, ino, 0);
6552 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6553 return 0;
6554 return 1;
6555 } else
6556 not_fixed++;
6557 problem = 0;
6558 }
6559
6560 if (inode.i_faddr) {
6561 if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
6562 inode.i_faddr = 0;
6563 inode_modified++;
6564 } else
6565 not_fixed++;
6566 }
6567
6568 switch (fs->super->s_creator_os) {
6569 case EXT2_OS_LINUX:
6570 frag = &inode.osd2.linux2.l_i_frag;
6571 fsize = &inode.osd2.linux2.l_i_fsize;
6572 break;
6573 case EXT2_OS_HURD:
6574 frag = &inode.osd2.hurd2.h_i_frag;
6575 fsize = &inode.osd2.hurd2.h_i_fsize;
6576 break;
6577 case EXT2_OS_MASIX:
6578 frag = &inode.osd2.masix2.m_i_frag;
6579 fsize = &inode.osd2.masix2.m_i_fsize;
6580 break;
6581 default:
6582 frag = fsize = 0;
6583 }
6584 if (frag && *frag) {
6585 pctx.num = *frag;
6586 if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
6587 *frag = 0;
6588 inode_modified++;
6589 } else
6590 not_fixed++;
6591 pctx.num = 0;
6592 }
6593 if (fsize && *fsize) {
6594 pctx.num = *fsize;
6595 if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
6596 *fsize = 0;
6597 inode_modified++;
6598 } else
6599 not_fixed++;
6600 pctx.num = 0;
6601 }
6602
6603 if (inode.i_file_acl &&
6604 ((inode.i_file_acl < fs->super->s_first_data_block) ||
6605 (inode.i_file_acl >= fs->super->s_blocks_count))) {
6606 if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
6607 inode.i_file_acl = 0;
6608 inode_modified++;
6609 } else
6610 not_fixed++;
6611 }
6612 if (inode.i_dir_acl &&
6613 LINUX_S_ISDIR(inode.i_mode)) {
6614 if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
6615 inode.i_dir_acl = 0;
6616 inode_modified++;
6617 } else
6618 not_fixed++;
6619 }
6620
6621 if (inode_modified)
6622 e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
6623 if (!not_fixed)
6624 ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
6625 return 0;
6626}
6627
6628
6629/*
6630 * allocate_dir_block --- this function allocates a new directory
6631 * block for a particular inode; this is done if a directory has
6632 * a "hole" in it, or if a directory has a illegal block number
6633 * that was zeroed out and now needs to be replaced.
6634 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006635static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006636 struct problem_context *pctx)
6637{
6638 ext2_filsys fs = ctx->fs;
6639 blk_t blk;
6640 char *block;
6641 struct ext2_inode inode;
6642
6643 if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
6644 return 1;
6645
6646 /*
6647 * Read the inode and block bitmaps in; we'll be messing with
6648 * them.
6649 */
6650 e2fsck_read_bitmaps(ctx);
6651
6652 /*
6653 * First, find a free block
6654 */
6655 pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6656 if (pctx->errcode) {
6657 pctx->str = "ext2fs_new_block";
6658 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6659 return 1;
6660 }
6661 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6662 ext2fs_mark_block_bitmap(fs->block_map, blk);
6663 ext2fs_mark_bb_dirty(fs);
6664
6665 /*
6666 * Now let's create the actual data block for the inode
6667 */
6668 if (db->blockcnt)
6669 pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
6670 else
6671 pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
6672 EXT2_ROOT_INO, &block);
6673
6674 if (pctx->errcode) {
6675 pctx->str = "ext2fs_new_dir_block";
6676 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6677 return 1;
6678 }
6679
6680 pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
6681 ext2fs_free_mem(&block);
6682 if (pctx->errcode) {
6683 pctx->str = "ext2fs_write_dir_block";
6684 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6685 return 1;
6686 }
6687
6688 /*
6689 * Update the inode block count
6690 */
6691 e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
6692 inode.i_blocks += fs->blocksize / 512;
6693 if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
6694 inode.i_size = (db->blockcnt+1) * fs->blocksize;
6695 e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
6696
6697 /*
6698 * Finally, update the block pointers for the inode
6699 */
6700 db->blk = blk;
6701 pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
6702 0, update_dir_block, db);
6703 if (pctx->errcode) {
6704 pctx->str = "ext2fs_block_iterate";
6705 fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
6706 return 1;
6707 }
6708
6709 return 0;
6710}
6711
6712/*
6713 * This is a helper function for allocate_dir_block().
6714 */
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006715static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006716 blk_t *block_nr,
6717 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00006718 blk_t ref_block FSCK_ATTR((unused)),
6719 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006720 void *priv_data)
6721{
6722 struct ext2_db_entry *db;
6723
6724 db = (struct ext2_db_entry *) priv_data;
6725 if (db->blockcnt == (int) blockcnt) {
6726 *block_nr = db->blk;
6727 return BLOCK_CHANGED;
6728 }
6729 return 0;
6730}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006731
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006732/*
6733 * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
6734 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006735 * Pass #3 assures that all directories are connected to the
6736 * filesystem tree, using the following algorithm:
6737 *
6738 * First, the root directory is checked to make sure it exists; if
6739 * not, e2fsck will offer to create a new one. It is then marked as
6740 * "done".
6741 *
6742 * Then, pass3 interates over all directory inodes; for each directory
6743 * it attempts to trace up the filesystem tree, using dirinfo.parent
6744 * until it reaches a directory which has been marked "done". If it
6745 * can not do so, then the directory must be disconnected, and e2fsck
6746 * will offer to reconnect it to /lost+found. While it is chasing
6747 * parent pointers up the filesystem tree, if pass3 sees a directory
6748 * twice, then it has detected a filesystem loop, and it will again
6749 * offer to reconnect the directory to /lost+found in to break the
6750 * filesystem loop.
6751 *
6752 * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
6753 * reconnect inodes to /lost+found; this subroutine is also used by
6754 * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
6755 * is responsible for creating /lost+found if it does not exist.
6756 *
6757 * Pass 3 frees the following data structures:
6758 * - The dirinfo directory information cache.
6759 */
6760
6761static void check_root(e2fsck_t ctx);
6762static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6763 struct problem_context *pctx);
6764static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
6765
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006766static ext2fs_inode_bitmap inode_loop_detect;
6767static ext2fs_inode_bitmap inode_done_map;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006768
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00006769static void e2fsck_pass3(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006770{
6771 ext2_filsys fs = ctx->fs;
6772 int i;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006773 struct problem_context pctx;
6774 struct dir_info *dir;
6775 unsigned long maxdirs, count;
6776
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006777 clear_problem_context(&pctx);
6778
Rob Landley3e72c592006-04-06 22:49:04 +00006779 /* Pass 3 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006780
6781 if (!(ctx->options & E2F_OPT_PREEN))
6782 fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
6783
6784 /*
6785 * Allocate some bitmaps to do loop detection.
6786 */
6787 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
6788 &inode_done_map);
6789 if (pctx.errcode) {
6790 pctx.num = 2;
6791 fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
6792 ctx->flags |= E2F_FLAG_ABORT;
6793 goto abort_exit;
6794 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006795 check_root(ctx);
6796 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6797 goto abort_exit;
6798
6799 ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
6800
6801 maxdirs = e2fsck_get_num_dirinfo(ctx);
6802 count = 1;
6803
6804 if (ctx->progress)
6805 if ((ctx->progress)(ctx, 3, 0, maxdirs))
6806 goto abort_exit;
6807
6808 for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
6809 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
6810 goto abort_exit;
6811 if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
6812 goto abort_exit;
6813 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
6814 if (check_directory(ctx, dir, &pctx))
6815 goto abort_exit;
6816 }
6817
6818 /*
6819 * Force the creation of /lost+found if not present
6820 */
6821 if ((ctx->flags & E2F_OPT_READONLY) == 0)
6822 e2fsck_get_lost_and_found(ctx, 1);
6823
6824 /*
6825 * If there are any directories that need to be indexed or
6826 * optimized, do it here.
6827 */
6828 e2fsck_rehash_directories(ctx);
6829
6830abort_exit:
6831 e2fsck_free_dir_info(ctx);
Rob Landleye7c43b62006-03-01 16:39:45 +00006832 ext2fs_free_inode_bitmap(inode_loop_detect);
6833 inode_loop_detect = 0;
6834 ext2fs_free_inode_bitmap(inode_done_map);
6835 inode_done_map = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00006836}
6837
6838/*
6839 * This makes sure the root inode is present; if not, we ask if the
6840 * user wants us to create it. Not creating it is a fatal error.
6841 */
6842static void check_root(e2fsck_t ctx)
6843{
6844 ext2_filsys fs = ctx->fs;
6845 blk_t blk;
6846 struct ext2_inode inode;
6847 char * block;
6848 struct problem_context pctx;
6849
6850 clear_problem_context(&pctx);
6851
6852 if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
6853 /*
6854 * If the root inode is not a directory, die here. The
6855 * user must have answered 'no' in pass1 when we
6856 * offered to clear it.
6857 */
6858 if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
6859 EXT2_ROOT_INO))) {
6860 fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
6861 ctx->flags |= E2F_FLAG_ABORT;
6862 }
6863 return;
6864 }
6865
6866 if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
6867 fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
6868 ctx->flags |= E2F_FLAG_ABORT;
6869 return;
6870 }
6871
6872 e2fsck_read_bitmaps(ctx);
6873
6874 /*
6875 * First, find a free block
6876 */
6877 pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
6878 if (pctx.errcode) {
6879 pctx.str = "ext2fs_new_block";
6880 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6881 ctx->flags |= E2F_FLAG_ABORT;
6882 return;
6883 }
6884 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
6885 ext2fs_mark_block_bitmap(fs->block_map, blk);
6886 ext2fs_mark_bb_dirty(fs);
6887
6888 /*
6889 * Now let's create the actual data block for the inode
6890 */
6891 pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
6892 &block);
6893 if (pctx.errcode) {
6894 pctx.str = "ext2fs_new_dir_block";
6895 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6896 ctx->flags |= E2F_FLAG_ABORT;
6897 return;
6898 }
6899
6900 pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
6901 if (pctx.errcode) {
6902 pctx.str = "ext2fs_write_dir_block";
6903 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6904 ctx->flags |= E2F_FLAG_ABORT;
6905 return;
6906 }
6907 ext2fs_free_mem(&block);
6908
6909 /*
6910 * Set up the inode structure
6911 */
6912 memset(&inode, 0, sizeof(inode));
6913 inode.i_mode = 040755;
6914 inode.i_size = fs->blocksize;
6915 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
6916 inode.i_links_count = 2;
6917 inode.i_blocks = fs->blocksize / 512;
6918 inode.i_block[0] = blk;
6919
6920 /*
6921 * Write out the inode.
6922 */
6923 pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
6924 if (pctx.errcode) {
6925 pctx.str = "ext2fs_write_inode";
6926 fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
6927 ctx->flags |= E2F_FLAG_ABORT;
6928 return;
6929 }
6930
6931 /*
6932 * Miscellaneous bookkeeping...
6933 */
6934 e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
6935 ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
6936 ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
6937
6938 ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
6939 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
6940 ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
6941 ext2fs_mark_ib_dirty(fs);
6942}
6943
6944/*
6945 * This subroutine is responsible for making sure that a particular
6946 * directory is connected to the root; if it isn't we trace it up as
6947 * far as we can go, and then offer to connect the resulting parent to
6948 * the lost+found. We have to do loop detection; if we ever discover
6949 * a loop, we treat that as a disconnected directory and offer to
6950 * reparent it to lost+found.
6951 *
6952 * However, loop detection is expensive, because for very large
6953 * filesystems, the inode_loop_detect bitmap is huge, and clearing it
6954 * is non-trivial. Loops in filesystems are also a rare error case,
6955 * and we shouldn't optimize for error cases. So we try two passes of
6956 * the algorithm. The first time, we ignore loop detection and merely
6957 * increment a counter; if the counter exceeds some extreme threshold,
6958 * then we try again with the loop detection bitmap enabled.
6959 */
6960static int check_directory(e2fsck_t ctx, struct dir_info *dir,
6961 struct problem_context *pctx)
6962{
6963 ext2_filsys fs = ctx->fs;
6964 struct dir_info *p = dir;
6965 int loop_pass = 0, parent_count = 0;
6966
6967 if (!p)
6968 return 0;
6969
6970 while (1) {
6971 /*
6972 * Mark this inode as being "done"; by the time we
6973 * return from this function, the inode we either be
6974 * verified as being connected to the directory tree,
6975 * or we will have offered to reconnect this to
6976 * lost+found.
6977 *
6978 * If it was marked done already, then we've reached a
6979 * parent we've already checked.
6980 */
6981 if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
6982 break;
6983
6984 /*
6985 * If this directory doesn't have a parent, or we've
6986 * seen the parent once already, then offer to
6987 * reparent it to lost+found
6988 */
6989 if (!p->parent ||
6990 (loop_pass &&
6991 (ext2fs_test_inode_bitmap(inode_loop_detect,
6992 p->parent)))) {
6993 pctx->ino = p->ino;
6994 if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
6995 if (e2fsck_reconnect_file(ctx, pctx->ino))
6996 ext2fs_unmark_valid(fs);
6997 else {
6998 p = e2fsck_get_dir_info(ctx, pctx->ino);
6999 p->parent = ctx->lost_and_found;
7000 fix_dotdot(ctx, p, ctx->lost_and_found);
7001 }
7002 }
7003 break;
7004 }
7005 p = e2fsck_get_dir_info(ctx, p->parent);
7006 if (!p) {
7007 fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
7008 return 0;
7009 }
7010 if (loop_pass) {
7011 ext2fs_mark_inode_bitmap(inode_loop_detect,
7012 p->ino);
7013 } else if (parent_count++ > 2048) {
7014 /*
7015 * If we've run into a path depth that's
7016 * greater than 2048, try again with the inode
7017 * loop bitmap turned on and start from the
7018 * top.
7019 */
7020 loop_pass = 1;
7021 if (inode_loop_detect)
7022 ext2fs_clear_inode_bitmap(inode_loop_detect);
7023 else {
7024 pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
7025 if (pctx->errcode) {
7026 pctx->num = 1;
7027 fix_problem(ctx,
7028 PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
7029 ctx->flags |= E2F_FLAG_ABORT;
7030 return -1;
7031 }
7032 }
7033 p = dir;
7034 }
7035 }
7036
7037 /*
7038 * Make sure that .. and the parent directory are the same;
7039 * offer to fix it if not.
7040 */
7041 if (dir->parent != dir->dotdot) {
7042 pctx->ino = dir->ino;
7043 pctx->ino2 = dir->dotdot;
7044 pctx->dir = dir->parent;
7045 if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
7046 fix_dotdot(ctx, dir, dir->parent);
7047 }
7048 return 0;
7049}
7050
7051/*
7052 * This routine gets the lost_and_found inode, making it a directory
7053 * if necessary
7054 */
7055ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
7056{
7057 ext2_filsys fs = ctx->fs;
7058 ext2_ino_t ino;
7059 blk_t blk;
7060 errcode_t retval;
7061 struct ext2_inode inode;
7062 char * block;
7063 static const char name[] = "lost+found";
7064 struct problem_context pctx;
7065 struct dir_info *dirinfo;
7066
7067 if (ctx->lost_and_found)
7068 return ctx->lost_and_found;
7069
7070 clear_problem_context(&pctx);
7071
7072 retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
7073 sizeof(name)-1, 0, &ino);
7074 if (retval && !fix)
7075 return 0;
7076 if (!retval) {
7077 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
7078 ctx->lost_and_found = ino;
7079 return ino;
7080 }
7081
7082 /* Lost+found isn't a directory! */
7083 if (!fix)
7084 return 0;
7085 pctx.ino = ino;
7086 if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
7087 return 0;
7088
7089 /* OK, unlink the old /lost+found file. */
7090 pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
7091 if (pctx.errcode) {
7092 pctx.str = "ext2fs_unlink";
7093 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7094 return 0;
7095 }
7096 dirinfo = e2fsck_get_dir_info(ctx, ino);
7097 if (dirinfo)
7098 dirinfo->parent = 0;
7099 e2fsck_adjust_inode_count(ctx, ino, -1);
7100 } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
7101 pctx.errcode = retval;
7102 fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
7103 }
7104 if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
7105 return 0;
7106
7107 /*
7108 * Read the inode and block bitmaps in; we'll be messing with
7109 * them.
7110 */
7111 e2fsck_read_bitmaps(ctx);
7112
7113 /*
7114 * First, find a free block
7115 */
7116 retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
7117 if (retval) {
7118 pctx.errcode = retval;
7119 fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
7120 return 0;
7121 }
7122 ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
7123 ext2fs_block_alloc_stats(fs, blk, +1);
7124
7125 /*
7126 * Next find a free inode.
7127 */
7128 retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
7129 ctx->inode_used_map, &ino);
7130 if (retval) {
7131 pctx.errcode = retval;
7132 fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
7133 return 0;
7134 }
7135 ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
7136 ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
7137 ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
7138
7139 /*
7140 * Now let's create the actual data block for the inode
7141 */
7142 retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
7143 if (retval) {
7144 pctx.errcode = retval;
7145 fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
7146 return 0;
7147 }
7148
7149 retval = ext2fs_write_dir_block(fs, blk, block);
7150 ext2fs_free_mem(&block);
7151 if (retval) {
7152 pctx.errcode = retval;
7153 fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
7154 return 0;
7155 }
7156
7157 /*
7158 * Set up the inode structure
7159 */
7160 memset(&inode, 0, sizeof(inode));
7161 inode.i_mode = 040700;
7162 inode.i_size = fs->blocksize;
7163 inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
7164 inode.i_links_count = 2;
7165 inode.i_blocks = fs->blocksize / 512;
7166 inode.i_block[0] = blk;
7167
7168 /*
7169 * Next, write out the inode.
7170 */
7171 pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
7172 if (pctx.errcode) {
7173 pctx.str = "ext2fs_write_inode";
7174 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7175 return 0;
7176 }
7177 /*
7178 * Finally, create the directory link
7179 */
7180 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
7181 if (pctx.errcode) {
7182 pctx.str = "ext2fs_link";
7183 fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
7184 return 0;
7185 }
7186
7187 /*
7188 * Miscellaneous bookkeeping that needs to be kept straight.
7189 */
7190 e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
7191 e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
7192 ext2fs_icount_store(ctx->inode_count, ino, 2);
7193 ext2fs_icount_store(ctx->inode_link_info, ino, 2);
7194 ctx->lost_and_found = ino;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007195 return ino;
7196}
7197
7198/*
7199 * This routine will connect a file to lost+found
7200 */
7201int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
7202{
7203 ext2_filsys fs = ctx->fs;
7204 errcode_t retval;
7205 char name[80];
7206 struct problem_context pctx;
7207 struct ext2_inode inode;
7208 int file_type = 0;
7209
7210 clear_problem_context(&pctx);
7211 pctx.ino = ino;
7212
7213 if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
7214 if (e2fsck_get_lost_and_found(ctx, 1) == 0)
7215 ctx->bad_lost_and_found++;
7216 }
7217 if (ctx->bad_lost_and_found) {
7218 fix_problem(ctx, PR_3_NO_LPF, &pctx);
7219 return 1;
7220 }
7221
7222 sprintf(name, "#%u", ino);
7223 if (ext2fs_read_inode(fs, ino, &inode) == 0)
7224 file_type = ext2_file_type(inode.i_mode);
7225 retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
7226 if (retval == EXT2_ET_DIR_NO_SPACE) {
7227 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
7228 return 1;
7229 retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
7230 1, 0);
7231 if (retval) {
7232 pctx.errcode = retval;
7233 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
7234 return 1;
7235 }
7236 retval = ext2fs_link(fs, ctx->lost_and_found, name,
7237 ino, file_type);
7238 }
7239 if (retval) {
7240 pctx.errcode = retval;
7241 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
7242 return 1;
7243 }
7244 e2fsck_adjust_inode_count(ctx, ino, 1);
7245
7246 return 0;
7247}
7248
7249/*
7250 * Utility routine to adjust the inode counts on an inode.
7251 */
7252errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
7253{
7254 ext2_filsys fs = ctx->fs;
7255 errcode_t retval;
7256 struct ext2_inode inode;
7257
7258 if (!ino)
7259 return 0;
7260
7261 retval = ext2fs_read_inode(fs, ino, &inode);
7262 if (retval)
7263 return retval;
7264
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007265 if (adj == 1) {
7266 ext2fs_icount_increment(ctx->inode_count, ino, 0);
7267 if (inode.i_links_count == (__u16) ~0)
7268 return 0;
7269 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
7270 inode.i_links_count++;
7271 } else if (adj == -1) {
7272 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
7273 if (inode.i_links_count == 0)
7274 return 0;
7275 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
7276 inode.i_links_count--;
7277 }
7278
7279 retval = ext2fs_write_inode(fs, ino, &inode);
7280 if (retval)
7281 return retval;
7282
7283 return 0;
7284}
7285
7286/*
7287 * Fix parent --- this routine fixes up the parent of a directory.
7288 */
7289struct fix_dotdot_struct {
7290 ext2_filsys fs;
7291 ext2_ino_t parent;
7292 int done;
7293 e2fsck_t ctx;
7294};
7295
7296static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007297 int offset FSCK_ATTR((unused)),
7298 int blocksize FSCK_ATTR((unused)),
7299 char *buf FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007300 void *priv_data)
7301{
7302 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
7303 errcode_t retval;
7304 struct problem_context pctx;
7305
7306 if ((dirent->name_len & 0xFF) != 2)
7307 return 0;
7308 if (strncmp(dirent->name, "..", 2))
7309 return 0;
7310
7311 clear_problem_context(&pctx);
7312
7313 retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
7314 if (retval) {
7315 pctx.errcode = retval;
7316 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7317 }
7318 retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
7319 if (retval) {
7320 pctx.errcode = retval;
7321 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
7322 }
7323 dirent->inode = fp->parent;
7324
7325 fp->done++;
7326 return DIRENT_ABORT | DIRENT_CHANGED;
7327}
7328
7329static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
7330{
7331 ext2_filsys fs = ctx->fs;
7332 errcode_t retval;
7333 struct fix_dotdot_struct fp;
7334 struct problem_context pctx;
7335
7336 fp.fs = fs;
7337 fp.parent = parent;
7338 fp.done = 0;
7339 fp.ctx = ctx;
7340
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007341 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
7342 0, fix_dotdot_proc, &fp);
7343 if (retval || !fp.done) {
7344 clear_problem_context(&pctx);
7345 pctx.ino = dir->ino;
7346 pctx.errcode = retval;
7347 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
7348 PR_3_FIX_PARENT_NOFIND, &pctx);
7349 ext2fs_unmark_valid(fs);
7350 }
7351 dir->dotdot = parent;
7352
7353 return;
7354}
7355
7356/*
7357 * These routines are responsible for expanding a /lost+found if it is
7358 * too small.
7359 */
7360
7361struct expand_dir_struct {
7362 int num;
7363 int guaranteed_size;
7364 int newblocks;
7365 int last_block;
7366 errcode_t err;
7367 e2fsck_t ctx;
7368};
7369
7370static int expand_dir_proc(ext2_filsys fs,
7371 blk_t *blocknr,
7372 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +00007373 blk_t ref_block FSCK_ATTR((unused)),
7374 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007375 void *priv_data)
7376{
7377 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
7378 blk_t new_blk;
7379 static blk_t last_blk = 0;
7380 char *block;
7381 errcode_t retval;
7382 e2fsck_t ctx;
7383
7384 ctx = es->ctx;
7385
7386 if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
7387 return BLOCK_ABORT;
7388
7389 if (blockcnt > 0)
7390 es->last_block = blockcnt;
7391 if (*blocknr) {
7392 last_blk = *blocknr;
7393 return 0;
7394 }
7395 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
7396 &new_blk);
7397 if (retval) {
7398 es->err = retval;
7399 return BLOCK_ABORT;
7400 }
7401 if (blockcnt > 0) {
7402 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
7403 if (retval) {
7404 es->err = retval;
7405 return BLOCK_ABORT;
7406 }
7407 es->num--;
7408 retval = ext2fs_write_dir_block(fs, new_blk, block);
7409 } else {
7410 retval = ext2fs_get_mem(fs->blocksize, &block);
7411 if (retval) {
7412 es->err = retval;
7413 return BLOCK_ABORT;
7414 }
7415 memset(block, 0, fs->blocksize);
7416 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
7417 }
7418 if (retval) {
7419 es->err = retval;
7420 return BLOCK_ABORT;
7421 }
7422 ext2fs_free_mem(&block);
7423 *blocknr = new_blk;
7424 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
7425 ext2fs_block_alloc_stats(fs, new_blk, +1);
7426 es->newblocks++;
7427
7428 if (es->num == 0)
7429 return (BLOCK_CHANGED | BLOCK_ABORT);
7430 else
7431 return BLOCK_CHANGED;
7432}
7433
7434errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
7435 int num, int guaranteed_size)
7436{
7437 ext2_filsys fs = ctx->fs;
7438 errcode_t retval;
7439 struct expand_dir_struct es;
7440 struct ext2_inode inode;
7441
7442 if (!(fs->flags & EXT2_FLAG_RW))
7443 return EXT2_ET_RO_FILSYS;
7444
7445 /*
7446 * Read the inode and block bitmaps in; we'll be messing with
7447 * them.
7448 */
7449 e2fsck_read_bitmaps(ctx);
7450
7451 retval = ext2fs_check_directory(fs, dir);
7452 if (retval)
7453 return retval;
7454
7455 es.num = num;
7456 es.guaranteed_size = guaranteed_size;
7457 es.last_block = 0;
7458 es.err = 0;
7459 es.newblocks = 0;
7460 es.ctx = ctx;
7461
7462 retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
7463 0, expand_dir_proc, &es);
7464
7465 if (es.err)
7466 return es.err;
7467
7468 /*
7469 * Update the size and block count fields in the inode.
7470 */
7471 retval = ext2fs_read_inode(fs, dir, &inode);
7472 if (retval)
7473 return retval;
7474
7475 inode.i_size = (es.last_block + 1) * fs->blocksize;
7476 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
7477
7478 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
7479
7480 return 0;
7481}
7482
7483/*
7484 * pass4.c -- pass #4 of e2fsck: Check reference counts
7485 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007486 * Pass 4 frees the following data structures:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007487 * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
7488 */
7489
7490/*
7491 * This routine is called when an inode is not connected to the
7492 * directory tree.
7493 *
7494 * This subroutine returns 1 then the caller shouldn't bother with the
7495 * rest of the pass 4 tests.
7496 */
7497static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
7498{
7499 ext2_filsys fs = ctx->fs;
7500 struct ext2_inode inode;
7501 struct problem_context pctx;
7502
7503 e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
7504 clear_problem_context(&pctx);
7505 pctx.ino = i;
7506 pctx.inode = &inode;
7507
7508 /*
7509 * Offer to delete any zero-length files that does not have
7510 * blocks. If there is an EA block, it might have useful
7511 * information, so we won't prompt to delete it, but let it be
7512 * reconnected to lost+found.
7513 */
7514 if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
7515 LINUX_S_ISDIR(inode.i_mode))) {
7516 if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
7517 ext2fs_icount_store(ctx->inode_link_info, i, 0);
7518 inode.i_links_count = 0;
7519 inode.i_dtime = time(0);
7520 e2fsck_write_inode(ctx, i, &inode,
7521 "disconnect_inode");
7522 /*
7523 * Fix up the bitmaps...
7524 */
7525 e2fsck_read_bitmaps(ctx);
7526 ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
7527 ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
7528 ext2fs_inode_alloc_stats2(fs, i, -1,
7529 LINUX_S_ISDIR(inode.i_mode));
7530 return 0;
7531 }
7532 }
7533
7534 /*
7535 * Prompt to reconnect.
7536 */
7537 if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
7538 if (e2fsck_reconnect_file(ctx, i))
7539 ext2fs_unmark_valid(fs);
7540 } else {
7541 /*
7542 * If we don't attach the inode, then skip the
7543 * i_links_test since there's no point in trying to
7544 * force i_links_count to zero.
7545 */
7546 ext2fs_unmark_valid(fs);
7547 return 1;
7548 }
7549 return 0;
7550}
7551
7552
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00007553static void e2fsck_pass4(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007554{
7555 ext2_filsys fs = ctx->fs;
7556 ext2_ino_t i;
7557 struct ext2_inode inode;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007558 struct problem_context pctx;
7559 __u16 link_count, link_counted;
7560 char *buf = 0;
7561 int group, maxgroup;
7562
Rob Landley3e72c592006-04-06 22:49:04 +00007563 /* Pass 4 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007564
7565 clear_problem_context(&pctx);
7566
7567 if (!(ctx->options & E2F_OPT_PREEN))
7568 fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
7569
7570 group = 0;
7571 maxgroup = fs->group_desc_count;
7572 if (ctx->progress)
7573 if ((ctx->progress)(ctx, 4, 0, maxgroup))
7574 return;
7575
7576 for (i=1; i <= fs->super->s_inodes_count; i++) {
7577 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
7578 return;
7579 if ((i % fs->super->s_inodes_per_group) == 0) {
7580 group++;
7581 if (ctx->progress)
7582 if ((ctx->progress)(ctx, 4, group, maxgroup))
7583 return;
7584 }
7585 if (i == EXT2_BAD_INO ||
7586 (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
7587 continue;
7588 if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
7589 (ctx->inode_imagic_map &&
Rob Landleyd8f66012006-05-05 17:29:09 +00007590 ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007591 continue;
7592 ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
7593 ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
7594 if (link_counted == 0) {
7595 if (!buf)
7596 buf = e2fsck_allocate_memory(ctx,
7597 fs->blocksize, "bad_inode buffer");
7598 if (e2fsck_process_bad_inode(ctx, 0, i, buf))
7599 continue;
7600 if (disconnect_inode(ctx, i))
7601 continue;
7602 ext2fs_icount_fetch(ctx->inode_link_info, i,
7603 &link_count);
7604 ext2fs_icount_fetch(ctx->inode_count, i,
7605 &link_counted);
7606 }
7607 if (link_counted != link_count) {
7608 e2fsck_read_inode(ctx, i, &inode, "pass4");
7609 pctx.ino = i;
7610 pctx.inode = &inode;
7611 if (link_count != inode.i_links_count) {
7612 pctx.num = link_count;
7613 fix_problem(ctx,
7614 PR_4_INCONSISTENT_COUNT, &pctx);
7615 }
7616 pctx.num = link_counted;
7617 if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
7618 inode.i_links_count = link_counted;
7619 e2fsck_write_inode(ctx, i, &inode, "pass4");
7620 }
7621 }
7622 }
7623 ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
7624 ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007625 ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
7626 ctx->inode_imagic_map = 0;
Rob Landleye7c43b62006-03-01 16:39:45 +00007627 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007628}
7629
7630/*
7631 * pass5.c --- check block and inode bitmaps against on-disk bitmaps
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007632 */
7633
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00007634#define NO_BLK ((blk_t) -1)
7635
7636static void print_bitmap_problem(e2fsck_t ctx, int problem,
7637 struct problem_context *pctx)
7638{
7639 switch (problem) {
7640 case PR_5_BLOCK_UNUSED:
7641 if (pctx->blk == pctx->blk2)
7642 pctx->blk2 = 0;
7643 else
7644 problem = PR_5_BLOCK_RANGE_UNUSED;
7645 break;
7646 case PR_5_BLOCK_USED:
7647 if (pctx->blk == pctx->blk2)
7648 pctx->blk2 = 0;
7649 else
7650 problem = PR_5_BLOCK_RANGE_USED;
7651 break;
7652 case PR_5_INODE_UNUSED:
7653 if (pctx->ino == pctx->ino2)
7654 pctx->ino2 = 0;
7655 else
7656 problem = PR_5_INODE_RANGE_UNUSED;
7657 break;
7658 case PR_5_INODE_USED:
7659 if (pctx->ino == pctx->ino2)
7660 pctx->ino2 = 0;
7661 else
7662 problem = PR_5_INODE_RANGE_USED;
7663 break;
7664 }
7665 fix_problem(ctx, problem, pctx);
7666 pctx->blk = pctx->blk2 = NO_BLK;
7667 pctx->ino = pctx->ino2 = 0;
7668}
7669
7670static void check_block_bitmaps(e2fsck_t ctx)
7671{
7672 ext2_filsys fs = ctx->fs;
7673 blk_t i;
7674 int *free_array;
7675 int group = 0;
7676 unsigned int blocks = 0;
7677 unsigned int free_blocks = 0;
7678 int group_free = 0;
7679 int actual, bitmap;
7680 struct problem_context pctx;
7681 int problem, save_problem, fixit, had_problem;
7682 errcode_t retval;
7683
7684 clear_problem_context(&pctx);
7685 free_array = (int *) e2fsck_allocate_memory(ctx,
7686 fs->group_desc_count * sizeof(int), "free block count array");
7687
7688 if ((fs->super->s_first_data_block <
7689 ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
7690 (fs->super->s_blocks_count-1 >
7691 ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
7692 pctx.num = 1;
7693 pctx.blk = fs->super->s_first_data_block;
7694 pctx.blk2 = fs->super->s_blocks_count -1;
7695 pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
7696 pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
7697 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7698
7699 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7700 return;
7701 }
7702
7703 if ((fs->super->s_first_data_block <
7704 ext2fs_get_block_bitmap_start(fs->block_map)) ||
7705 (fs->super->s_blocks_count-1 >
7706 ext2fs_get_block_bitmap_end(fs->block_map))) {
7707 pctx.num = 2;
7708 pctx.blk = fs->super->s_first_data_block;
7709 pctx.blk2 = fs->super->s_blocks_count -1;
7710 pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
7711 pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
7712 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7713
7714 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7715 return;
7716 }
7717
7718redo_counts:
7719 had_problem = 0;
7720 save_problem = 0;
7721 pctx.blk = pctx.blk2 = NO_BLK;
7722 for (i = fs->super->s_first_data_block;
7723 i < fs->super->s_blocks_count;
7724 i++) {
7725 actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
7726 bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
7727
7728 if (actual == bitmap)
7729 goto do_counts;
7730
7731 if (!actual && bitmap) {
7732 /*
7733 * Block not used, but marked in use in the bitmap.
7734 */
7735 problem = PR_5_BLOCK_UNUSED;
7736 } else {
7737 /*
7738 * Block used, but not marked in use in the bitmap.
7739 */
7740 problem = PR_5_BLOCK_USED;
7741 }
7742 if (pctx.blk == NO_BLK) {
7743 pctx.blk = pctx.blk2 = i;
7744 save_problem = problem;
7745 } else {
7746 if ((problem == save_problem) &&
7747 (pctx.blk2 == i-1))
7748 pctx.blk2++;
7749 else {
7750 print_bitmap_problem(ctx, save_problem, &pctx);
7751 pctx.blk = pctx.blk2 = i;
7752 save_problem = problem;
7753 }
7754 }
7755 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7756 had_problem++;
7757
7758 do_counts:
7759 if (!bitmap) {
7760 group_free++;
7761 free_blocks++;
7762 }
7763 blocks ++;
7764 if ((blocks == fs->super->s_blocks_per_group) ||
7765 (i == fs->super->s_blocks_count-1)) {
7766 free_array[group] = group_free;
7767 group ++;
7768 blocks = 0;
7769 group_free = 0;
7770 if (ctx->progress)
7771 if ((ctx->progress)(ctx, 5, group,
7772 fs->group_desc_count*2))
7773 return;
7774 }
7775 }
7776 if (pctx.blk != NO_BLK)
7777 print_bitmap_problem(ctx, save_problem, &pctx);
7778 if (had_problem)
7779 fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
7780 else
7781 fixit = -1;
7782 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7783
7784 if (fixit == 1) {
7785 ext2fs_free_block_bitmap(fs->block_map);
7786 retval = ext2fs_copy_bitmap(ctx->block_found_map,
7787 &fs->block_map);
7788 if (retval) {
7789 clear_problem_context(&pctx);
7790 fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
7791 ctx->flags |= E2F_FLAG_ABORT;
7792 return;
7793 }
7794 ext2fs_set_bitmap_padding(fs->block_map);
7795 ext2fs_mark_bb_dirty(fs);
7796
7797 /* Redo the counts */
7798 blocks = 0; free_blocks = 0; group_free = 0; group = 0;
7799 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7800 goto redo_counts;
7801 } else if (fixit == 0)
7802 ext2fs_unmark_valid(fs);
7803
7804 for (i = 0; i < fs->group_desc_count; i++) {
7805 if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
7806 pctx.group = i;
7807 pctx.blk = fs->group_desc[i].bg_free_blocks_count;
7808 pctx.blk2 = free_array[i];
7809
7810 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
7811 &pctx)) {
7812 fs->group_desc[i].bg_free_blocks_count =
7813 free_array[i];
7814 ext2fs_mark_super_dirty(fs);
7815 } else
7816 ext2fs_unmark_valid(fs);
7817 }
7818 }
7819 if (free_blocks != fs->super->s_free_blocks_count) {
7820 pctx.group = 0;
7821 pctx.blk = fs->super->s_free_blocks_count;
7822 pctx.blk2 = free_blocks;
7823
7824 if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
7825 fs->super->s_free_blocks_count = free_blocks;
7826 ext2fs_mark_super_dirty(fs);
7827 } else
7828 ext2fs_unmark_valid(fs);
7829 }
7830 ext2fs_free_mem(&free_array);
7831}
7832
7833static void check_inode_bitmaps(e2fsck_t ctx)
7834{
7835 ext2_filsys fs = ctx->fs;
7836 ext2_ino_t i;
7837 unsigned int free_inodes = 0;
7838 int group_free = 0;
7839 int dirs_count = 0;
7840 int group = 0;
7841 unsigned int inodes = 0;
7842 int *free_array;
7843 int *dir_array;
7844 int actual, bitmap;
7845 errcode_t retval;
7846 struct problem_context pctx;
7847 int problem, save_problem, fixit, had_problem;
7848
7849 clear_problem_context(&pctx);
7850 free_array = (int *) e2fsck_allocate_memory(ctx,
7851 fs->group_desc_count * sizeof(int), "free inode count array");
7852
7853 dir_array = (int *) e2fsck_allocate_memory(ctx,
7854 fs->group_desc_count * sizeof(int), "directory count array");
7855
7856 if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
7857 (fs->super->s_inodes_count >
7858 ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
7859 pctx.num = 3;
7860 pctx.blk = 1;
7861 pctx.blk2 = fs->super->s_inodes_count;
7862 pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
7863 pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
7864 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7865
7866 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7867 return;
7868 }
7869 if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
7870 (fs->super->s_inodes_count >
7871 ext2fs_get_inode_bitmap_end(fs->inode_map))) {
7872 pctx.num = 4;
7873 pctx.blk = 1;
7874 pctx.blk2 = fs->super->s_inodes_count;
7875 pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
7876 pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
7877 fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
7878
7879 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
7880 return;
7881 }
7882
7883redo_counts:
7884 had_problem = 0;
7885 save_problem = 0;
7886 pctx.ino = pctx.ino2 = 0;
7887 for (i = 1; i <= fs->super->s_inodes_count; i++) {
7888 actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
7889 bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
7890
7891 if (actual == bitmap)
7892 goto do_counts;
7893
7894 if (!actual && bitmap) {
7895 /*
7896 * Inode wasn't used, but marked in bitmap
7897 */
7898 problem = PR_5_INODE_UNUSED;
7899 } else /* if (actual && !bitmap) */ {
7900 /*
7901 * Inode used, but not in bitmap
7902 */
7903 problem = PR_5_INODE_USED;
7904 }
7905 if (pctx.ino == 0) {
7906 pctx.ino = pctx.ino2 = i;
7907 save_problem = problem;
7908 } else {
7909 if ((problem == save_problem) &&
7910 (pctx.ino2 == i-1))
7911 pctx.ino2++;
7912 else {
7913 print_bitmap_problem(ctx, save_problem, &pctx);
7914 pctx.ino = pctx.ino2 = i;
7915 save_problem = problem;
7916 }
7917 }
7918 ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
7919 had_problem++;
7920
7921do_counts:
7922 if (!bitmap) {
7923 group_free++;
7924 free_inodes++;
7925 } else {
7926 if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
7927 dirs_count++;
7928 }
7929 inodes++;
7930 if ((inodes == fs->super->s_inodes_per_group) ||
7931 (i == fs->super->s_inodes_count)) {
7932 free_array[group] = group_free;
7933 dir_array[group] = dirs_count;
7934 group ++;
7935 inodes = 0;
7936 group_free = 0;
7937 dirs_count = 0;
7938 if (ctx->progress)
7939 if ((ctx->progress)(ctx, 5,
7940 group + fs->group_desc_count,
7941 fs->group_desc_count*2))
7942 return;
7943 }
7944 }
7945 if (pctx.ino)
7946 print_bitmap_problem(ctx, save_problem, &pctx);
7947
7948 if (had_problem)
7949 fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
7950 else
7951 fixit = -1;
7952 ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
7953
7954 if (fixit == 1) {
7955 ext2fs_free_inode_bitmap(fs->inode_map);
7956 retval = ext2fs_copy_bitmap(ctx->inode_used_map,
7957 &fs->inode_map);
7958 if (retval) {
7959 clear_problem_context(&pctx);
7960 fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
7961 ctx->flags |= E2F_FLAG_ABORT;
7962 return;
7963 }
7964 ext2fs_set_bitmap_padding(fs->inode_map);
7965 ext2fs_mark_ib_dirty(fs);
7966
7967 /* redo counts */
7968 inodes = 0; free_inodes = 0; group_free = 0;
7969 dirs_count = 0; group = 0;
7970 memset(free_array, 0, fs->group_desc_count * sizeof(int));
7971 memset(dir_array, 0, fs->group_desc_count * sizeof(int));
7972 goto redo_counts;
7973 } else if (fixit == 0)
7974 ext2fs_unmark_valid(fs);
7975
7976 for (i = 0; i < fs->group_desc_count; i++) {
7977 if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
7978 pctx.group = i;
7979 pctx.ino = fs->group_desc[i].bg_free_inodes_count;
7980 pctx.ino2 = free_array[i];
7981 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
7982 &pctx)) {
7983 fs->group_desc[i].bg_free_inodes_count =
7984 free_array[i];
7985 ext2fs_mark_super_dirty(fs);
7986 } else
7987 ext2fs_unmark_valid(fs);
7988 }
7989 if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
7990 pctx.group = i;
7991 pctx.ino = fs->group_desc[i].bg_used_dirs_count;
7992 pctx.ino2 = dir_array[i];
7993
7994 if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
7995 &pctx)) {
7996 fs->group_desc[i].bg_used_dirs_count =
7997 dir_array[i];
7998 ext2fs_mark_super_dirty(fs);
7999 } else
8000 ext2fs_unmark_valid(fs);
8001 }
8002 }
8003 if (free_inodes != fs->super->s_free_inodes_count) {
8004 pctx.group = -1;
8005 pctx.ino = fs->super->s_free_inodes_count;
8006 pctx.ino2 = free_inodes;
8007
8008 if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
8009 fs->super->s_free_inodes_count = free_inodes;
8010 ext2fs_mark_super_dirty(fs);
8011 } else
8012 ext2fs_unmark_valid(fs);
8013 }
8014 ext2fs_free_mem(&free_array);
8015 ext2fs_free_mem(&dir_array);
8016}
8017
8018static void check_inode_end(e2fsck_t ctx)
8019{
8020 ext2_filsys fs = ctx->fs;
8021 ext2_ino_t end, save_inodes_count, i;
8022 struct problem_context pctx;
8023
8024 clear_problem_context(&pctx);
8025
8026 end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
8027 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
8028 &save_inodes_count);
8029 if (pctx.errcode) {
8030 pctx.num = 1;
8031 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8032 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8033 return;
8034 }
8035 if (save_inodes_count == end)
8036 return;
8037
8038 for (i = save_inodes_count + 1; i <= end; i++) {
8039 if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
8040 if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
8041 for (i = save_inodes_count + 1; i <= end; i++)
8042 ext2fs_mark_inode_bitmap(fs->inode_map,
8043 i);
8044 ext2fs_mark_ib_dirty(fs);
8045 } else
8046 ext2fs_unmark_valid(fs);
8047 break;
8048 }
8049 }
8050
8051 pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
8052 save_inodes_count, 0);
8053 if (pctx.errcode) {
8054 pctx.num = 2;
8055 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8056 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8057 return;
8058 }
8059}
8060
8061static void check_block_end(e2fsck_t ctx)
8062{
8063 ext2_filsys fs = ctx->fs;
8064 blk_t end, save_blocks_count, i;
8065 struct problem_context pctx;
8066
8067 clear_problem_context(&pctx);
8068
8069 end = fs->block_map->start +
8070 (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
8071 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
8072 &save_blocks_count);
8073 if (pctx.errcode) {
8074 pctx.num = 3;
8075 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8076 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8077 return;
8078 }
8079 if (save_blocks_count == end)
8080 return;
8081
8082 for (i = save_blocks_count + 1; i <= end; i++) {
8083 if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
8084 if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
8085 for (i = save_blocks_count + 1; i <= end; i++)
8086 ext2fs_mark_block_bitmap(fs->block_map,
8087 i);
8088 ext2fs_mark_bb_dirty(fs);
8089 } else
8090 ext2fs_unmark_valid(fs);
8091 break;
8092 }
8093 }
8094
8095 pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
8096 save_blocks_count, 0);
8097 if (pctx.errcode) {
8098 pctx.num = 4;
8099 fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
8100 ctx->flags |= E2F_FLAG_ABORT; /* fatal */
8101 return;
8102 }
8103}
8104
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008105static void e2fsck_pass5(e2fsck_t ctx)
8106{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008107 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008108
Rob Landley3e72c592006-04-06 22:49:04 +00008109 /* Pass 5 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008110
8111 clear_problem_context(&pctx);
8112
8113 if (!(ctx->options & E2F_OPT_PREEN))
8114 fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
8115
8116 if (ctx->progress)
8117 if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
8118 return;
8119
8120 e2fsck_read_bitmaps(ctx);
8121
8122 check_block_bitmaps(ctx);
8123 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8124 return;
8125 check_inode_bitmaps(ctx);
8126 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8127 return;
8128 check_inode_end(ctx);
8129 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8130 return;
8131 check_block_end(ctx);
8132 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
8133 return;
8134
8135 ext2fs_free_inode_bitmap(ctx->inode_used_map);
8136 ctx->inode_used_map = 0;
8137 ext2fs_free_inode_bitmap(ctx->inode_dir_map);
8138 ctx->inode_dir_map = 0;
8139 ext2fs_free_block_bitmap(ctx->block_found_map);
8140 ctx->block_found_map = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008141}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008142
8143/*
8144 * problem.c --- report filesystem problems to the user
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008145 */
8146
8147#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
8148#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
8149#define PR_NO_DEFAULT 0x000004 /* Default to no */
8150#define PR_MSG_ONLY 0x000008 /* Print message only */
8151
8152/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
8153
8154#define PR_FATAL 0x001000 /* Fatal error */
8155#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
8156 /* ask another */
8157#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
8158#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
8159#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
8160#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
8161#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
8162
8163
8164#define PROMPT_NONE 0
8165#define PROMPT_FIX 1
8166#define PROMPT_CLEAR 2
8167#define PROMPT_RELOCATE 3
8168#define PROMPT_ALLOCATE 4
8169#define PROMPT_EXPAND 5
8170#define PROMPT_CONNECT 6
8171#define PROMPT_CREATE 7
8172#define PROMPT_SALVAGE 8
8173#define PROMPT_TRUNCATE 9
8174#define PROMPT_CLEAR_INODE 10
8175#define PROMPT_ABORT 11
8176#define PROMPT_SPLIT 12
8177#define PROMPT_CONTINUE 13
8178#define PROMPT_CLONE 14
8179#define PROMPT_DELETE 15
8180#define PROMPT_SUPPRESS 16
8181#define PROMPT_UNLINK 17
8182#define PROMPT_CLEAR_HTREE 18
8183#define PROMPT_RECREATE 19
8184#define PROMPT_NULL 20
8185
8186struct e2fsck_problem {
8187 problem_t e2p_code;
8188 const char * e2p_description;
8189 char prompt;
8190 int flags;
8191 problem_t second_code;
8192};
8193
8194struct latch_descr {
8195 int latch_code;
8196 problem_t question;
8197 problem_t end_message;
8198 int flags;
8199};
8200
8201/*
8202 * These are the prompts which are used to ask the user if they want
8203 * to fix a problem.
8204 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008205static const char * const prompt[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008206 N_("(no prompt)"), /* 0 */
8207 N_("Fix"), /* 1 */
8208 N_("Clear"), /* 2 */
8209 N_("Relocate"), /* 3 */
8210 N_("Allocate"), /* 4 */
8211 N_("Expand"), /* 5 */
8212 N_("Connect to /lost+found"), /* 6 */
8213 N_("Create"), /* 7 */
8214 N_("Salvage"), /* 8 */
8215 N_("Truncate"), /* 9 */
8216 N_("Clear inode"), /* 10 */
8217 N_("Abort"), /* 11 */
8218 N_("Split"), /* 12 */
8219 N_("Continue"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008220 N_("Clone multiply-claimed blocks"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008221 N_("Delete file"), /* 15 */
8222 N_("Suppress messages"),/* 16 */
8223 N_("Unlink"), /* 17 */
8224 N_("Clear HTree index"),/* 18 */
8225 N_("Recreate"), /* 19 */
8226 "", /* 20 */
8227};
8228
8229/*
8230 * These messages are printed when we are preen mode and we will be
8231 * automatically fixing the problem.
8232 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00008233static const char * const preen_msg[] = {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008234 N_("(NONE)"), /* 0 */
8235 N_("FIXED"), /* 1 */
8236 N_("CLEARED"), /* 2 */
8237 N_("RELOCATED"), /* 3 */
8238 N_("ALLOCATED"), /* 4 */
8239 N_("EXPANDED"), /* 5 */
8240 N_("RECONNECTED"), /* 6 */
8241 N_("CREATED"), /* 7 */
8242 N_("SALVAGED"), /* 8 */
8243 N_("TRUNCATED"), /* 9 */
8244 N_("INODE CLEARED"), /* 10 */
8245 N_("ABORTED"), /* 11 */
8246 N_("SPLIT"), /* 12 */
8247 N_("CONTINUING"), /* 13 */
Mike Frysinger874af852006-03-08 07:03:27 +00008248 N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008249 N_("FILE DELETED"), /* 15 */
8250 N_("SUPPRESSED"), /* 16 */
8251 N_("UNLINKED"), /* 17 */
8252 N_("HTREE INDEX CLEARED"),/* 18 */
8253 N_("WILL RECREATE"), /* 19 */
8254 "", /* 20 */
8255};
8256
8257static const struct e2fsck_problem problem_table[] = {
8258
8259 /* Pre-Pass 1 errors */
8260
8261 /* Block bitmap not in group */
8262 { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
8263 PROMPT_RELOCATE, PR_LATCH_RELOC },
8264
8265 /* Inode bitmap not in group */
8266 { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
8267 PROMPT_RELOCATE, PR_LATCH_RELOC },
8268
8269 /* Inode table not in group */
8270 { PR_0_ITABLE_NOT_GROUP,
8271 N_("@i table for @g %g is not in @g. (@b %b)\n"
8272 "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
8273 PROMPT_RELOCATE, PR_LATCH_RELOC },
8274
8275 /* Superblock corrupt */
8276 { PR_0_SB_CORRUPT,
8277 N_("\nThe @S could not be read or does not describe a correct ext2\n"
8278 "@f. If the @v is valid and it really contains an ext2\n"
8279 "@f (and not swap or ufs or something else), then the @S\n"
8280 "is corrupt, and you might try running e2fsck with an alternate @S:\n"
8281 " e2fsck -b %S <@v>\n\n"),
8282 PROMPT_NONE, PR_FATAL },
8283
8284 /* Filesystem size is wrong */
8285 { PR_0_FS_SIZE_WRONG,
8286 N_("The @f size (according to the @S) is %b @bs\n"
8287 "The physical size of the @v is %c @bs\n"
8288 "Either the @S or the partition table is likely to be corrupt!\n"),
8289 PROMPT_ABORT, 0 },
8290
8291 /* Fragments not supported */
8292 { PR_0_NO_FRAGMENTS,
8293 N_("@S @b_size = %b, fragsize = %c.\n"
8294 "This version of e2fsck does not support fragment sizes different\n"
8295 "from the @b size.\n"),
8296 PROMPT_NONE, PR_FATAL },
8297
8298 /* Bad blocks_per_group */
8299 { PR_0_BLOCKS_PER_GROUP,
8300 N_("@S @bs_per_group = %b, should have been %c\n"),
8301 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8302
8303 /* Bad first_data_block */
8304 { PR_0_FIRST_DATA_BLOCK,
8305 N_("@S first_data_@b = %b, should have been %c\n"),
8306 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8307
8308 /* Adding UUID to filesystem */
8309 { PR_0_ADD_UUID,
8310 N_("@f did not have a UUID; generating one.\n\n"),
8311 PROMPT_NONE, 0 },
8312
8313 /* Relocate hint */
8314 { PR_0_RELOCATE_HINT,
Mike Frysinger874af852006-03-08 07:03:27 +00008315 N_("Note: if several inode or block bitmap blocks or part\n"
8316 "of the inode table require relocation, you may wish to try\n"
8317 "running e2fsck with the '-b %S' option first. The problem\n"
8318 "may lie only with the primary block group descriptors, and\n"
8319 "the backup block group descriptors may be OK.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008320 PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
8321
8322 /* Miscellaneous superblock corruption */
8323 { PR_0_MISC_CORRUPT_SUPER,
8324 N_("Corruption found in @S. (%s = %N).\n"),
8325 PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
8326
8327 /* Error determing physical device size of filesystem */
8328 { PR_0_GETSIZE_ERROR,
8329 N_("Error determining size of the physical @v: %m\n"),
8330 PROMPT_NONE, PR_FATAL },
8331
8332 /* Inode count in superblock is incorrect */
8333 { PR_0_INODE_COUNT_WRONG,
Mike Frysinger874af852006-03-08 07:03:27 +00008334 N_("@i count in @S is %i, @s %j.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008335 PROMPT_FIX, 0 },
8336
8337 { PR_0_HURD_CLEAR_FILETYPE,
8338 N_("The Hurd does not support the filetype feature.\n"),
8339 PROMPT_CLEAR, 0 },
8340
8341 /* Journal inode is invalid */
8342 { PR_0_JOURNAL_BAD_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008343 N_("@S has an @n ext3 @j (@i %i).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008344 PROMPT_CLEAR, PR_PREEN_OK },
8345
8346 /* The external journal has (unsupported) multiple filesystems */
8347 { PR_0_JOURNAL_UNSUPP_MULTIFS,
8348 N_("External @j has multiple @f users (unsupported).\n"),
8349 PROMPT_NONE, PR_FATAL },
8350
8351 /* Can't find external journal */
8352 { PR_0_CANT_FIND_JOURNAL,
8353 N_("Can't find external @j\n"),
8354 PROMPT_NONE, PR_FATAL },
8355
8356 /* External journal has bad superblock */
8357 { PR_0_EXT_JOURNAL_BAD_SUPER,
8358 N_("External @j has bad @S\n"),
8359 PROMPT_NONE, PR_FATAL },
8360
8361 /* Superblock has a bad journal UUID */
8362 { PR_0_JOURNAL_BAD_UUID,
8363 N_("External @j does not support this @f\n"),
8364 PROMPT_NONE, PR_FATAL },
8365
8366 /* Journal has an unknown superblock type */
8367 { PR_0_JOURNAL_UNSUPP_SUPER,
8368 N_("Ext3 @j @S is unknown type %N (unsupported).\n"
8369 "It is likely that your copy of e2fsck is old and/or doesn't "
8370 "support this @j format.\n"
8371 "It is also possible the @j @S is corrupt.\n"),
8372 PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
8373
8374 /* Journal superblock is corrupt */
8375 { PR_0_JOURNAL_BAD_SUPER,
8376 N_("Ext3 @j @S is corrupt.\n"),
8377 PROMPT_FIX, PR_PREEN_OK },
8378
8379 /* Superblock flag should be cleared */
8380 { PR_0_JOURNAL_HAS_JOURNAL,
8381 N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
8382 PROMPT_CLEAR, PR_PREEN_OK },
8383
8384 /* Superblock flag is incorrect */
8385 { PR_0_JOURNAL_RECOVER_SET,
8386 N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
8387 PROMPT_CLEAR, PR_PREEN_OK },
8388
8389 /* Journal has data, but recovery flag is clear */
8390 { PR_0_JOURNAL_RECOVERY_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008391 N_("ext3 recovery flag is clear, but @j has data.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008392 PROMPT_NONE, 0 },
8393
8394 /* Ask if we should clear the journal */
8395 { PR_0_JOURNAL_RESET_JOURNAL,
8396 N_("Clear @j"),
8397 PROMPT_NULL, PR_PREEN_NOMSG },
8398
8399 /* Ask if we should run the journal anyway */
8400 { PR_0_JOURNAL_RUN,
8401 N_("Run @j anyway"),
8402 PROMPT_NULL, 0 },
8403
8404 /* Run the journal by default */
8405 { PR_0_JOURNAL_RUN_DEFAULT,
8406 N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
8407 PROMPT_NONE, 0 },
8408
8409 /* Clearing orphan inode */
8410 { PR_0_ORPHAN_CLEAR_INODE,
8411 N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
8412 PROMPT_NONE, 0 },
8413
8414 /* Illegal block found in orphaned inode */
8415 { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
8416 N_("@I @b #%B (%b) found in @o @i %i.\n"),
8417 PROMPT_NONE, 0 },
8418
8419 /* Already cleared block found in orphaned inode */
8420 { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
8421 N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
8422 PROMPT_NONE, 0 },
8423
8424 /* Illegal orphan inode in superblock */
8425 { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
8426 N_("@I @o @i %i in @S.\n"),
8427 PROMPT_NONE, 0 },
8428
8429 /* Illegal inode in orphaned inode list */
8430 { PR_0_ORPHAN_ILLEGAL_INODE,
8431 N_("@I @i %i in @o @i list.\n"),
8432 PROMPT_NONE, 0 },
8433
8434 /* Filesystem revision is 0, but feature flags are set */
8435 { PR_0_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008436 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008437 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8438
8439 /* Journal superblock has an unknown read-only feature flag set */
8440 { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
8441 N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
8442 PROMPT_ABORT, 0 },
8443
8444 /* Journal superblock has an unknown incompatible feature flag set */
8445 { PR_0_JOURNAL_UNSUPP_INCOMPAT,
8446 N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
8447 PROMPT_ABORT, 0 },
8448
8449 /* Journal has unsupported version number */
8450 { PR_0_JOURNAL_UNSUPP_VERSION,
8451 N_("@j version not supported by this e2fsck.\n"),
8452 PROMPT_ABORT, 0 },
8453
8454 /* Moving journal to hidden file */
8455 { PR_0_MOVE_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008456 N_("Moving @j from /%s to hidden @i.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008457 PROMPT_NONE, 0 },
8458
8459 /* Error moving journal to hidden file */
8460 { PR_0_ERR_MOVE_JOURNAL,
8461 N_("Error moving @j: %m\n\n"),
8462 PROMPT_NONE, 0 },
8463
8464 /* Clearing V2 journal superblock */
8465 { PR_0_CLEAR_V2_JOURNAL,
Mike Frysinger874af852006-03-08 07:03:27 +00008466 N_("Found @n V2 @j @S fields (from V1 @j).\n"
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008467 "Clearing fields beyond the V1 @j @S...\n\n"),
8468 PROMPT_NONE, 0 },
8469
8470 /* Backup journal inode blocks */
8471 { PR_0_BACKUP_JNL,
8472 N_("Backing up @j @i @b information.\n\n"),
8473 PROMPT_NONE, 0 },
8474
8475 /* Reserved blocks w/o resize_inode */
8476 { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
8477 N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
8478 "is %N; @s zero. "),
8479 PROMPT_FIX, 0 },
8480
8481 /* Resize_inode not enabled, but resize inode is non-zero */
8482 { PR_0_CLEAR_RESIZE_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008483 N_("Resize_@i not enabled, but the resize @i is non-zero. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008484 PROMPT_CLEAR, 0 },
8485
8486 /* Resize inode invalid */
8487 { PR_0_RESIZE_INODE_INVALID,
8488 N_("Resize @i not valid. "),
8489 PROMPT_RECREATE, 0 },
8490
8491 /* Pass 1 errors */
8492
8493 /* Pass 1: Checking inodes, blocks, and sizes */
8494 { PR_1_PASS_HEADER,
8495 N_("Pass 1: Checking @is, @bs, and sizes\n"),
8496 PROMPT_NONE, 0 },
8497
8498 /* Root directory is not an inode */
8499 { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
8500 PROMPT_CLEAR, 0 },
8501
8502 /* Root directory has dtime set */
8503 { PR_1_ROOT_DTIME,
8504 N_("@r has dtime set (probably due to old mke2fs). "),
8505 PROMPT_FIX, PR_PREEN_OK },
8506
8507 /* Reserved inode has bad mode */
8508 { PR_1_RESERVED_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00008509 N_("Reserved @i %i (%Q) has @n mode. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008510 PROMPT_CLEAR, PR_PREEN_OK },
8511
8512 /* Deleted inode has zero dtime */
8513 { PR_1_ZERO_DTIME,
8514 N_("@D @i %i has zero dtime. "),
8515 PROMPT_FIX, PR_PREEN_OK },
8516
8517 /* Inode in use, but dtime set */
8518 { PR_1_SET_DTIME,
8519 N_("@i %i is in use, but has dtime set. "),
8520 PROMPT_FIX, PR_PREEN_OK },
8521
8522 /* Zero-length directory */
8523 { PR_1_ZERO_LENGTH_DIR,
8524 N_("@i %i is a @z @d. "),
8525 PROMPT_CLEAR, PR_PREEN_OK },
8526
8527 /* Block bitmap conflicts with some other fs block */
8528 { PR_1_BB_CONFLICT,
8529 N_("@g %g's @b @B at %b @C.\n"),
8530 PROMPT_RELOCATE, 0 },
8531
8532 /* Inode bitmap conflicts with some other fs block */
8533 { PR_1_IB_CONFLICT,
8534 N_("@g %g's @i @B at %b @C.\n"),
8535 PROMPT_RELOCATE, 0 },
8536
8537 /* Inode table conflicts with some other fs block */
8538 { PR_1_ITABLE_CONFLICT,
8539 N_("@g %g's @i table at %b @C.\n"),
8540 PROMPT_RELOCATE, 0 },
8541
8542 /* Block bitmap is on a bad block */
8543 { PR_1_BB_BAD_BLOCK,
8544 N_("@g %g's @b @B (%b) is bad. "),
8545 PROMPT_RELOCATE, 0 },
8546
8547 /* Inode bitmap is on a bad block */
8548 { PR_1_IB_BAD_BLOCK,
8549 N_("@g %g's @i @B (%b) is bad. "),
8550 PROMPT_RELOCATE, 0 },
8551
8552 /* Inode has incorrect i_size */
8553 { PR_1_BAD_I_SIZE,
8554 N_("@i %i, i_size is %Is, @s %N. "),
8555 PROMPT_FIX, PR_PREEN_OK },
8556
8557 /* Inode has incorrect i_blocks */
8558 { PR_1_BAD_I_BLOCKS,
8559 N_("@i %i, i_@bs is %Ib, @s %N. "),
8560 PROMPT_FIX, PR_PREEN_OK },
8561
8562 /* Illegal blocknumber in inode */
8563 { PR_1_ILLEGAL_BLOCK_NUM,
8564 N_("@I @b #%B (%b) in @i %i. "),
8565 PROMPT_CLEAR, PR_LATCH_BLOCK },
8566
8567 /* Block number overlaps fs metadata */
8568 { PR_1_BLOCK_OVERLAPS_METADATA,
8569 N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
8570 PROMPT_CLEAR, PR_LATCH_BLOCK },
8571
8572 /* Inode has illegal blocks (latch question) */
8573 { PR_1_INODE_BLOCK_LATCH,
8574 N_("@i %i has illegal @b(s). "),
8575 PROMPT_CLEAR, 0 },
8576
8577 /* Too many bad blocks in inode */
8578 { PR_1_TOO_MANY_BAD_BLOCKS,
8579 N_("Too many illegal @bs in @i %i.\n"),
8580 PROMPT_CLEAR_INODE, PR_NO_OK },
8581
8582 /* Illegal block number in bad block inode */
8583 { PR_1_BB_ILLEGAL_BLOCK_NUM,
8584 N_("@I @b #%B (%b) in bad @b @i. "),
8585 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8586
8587 /* Bad block inode has illegal blocks (latch question) */
8588 { PR_1_INODE_BBLOCK_LATCH,
8589 N_("Bad @b @i has illegal @b(s). "),
8590 PROMPT_CLEAR, 0 },
8591
8592 /* Duplicate or bad blocks in use! */
8593 { PR_1_DUP_BLOCKS_PREENSTOP,
8594 N_("Duplicate or bad @b in use!\n"),
8595 PROMPT_NONE, 0 },
8596
8597 /* Bad block used as bad block indirect block */
8598 { PR_1_BBINODE_BAD_METABLOCK,
8599 N_("Bad @b %b used as bad @b @i indirect @b. "),
8600 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8601
8602 /* Inconsistency can't be fixed prompt */
8603 { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
8604 N_("\nThe bad @b @i has probably been corrupted. You probably\n"
8605 "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
8606 "in the @f.\n"),
8607 PROMPT_CONTINUE, PR_PREEN_NOMSG },
8608
8609 /* Bad primary block */
8610 { PR_1_BAD_PRIMARY_BLOCK,
8611 N_("\nIf the @b is really bad, the @f can not be fixed.\n"),
8612 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
8613
8614 /* Bad primary block prompt */
8615 { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
Mike Frysinger874af852006-03-08 07:03:27 +00008616 N_("You can remove this @b from the bad @b list and hope\n"
8617 "that the @b is really OK. But there are no guarantees.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008618 PROMPT_CLEAR, PR_PREEN_NOMSG },
8619
8620 /* Bad primary superblock */
8621 { PR_1_BAD_PRIMARY_SUPERBLOCK,
8622 N_("The primary @S (%b) is on the bad @b list.\n"),
8623 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8624
8625 /* Bad primary block group descriptors */
8626 { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
8627 N_("Block %b in the primary @g descriptors "
8628 "is on the bad @b list\n"),
8629 PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
8630
8631 /* Bad superblock in group */
8632 { PR_1_BAD_SUPERBLOCK,
8633 N_("Warning: Group %g's @S (%b) is bad.\n"),
8634 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8635
8636 /* Bad block group descriptors in group */
8637 { PR_1_BAD_GROUP_DESCRIPTORS,
8638 N_("Warning: Group %g's copy of the @g descriptors has a bad "
8639 "@b (%b).\n"),
8640 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
8641
8642 /* Block claimed for no reason */
8643 { PR_1_PROGERR_CLAIMED_BLOCK,
8644 N_("Programming error? @b #%b claimed for no reason in "
8645 "process_bad_@b.\n"),
8646 PROMPT_NONE, PR_PREEN_OK },
8647
8648 /* Error allocating blocks for relocating metadata */
8649 { PR_1_RELOC_BLOCK_ALLOCATE,
8650 N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
8651 PROMPT_NONE, PR_PREEN_OK },
8652
8653 /* Error allocating block buffer during relocation process */
8654 { PR_1_RELOC_MEMORY_ALLOCATE,
8655 N_("@A @b buffer for relocating %s\n"),
8656 PROMPT_NONE, PR_PREEN_OK },
8657
8658 /* Relocating metadata group information from X to Y */
8659 { PR_1_RELOC_FROM_TO,
8660 N_("Relocating @g %g's %s from %b to %c...\n"),
8661 PROMPT_NONE, PR_PREEN_OK },
8662
8663 /* Relocating metatdata group information to X */
8664 { PR_1_RELOC_TO,
8665 N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
8666 PROMPT_NONE, PR_PREEN_OK },
8667
8668 /* Block read error during relocation process */
8669 { PR_1_RELOC_READ_ERR,
8670 N_("Warning: could not read @b %b of %s: %m\n"),
8671 PROMPT_NONE, PR_PREEN_OK },
8672
8673 /* Block write error during relocation process */
8674 { PR_1_RELOC_WRITE_ERR,
8675 N_("Warning: could not write @b %b for %s: %m\n"),
8676 PROMPT_NONE, PR_PREEN_OK },
8677
8678 /* Error allocating inode bitmap */
8679 { PR_1_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008680 N_("@A @i @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008681 PROMPT_NONE, PR_FATAL },
8682
8683 /* Error allocating block bitmap */
8684 { PR_1_ALLOCATE_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008685 N_("@A @b @B (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008686 PROMPT_NONE, PR_FATAL },
8687
8688 /* Error allocating icount structure */
8689 { PR_1_ALLOCATE_ICOUNT,
8690 N_("@A icount link information: %m\n"),
8691 PROMPT_NONE, PR_FATAL },
8692
8693 /* Error allocating dbcount */
8694 { PR_1_ALLOCATE_DBCOUNT,
8695 N_("@A @d @b array: %m\n"),
8696 PROMPT_NONE, PR_FATAL },
8697
8698 /* Error while scanning inodes */
8699 { PR_1_ISCAN_ERROR,
8700 N_("Error while scanning @is (%i): %m\n"),
8701 PROMPT_NONE, PR_FATAL },
8702
8703 /* Error while iterating over blocks */
8704 { PR_1_BLOCK_ITERATE,
8705 N_("Error while iterating over @bs in @i %i: %m\n"),
8706 PROMPT_NONE, PR_FATAL },
8707
8708 /* Error while storing inode count information */
8709 { PR_1_ICOUNT_STORE,
8710 N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
8711 PROMPT_NONE, PR_FATAL },
8712
8713 /* Error while storing directory block information */
8714 { PR_1_ADD_DBLOCK,
8715 N_("Error storing @d @b information "
8716 "(@i=%i, @b=%b, num=%N): %m\n"),
8717 PROMPT_NONE, PR_FATAL },
8718
8719 /* Error while reading inode (for clearing) */
8720 { PR_1_READ_INODE,
8721 N_("Error reading @i %i: %m\n"),
8722 PROMPT_NONE, PR_FATAL },
8723
8724 /* Suppress messages prompt */
8725 { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
8726
8727 /* Imagic flag set on an inode when filesystem doesn't support it */
8728 { PR_1_SET_IMAGIC,
8729 N_("@i %i has imagic flag set. "),
8730 PROMPT_CLEAR, 0 },
8731
8732 /* Immutable flag set on a device or socket inode */
8733 { PR_1_SET_IMMUTABLE,
8734 N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
8735 "or append-only flag set. "),
8736 PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
8737
8738 /* Compression flag set on an inode when filesystem doesn't support it */
8739 { PR_1_COMPR_SET,
8740 N_("@i %i has @cion flag set on @f without @cion support. "),
8741 PROMPT_CLEAR, 0 },
8742
8743 /* Non-zero size for device, fifo or socket inode */
8744 { PR_1_SET_NONZSIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008745 N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008746 PROMPT_FIX, PR_PREEN_OK },
8747
8748 /* Filesystem revision is 0, but feature flags are set */
8749 { PR_1_FS_REV_LEVEL,
Mike Frysinger874af852006-03-08 07:03:27 +00008750 N_("@f has feature flag(s) set, but is a revision 0 @f. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008751 PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
8752
8753 /* Journal inode is not in use, but contains data */
8754 { PR_1_JOURNAL_INODE_NOT_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00008755 N_("@j @i is not in use, but contains data. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008756 PROMPT_CLEAR, PR_PREEN_OK },
8757
8758 /* Journal has bad mode */
8759 { PR_1_JOURNAL_BAD_MODE,
8760 N_("@j is not regular file. "),
8761 PROMPT_FIX, PR_PREEN_OK },
8762
8763 /* Deal with inodes that were part of orphan linked list */
8764 { PR_1_LOW_DTIME,
Mike Frysinger874af852006-03-08 07:03:27 +00008765 N_("@i %i was part of the @o @i list. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008766 PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
8767
8768 /* Deal with inodes that were part of corrupted orphan linked
8769 list (latch question) */
8770 { PR_1_ORPHAN_LIST_REFUGEES,
8771 N_("@is that were part of a corrupted orphan linked list found. "),
8772 PROMPT_FIX, 0 },
8773
8774 /* Error allocating refcount structure */
8775 { PR_1_ALLOCATE_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008776 N_("@A refcount structure (%N): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008777 PROMPT_NONE, PR_FATAL },
8778
8779 /* Error reading extended attribute block */
8780 { PR_1_READ_EA_BLOCK,
8781 N_("Error reading @a @b %b for @i %i. "),
8782 PROMPT_CLEAR, 0 },
8783
8784 /* Invalid extended attribute block */
8785 { PR_1_BAD_EA_BLOCK,
8786 N_("@i %i has a bad @a @b %b. "),
8787 PROMPT_CLEAR, 0 },
8788
8789 /* Error reading Extended Attribute block while fixing refcount */
8790 { PR_1_EXTATTR_READ_ABORT,
8791 N_("Error reading @a @b %b (%m). "),
8792 PROMPT_ABORT, 0 },
8793
8794 /* Extended attribute reference count incorrect */
8795 { PR_1_EXTATTR_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008796 N_("@a @b %b has reference count %B, @s %N. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008797 PROMPT_FIX, 0 },
8798
8799 /* Error writing Extended Attribute block while fixing refcount */
8800 { PR_1_EXTATTR_WRITE,
8801 N_("Error writing @a @b %b (%m). "),
8802 PROMPT_ABORT, 0 },
8803
8804 /* Multiple EA blocks not supported */
8805 { PR_1_EA_MULTI_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008806 N_("@a @b %b has h_@bs > 1. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008807 PROMPT_CLEAR, 0},
8808
8809 /* Error allocating EA region allocation structure */
8810 { PR_1_EA_ALLOC_REGION,
Mike Frysinger874af852006-03-08 07:03:27 +00008811 N_("@A @a @b %b. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008812 PROMPT_ABORT, 0},
8813
8814 /* Error EA allocation collision */
8815 { PR_1_EA_ALLOC_COLLISION,
8816 N_("@a @b %b is corrupt (allocation collision). "),
8817 PROMPT_CLEAR, 0},
8818
8819 /* Bad extended attribute name */
8820 { PR_1_EA_BAD_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00008821 N_("@a @b %b is corrupt (@n name). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008822 PROMPT_CLEAR, 0},
8823
8824 /* Bad extended attribute value */
8825 { PR_1_EA_BAD_VALUE,
Mike Frysinger874af852006-03-08 07:03:27 +00008826 N_("@a @b %b is corrupt (@n value). "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008827 PROMPT_CLEAR, 0},
8828
8829 /* Inode too big (latch question) */
8830 { PR_1_INODE_TOOBIG,
8831 N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
8832
8833 /* Directory too big */
8834 { PR_1_TOOBIG_DIR,
8835 N_("@b #%B (%b) causes @d to be too big. "),
8836 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8837
8838 /* Regular file too big */
8839 { PR_1_TOOBIG_REG,
8840 N_("@b #%B (%b) causes file to be too big. "),
8841 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8842
8843 /* Symlink too big */
8844 { PR_1_TOOBIG_SYMLINK,
8845 N_("@b #%B (%b) causes symlink to be too big. "),
8846 PROMPT_CLEAR, PR_LATCH_TOOBIG },
8847
8848 /* INDEX_FL flag set on a non-HTREE filesystem */
8849 { PR_1_HTREE_SET,
8850 N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
8851 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8852
8853 /* INDEX_FL flag set on a non-directory */
8854 { PR_1_HTREE_NODIR,
8855 N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
8856 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8857
8858 /* Invalid root node in HTREE directory */
8859 { PR_1_HTREE_BADROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00008860 N_("@h %i has an @n root node.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008861 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8862
8863 /* Unsupported hash version in HTREE directory */
8864 { PR_1_HTREE_HASHV,
8865 N_("@h %i has an unsupported hash version (%N)\n"),
8866 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8867
8868 /* Incompatible flag in HTREE root node */
8869 { PR_1_HTREE_INCOMPAT,
8870 N_("@h %i uses an incompatible htree root node flag.\n"),
8871 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8872
8873 /* HTREE too deep */
8874 { PR_1_HTREE_DEPTH,
8875 N_("@h %i has a tree depth (%N) which is too big\n"),
8876 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
8877
8878 /* Bad block has indirect block that conflicts with filesystem block */
8879 { PR_1_BB_FS_BLOCK,
8880 N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
8881 "@f metadata. "),
8882 PROMPT_CLEAR, PR_LATCH_BBLOCK },
8883
8884 /* Resize inode failed */
8885 { PR_1_RESIZE_INODE_CREATE,
8886 N_("Resize @i (re)creation failed: %m."),
8887 PROMPT_ABORT, 0 },
8888
8889 /* invalid inode->i_extra_isize */
8890 { PR_1_EXTRA_ISIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008891 N_("@i %i has a extra size (%IS) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008892 PROMPT_FIX, PR_PREEN_OK },
8893
8894 /* invalid ea entry->e_name_len */
8895 { PR_1_ATTR_NAME_LEN,
Mike Frysinger874af852006-03-08 07:03:27 +00008896 N_("@a in @i %i has a namelen (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008897 PROMPT_CLEAR, PR_PREEN_OK },
8898
8899 /* invalid ea entry->e_value_size */
8900 { PR_1_ATTR_VALUE_SIZE,
Mike Frysinger874af852006-03-08 07:03:27 +00008901 N_("@a in @i %i has a value size (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008902 PROMPT_CLEAR, PR_PREEN_OK },
8903
8904 /* invalid ea entry->e_value_offs */
8905 { PR_1_ATTR_VALUE_OFFSET,
Mike Frysinger874af852006-03-08 07:03:27 +00008906 N_("@a in @i %i has a value offset (%N) which is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008907 PROMPT_CLEAR, PR_PREEN_OK },
8908
8909 /* invalid ea entry->e_value_block */
8910 { PR_1_ATTR_VALUE_BLOCK,
Mike Frysinger874af852006-03-08 07:03:27 +00008911 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 +00008912 PROMPT_CLEAR, PR_PREEN_OK },
8913
8914 /* invalid ea entry->e_hash */
8915 { PR_1_ATTR_HASH,
Mike Frysinger874af852006-03-08 07:03:27 +00008916 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 +00008917 PROMPT_CLEAR, PR_PREEN_OK },
8918
8919 /* Pass 1b errors */
8920
8921 /* Pass 1B: Rescan for duplicate/bad blocks */
8922 { PR_1B_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008923 N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
8924 "Pass 1B: Rescanning for @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008925 PROMPT_NONE, 0 },
8926
8927 /* Duplicate/bad block(s) header */
8928 { PR_1B_DUP_BLOCK_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008929 N_("@m @b(s) in @i %i:"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008930 PROMPT_NONE, 0 },
8931
8932 /* Duplicate/bad block(s) in inode */
8933 { PR_1B_DUP_BLOCK,
8934 " %b",
8935 PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
8936
8937 /* Duplicate/bad block(s) end */
8938 { PR_1B_DUP_BLOCK_END,
8939 "\n",
8940 PROMPT_NONE, PR_PREEN_NOHDR },
8941
8942 /* Error while scanning inodes */
8943 { PR_1B_ISCAN_ERROR,
8944 N_("Error while scanning inodes (%i): %m\n"),
8945 PROMPT_NONE, PR_FATAL },
8946
8947 /* Error allocating inode bitmap */
8948 { PR_1B_ALLOCATE_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00008949 N_("@A @i @B (@i_dup_map): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008950 PROMPT_NONE, PR_FATAL },
8951
8952 /* Error while iterating over blocks */
8953 { PR_1B_BLOCK_ITERATE,
8954 N_("Error while iterating over @bs in @i %i (%s): %m\n"),
8955 PROMPT_NONE, 0 },
8956
8957 /* Error adjusting EA refcount */
8958 { PR_1B_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00008959 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008960 PROMPT_NONE, 0 },
8961
8962
Mike Frysinger874af852006-03-08 07:03:27 +00008963 /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008964 { PR_1C_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008965 N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008966 PROMPT_NONE, 0 },
8967
8968
Mike Frysinger874af852006-03-08 07:03:27 +00008969 /* Pass 1D: Reconciling multiply-claimed blocks */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008970 { PR_1D_PASS_HEADER,
Mike Frysinger874af852006-03-08 07:03:27 +00008971 N_("Pass 1D: Reconciling @m @bs\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008972 PROMPT_NONE, 0 },
8973
8974 /* File has duplicate blocks */
8975 { PR_1D_DUP_FILE,
8976 N_("File %Q (@i #%i, mod time %IM) \n"
Mike Frysinger874af852006-03-08 07:03:27 +00008977 " has %B @m @b(s), shared with %N file(s):\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008978 PROMPT_NONE, 0 },
8979
8980 /* List of files sharing duplicate blocks */
8981 { PR_1D_DUP_FILE_LIST,
8982 N_("\t%Q (@i #%i, mod time %IM)\n"),
8983 PROMPT_NONE, 0 },
8984
8985 /* File sharing blocks with filesystem metadata */
8986 { PR_1D_SHARE_METADATA,
8987 N_("\t<@f metadata>\n"),
8988 PROMPT_NONE, 0 },
8989
8990 /* Report of how many duplicate/bad inodes */
8991 { PR_1D_NUM_DUP_INODES,
Mike Frysinger874af852006-03-08 07:03:27 +00008992 N_("(There are %N @is containing @m @bs.)\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008993 PROMPT_NONE, 0 },
8994
8995 /* Duplicated blocks already reassigned or cloned. */
8996 { PR_1D_DUP_BLOCKS_DEALT,
Mike Frysinger874af852006-03-08 07:03:27 +00008997 N_("@m @bs already reassigned or cloned.\n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00008998 PROMPT_NONE, 0 },
8999
9000 /* Clone duplicate/bad blocks? */
9001 { PR_1D_CLONE_QUESTION,
9002 "", PROMPT_CLONE, PR_NO_OK },
9003
9004 /* Delete file? */
9005 { PR_1D_DELETE_QUESTION,
9006 "", PROMPT_DELETE, 0 },
9007
9008 /* Couldn't clone file (error) */
9009 { PR_1D_CLONE_ERROR,
9010 N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
9011
9012 /* Pass 2 errors */
9013
9014 /* Pass 2: Checking directory structure */
9015 { PR_2_PASS_HEADER,
9016 N_("Pass 2: Checking @d structure\n"),
9017 PROMPT_NONE, 0 },
9018
9019 /* Bad inode number for '.' */
9020 { PR_2_BAD_INODE_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009021 N_("@n @i number for '.' in @d @i %i.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009022 PROMPT_FIX, 0 },
9023
9024 /* Directory entry has bad inode number */
9025 { PR_2_BAD_INO,
Mike Frysinger874af852006-03-08 07:03:27 +00009026 N_("@E has @n @i #: %Di.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009027 PROMPT_CLEAR, 0 },
9028
9029 /* Directory entry has deleted or unused inode */
9030 { PR_2_UNUSED_INODE,
9031 N_("@E has @D/unused @i %Di. "),
9032 PROMPT_CLEAR, PR_PREEN_OK },
9033
9034 /* Directry entry is link to '.' */
9035 { PR_2_LINK_DOT,
9036 N_("@E @L to '.' "),
9037 PROMPT_CLEAR, 0 },
9038
9039 /* Directory entry points to inode now located in a bad block */
9040 { PR_2_BB_INODE,
9041 N_("@E points to @i (%Di) located in a bad @b.\n"),
9042 PROMPT_CLEAR, 0 },
9043
9044 /* Directory entry contains a link to a directory */
9045 { PR_2_LINK_DIR,
9046 N_("@E @L to @d %P (%Di).\n"),
9047 PROMPT_CLEAR, 0 },
9048
9049 /* Directory entry contains a link to the root directry */
9050 { PR_2_LINK_ROOT,
9051 N_("@E @L to the @r.\n"),
9052 PROMPT_CLEAR, 0 },
9053
9054 /* Directory entry has illegal characters in its name */
9055 { PR_2_BAD_NAME,
9056 N_("@E has illegal characters in its name.\n"),
9057 PROMPT_FIX, 0 },
9058
9059 /* Missing '.' in directory inode */
9060 { PR_2_MISSING_DOT,
9061 N_("Missing '.' in @d @i %i.\n"),
9062 PROMPT_FIX, 0 },
9063
9064 /* Missing '..' in directory inode */
9065 { PR_2_MISSING_DOT_DOT,
9066 N_("Missing '..' in @d @i %i.\n"),
9067 PROMPT_FIX, 0 },
9068
9069 /* First entry in directory inode doesn't contain '.' */
9070 { PR_2_1ST_NOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009071 N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009072 PROMPT_FIX, 0 },
9073
9074 /* Second entry in directory inode doesn't contain '..' */
9075 { PR_2_2ND_NOT_DOT_DOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009076 N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009077 PROMPT_FIX, 0 },
9078
9079 /* i_faddr should be zero */
9080 { PR_2_FADDR_ZERO,
9081 N_("i_faddr @F %IF, @s zero.\n"),
9082 PROMPT_CLEAR, 0 },
9083
9084 /* i_file_acl should be zero */
9085 { PR_2_FILE_ACL_ZERO,
9086 N_("i_file_acl @F %If, @s zero.\n"),
9087 PROMPT_CLEAR, 0 },
9088
9089 /* i_dir_acl should be zero */
9090 { PR_2_DIR_ACL_ZERO,
9091 N_("i_dir_acl @F %Id, @s zero.\n"),
9092 PROMPT_CLEAR, 0 },
9093
9094 /* i_frag should be zero */
9095 { PR_2_FRAG_ZERO,
9096 N_("i_frag @F %N, @s zero.\n"),
9097 PROMPT_CLEAR, 0 },
9098
9099 /* i_fsize should be zero */
9100 { PR_2_FSIZE_ZERO,
9101 N_("i_fsize @F %N, @s zero.\n"),
9102 PROMPT_CLEAR, 0 },
9103
9104 /* inode has bad mode */
9105 { PR_2_BAD_MODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009106 N_("@i %i (%Q) has @n mode (%Im).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009107 PROMPT_CLEAR, 0 },
9108
9109 /* directory corrupted */
9110 { PR_2_DIR_CORRUPTED,
9111 N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
9112 PROMPT_SALVAGE, 0 },
9113
9114 /* filename too long */
9115 { PR_2_FILENAME_LONG,
9116 N_("@d @i %i, @b %B, offset %N: filename too long\n"),
9117 PROMPT_TRUNCATE, 0 },
9118
9119 /* Directory inode has a missing block (hole) */
9120 { PR_2_DIRECTORY_HOLE,
9121 N_("@d @i %i has an unallocated @b #%B. "),
9122 PROMPT_ALLOCATE, 0 },
9123
9124 /* '.' is not NULL terminated */
9125 { PR_2_DOT_NULL_TERM,
9126 N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
9127 PROMPT_FIX, 0 },
9128
9129 /* '..' is not NULL terminated */
9130 { PR_2_DOT_DOT_NULL_TERM,
9131 N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
9132 PROMPT_FIX, 0 },
9133
9134 /* Illegal character device inode */
9135 { PR_2_BAD_CHAR_DEV,
9136 N_("@i %i (%Q) is an @I character @v.\n"),
9137 PROMPT_CLEAR, 0 },
9138
9139 /* Illegal block device inode */
9140 { PR_2_BAD_BLOCK_DEV,
9141 N_("@i %i (%Q) is an @I @b @v.\n"),
9142 PROMPT_CLEAR, 0 },
9143
9144 /* Duplicate '.' entry */
9145 { PR_2_DUP_DOT,
9146 N_("@E is duplicate '.' @e.\n"),
9147 PROMPT_FIX, 0 },
9148
9149 /* Duplicate '..' entry */
9150 { PR_2_DUP_DOT_DOT,
9151 N_("@E is duplicate '..' @e.\n"),
9152 PROMPT_FIX, 0 },
9153
9154 /* Internal error: couldn't find dir_info */
9155 { PR_2_NO_DIRINFO,
9156 N_("Internal error: couldn't find dir_info for %i.\n"),
9157 PROMPT_NONE, PR_FATAL },
9158
9159 /* Final rec_len is wrong */
9160 { PR_2_FINAL_RECLEN,
Mike Frysinger874af852006-03-08 07:03:27 +00009161 N_("@E has rec_len of %Dr, @s %N.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009162 PROMPT_FIX, 0 },
9163
9164 /* Error allocating icount structure */
9165 { PR_2_ALLOCATE_ICOUNT,
9166 N_("@A icount structure: %m\n"),
9167 PROMPT_NONE, PR_FATAL },
9168
9169 /* Error iterating over directory blocks */
9170 { PR_2_DBLIST_ITERATE,
9171 N_("Error iterating over @d @bs: %m\n"),
9172 PROMPT_NONE, PR_FATAL },
9173
9174 /* Error reading directory block */
9175 { PR_2_READ_DIRBLOCK,
9176 N_("Error reading @d @b %b (@i %i): %m\n"),
9177 PROMPT_CONTINUE, 0 },
9178
9179 /* Error writing directory block */
9180 { PR_2_WRITE_DIRBLOCK,
9181 N_("Error writing @d @b %b (@i %i): %m\n"),
9182 PROMPT_CONTINUE, 0 },
9183
9184 /* Error allocating new directory block */
9185 { PR_2_ALLOC_DIRBOCK,
9186 N_("@A new @d @b for @i %i (%s): %m\n"),
9187 PROMPT_NONE, 0 },
9188
9189 /* Error deallocating inode */
9190 { PR_2_DEALLOC_INODE,
9191 N_("Error deallocating @i %i: %m\n"),
9192 PROMPT_NONE, PR_FATAL },
9193
9194 /* Directory entry for '.' is big. Split? */
9195 { PR_2_SPLIT_DOT,
9196 N_("@d @e for '.' is big. "),
9197 PROMPT_SPLIT, PR_NO_OK },
9198
9199 /* Illegal FIFO inode */
9200 { PR_2_BAD_FIFO,
9201 N_("@i %i (%Q) is an @I FIFO.\n"),
9202 PROMPT_CLEAR, 0 },
9203
9204 /* Illegal socket inode */
9205 { PR_2_BAD_SOCKET,
9206 N_("@i %i (%Q) is an @I socket.\n"),
9207 PROMPT_CLEAR, 0 },
9208
9209 /* Directory filetype not set */
9210 { PR_2_SET_FILETYPE,
9211 N_("Setting filetype for @E to %N.\n"),
9212 PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
9213
9214 /* Directory filetype incorrect */
9215 { PR_2_BAD_FILETYPE,
Mike Frysinger874af852006-03-08 07:03:27 +00009216 N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009217 PROMPT_FIX, 0 },
9218
9219 /* Directory filetype set on filesystem */
9220 { PR_2_CLEAR_FILETYPE,
9221 N_("@E has filetype set.\n"),
9222 PROMPT_CLEAR, PR_PREEN_OK },
9223
9224 /* Directory filename is null */
9225 { PR_2_NULL_NAME,
Mike Frysinger874af852006-03-08 07:03:27 +00009226 N_("@E has a @z name.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009227 PROMPT_CLEAR, 0 },
9228
9229 /* Invalid symlink */
9230 { PR_2_INVALID_SYMLINK,
Mike Frysinger874af852006-03-08 07:03:27 +00009231 N_("Symlink %Q (@i #%i) is @n.\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009232 PROMPT_CLEAR, 0 },
9233
9234 /* i_file_acl (extended attribute block) is bad */
9235 { PR_2_FILE_ACL_BAD,
Mike Frysinger874af852006-03-08 07:03:27 +00009236 N_("@a @b @F @n (%If).\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009237 PROMPT_CLEAR, 0 },
9238
9239 /* Filesystem contains large files, but has no such flag in sb */
9240 { PR_2_FEATURE_LARGE_FILES,
9241 N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
9242 PROMPT_FIX, 0 },
9243
9244 /* Node in HTREE directory not referenced */
9245 { PR_2_HTREE_NOTREF,
9246 N_("@p @h %d: node (%B) not referenced\n"),
9247 PROMPT_NONE, 0 },
9248
9249 /* Node in HTREE directory referenced twice */
9250 { PR_2_HTREE_DUPREF,
9251 N_("@p @h %d: node (%B) referenced twice\n"),
9252 PROMPT_NONE, 0 },
9253
9254 /* Node in HTREE directory has bad min hash */
9255 { PR_2_HTREE_MIN_HASH,
9256 N_("@p @h %d: node (%B) has bad min hash\n"),
9257 PROMPT_NONE, 0 },
9258
9259 /* Node in HTREE directory has bad max hash */
9260 { PR_2_HTREE_MAX_HASH,
9261 N_("@p @h %d: node (%B) has bad max hash\n"),
9262 PROMPT_NONE, 0 },
9263
9264 /* Clear invalid HTREE directory */
9265 { PR_2_HTREE_CLEAR,
Mike Frysinger874af852006-03-08 07:03:27 +00009266 N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009267
9268 /* Bad block in htree interior node */
9269 { PR_2_HTREE_BADBLK,
9270 N_("@p @h %d (%q): bad @b number %b.\n"),
9271 PROMPT_CLEAR_HTREE, 0 },
9272
9273 /* Error adjusting EA refcount */
9274 { PR_2_ADJ_EA_REFCOUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009275 N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009276 PROMPT_NONE, PR_FATAL },
9277
9278 /* Invalid HTREE root node */
9279 { PR_2_HTREE_BAD_ROOT,
Mike Frysinger874af852006-03-08 07:03:27 +00009280 N_("@p @h %d: root node is @n\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009281 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9282
9283 /* Invalid HTREE limit */
9284 { PR_2_HTREE_BAD_LIMIT,
Mike Frysinger874af852006-03-08 07:03:27 +00009285 N_("@p @h %d: node (%B) has @n limit (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009286 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9287
9288 /* Invalid HTREE count */
9289 { PR_2_HTREE_BAD_COUNT,
Mike Frysinger874af852006-03-08 07:03:27 +00009290 N_("@p @h %d: node (%B) has @n count (%N)\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009291 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9292
9293 /* HTREE interior node has out-of-order hashes in table */
9294 { PR_2_HTREE_HASH_ORDER,
9295 N_("@p @h %d: node (%B) has an unordered hash table\n"),
9296 PROMPT_CLEAR_HTREE, PR_PREEN_OK },
9297
Mike Frysinger874af852006-03-08 07:03:27 +00009298 /* Node in HTREE directory has invalid depth */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009299 { PR_2_HTREE_BAD_DEPTH,
Mike Frysinger874af852006-03-08 07:03:27 +00009300 N_("@p @h %d: node (%B) has @n depth\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009301 PROMPT_NONE, 0 },
9302
9303 /* Duplicate directory entry found */
9304 { PR_2_DUPLICATE_DIRENT,
9305 N_("Duplicate @E found. "),
9306 PROMPT_CLEAR, 0 },
9307
9308 /* Non-unique filename found */
9309 { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
9310 N_("@E has a non-unique filename.\nRename to %s"),
9311 PROMPT_NULL, 0 },
9312
9313 /* Duplicate directory entry found */
9314 { PR_2_REPORT_DUP_DIRENT,
9315 N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
9316 PROMPT_NONE, 0 },
9317
9318 /* Pass 3 errors */
9319
9320 /* Pass 3: Checking directory connectivity */
9321 { PR_3_PASS_HEADER,
9322 N_("Pass 3: Checking @d connectivity\n"),
9323 PROMPT_NONE, 0 },
9324
9325 /* Root inode not allocated */
9326 { PR_3_NO_ROOT_INODE,
9327 N_("@r not allocated. "),
9328 PROMPT_ALLOCATE, 0 },
9329
9330 /* No room in lost+found */
9331 { PR_3_EXPAND_LF_DIR,
9332 N_("No room in @l @d. "),
9333 PROMPT_EXPAND, 0 },
9334
9335 /* Unconnected directory inode */
9336 { PR_3_UNCONNECTED_DIR,
9337 N_("Unconnected @d @i %i (%p)\n"),
9338 PROMPT_CONNECT, 0 },
9339
9340 /* /lost+found not found */
9341 { PR_3_NO_LF_DIR,
9342 N_("/@l not found. "),
9343 PROMPT_CREATE, PR_PREEN_OK },
9344
9345 /* .. entry is incorrect */
9346 { PR_3_BAD_DOT_DOT,
9347 N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
9348 PROMPT_FIX, 0 },
9349
9350 /* Bad or non-existent /lost+found. Cannot reconnect */
9351 { PR_3_NO_LPF,
9352 N_("Bad or non-existent /@l. Cannot reconnect.\n"),
9353 PROMPT_NONE, 0 },
9354
9355 /* Could not expand /lost+found */
9356 { PR_3_CANT_EXPAND_LPF,
9357 N_("Could not expand /@l: %m\n"),
9358 PROMPT_NONE, 0 },
9359
9360 /* Could not reconnect inode */
9361 { PR_3_CANT_RECONNECT,
9362 N_("Could not reconnect %i: %m\n"),
9363 PROMPT_NONE, 0 },
9364
9365 /* Error while trying to find /lost+found */
9366 { PR_3_ERR_FIND_LPF,
9367 N_("Error while trying to find /@l: %m\n"),
9368 PROMPT_NONE, 0 },
9369
9370 /* Error in ext2fs_new_block while creating /lost+found */
9371 { PR_3_ERR_LPF_NEW_BLOCK,
9372 N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
9373 PROMPT_NONE, 0 },
9374
9375 /* Error in ext2fs_new_inode while creating /lost+found */
9376 { PR_3_ERR_LPF_NEW_INODE,
9377 N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
9378 PROMPT_NONE, 0 },
9379
9380 /* Error in ext2fs_new_dir_block while creating /lost+found */
9381 { PR_3_ERR_LPF_NEW_DIR_BLOCK,
9382 N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
9383 PROMPT_NONE, 0 },
9384
9385 /* Error while writing directory block for /lost+found */
9386 { PR_3_ERR_LPF_WRITE_BLOCK,
9387 N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
9388 PROMPT_NONE, 0 },
9389
9390 /* Error while adjusting inode count */
9391 { PR_3_ADJUST_INODE,
9392 N_("Error while adjusting @i count on @i %i\n"),
9393 PROMPT_NONE, 0 },
9394
9395 /* Couldn't fix parent directory -- error */
9396 { PR_3_FIX_PARENT_ERR,
9397 N_("Couldn't fix parent of @i %i: %m\n\n"),
9398 PROMPT_NONE, 0 },
9399
9400 /* Couldn't fix parent directory -- couldn't find it */
9401 { PR_3_FIX_PARENT_NOFIND,
Mike Frysinger874af852006-03-08 07:03:27 +00009402 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 +00009403 PROMPT_NONE, 0 },
9404
9405 /* Error allocating inode bitmap */
9406 { PR_3_ALLOCATE_IBITMAP_ERROR,
9407 N_("@A @i @B (%N): %m\n"),
9408 PROMPT_NONE, PR_FATAL },
9409
9410 /* Error creating root directory */
9411 { PR_3_CREATE_ROOT_ERROR,
9412 N_("Error creating root @d (%s): %m\n"),
9413 PROMPT_NONE, PR_FATAL },
9414
9415 /* Error creating lost and found directory */
9416 { PR_3_CREATE_LPF_ERROR,
9417 N_("Error creating /@l @d (%s): %m\n"),
9418 PROMPT_NONE, PR_FATAL },
9419
9420 /* Root inode is not directory; aborting */
9421 { PR_3_ROOT_NOT_DIR_ABORT,
9422 N_("@r is not a @d; aborting.\n"),
9423 PROMPT_NONE, PR_FATAL },
9424
9425 /* Cannot proceed without a root inode. */
9426 { PR_3_NO_ROOT_INODE_ABORT,
9427 N_("Cannot proceed without a @r.\n"),
9428 PROMPT_NONE, PR_FATAL },
9429
9430 /* Internal error: couldn't find dir_info */
9431 { PR_3_NO_DIRINFO,
9432 N_("Internal error: couldn't find dir_info for %i.\n"),
9433 PROMPT_NONE, PR_FATAL },
9434
9435 /* Lost+found not a directory */
9436 { PR_3_LPF_NOTDIR,
9437 N_("/@l is not a @d (ino=%i)\n"),
9438 PROMPT_UNLINK, 0 },
9439
9440 /* Pass 3A Directory Optimization */
9441
9442 /* Pass 3A: Optimizing directories */
9443 { PR_3A_PASS_HEADER,
9444 N_("Pass 3A: Optimizing directories\n"),
9445 PROMPT_NONE, PR_PREEN_NOMSG },
9446
9447 /* Error iterating over directories */
9448 { PR_3A_OPTIMIZE_ITER,
9449 N_("Failed to create dirs_to_hash iterator: %m"),
9450 PROMPT_NONE, 0 },
9451
9452 /* Error rehash directory */
9453 { PR_3A_OPTIMIZE_DIR_ERR,
9454 N_("Failed to optimize directory %q (%d): %m"),
9455 PROMPT_NONE, 0 },
9456
9457 /* Rehashing dir header */
9458 { PR_3A_OPTIMIZE_DIR_HEADER,
9459 N_("Optimizing directories: "),
9460 PROMPT_NONE, PR_MSG_ONLY },
9461
9462 /* Rehashing directory %d */
9463 { PR_3A_OPTIMIZE_DIR,
9464 " %d",
9465 PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
9466
9467 /* Rehashing dir end */
9468 { PR_3A_OPTIMIZE_DIR_END,
9469 "\n",
9470 PROMPT_NONE, PR_PREEN_NOHDR },
9471
9472 /* Pass 4 errors */
9473
9474 /* Pass 4: Checking reference counts */
9475 { PR_4_PASS_HEADER,
9476 N_("Pass 4: Checking reference counts\n"),
9477 PROMPT_NONE, 0 },
9478
9479 /* Unattached zero-length inode */
9480 { PR_4_ZERO_LEN_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009481 N_("@u @z @i %i. "),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009482 PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
9483
9484 /* Unattached inode */
9485 { PR_4_UNATTACHED_INODE,
Mike Frysinger874af852006-03-08 07:03:27 +00009486 N_("@u @i %i\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009487 PROMPT_CONNECT, 0 },
9488
9489 /* Inode ref count wrong */
9490 { PR_4_BAD_REF_COUNT,
9491 N_("@i %i ref count is %Il, @s %N. "),
9492 PROMPT_FIX, PR_PREEN_OK },
9493
9494 { PR_4_INCONSISTENT_COUNT,
9495 N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
9496 "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
9497 "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
Mike Frysinger874af852006-03-08 07:03:27 +00009498 "They @s the same!\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009499 PROMPT_NONE, 0 },
9500
9501 /* Pass 5 errors */
9502
9503 /* Pass 5: Checking group summary information */
9504 { PR_5_PASS_HEADER,
9505 N_("Pass 5: Checking @g summary information\n"),
9506 PROMPT_NONE, 0 },
9507
9508 /* Padding at end of inode bitmap is not set. */
9509 { PR_5_INODE_BMAP_PADDING,
9510 N_("Padding at end of @i @B is not set. "),
9511 PROMPT_FIX, PR_PREEN_OK },
9512
9513 /* Padding at end of block bitmap is not set. */
9514 { PR_5_BLOCK_BMAP_PADDING,
9515 N_("Padding at end of @b @B is not set. "),
9516 PROMPT_FIX, PR_PREEN_OK },
9517
9518 /* Block bitmap differences header */
9519 { PR_5_BLOCK_BITMAP_HEADER,
9520 N_("@b @B differences: "),
9521 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
9522
9523 /* Block not used, but marked in bitmap */
9524 { PR_5_BLOCK_UNUSED,
9525 " -%b",
9526 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9527
9528 /* Block used, but not marked used in bitmap */
9529 { PR_5_BLOCK_USED,
9530 " +%b",
9531 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9532
9533 /* Block bitmap differences end */
9534 { PR_5_BLOCK_BITMAP_END,
9535 "\n",
9536 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9537
9538 /* Inode bitmap differences header */
9539 { PR_5_INODE_BITMAP_HEADER,
9540 N_("@i @B differences: "),
9541 PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
9542
9543 /* Inode not used, but marked in bitmap */
9544 { PR_5_INODE_UNUSED,
9545 " -%i",
9546 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9547
9548 /* Inode used, but not marked used in bitmap */
9549 { PR_5_INODE_USED,
9550 " +%i",
9551 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9552
9553 /* Inode bitmap differences end */
9554 { PR_5_INODE_BITMAP_END,
9555 "\n",
9556 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9557
9558 /* Free inodes count for group wrong */
9559 { PR_5_FREE_INODE_COUNT_GROUP,
9560 N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
9561 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9562
9563 /* Directories count for group wrong */
9564 { PR_5_FREE_DIR_COUNT_GROUP,
9565 N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
9566 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9567
9568 /* Free inodes count wrong */
9569 { PR_5_FREE_INODE_COUNT,
9570 N_("Free @is count wrong (%i, counted=%j).\n"),
9571 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9572
9573 /* Free blocks count for group wrong */
9574 { PR_5_FREE_BLOCK_COUNT_GROUP,
9575 N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
9576 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9577
9578 /* Free blocks count wrong */
9579 { PR_5_FREE_BLOCK_COUNT,
9580 N_("Free @bs count wrong (%b, counted=%c).\n"),
9581 PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
9582
9583 /* Programming error: bitmap endpoints don't match */
9584 { PR_5_BMAP_ENDPOINTS,
9585 N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
9586 "match calculated @B endpoints (%i, %j)\n"),
9587 PROMPT_NONE, PR_FATAL },
9588
9589 /* Internal error: fudging end of bitmap */
9590 { PR_5_FUDGE_BITMAP_ERROR,
9591 N_("Internal error: fudging end of bitmap (%N)\n"),
9592 PROMPT_NONE, PR_FATAL },
9593
9594 /* Error copying in replacement inode bitmap */
9595 { PR_5_COPY_IBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009596 N_("Error copying in replacement @i @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009597 PROMPT_NONE, PR_FATAL },
9598
9599 /* Error copying in replacement block bitmap */
9600 { PR_5_COPY_BBITMAP_ERROR,
Mike Frysinger874af852006-03-08 07:03:27 +00009601 N_("Error copying in replacement @b @B: %m\n"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009602 PROMPT_NONE, PR_FATAL },
9603
9604 /* Block range not used, but marked in bitmap */
9605 { PR_5_BLOCK_RANGE_UNUSED,
9606 " -(%b--%c)",
9607 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9608
9609 /* Block range used, but not marked used in bitmap */
9610 { PR_5_BLOCK_RANGE_USED,
9611 " +(%b--%c)",
9612 PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9613
9614 /* Inode range not used, but marked in bitmap */
9615 { PR_5_INODE_RANGE_UNUSED,
9616 " -(%i--%j)",
9617 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9618
9619 /* Inode range used, but not marked used in bitmap */
9620 { PR_5_INODE_RANGE_USED,
9621 " +(%i--%j)",
9622 PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
9623
9624 { 0 }
9625};
9626
9627/*
9628 * This is the latch flags register. It allows several problems to be
9629 * "latched" together. This means that the user has to answer but one
9630 * question for the set of problems, and all of the associated
9631 * problems will be either fixed or not fixed.
9632 */
9633static struct latch_descr pr_latch_info[] = {
9634 { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
9635 { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
9636 { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
9637 { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
9638 { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
9639 { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
9640 { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
9641 { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
9642 { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
9643 { -1, 0, 0 },
9644};
9645
9646static const struct e2fsck_problem *find_problem(problem_t code)
9647{
9648 int i;
9649
9650 for (i=0; problem_table[i].e2p_code; i++) {
9651 if (problem_table[i].e2p_code == code)
9652 return &problem_table[i];
9653 }
9654 return 0;
9655}
9656
9657static struct latch_descr *find_latch(int code)
9658{
9659 int i;
9660
9661 for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
9662 if (pr_latch_info[i].latch_code == code)
9663 return &pr_latch_info[i];
9664 }
9665 return 0;
9666}
9667
9668int end_problem_latch(e2fsck_t ctx, int mask)
9669{
9670 struct latch_descr *ldesc;
9671 struct problem_context pctx;
9672 int answer = -1;
9673
9674 ldesc = find_latch(mask);
9675 if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
9676 clear_problem_context(&pctx);
9677 answer = fix_problem(ctx, ldesc->end_message, &pctx);
9678 }
9679 ldesc->flags &= ~(PRL_VARIABLE);
9680 return answer;
9681}
9682
9683int set_latch_flags(int mask, int setflags, int clearflags)
9684{
9685 struct latch_descr *ldesc;
9686
9687 ldesc = find_latch(mask);
9688 if (!ldesc)
9689 return -1;
9690 ldesc->flags |= setflags;
9691 ldesc->flags &= ~clearflags;
9692 return 0;
9693}
9694
9695void clear_problem_context(struct problem_context *ctx)
9696{
9697 memset(ctx, 0, sizeof(struct problem_context));
9698 ctx->blkcount = -1;
9699 ctx->group = -1;
9700}
9701
9702int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
9703{
9704 ext2_filsys fs = ctx->fs;
9705 const struct e2fsck_problem *ptr;
9706 struct latch_descr *ldesc = 0;
9707 const char *message;
9708 int def_yn, answer, ans;
9709 int print_answer = 0;
9710 int suppress = 0;
9711
9712 ptr = find_problem(code);
9713 if (!ptr) {
9714 printf(_("Unhandled error code (0x%x)!\n"), code);
9715 return 0;
9716 }
9717 def_yn = 1;
9718 if ((ptr->flags & PR_NO_DEFAULT) ||
9719 ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
9720 (ctx->options & E2F_OPT_NO))
9721 def_yn= 0;
9722
9723 /*
9724 * Do special latch processing. This is where we ask the
9725 * latch question, if it exists
9726 */
9727 if (ptr->flags & PR_LATCH_MASK) {
9728 ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
9729 if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
9730 ans = fix_problem(ctx, ldesc->question, pctx);
9731 if (ans == 1)
9732 ldesc->flags |= PRL_YES;
9733 if (ans == 0)
9734 ldesc->flags |= PRL_NO;
9735 ldesc->flags |= PRL_LATCHED;
9736 }
9737 if (ldesc->flags & PRL_SUPPRESS)
9738 suppress++;
9739 }
9740 if ((ptr->flags & PR_PREEN_NOMSG) &&
9741 (ctx->options & E2F_OPT_PREEN))
9742 suppress++;
9743 if ((ptr->flags & PR_NO_NOMSG) &&
9744 (ctx->options & E2F_OPT_NO))
9745 suppress++;
9746 if (!suppress) {
9747 message = ptr->e2p_description;
9748 if ((ctx->options & E2F_OPT_PREEN) &&
9749 !(ptr->flags & PR_PREEN_NOHDR)) {
9750 printf("%s: ", ctx->device_name ?
9751 ctx->device_name : ctx->filesystem_name);
9752 }
9753 if (*message)
9754 print_e2fsck_message(ctx, _(message), pctx, 1);
9755 }
9756 if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
9757 preenhalt(ctx);
9758
9759 if (ptr->flags & PR_FATAL)
Rob Landley7c94bed2006-05-03 21:58:45 +00009760 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009761
9762 if (ptr->prompt == PROMPT_NONE) {
9763 if (ptr->flags & PR_NOCOLLATE)
9764 answer = -1;
9765 else
9766 answer = def_yn;
9767 } else {
9768 if (ctx->options & E2F_OPT_PREEN) {
9769 answer = def_yn;
9770 if (!(ptr->flags & PR_PREEN_NOMSG))
9771 print_answer = 1;
9772 } else if ((ptr->flags & PR_LATCH_MASK) &&
9773 (ldesc->flags & (PRL_YES | PRL_NO))) {
9774 if (!suppress)
9775 print_answer = 1;
9776 if (ldesc->flags & PRL_YES)
9777 answer = 1;
9778 else
9779 answer = 0;
9780 } else
9781 answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
9782 if (!answer && !(ptr->flags & PR_NO_OK))
9783 ext2fs_unmark_valid(fs);
9784
9785 if (print_answer)
9786 printf("%s.\n", answer ?
9787 _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
9788
9789 }
9790
9791 if ((ptr->prompt == PROMPT_ABORT) && answer)
Rob Landley7c94bed2006-05-03 21:58:45 +00009792 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009793
9794 if (ptr->flags & PR_AFTER_CODE)
9795 answer = fix_problem(ctx, ptr->second_code, pctx);
9796
9797 return answer;
9798}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009799
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009800/*
9801 * linux/fs/recovery.c
9802 *
9803 * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009804 */
9805
9806/*
9807 * Maintain information about the progress of the recovery job, so that
9808 * the different passes can carry information between them.
9809 */
9810struct recovery_info
9811{
9812 tid_t start_transaction;
9813 tid_t end_transaction;
9814
9815 int nr_replays;
9816 int nr_revokes;
9817 int nr_revoke_hits;
9818};
9819
9820enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
9821static int do_one_pass(journal_t *journal,
9822 struct recovery_info *info, enum passtype pass);
9823static int scan_revoke_records(journal_t *, struct buffer_head *,
9824 tid_t, struct recovery_info *);
9825
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009826/*
9827 * Read a block from the journal
9828 */
9829
9830static int jread(struct buffer_head **bhp, journal_t *journal,
9831 unsigned int offset)
9832{
9833 int err;
9834 unsigned long blocknr;
9835 struct buffer_head *bh;
9836
9837 *bhp = NULL;
9838
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009839 err = journal_bmap(journal, offset, &blocknr);
9840
9841 if (err) {
Rob Landley43ac8882006-04-01 00:40:33 +00009842 printf ("JBD: bad block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009843 return err;
9844 }
9845
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +00009846 bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009847 if (!bh)
9848 return -ENOMEM;
9849
9850 if (!buffer_uptodate(bh)) {
9851 /* If this is a brand new buffer, start readahead.
9852 Otherwise, we assume we are already reading it. */
9853 if (!buffer_req(bh))
9854 do_readahead(journal, offset);
9855 wait_on_buffer(bh);
9856 }
9857
9858 if (!buffer_uptodate(bh)) {
Rob Landley43ac8882006-04-01 00:40:33 +00009859 printf ("JBD: Failed to read block at offset %u\n", offset);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009860 brelse(bh);
9861 return -EIO;
9862 }
9863
9864 *bhp = bh;
9865 return 0;
9866}
9867
9868
9869/*
9870 * Count the number of in-use tags in a journal descriptor block.
9871 */
9872
9873static int count_tags(struct buffer_head *bh, int size)
9874{
9875 char * tagp;
9876 journal_block_tag_t * tag;
9877 int nr = 0;
9878
9879 tagp = &bh->b_data[sizeof(journal_header_t)];
9880
9881 while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
9882 tag = (journal_block_tag_t *) tagp;
9883
9884 nr++;
9885 tagp += sizeof(journal_block_tag_t);
9886 if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
9887 tagp += 16;
9888
9889 if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
9890 break;
9891 }
9892
9893 return nr;
9894}
9895
9896
9897/* Make sure we wrap around the log correctly! */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00009898#define wrap(journal, var) \
9899do { \
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009900 if (var >= (journal)->j_last) \
9901 var -= ((journal)->j_last - (journal)->j_first); \
9902} while (0)
9903
9904/**
9905 * int journal_recover(journal_t *journal) - recovers a on-disk journal
9906 * @journal: the journal to recover
9907 *
9908 * The primary function for recovering the log contents when mounting a
9909 * journaled device.
9910 *
9911 * Recovery is done in three passes. In the first pass, we look for the
9912 * end of the log. In the second, we assemble the list of revoke
9913 * blocks. In the third and final pass, we replay any un-revoked blocks
9914 * in the log.
9915 */
9916int journal_recover(journal_t *journal)
9917{
9918 int err;
9919 journal_superblock_t * sb;
9920
9921 struct recovery_info info;
9922
9923 memset(&info, 0, sizeof(info));
9924 sb = journal->j_superblock;
9925
9926 /*
9927 * The journal superblock's s_start field (the current log head)
9928 * is always zero if, and only if, the journal was cleanly
9929 * unmounted.
9930 */
9931
9932 if (!sb->s_start) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009933 journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
9934 return 0;
9935 }
9936
9937 err = do_one_pass(journal, &info, PASS_SCAN);
9938 if (!err)
9939 err = do_one_pass(journal, &info, PASS_REVOKE);
9940 if (!err)
9941 err = do_one_pass(journal, &info, PASS_REPLAY);
9942
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009943 /* Restart the log at the next transaction ID, thus invalidating
9944 * any existing commit records in the log. */
9945 journal->j_transaction_sequence = ++info.end_transaction;
9946
9947 journal_clear_revoke(journal);
9948 sync_blockdev(journal->j_fs_dev);
9949 return err;
9950}
9951
9952static int do_one_pass(journal_t *journal,
9953 struct recovery_info *info, enum passtype pass)
9954{
9955 unsigned int first_commit_ID, next_commit_ID;
9956 unsigned long next_log_block;
9957 int err, success = 0;
9958 journal_superblock_t * sb;
9959 journal_header_t * tmp;
9960 struct buffer_head * bh;
9961 unsigned int sequence;
9962 int blocktype;
9963
9964 /* Precompute the maximum metadata descriptors in a descriptor block */
9965 int MAX_BLOCKS_PER_DESC;
9966 MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
9967 / sizeof(journal_block_tag_t));
9968
9969 /*
9970 * First thing is to establish what we expect to find in the log
9971 * (in terms of transaction IDs), and where (in terms of log
9972 * block offsets): query the superblock.
9973 */
9974
9975 sb = journal->j_superblock;
9976 next_commit_ID = ntohl(sb->s_sequence);
9977 next_log_block = ntohl(sb->s_start);
9978
9979 first_commit_ID = next_commit_ID;
9980 if (pass == PASS_SCAN)
9981 info->start_transaction = first_commit_ID;
9982
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +00009983 /*
9984 * Now we walk through the log, transaction by transaction,
9985 * making sure that each transaction has a commit block in the
9986 * expected place. Each complete transaction gets replayed back
9987 * into the main filesystem.
9988 */
9989
9990 while (1) {
9991 int flags;
9992 char * tagp;
9993 journal_block_tag_t * tag;
9994 struct buffer_head * obh;
9995 struct buffer_head * nbh;
9996
9997 /* If we already know where to stop the log traversal,
9998 * check right now that we haven't gone past the end of
9999 * the log. */
10000
10001 if (pass != PASS_SCAN)
10002 if (tid_geq(next_commit_ID, info->end_transaction))
10003 break;
10004
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010005 /* Skip over each chunk of the transaction looking
10006 * either the next descriptor block or the final commit
10007 * record. */
10008
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010009 err = jread(&bh, journal, next_log_block);
10010 if (err)
10011 goto failed;
10012
10013 next_log_block++;
10014 wrap(journal, next_log_block);
10015
10016 /* What kind of buffer is it?
10017 *
10018 * If it is a descriptor block, check that it has the
10019 * expected sequence number. Otherwise, we're all done
10020 * here. */
10021
10022 tmp = (journal_header_t *)bh->b_data;
10023
10024 if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
10025 brelse(bh);
10026 break;
10027 }
10028
10029 blocktype = ntohl(tmp->h_blocktype);
10030 sequence = ntohl(tmp->h_sequence);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010031
10032 if (sequence != next_commit_ID) {
10033 brelse(bh);
10034 break;
10035 }
10036
10037 /* OK, we have a valid descriptor block which matches
10038 * all of the sequence number checks. What are we going
10039 * to do with it? That depends on the pass... */
10040
10041 switch(blocktype) {
10042 case JFS_DESCRIPTOR_BLOCK:
10043 /* If it is a valid descriptor block, replay it
10044 * in pass REPLAY; otherwise, just skip over the
10045 * blocks it describes. */
10046 if (pass != PASS_REPLAY) {
10047 next_log_block +=
10048 count_tags(bh, journal->j_blocksize);
10049 wrap(journal, next_log_block);
10050 brelse(bh);
10051 continue;
10052 }
10053
10054 /* A descriptor block: we can now write all of
10055 * the data blocks. Yay, useful work is finally
10056 * getting done here! */
10057
10058 tagp = &bh->b_data[sizeof(journal_header_t)];
10059 while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
10060 <= journal->j_blocksize) {
10061 unsigned long io_block;
10062
10063 tag = (journal_block_tag_t *) tagp;
10064 flags = ntohl(tag->t_flags);
10065
10066 io_block = next_log_block++;
10067 wrap(journal, next_log_block);
10068 err = jread(&obh, journal, io_block);
10069 if (err) {
10070 /* Recover what we can, but
10071 * report failure at the end. */
10072 success = err;
Rob Landley43ac8882006-04-01 00:40:33 +000010073 printf ("JBD: IO error %d recovering "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010074 "block %ld in log\n",
10075 err, io_block);
10076 } else {
10077 unsigned long blocknr;
10078
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010079 blocknr = ntohl(tag->t_blocknr);
10080
10081 /* If the block has been
10082 * revoked, then we're all done
10083 * here. */
10084 if (journal_test_revoke
10085 (journal, blocknr,
10086 next_commit_ID)) {
10087 brelse(obh);
10088 ++info->nr_revoke_hits;
10089 goto skip_write;
10090 }
10091
10092 /* Find a buffer for the new
10093 * data being restored */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010094 nbh = getblk(journal->j_fs_dev,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010095 blocknr,
10096 journal->j_blocksize);
10097 if (nbh == NULL) {
Rob Landley43ac8882006-04-01 00:40:33 +000010098 printf ("JBD: Out of memory "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010099 "during recovery.\n");
10100 err = -ENOMEM;
10101 brelse(bh);
10102 brelse(obh);
10103 goto failed;
10104 }
10105
10106 lock_buffer(nbh);
10107 memcpy(nbh->b_data, obh->b_data,
10108 journal->j_blocksize);
10109 if (flags & JFS_FLAG_ESCAPE) {
10110 *((unsigned int *)bh->b_data) =
10111 htonl(JFS_MAGIC_NUMBER);
10112 }
10113
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010114 mark_buffer_uptodate(nbh, 1);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010115 mark_buffer_dirty(nbh);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010116 ++info->nr_replays;
10117 /* ll_rw_block(WRITE, 1, &nbh); */
10118 unlock_buffer(nbh);
10119 brelse(obh);
10120 brelse(nbh);
10121 }
10122
10123 skip_write:
10124 tagp += sizeof(journal_block_tag_t);
10125 if (!(flags & JFS_FLAG_SAME_UUID))
10126 tagp += 16;
10127
10128 if (flags & JFS_FLAG_LAST_TAG)
10129 break;
10130 }
10131
10132 brelse(bh);
10133 continue;
10134
10135 case JFS_COMMIT_BLOCK:
10136 /* Found an expected commit block: not much to
10137 * do other than move on to the next sequence
10138 * number. */
10139 brelse(bh);
10140 next_commit_ID++;
10141 continue;
10142
10143 case JFS_REVOKE_BLOCK:
10144 /* If we aren't in the REVOKE pass, then we can
10145 * just skip over this block. */
10146 if (pass != PASS_REVOKE) {
10147 brelse(bh);
10148 continue;
10149 }
10150
10151 err = scan_revoke_records(journal, bh,
10152 next_commit_ID, info);
10153 brelse(bh);
10154 if (err)
10155 goto failed;
10156 continue;
10157
10158 default:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010159 goto done;
10160 }
10161 }
10162
10163 done:
10164 /*
10165 * We broke out of the log scan loop: either we came to the
10166 * known end of the log or we found an unexpected block in the
10167 * log. If the latter happened, then we know that the "current"
10168 * transaction marks the end of the valid log.
10169 */
10170
10171 if (pass == PASS_SCAN)
10172 info->end_transaction = next_commit_ID;
10173 else {
10174 /* It's really bad news if different passes end up at
10175 * different places (but possible due to IO errors). */
10176 if (info->end_transaction != next_commit_ID) {
Rob Landley43ac8882006-04-01 00:40:33 +000010177 printf ("JBD: recovery pass %d ended at "
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010178 "transaction %u, expected %u\n",
10179 pass, next_commit_ID, info->end_transaction);
10180 if (!success)
10181 success = -EIO;
10182 }
10183 }
10184
10185 return success;
10186
10187 failed:
10188 return err;
10189}
10190
10191
10192/* Scan a revoke record, marking all blocks mentioned as revoked. */
10193
10194static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
10195 tid_t sequence, struct recovery_info *info)
10196{
10197 journal_revoke_header_t *header;
10198 int offset, max;
10199
10200 header = (journal_revoke_header_t *) bh->b_data;
10201 offset = sizeof(journal_revoke_header_t);
10202 max = ntohl(header->r_count);
10203
10204 while (offset < max) {
10205 unsigned long blocknr;
10206 int err;
10207
10208 blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
10209 offset += 4;
10210 err = journal_set_revoke(journal, blocknr, sequence);
10211 if (err)
10212 return err;
10213 ++info->nr_revokes;
10214 }
10215 return 0;
10216}
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010217
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010218
10219/*
10220 * rehash.c --- rebuild hash tree directories
10221 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010222 * This algorithm is designed for simplicity of implementation and to
10223 * pack the directory as much as possible. It however requires twice
10224 * as much memory as the size of the directory. The maximum size
10225 * directory supported using a 4k blocksize is roughly a gigabyte, and
10226 * so there may very well be problems with machines that don't have
10227 * virtual memory, and obscenely large directories.
10228 *
10229 * An alternate algorithm which is much more disk intensive could be
10230 * written, and probably will need to be written in the future. The
10231 * design goals of such an algorithm are: (a) use (roughly) constant
10232 * amounts of memory, no matter how large the directory, (b) the
10233 * directory must be safe at all times, even if e2fsck is interrupted
10234 * in the middle, (c) we must use minimal amounts of extra disk
10235 * blocks. This pretty much requires an incremental approach, where
10236 * we are reading from one part of the directory, and inserting into
10237 * the front half. So the algorithm will have to keep track of a
10238 * moving block boundary between the new tree and the old tree, and
10239 * files will need to be moved from the old directory and inserted
10240 * into the new tree. If the new directory requires space which isn't
10241 * yet available, blocks from the beginning part of the old directory
10242 * may need to be moved to the end of the directory to make room for
10243 * the new tree:
10244 *
10245 * --------------------------------------------------------
10246 * | new tree | | old tree |
10247 * --------------------------------------------------------
10248 * ^ ptr ^ptr
10249 * tail new head old
10250 *
10251 * This is going to be a pain in the tuckus to implement, and will
10252 * require a lot more disk accesses. So I'm going to skip it for now;
10253 * it's only really going to be an issue for really, really big
10254 * filesystems (when we reach the level of tens of millions of files
10255 * in a single directory). It will probably be easier to simply
10256 * require that e2fsck use VM first.
10257 */
10258
10259struct fill_dir_struct {
10260 char *buf;
10261 struct ext2_inode *inode;
10262 int err;
10263 e2fsck_t ctx;
10264 struct hash_entry *harray;
10265 int max_array, num_array;
10266 int dir_size;
10267 int compress;
10268 ino_t parent;
10269};
10270
10271struct hash_entry {
10272 ext2_dirhash_t hash;
10273 ext2_dirhash_t minor_hash;
10274 struct ext2_dir_entry *dir;
10275};
10276
10277struct out_dir {
10278 int num;
10279 int max;
10280 char *buf;
10281 ext2_dirhash_t *hashes;
10282};
10283
10284static int fill_dir_block(ext2_filsys fs,
10285 blk_t *block_nr,
10286 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010287 blk_t ref_block FSCK_ATTR((unused)),
10288 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010289 void *priv_data)
10290{
10291 struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
10292 struct hash_entry *new_array, *ent;
10293 struct ext2_dir_entry *dirent;
10294 char *dir;
10295 unsigned int offset, dir_offset;
10296
10297 if (blockcnt < 0)
10298 return 0;
10299
10300 offset = blockcnt * fs->blocksize;
10301 if (offset + fs->blocksize > fd->inode->i_size) {
10302 fd->err = EXT2_ET_DIR_CORRUPTED;
10303 return BLOCK_ABORT;
10304 }
10305 dir = (fd->buf+offset);
10306 if (HOLE_BLKADDR(*block_nr)) {
10307 memset(dir, 0, fs->blocksize);
10308 dirent = (struct ext2_dir_entry *) dir;
10309 dirent->rec_len = fs->blocksize;
10310 } else {
10311 fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
10312 if (fd->err)
10313 return BLOCK_ABORT;
10314 }
10315 /* While the directory block is "hot", index it. */
10316 dir_offset = 0;
10317 while (dir_offset < fs->blocksize) {
10318 dirent = (struct ext2_dir_entry *) (dir + dir_offset);
10319 if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
10320 (dirent->rec_len < 8) ||
10321 ((dirent->rec_len % 4) != 0) ||
10322 (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
10323 fd->err = EXT2_ET_DIR_CORRUPTED;
10324 return BLOCK_ABORT;
10325 }
10326 dir_offset += dirent->rec_len;
10327 if (dirent->inode == 0)
10328 continue;
10329 if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
10330 (dirent->name[0] == '.'))
10331 continue;
10332 if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
10333 (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
10334 fd->parent = dirent->inode;
10335 continue;
10336 }
10337 if (fd->num_array >= fd->max_array) {
10338 new_array = realloc(fd->harray,
10339 sizeof(struct hash_entry) * (fd->max_array+500));
10340 if (!new_array) {
10341 fd->err = ENOMEM;
10342 return BLOCK_ABORT;
10343 }
10344 fd->harray = new_array;
10345 fd->max_array += 500;
10346 }
10347 ent = fd->harray + fd->num_array++;
10348 ent->dir = dirent;
10349 fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
10350 if (fd->compress)
10351 ent->hash = ent->minor_hash = 0;
10352 else {
10353 fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
10354 dirent->name,
10355 dirent->name_len & 0xFF,
10356 fs->super->s_hash_seed,
10357 &ent->hash, &ent->minor_hash);
10358 if (fd->err)
10359 return BLOCK_ABORT;
10360 }
10361 }
10362
10363 return 0;
10364}
10365
10366/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010367static int name_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010368{
10369 const struct hash_entry *he_a = (const struct hash_entry *) a;
10370 const struct hash_entry *he_b = (const struct hash_entry *) b;
10371 int ret;
10372 int min_len;
10373
10374 min_len = he_a->dir->name_len;
10375 if (min_len > he_b->dir->name_len)
10376 min_len = he_b->dir->name_len;
10377
10378 ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
10379 if (ret == 0) {
10380 if (he_a->dir->name_len > he_b->dir->name_len)
10381 ret = 1;
10382 else if (he_a->dir->name_len < he_b->dir->name_len)
10383 ret = -1;
10384 else
10385 ret = he_b->dir->inode - he_a->dir->inode;
10386 }
10387 return ret;
10388}
10389
10390/* Used for sorting the hash entry */
Rob Landley7c94bed2006-05-03 21:58:45 +000010391static int hash_cmp(const void *a, const void *b)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010392{
10393 const struct hash_entry *he_a = (const struct hash_entry *) a;
10394 const struct hash_entry *he_b = (const struct hash_entry *) b;
10395 int ret;
10396
10397 if (he_a->hash > he_b->hash)
10398 ret = 1;
10399 else if (he_a->hash < he_b->hash)
10400 ret = -1;
10401 else {
10402 if (he_a->minor_hash > he_b->minor_hash)
10403 ret = 1;
10404 else if (he_a->minor_hash < he_b->minor_hash)
10405 ret = -1;
10406 else
10407 ret = name_cmp(a, b);
10408 }
10409 return ret;
10410}
10411
10412static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
10413 int blocks)
10414{
10415 void *new_mem;
10416
10417 if (outdir->max) {
10418 new_mem = realloc(outdir->buf, blocks * fs->blocksize);
10419 if (!new_mem)
10420 return ENOMEM;
10421 outdir->buf = new_mem;
10422 new_mem = realloc(outdir->hashes,
10423 blocks * sizeof(ext2_dirhash_t));
10424 if (!new_mem)
10425 return ENOMEM;
10426 outdir->hashes = new_mem;
10427 } else {
10428 outdir->buf = malloc(blocks * fs->blocksize);
10429 outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
10430 outdir->num = 0;
10431 }
10432 outdir->max = blocks;
10433 return 0;
10434}
10435
10436static void free_out_dir(struct out_dir *outdir)
10437{
Rob Landleye7c43b62006-03-01 16:39:45 +000010438 free(outdir->buf);
10439 free(outdir->hashes);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010440 outdir->max = 0;
10441 outdir->num =0;
10442}
10443
10444static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
10445 char ** ret)
10446{
10447 errcode_t retval;
10448
10449 if (outdir->num >= outdir->max) {
10450 retval = alloc_size_dir(fs, outdir, outdir->max + 50);
10451 if (retval)
10452 return retval;
10453 }
10454 *ret = outdir->buf + (outdir->num++ * fs->blocksize);
10455 memset(*ret, 0, fs->blocksize);
10456 return 0;
10457}
10458
10459/*
10460 * This function is used to make a unique filename. We do this by
10461 * appending ~0, and then incrementing the number. However, we cannot
10462 * expand the length of the filename beyond the padding available in
10463 * the directory entry.
10464 */
10465static void mutate_name(char *str, __u16 *len)
10466{
10467 int i;
10468 __u16 l = *len & 0xFF, h = *len & 0xff00;
10469
10470 /*
10471 * First check to see if it looks the name has been mutated
10472 * already
10473 */
10474 for (i = l-1; i > 0; i--) {
10475 if (!isdigit(str[i]))
10476 break;
10477 }
10478 if ((i == l-1) || (str[i] != '~')) {
10479 if (((l-1) & 3) < 2)
10480 l += 2;
10481 else
10482 l = (l+3) & ~3;
10483 str[l-2] = '~';
10484 str[l-1] = '0';
10485 *len = l | h;
10486 return;
10487 }
10488 for (i = l-1; i >= 0; i--) {
10489 if (isdigit(str[i])) {
10490 if (str[i] == '9')
10491 str[i] = '0';
10492 else {
10493 str[i]++;
10494 return;
10495 }
10496 continue;
10497 }
10498 if (i == 1) {
10499 if (str[0] == 'z')
10500 str[0] = 'A';
10501 else if (str[0] == 'Z') {
10502 str[0] = '~';
10503 str[1] = '0';
10504 } else
10505 str[0]++;
10506 } else if (i > 0) {
10507 str[i] = '1';
10508 str[i-1] = '~';
10509 } else {
10510 if (str[0] == '~')
10511 str[0] = 'a';
10512 else
10513 str[0]++;
10514 }
10515 break;
10516 }
10517}
10518
10519static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
10520 ext2_ino_t ino,
10521 struct fill_dir_struct *fd)
10522{
10523 struct problem_context pctx;
10524 struct hash_entry *ent, *prev;
10525 int i, j;
10526 int fixed = 0;
10527 char new_name[256];
10528 __u16 new_len;
10529
10530 clear_problem_context(&pctx);
10531 pctx.ino = ino;
10532
10533 for (i=1; i < fd->num_array; i++) {
10534 ent = fd->harray + i;
10535 prev = ent - 1;
10536 if (!ent->dir->inode ||
10537 ((ent->dir->name_len & 0xFF) !=
10538 (prev->dir->name_len & 0xFF)) ||
10539 (strncmp(ent->dir->name, prev->dir->name,
10540 ent->dir->name_len & 0xFF)))
10541 continue;
10542 pctx.dirent = ent->dir;
10543 if ((ent->dir->inode == prev->dir->inode) &&
10544 fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
10545 e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
10546 ent->dir->inode = 0;
10547 fixed++;
10548 continue;
10549 }
10550 memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
10551 new_len = ent->dir->name_len;
10552 mutate_name(new_name, &new_len);
10553 for (j=0; j < fd->num_array; j++) {
10554 if ((i==j) ||
10555 ((ent->dir->name_len & 0xFF) !=
10556 (fd->harray[j].dir->name_len & 0xFF)) ||
10557 (strncmp(new_name, fd->harray[j].dir->name,
10558 new_len & 0xFF)))
10559 continue;
10560 mutate_name(new_name, &new_len);
10561
10562 j = -1;
10563 }
10564 new_name[new_len & 0xFF] = 0;
10565 pctx.str = new_name;
10566 if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
10567 memcpy(ent->dir->name, new_name, new_len & 0xFF);
10568 ent->dir->name_len = new_len;
10569 ext2fs_dirhash(fs->super->s_def_hash_version,
10570 ent->dir->name,
10571 ent->dir->name_len & 0xFF,
10572 fs->super->s_hash_seed,
10573 &ent->hash, &ent->minor_hash);
10574 fixed++;
10575 }
10576 }
10577 return fixed;
10578}
10579
10580
10581static errcode_t copy_dir_entries(ext2_filsys fs,
10582 struct fill_dir_struct *fd,
10583 struct out_dir *outdir)
10584{
10585 errcode_t retval;
10586 char *block_start;
10587 struct hash_entry *ent;
10588 struct ext2_dir_entry *dirent;
10589 int i, rec_len, left;
10590 ext2_dirhash_t prev_hash;
10591 int offset;
10592
10593 outdir->max = 0;
10594 retval = alloc_size_dir(fs, outdir,
10595 (fd->dir_size / fs->blocksize) + 2);
10596 if (retval)
10597 return retval;
10598 outdir->num = fd->compress ? 0 : 1;
10599 offset = 0;
10600 outdir->hashes[0] = 0;
10601 prev_hash = 1;
10602 if ((retval = get_next_block(fs, outdir, &block_start)))
10603 return retval;
10604 dirent = (struct ext2_dir_entry *) block_start;
10605 left = fs->blocksize;
10606 for (i=0; i < fd->num_array; i++) {
10607 ent = fd->harray + i;
10608 if (ent->dir->inode == 0)
10609 continue;
10610 rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
10611 if (rec_len > left) {
10612 if (left)
10613 dirent->rec_len += left;
10614 if ((retval = get_next_block(fs, outdir,
10615 &block_start)))
10616 return retval;
10617 offset = 0;
10618 }
10619 left = fs->blocksize - offset;
10620 dirent = (struct ext2_dir_entry *) (block_start + offset);
10621 if (offset == 0) {
10622 if (ent->hash == prev_hash)
10623 outdir->hashes[outdir->num-1] = ent->hash | 1;
10624 else
10625 outdir->hashes[outdir->num-1] = ent->hash;
10626 }
10627 dirent->inode = ent->dir->inode;
10628 dirent->name_len = ent->dir->name_len;
10629 dirent->rec_len = rec_len;
10630 memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
10631 offset += rec_len;
10632 left -= rec_len;
10633 if (left < 12) {
10634 dirent->rec_len += left;
10635 offset += left;
10636 left = 0;
10637 }
10638 prev_hash = ent->hash;
10639 }
10640 if (left)
10641 dirent->rec_len += left;
10642
10643 return 0;
10644}
10645
10646
10647static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
10648 ext2_ino_t ino, ext2_ino_t parent)
10649{
10650 struct ext2_dir_entry *dir;
10651 struct ext2_dx_root_info *root;
10652 struct ext2_dx_countlimit *limits;
10653 int filetype = 0;
10654
10655 if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
10656 filetype = EXT2_FT_DIR << 8;
10657
10658 memset(buf, 0, fs->blocksize);
10659 dir = (struct ext2_dir_entry *) buf;
10660 dir->inode = ino;
10661 dir->name[0] = '.';
10662 dir->name_len = 1 | filetype;
10663 dir->rec_len = 12;
10664 dir = (struct ext2_dir_entry *) (buf + 12);
10665 dir->inode = parent;
10666 dir->name[0] = '.';
10667 dir->name[1] = '.';
10668 dir->name_len = 2 | filetype;
10669 dir->rec_len = fs->blocksize - 12;
10670
10671 root = (struct ext2_dx_root_info *) (buf+24);
10672 root->reserved_zero = 0;
10673 root->hash_version = fs->super->s_def_hash_version;
10674 root->info_length = 8;
10675 root->indirect_levels = 0;
10676 root->unused_flags = 0;
10677
10678 limits = (struct ext2_dx_countlimit *) (buf+32);
10679 limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
10680 limits->count = 0;
10681
10682 return root;
10683}
10684
10685
10686static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
10687{
10688 struct ext2_dir_entry *dir;
10689 struct ext2_dx_countlimit *limits;
10690
10691 memset(buf, 0, fs->blocksize);
10692 dir = (struct ext2_dir_entry *) buf;
10693 dir->inode = 0;
10694 dir->rec_len = fs->blocksize;
10695
10696 limits = (struct ext2_dx_countlimit *) (buf+8);
10697 limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
10698 limits->count = 0;
10699
10700 return (struct ext2_dx_entry *) limits;
10701}
10702
10703/*
10704 * This function takes the leaf nodes which have been written in
10705 * outdir, and populates the root node and any necessary interior nodes.
10706 */
10707static errcode_t calculate_tree(ext2_filsys fs,
10708 struct out_dir *outdir,
10709 ext2_ino_t ino,
10710 ext2_ino_t parent)
10711{
10712 struct ext2_dx_root_info *root_info;
10713 struct ext2_dx_entry *root, *dx_ent = 0;
10714 struct ext2_dx_countlimit *root_limit, *limit;
10715 errcode_t retval;
10716 char * block_start;
10717 int i, c1, c2, nblks;
10718 int limit_offset, root_offset;
10719
10720 root_info = set_root_node(fs, outdir->buf, ino, parent);
10721 root_offset = limit_offset = ((char *) root_info - outdir->buf) +
10722 root_info->info_length;
10723 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10724 c1 = root_limit->limit;
10725 nblks = outdir->num;
10726
10727 /* Write out the pointer blocks */
10728 if (nblks-1 <= c1) {
10729 /* Just write out the root block, and we're done */
10730 root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
10731 for (i=1; i < nblks; i++) {
10732 root->block = ext2fs_cpu_to_le32(i);
10733 if (i != 1)
10734 root->hash =
10735 ext2fs_cpu_to_le32(outdir->hashes[i]);
10736 root++;
10737 c1--;
10738 }
10739 } else {
10740 c2 = 0;
10741 limit = 0;
10742 root_info->indirect_levels = 1;
10743 for (i=1; i < nblks; i++) {
10744 if (c1 == 0)
10745 return ENOSPC;
10746 if (c2 == 0) {
10747 if (limit)
10748 limit->limit = limit->count =
10749 ext2fs_cpu_to_le16(limit->limit);
10750 root = (struct ext2_dx_entry *)
10751 (outdir->buf + root_offset);
10752 root->block = ext2fs_cpu_to_le32(outdir->num);
10753 if (i != 1)
10754 root->hash =
10755 ext2fs_cpu_to_le32(outdir->hashes[i]);
10756 if ((retval = get_next_block(fs, outdir,
10757 &block_start)))
10758 return retval;
10759 dx_ent = set_int_node(fs, block_start);
10760 limit = (struct ext2_dx_countlimit *) dx_ent;
10761 c2 = limit->limit;
10762 root_offset += sizeof(struct ext2_dx_entry);
10763 c1--;
10764 }
10765 dx_ent->block = ext2fs_cpu_to_le32(i);
10766 if (c2 != limit->limit)
10767 dx_ent->hash =
10768 ext2fs_cpu_to_le32(outdir->hashes[i]);
10769 dx_ent++;
10770 c2--;
10771 }
10772 limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
10773 limit->limit = ext2fs_cpu_to_le16(limit->limit);
10774 }
10775 root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
10776 root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
10777 root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
10778
10779 return 0;
10780}
10781
10782struct write_dir_struct {
10783 struct out_dir *outdir;
10784 errcode_t err;
10785 e2fsck_t ctx;
10786 int cleared;
10787};
10788
10789/*
10790 * Helper function which writes out a directory block.
10791 */
10792static int write_dir_block(ext2_filsys fs,
10793 blk_t *block_nr,
10794 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000010795 blk_t ref_block FSCK_ATTR((unused)),
10796 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010797 void *priv_data)
10798{
10799 struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
10800 blk_t blk;
10801 char *dir;
10802
10803 if (*block_nr == 0)
10804 return 0;
10805 if (blockcnt >= wd->outdir->num) {
10806 e2fsck_read_bitmaps(wd->ctx);
10807 blk = *block_nr;
10808 ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
10809 ext2fs_block_alloc_stats(fs, blk, -1);
10810 *block_nr = 0;
10811 wd->cleared++;
10812 return BLOCK_CHANGED;
10813 }
10814 if (blockcnt < 0)
10815 return 0;
10816
10817 dir = wd->outdir->buf + (blockcnt * fs->blocksize);
10818 wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
10819 if (wd->err)
10820 return BLOCK_ABORT;
10821 return 0;
10822}
10823
10824static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
10825 struct out_dir *outdir,
10826 ext2_ino_t ino, int compress)
10827{
10828 struct write_dir_struct wd;
10829 errcode_t retval;
10830 struct ext2_inode inode;
10831
10832 retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
10833 if (retval)
10834 return retval;
10835
10836 wd.outdir = outdir;
10837 wd.err = 0;
10838 wd.ctx = ctx;
10839 wd.cleared = 0;
10840
10841 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10842 write_dir_block, &wd);
10843 if (retval)
10844 return retval;
10845 if (wd.err)
10846 return wd.err;
10847
10848 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10849 if (compress)
10850 inode.i_flags &= ~EXT2_INDEX_FL;
10851 else
10852 inode.i_flags |= EXT2_INDEX_FL;
10853 inode.i_size = outdir->num * fs->blocksize;
10854 inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
10855 e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
10856
10857 return 0;
10858}
10859
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000010860static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010861{
10862 ext2_filsys fs = ctx->fs;
10863 errcode_t retval;
10864 struct ext2_inode inode;
10865 char *dir_buf = 0;
10866 struct fill_dir_struct fd;
10867 struct out_dir outdir;
10868
10869 outdir.max = outdir.num = 0;
10870 outdir.buf = 0;
10871 outdir.hashes = 0;
10872 e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
10873
10874 retval = ENOMEM;
10875 fd.harray = 0;
10876 dir_buf = malloc(inode.i_size);
10877 if (!dir_buf)
10878 goto errout;
10879
10880 fd.max_array = inode.i_size / 32;
10881 fd.num_array = 0;
10882 fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
10883 if (!fd.harray)
10884 goto errout;
10885
10886 fd.ctx = ctx;
10887 fd.buf = dir_buf;
10888 fd.inode = &inode;
10889 fd.err = 0;
10890 fd.dir_size = 0;
10891 fd.compress = 0;
10892 if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
10893 (inode.i_size / fs->blocksize) < 2)
10894 fd.compress = 1;
10895 fd.parent = 0;
10896
10897 /* Read in the entire directory into memory */
10898 retval = ext2fs_block_iterate2(fs, ino, 0, 0,
10899 fill_dir_block, &fd);
10900 if (fd.err) {
10901 retval = fd.err;
10902 goto errout;
10903 }
10904
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010905 /* Sort the list */
10906resort:
10907 if (fd.compress)
10908 qsort(fd.harray+2, fd.num_array-2,
10909 sizeof(struct hash_entry), name_cmp);
10910 else
10911 qsort(fd.harray, fd.num_array,
10912 sizeof(struct hash_entry), hash_cmp);
10913
10914 /*
10915 * Look for duplicates
10916 */
10917 if (duplicate_search_and_fix(ctx, fs, ino, &fd))
10918 goto resort;
10919
10920 if (ctx->options & E2F_OPT_NO) {
10921 retval = 0;
10922 goto errout;
10923 }
10924
10925 /*
10926 * Copy the directory entries. In a htree directory these
10927 * will become the leaf nodes.
10928 */
10929 retval = copy_dir_entries(fs, &fd, &outdir);
10930 if (retval)
10931 goto errout;
10932
10933 free(dir_buf); dir_buf = 0;
10934
10935 if (!fd.compress) {
10936 /* Calculate the interior nodes */
10937 retval = calculate_tree(fs, &outdir, ino, fd.parent);
10938 if (retval)
10939 goto errout;
10940 }
10941
10942 retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010943
10944errout:
Rob Landleye7c43b62006-03-01 16:39:45 +000010945 free(dir_buf);
10946 free(fd.harray);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010947
10948 free_out_dir(&outdir);
10949 return retval;
10950}
10951
10952void e2fsck_rehash_directories(e2fsck_t ctx)
10953{
10954 struct problem_context pctx;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010955 struct dir_info *dir;
10956 ext2_u32_iterate iter;
10957 ext2_ino_t ino;
10958 errcode_t retval;
10959 int i, cur, max, all_dirs, dir_index, first = 1;
10960
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000010961 all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
10962
10963 if (!ctx->dirs_to_hash && !all_dirs)
10964 return;
10965
10966 e2fsck_get_lost_and_found(ctx, 0);
10967
10968 clear_problem_context(&pctx);
10969
10970 dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
10971 cur = 0;
10972 if (all_dirs) {
10973 i = 0;
10974 max = e2fsck_get_num_dirinfo(ctx);
10975 } else {
10976 retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
10977 &iter);
10978 if (retval) {
10979 pctx.errcode = retval;
10980 fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
10981 return;
10982 }
10983 max = ext2fs_u32_list_count(ctx->dirs_to_hash);
10984 }
10985 while (1) {
10986 if (all_dirs) {
10987 if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
10988 break;
10989 ino = dir->ino;
10990 } else {
10991 if (!ext2fs_u32_list_iterate(iter, &ino))
10992 break;
10993 }
10994 if (ino == ctx->lost_and_found)
10995 continue;
10996 pctx.dir = ino;
10997 if (first) {
10998 fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
10999 first = 0;
11000 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011001 pctx.errcode = e2fsck_rehash_dir(ctx, ino);
11002 if (pctx.errcode) {
11003 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11004 fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
11005 }
11006 if (ctx->progress && !ctx->progress_fd)
11007 e2fsck_simple_progress(ctx, "Rebuilding directory",
11008 100.0 * (float) (++cur) / (float) max, ino);
11009 }
11010 end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
11011 if (!all_dirs)
11012 ext2fs_u32_list_iterate_end(iter);
11013
Rob Landleye7c43b62006-03-01 16:39:45 +000011014 ext2fs_u32_list_free(ctx->dirs_to_hash);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011015 ctx->dirs_to_hash = 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011016}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011017
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011018/*
11019 * linux/fs/revoke.c
11020 *
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011021 * Journal revoke routines for the generic filesystem journaling code;
11022 * part of the ext2fs journaling system.
11023 *
11024 * Revoke is the mechanism used to prevent old log records for deleted
11025 * metadata from being replayed on top of newer data using the same
11026 * blocks. The revoke mechanism is used in two separate places:
11027 *
11028 * + Commit: during commit we write the entire list of the current
11029 * transaction's revoked blocks to the journal
11030 *
11031 * + Recovery: during recovery we record the transaction ID of all
11032 * revoked blocks. If there are multiple revoke records in the log
11033 * for a single block, only the last one counts, and if there is a log
11034 * entry for a block beyond the last revoke, then that log entry still
11035 * gets replayed.
11036 *
11037 * We can get interactions between revokes and new log data within a
11038 * single transaction:
11039 *
11040 * Block is revoked and then journaled:
11041 * The desired end result is the journaling of the new block, so we
11042 * cancel the revoke before the transaction commits.
11043 *
11044 * Block is journaled and then revoked:
11045 * The revoke must take precedence over the write of the block, so we
11046 * need either to cancel the journal entry or to write the revoke
11047 * later in the log than the log block. In this case, we choose the
11048 * latter: journaling a block cancels any revoke record for that block
11049 * in the current transaction, so any revoke for that block in the
11050 * transaction must have happened after the block was journaled and so
11051 * the revoke must take precedence.
11052 *
11053 * Block is revoked and then written as data:
11054 * The data write is allowed to succeed, but the revoke is _not_
11055 * cancelled. We still need to prevent old log records from
11056 * overwriting the new data. We don't even need to clear the revoke
11057 * bit here.
11058 *
11059 * Revoke information on buffers is a tri-state value:
11060 *
11061 * RevokeValid clear: no cached revoke status, need to look it up
11062 * RevokeValid set, Revoked clear:
11063 * buffer has not been revoked, and cancel_revoke
11064 * need do nothing.
11065 * RevokeValid set, Revoked set:
11066 * buffer has been revoked.
11067 */
11068
11069static kmem_cache_t *revoke_record_cache;
11070static kmem_cache_t *revoke_table_cache;
11071
11072/* Each revoke record represents one single revoked block. During
11073 journal replay, this involves recording the transaction ID of the
11074 last transaction to revoke this block. */
11075
11076struct jbd_revoke_record_s
11077{
11078 struct list_head hash;
11079 tid_t sequence; /* Used for recovery only */
11080 unsigned long blocknr;
11081};
11082
11083
11084/* The revoke table is just a simple hash table of revoke records. */
11085struct jbd_revoke_table_s
11086{
11087 /* It is conceivable that we might want a larger hash table
11088 * for recovery. Must be a power of two. */
11089 int hash_size;
11090 int hash_shift;
11091 struct list_head *hash_table;
11092};
11093
11094
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011095/* Utility functions to maintain the revoke table */
11096
11097/* Borrowed from buffer.c: this is a tried and tested block hash function */
11098static inline int hash(journal_t *journal, unsigned long block)
11099{
11100 struct jbd_revoke_table_s *table = journal->j_revoke;
11101 int hash_shift = table->hash_shift;
11102
11103 return ((block << (hash_shift - 6)) ^
11104 (block >> 13) ^
11105 (block << (hash_shift - 12))) & (table->hash_size - 1);
11106}
11107
11108static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
11109 tid_t seq)
11110{
11111 struct list_head *hash_list;
11112 struct jbd_revoke_record_s *record;
11113
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011114 record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
11115 if (!record)
11116 goto oom;
11117
11118 record->sequence = seq;
11119 record->blocknr = blocknr;
11120 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11121 list_add(&record->hash, hash_list);
11122 return 0;
11123
11124oom:
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011125 return -ENOMEM;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011126}
11127
11128/* Find a revoke record in the journal's hash table. */
11129
11130static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
11131 unsigned long blocknr)
11132{
11133 struct list_head *hash_list;
11134 struct jbd_revoke_record_s *record;
11135
11136 hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
11137
11138 record = (struct jbd_revoke_record_s *) hash_list->next;
11139 while (&(record->hash) != hash_list) {
11140 if (record->blocknr == blocknr)
11141 return record;
11142 record = (struct jbd_revoke_record_s *) record->hash.next;
11143 }
11144 return NULL;
11145}
11146
11147int journal_init_revoke_caches(void)
11148{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011149 revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011150 if (revoke_record_cache == 0)
11151 return -ENOMEM;
11152
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011153 revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011154 if (revoke_table_cache == 0) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011155 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011156 revoke_record_cache = NULL;
11157 return -ENOMEM;
11158 }
11159 return 0;
11160}
11161
11162void journal_destroy_revoke_caches(void)
11163{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011164 do_cache_destroy(revoke_record_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011165 revoke_record_cache = 0;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011166 do_cache_destroy(revoke_table_cache);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011167 revoke_table_cache = 0;
11168}
11169
11170/* Initialise the revoke table for a given journal to a given size. */
11171
11172int journal_init_revoke(journal_t *journal, int hash_size)
11173{
11174 int shift, tmp;
11175
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011176 journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
11177 if (!journal->j_revoke)
11178 return -ENOMEM;
11179
11180 /* Check that the hash_size is a power of two */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011181 journal->j_revoke->hash_size = hash_size;
11182
11183 shift = 0;
11184 tmp = hash_size;
11185 while((tmp >>= 1UL) != 0UL)
11186 shift++;
11187 journal->j_revoke->hash_shift = shift;
11188
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011189 journal->j_revoke->hash_table = malloc(hash_size * sizeof(struct list_head));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011190 if (!journal->j_revoke->hash_table) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011191 free(journal->j_revoke);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011192 journal->j_revoke = NULL;
11193 return -ENOMEM;
11194 }
11195
11196 for (tmp = 0; tmp < hash_size; tmp++)
11197 INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
11198
11199 return 0;
11200}
11201
11202/* Destoy a journal's revoke table. The table must already be empty! */
11203
11204void journal_destroy_revoke(journal_t *journal)
11205{
11206 struct jbd_revoke_table_s *table;
11207 struct list_head *hash_list;
11208 int i;
11209
11210 table = journal->j_revoke;
11211 if (!table)
11212 return;
11213
11214 for (i=0; i<table->hash_size; i++) {
11215 hash_list = &table->hash_table[i];
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011216 }
11217
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011218 free(table->hash_table);
11219 free(table);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011220 journal->j_revoke = NULL;
11221}
11222
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011223/*
11224 * Revoke support for recovery.
11225 *
11226 * Recovery needs to be able to:
11227 *
11228 * record all revoke records, including the tid of the latest instance
11229 * of each revoke in the journal
11230 *
11231 * check whether a given block in a given transaction should be replayed
11232 * (ie. has not been revoked by a revoke record in that or a subsequent
11233 * transaction)
11234 *
11235 * empty the revoke table after recovery.
11236 */
11237
11238/*
11239 * First, setting revoke records. We create a new revoke record for
11240 * every block ever revoked in the log as we scan it for recovery, and
11241 * we update the existing records if we find multiple revokes for a
11242 * single block.
11243 */
11244
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011245int journal_set_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011246 tid_t sequence)
11247{
11248 struct jbd_revoke_record_s *record;
11249
11250 record = find_revoke_record(journal, blocknr);
11251 if (record) {
11252 /* If we have multiple occurences, only record the
11253 * latest sequence number in the hashed record */
11254 if (tid_gt(sequence, record->sequence))
11255 record->sequence = sequence;
11256 return 0;
11257 }
11258 return insert_revoke_hash(journal, blocknr, sequence);
11259}
11260
11261/*
11262 * Test revoke records. For a given block referenced in the log, has
11263 * that block been revoked? A revoke record with a given transaction
11264 * sequence number revokes all blocks in that transaction and earlier
11265 * ones, but later transactions still need replayed.
11266 */
11267
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011268int journal_test_revoke(journal_t *journal, unsigned long blocknr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011269 tid_t sequence)
11270{
11271 struct jbd_revoke_record_s *record;
11272
11273 record = find_revoke_record(journal, blocknr);
11274 if (!record)
11275 return 0;
11276 if (tid_gt(sequence, record->sequence))
11277 return 0;
11278 return 1;
11279}
11280
11281/*
11282 * Finally, once recovery is over, we need to clear the revoke table so
11283 * that it can be reused by the running filesystem.
11284 */
11285
11286void journal_clear_revoke(journal_t *journal)
11287{
11288 int i;
11289 struct list_head *hash_list;
11290 struct jbd_revoke_record_s *record;
11291 struct jbd_revoke_table_s *revoke_var;
11292
11293 revoke_var = journal->j_revoke;
11294
11295 for (i = 0; i < revoke_var->hash_size; i++) {
11296 hash_list = &revoke_var->hash_table[i];
11297 while (!list_empty(hash_list)) {
11298 record = (struct jbd_revoke_record_s*) hash_list->next;
11299 list_del(&record->hash);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011300 free(record);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011301 }
11302 }
11303}
11304
11305/*
11306 * e2fsck.c - superblock checks
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011307 */
11308
11309#define MIN_CHECK 1
11310#define MAX_CHECK 2
11311
11312static void check_super_value(e2fsck_t ctx, const char *descr,
11313 unsigned long value, int flags,
11314 unsigned long min_val, unsigned long max_val)
11315{
11316 struct problem_context pctx;
11317
11318 if (((flags & MIN_CHECK) && (value < min_val)) ||
11319 ((flags & MAX_CHECK) && (value > max_val))) {
11320 clear_problem_context(&pctx);
11321 pctx.num = value;
11322 pctx.str = descr;
11323 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11324 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11325 }
11326}
11327
11328/*
11329 * This routine may get stubbed out in special compilations of the
11330 * e2fsck code..
11331 */
11332#ifndef EXT2_SPECIAL_DEVICE_SIZE
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011333static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011334{
11335 return (ext2fs_get_device_size(ctx->filesystem_name,
11336 EXT2_BLOCK_SIZE(ctx->fs->super),
11337 &ctx->num_blocks));
11338}
11339#endif
11340
11341/*
11342 * helper function to release an inode
11343 */
11344struct process_block_struct {
11345 e2fsck_t ctx;
11346 char *buf;
11347 struct problem_context *pctx;
11348 int truncating;
11349 int truncate_offset;
11350 e2_blkcnt_t truncate_block;
11351 int truncated_blocks;
11352 int abort;
11353 errcode_t errcode;
11354};
11355
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011356static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011357 e2_blkcnt_t blockcnt,
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000011358 blk_t ref_blk FSCK_ATTR((unused)),
11359 int ref_offset FSCK_ATTR((unused)),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011360 void *priv_data)
11361{
11362 struct process_block_struct *pb;
11363 e2fsck_t ctx;
11364 struct problem_context *pctx;
11365 blk_t blk = *block_nr;
11366 int retval = 0;
11367
11368 pb = (struct process_block_struct *) priv_data;
11369 ctx = pb->ctx;
11370 pctx = pb->pctx;
11371
11372 pctx->blk = blk;
11373 pctx->blkcount = blockcnt;
11374
11375 if (HOLE_BLKADDR(blk))
11376 return 0;
11377
11378 if ((blk < fs->super->s_first_data_block) ||
11379 (blk >= fs->super->s_blocks_count)) {
11380 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
11381 return_abort:
11382 pb->abort = 1;
11383 return BLOCK_ABORT;
11384 }
11385
11386 if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
11387 fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
11388 goto return_abort;
11389 }
11390
11391 /*
11392 * If we are deleting an orphan, then we leave the fields alone.
11393 * If we are truncating an orphan, then update the inode fields
11394 * and clean up any partial block data.
11395 */
11396 if (pb->truncating) {
11397 /*
11398 * We only remove indirect blocks if they are
11399 * completely empty.
11400 */
11401 if (blockcnt < 0) {
11402 int i, limit;
11403 blk_t *bp;
11404
11405 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11406 pb->buf);
11407 if (pb->errcode)
11408 goto return_abort;
11409
11410 limit = fs->blocksize >> 2;
11411 for (i = 0, bp = (blk_t *) pb->buf;
11412 i < limit; i++, bp++)
11413 if (*bp)
11414 return 0;
11415 }
11416 /*
11417 * We don't remove direct blocks until we've reached
11418 * the truncation block.
11419 */
11420 if (blockcnt >= 0 && blockcnt < pb->truncate_block)
11421 return 0;
11422 /*
11423 * If part of the last block needs truncating, we do
11424 * it here.
11425 */
11426 if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
11427 pb->errcode = io_channel_read_blk(fs->io, blk, 1,
11428 pb->buf);
11429 if (pb->errcode)
11430 goto return_abort;
11431 memset(pb->buf + pb->truncate_offset, 0,
11432 fs->blocksize - pb->truncate_offset);
11433 pb->errcode = io_channel_write_blk(fs->io, blk, 1,
11434 pb->buf);
11435 if (pb->errcode)
11436 goto return_abort;
11437 }
11438 pb->truncated_blocks++;
11439 *block_nr = 0;
11440 retval |= BLOCK_CHANGED;
11441 }
11442
11443 ext2fs_block_alloc_stats(fs, blk, -1);
11444 return retval;
11445}
11446
11447/*
11448 * This function releases an inode. Returns 1 if an inconsistency was
11449 * found. If the inode has a link count, then it is being truncated and
11450 * not deleted.
11451 */
11452static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
11453 struct ext2_inode *inode, char *block_buf,
11454 struct problem_context *pctx)
11455{
11456 struct process_block_struct pb;
11457 ext2_filsys fs = ctx->fs;
11458 errcode_t retval;
11459 __u32 count;
11460
11461 if (!ext2fs_inode_has_valid_blocks(inode))
11462 return 0;
11463
11464 pb.buf = block_buf + 3 * ctx->fs->blocksize;
11465 pb.ctx = ctx;
11466 pb.abort = 0;
11467 pb.errcode = 0;
11468 pb.pctx = pctx;
11469 if (inode->i_links_count) {
11470 pb.truncating = 1;
11471 pb.truncate_block = (e2_blkcnt_t)
11472 ((((long long)inode->i_size_high << 32) +
11473 inode->i_size + fs->blocksize - 1) /
11474 fs->blocksize);
11475 pb.truncate_offset = inode->i_size % fs->blocksize;
11476 } else {
11477 pb.truncating = 0;
11478 pb.truncate_block = 0;
11479 pb.truncate_offset = 0;
11480 }
11481 pb.truncated_blocks = 0;
11482 retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
11483 block_buf, release_inode_block, &pb);
11484 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011485 bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011486 ino);
11487 return 1;
11488 }
11489 if (pb.abort)
11490 return 1;
11491
11492 /* Refresh the inode since ext2fs_block_iterate may have changed it */
11493 e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
11494
11495 if (pb.truncated_blocks)
11496 inode->i_blocks -= pb.truncated_blocks *
11497 (fs->blocksize / 512);
11498
11499 if (inode->i_file_acl) {
11500 retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
11501 block_buf, -1, &count);
11502 if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
11503 retval = 0;
11504 count = 1;
11505 }
11506 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000011507 bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011508 ino);
11509 return 1;
11510 }
11511 if (count == 0)
11512 ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
11513 inode->i_file_acl = 0;
11514 }
11515 return 0;
11516}
11517
11518/*
11519 * This function releases all of the orphan inodes. It returns 1 if
11520 * it hit some error, and 0 on success.
11521 */
11522static int release_orphan_inodes(e2fsck_t ctx)
11523{
11524 ext2_filsys fs = ctx->fs;
11525 ext2_ino_t ino, next_ino;
11526 struct ext2_inode inode;
11527 struct problem_context pctx;
11528 char *block_buf;
11529
11530 if ((ino = fs->super->s_last_orphan) == 0)
11531 return 0;
11532
11533 /*
11534 * Win or lose, we won't be using the head of the orphan inode
11535 * list again.
11536 */
11537 fs->super->s_last_orphan = 0;
11538 ext2fs_mark_super_dirty(fs);
11539
11540 /*
11541 * If the filesystem contains errors, don't run the orphan
11542 * list, since the orphan list can't be trusted; and we're
11543 * going to be running a full e2fsck run anyway...
11544 */
11545 if (fs->super->s_state & EXT2_ERROR_FS)
11546 return 0;
11547
11548 if ((ino < EXT2_FIRST_INODE(fs->super)) ||
11549 (ino > fs->super->s_inodes_count)) {
11550 clear_problem_context(&pctx);
11551 pctx.ino = ino;
11552 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
11553 return 1;
11554 }
11555
11556 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
11557 "block iterate buffer");
11558 e2fsck_read_bitmaps(ctx);
11559
11560 while (ino) {
11561 e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
11562 clear_problem_context(&pctx);
11563 pctx.ino = ino;
11564 pctx.inode = &inode;
11565 pctx.str = inode.i_links_count ? _("Truncating") :
11566 _("Clearing");
11567
11568 fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
11569
11570 next_ino = inode.i_dtime;
11571 if (next_ino &&
11572 ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
11573 (next_ino > fs->super->s_inodes_count))) {
11574 pctx.ino = next_ino;
11575 fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
11576 goto return_abort;
11577 }
11578
11579 if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
11580 goto return_abort;
11581
11582 if (!inode.i_links_count) {
11583 ext2fs_inode_alloc_stats2(fs, ino, -1,
11584 LINUX_S_ISDIR(inode.i_mode));
11585 inode.i_dtime = time(0);
11586 } else {
11587 inode.i_dtime = 0;
11588 }
11589 e2fsck_write_inode(ctx, ino, &inode, "delete_file");
11590 ino = next_ino;
11591 }
11592 ext2fs_free_mem(&block_buf);
11593 return 0;
11594return_abort:
11595 ext2fs_free_mem(&block_buf);
11596 return 1;
11597}
11598
11599/*
11600 * Check the resize inode to make sure it is sane. We check both for
11601 * the case where on-line resizing is not enabled (in which case the
11602 * resize inode should be cleared) as well as the case where on-line
11603 * resizing is enabled.
11604 */
11605static void check_resize_inode(e2fsck_t ctx)
11606{
11607 ext2_filsys fs = ctx->fs;
11608 struct ext2_inode inode;
11609 struct problem_context pctx;
11610 int i, j, gdt_off, ind_off;
11611 blk_t blk, pblk, expect;
11612 __u32 *dind_buf = 0, *ind_buf;
11613 errcode_t retval;
11614
11615 clear_problem_context(&pctx);
11616
11617 /*
11618 * If the resize inode feature isn't set, then
11619 * s_reserved_gdt_blocks must be zero.
11620 */
11621 if (!(fs->super->s_feature_compat &
11622 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11623 if (fs->super->s_reserved_gdt_blocks) {
11624 pctx.num = fs->super->s_reserved_gdt_blocks;
11625 if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
11626 &pctx)) {
11627 fs->super->s_reserved_gdt_blocks = 0;
11628 ext2fs_mark_super_dirty(fs);
11629 }
11630 }
11631 }
11632
Mike Frysinger874af852006-03-08 07:03:27 +000011633 /* Read the resize inode */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011634 pctx.ino = EXT2_RESIZE_INO;
11635 retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
11636 if (retval) {
11637 if (fs->super->s_feature_compat &
11638 EXT2_FEATURE_COMPAT_RESIZE_INODE)
11639 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11640 return;
11641 }
11642
11643 /*
11644 * If the resize inode feature isn't set, check to make sure
11645 * the resize inode is cleared; then we're done.
11646 */
11647 if (!(fs->super->s_feature_compat &
11648 EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
11649 for (i=0; i < EXT2_N_BLOCKS; i++) {
11650 if (inode.i_block[i])
11651 break;
11652 }
11653 if ((i < EXT2_N_BLOCKS) &&
11654 fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
11655 memset(&inode, 0, sizeof(inode));
11656 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11657 "clear_resize");
11658 }
11659 return;
11660 }
11661
11662 /*
11663 * The resize inode feature is enabled; check to make sure the
11664 * only block in use is the double indirect block
11665 */
11666 blk = inode.i_block[EXT2_DIND_BLOCK];
11667 for (i=0; i < EXT2_N_BLOCKS; i++) {
11668 if (i != EXT2_DIND_BLOCK && inode.i_block[i])
11669 break;
11670 }
11671 if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
11672 !(inode.i_mode & LINUX_S_IFREG) ||
11673 (blk < fs->super->s_first_data_block ||
11674 blk >= fs->super->s_blocks_count)) {
11675 resize_inode_invalid:
11676 if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
11677 memset(&inode, 0, sizeof(inode));
11678 e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
11679 "clear_resize");
11680 ctx->flags |= E2F_FLAG_RESIZE_INODE;
11681 }
11682 if (!(ctx->options & E2F_OPT_READONLY)) {
11683 fs->super->s_state &= ~EXT2_VALID_FS;
11684 ext2fs_mark_super_dirty(fs);
11685 }
11686 goto cleanup;
11687 }
11688 dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
11689 "resize dind buffer");
11690 ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
11691
11692 retval = ext2fs_read_ind_block(fs, blk, dind_buf);
11693 if (retval)
11694 goto resize_inode_invalid;
11695
11696 gdt_off = fs->desc_blocks;
11697 pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
11698 for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
11699 i++, gdt_off++, pblk++) {
11700 gdt_off %= fs->blocksize/4;
11701 if (dind_buf[gdt_off] != pblk)
11702 goto resize_inode_invalid;
11703 retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
11704 if (retval)
11705 goto resize_inode_invalid;
11706 ind_off = 0;
11707 for (j = 1; j < fs->group_desc_count; j++) {
11708 if (!ext2fs_bg_has_super(fs, j))
11709 continue;
11710 expect = pblk + (j * fs->super->s_blocks_per_group);
11711 if (ind_buf[ind_off] != expect)
11712 goto resize_inode_invalid;
11713 ind_off++;
11714 }
11715 }
11716
11717cleanup:
Rob Landleye7c43b62006-03-01 16:39:45 +000011718 ext2fs_free_mem(&dind_buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011719
11720 }
11721
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011722static void check_super_block(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011723{
11724 ext2_filsys fs = ctx->fs;
11725 blk_t first_block, last_block;
11726 struct ext2_super_block *sb = fs->super;
11727 struct ext2_group_desc *gd;
11728 blk_t blocks_per_group = fs->super->s_blocks_per_group;
11729 blk_t bpg_max;
11730 int inodes_per_block;
11731 int ipg_max;
11732 int inode_size;
11733 dgrp_t i;
11734 blk_t should_be;
11735 struct problem_context pctx;
11736 __u32 free_blocks = 0, free_inodes = 0;
11737
11738 inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
11739 ipg_max = inodes_per_block * (blocks_per_group - 4);
11740 if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
11741 ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
11742 bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
11743 if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
11744 bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
11745
11746 ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11747 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
11748 ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
11749 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
11750 ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
11751 sizeof(int) * fs->group_desc_count, "invalid_inode_table");
11752
11753 clear_problem_context(&pctx);
11754
11755 /*
11756 * Verify the super block constants...
11757 */
11758 check_super_value(ctx, "inodes_count", sb->s_inodes_count,
11759 MIN_CHECK, 1, 0);
11760 check_super_value(ctx, "blocks_count", sb->s_blocks_count,
11761 MIN_CHECK, 1, 0);
11762 check_super_value(ctx, "first_data_block", sb->s_first_data_block,
11763 MAX_CHECK, 0, sb->s_blocks_count);
11764 check_super_value(ctx, "log_block_size", sb->s_log_block_size,
11765 MIN_CHECK | MAX_CHECK, 0,
11766 EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
11767 check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
11768 MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
11769 check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
11770 MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
11771 bpg_max);
11772 check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
11773 MIN_CHECK | MAX_CHECK, 8, bpg_max);
11774 check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
11775 MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
11776 check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
11777 MAX_CHECK, 0, sb->s_blocks_count / 2);
11778 check_super_value(ctx, "reserved_gdt_blocks",
11779 sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
11780 fs->blocksize/4);
11781 inode_size = EXT2_INODE_SIZE(sb);
11782 check_super_value(ctx, "inode_size",
11783 inode_size, MIN_CHECK | MAX_CHECK,
11784 EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
11785 if (inode_size & (inode_size - 1)) {
11786 pctx.num = inode_size;
11787 pctx.str = "inode_size";
11788 fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
11789 ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
11790 return;
11791 }
11792
11793 if (!ctx->num_blocks) {
11794 pctx.errcode = e2fsck_get_device_size(ctx);
11795 if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
11796 fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
11797 ctx->flags |= E2F_FLAG_ABORT;
11798 return;
11799 }
11800 if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
11801 (ctx->num_blocks < sb->s_blocks_count)) {
11802 pctx.blk = sb->s_blocks_count;
11803 pctx.blk2 = ctx->num_blocks;
11804 if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
11805 ctx->flags |= E2F_FLAG_ABORT;
11806 return;
11807 }
11808 }
11809 }
11810
11811 if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
11812 pctx.blk = EXT2_BLOCK_SIZE(sb);
11813 pctx.blk2 = EXT2_FRAG_SIZE(sb);
11814 fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
11815 ctx->flags |= E2F_FLAG_ABORT;
11816 return;
11817 }
11818
11819 should_be = sb->s_frags_per_group >>
11820 (sb->s_log_block_size - sb->s_log_frag_size);
11821 if (sb->s_blocks_per_group != should_be) {
11822 pctx.blk = sb->s_blocks_per_group;
11823 pctx.blk2 = should_be;
11824 fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
11825 ctx->flags |= E2F_FLAG_ABORT;
11826 return;
11827 }
11828
11829 should_be = (sb->s_log_block_size == 0) ? 1 : 0;
11830 if (sb->s_first_data_block != should_be) {
11831 pctx.blk = sb->s_first_data_block;
11832 pctx.blk2 = should_be;
11833 fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
11834 ctx->flags |= E2F_FLAG_ABORT;
11835 return;
11836 }
11837
11838 should_be = sb->s_inodes_per_group * fs->group_desc_count;
11839 if (sb->s_inodes_count != should_be) {
11840 pctx.ino = sb->s_inodes_count;
11841 pctx.ino2 = should_be;
11842 if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
11843 sb->s_inodes_count = should_be;
11844 ext2fs_mark_super_dirty(fs);
11845 }
11846 }
11847
11848 /*
11849 * Verify the group descriptors....
11850 */
11851 first_block = sb->s_first_data_block;
11852 last_block = first_block + blocks_per_group;
11853
11854 for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
11855 pctx.group = i;
11856
11857 if (i == fs->group_desc_count - 1)
11858 last_block = sb->s_blocks_count;
11859 if ((gd->bg_block_bitmap < first_block) ||
11860 (gd->bg_block_bitmap >= last_block)) {
11861 pctx.blk = gd->bg_block_bitmap;
11862 if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
11863 gd->bg_block_bitmap = 0;
11864 }
11865 if (gd->bg_block_bitmap == 0) {
11866 ctx->invalid_block_bitmap_flag[i]++;
11867 ctx->invalid_bitmaps++;
11868 }
11869 if ((gd->bg_inode_bitmap < first_block) ||
11870 (gd->bg_inode_bitmap >= last_block)) {
11871 pctx.blk = gd->bg_inode_bitmap;
11872 if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
11873 gd->bg_inode_bitmap = 0;
11874 }
11875 if (gd->bg_inode_bitmap == 0) {
11876 ctx->invalid_inode_bitmap_flag[i]++;
11877 ctx->invalid_bitmaps++;
11878 }
11879 if ((gd->bg_inode_table < first_block) ||
11880 ((gd->bg_inode_table +
11881 fs->inode_blocks_per_group - 1) >= last_block)) {
11882 pctx.blk = gd->bg_inode_table;
11883 if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
11884 gd->bg_inode_table = 0;
11885 }
11886 if (gd->bg_inode_table == 0) {
11887 ctx->invalid_inode_table_flag[i]++;
11888 ctx->invalid_bitmaps++;
11889 }
11890 free_blocks += gd->bg_free_blocks_count;
11891 free_inodes += gd->bg_free_inodes_count;
11892 first_block += sb->s_blocks_per_group;
11893 last_block += sb->s_blocks_per_group;
11894
11895 if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
11896 (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
11897 (gd->bg_used_dirs_count > sb->s_inodes_per_group))
11898 ext2fs_unmark_valid(fs);
11899
11900 }
11901
11902 /*
11903 * Update the global counts from the block group counts. This
11904 * is needed for an experimental patch which eliminates
11905 * locking the entire filesystem when allocating blocks or
11906 * inodes; if the filesystem is not unmounted cleanly, the
11907 * global counts may not be accurate.
11908 */
11909 if ((free_blocks != sb->s_free_blocks_count) ||
11910 (free_inodes != sb->s_free_inodes_count)) {
11911 if (ctx->options & E2F_OPT_READONLY)
11912 ext2fs_unmark_valid(fs);
11913 else {
11914 sb->s_free_blocks_count = free_blocks;
11915 sb->s_free_inodes_count = free_inodes;
11916 ext2fs_mark_super_dirty(fs);
11917 }
11918 }
11919
11920 if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
11921 (sb->s_free_inodes_count > sb->s_inodes_count))
11922 ext2fs_unmark_valid(fs);
11923
11924
11925 /*
11926 * If we have invalid bitmaps, set the error state of the
11927 * filesystem.
11928 */
11929 if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
11930 sb->s_state &= ~EXT2_VALID_FS;
11931 ext2fs_mark_super_dirty(fs);
11932 }
11933
11934 clear_problem_context(&pctx);
11935
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011936 /*
11937 * If the UUID field isn't assigned, assign it.
11938 */
11939 if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
11940 if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
11941 uuid_generate(sb->s_uuid);
11942 ext2fs_mark_super_dirty(fs);
11943 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
11944 }
11945 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011946
Rob Landley3e72c592006-04-06 22:49:04 +000011947 /* FIXME - HURD support?
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011948 * For the Hurd, check to see if the filetype option is set,
11949 * since it doesn't support it.
11950 */
11951 if (!(ctx->options & E2F_OPT_READONLY) &&
11952 fs->super->s_creator_os == EXT2_OS_HURD &&
11953 (fs->super->s_feature_incompat &
11954 EXT2_FEATURE_INCOMPAT_FILETYPE)) {
11955 if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
11956 fs->super->s_feature_incompat &=
11957 ~EXT2_FEATURE_INCOMPAT_FILETYPE;
11958 ext2fs_mark_super_dirty(fs);
11959
11960 }
11961 }
11962
11963 /*
11964 * If we have any of the compatibility flags set, we need to have a
11965 * revision 1 filesystem. Most kernels will not check the flags on
11966 * a rev 0 filesystem and we may have corruption issues because of
11967 * the incompatible changes to the filesystem.
11968 */
11969 if (!(ctx->options & E2F_OPT_READONLY) &&
11970 fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
11971 (fs->super->s_feature_compat ||
11972 fs->super->s_feature_ro_compat ||
11973 fs->super->s_feature_incompat) &&
11974 fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
11975 ext2fs_update_dynamic_rev(fs);
11976 ext2fs_mark_super_dirty(fs);
11977 }
11978
11979 check_resize_inode(ctx);
11980
11981 /*
11982 * Clean up any orphan inodes, if present.
11983 */
11984 if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
11985 fs->super->s_state &= ~EXT2_VALID_FS;
11986 ext2fs_mark_super_dirty(fs);
11987 }
11988
11989 /*
11990 * Move the ext3 journal file, if necessary.
11991 */
11992 e2fsck_move_ext3_journal(ctx);
11993 return;
11994}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000011995
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011996/*
11997 * swapfs.c --- byte-swap an ext2 filesystem
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000011998 */
11999
12000#ifdef ENABLE_SWAPFS
12001
12002struct swap_block_struct {
12003 ext2_ino_t ino;
12004 int isdir;
12005 errcode_t errcode;
12006 char *dir_buf;
12007 struct ext2_inode *inode;
12008};
12009
12010/*
12011 * This is a helper function for block_iterate. We mark all of the
12012 * indirect and direct blocks as changed, so that block_iterate will
12013 * write them out.
12014 */
12015static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
12016 void *priv_data)
12017{
12018 errcode_t retval;
12019
12020 struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
12021
12022 if (sb->isdir && (blockcnt >= 0) && *block_nr) {
12023 retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
12024 if (retval) {
12025 sb->errcode = retval;
12026 return BLOCK_ABORT;
12027 }
12028 retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
12029 if (retval) {
12030 sb->errcode = retval;
12031 return BLOCK_ABORT;
12032 }
12033 }
12034 if (blockcnt >= 0) {
12035 if (blockcnt < EXT2_NDIR_BLOCKS)
12036 return 0;
12037 return BLOCK_CHANGED;
12038 }
12039 if (blockcnt == BLOCK_COUNT_IND) {
12040 if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
12041 return 0;
12042 return BLOCK_CHANGED;
12043 }
12044 if (blockcnt == BLOCK_COUNT_DIND) {
12045 if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
12046 return 0;
12047 return BLOCK_CHANGED;
12048 }
12049 if (blockcnt == BLOCK_COUNT_TIND) {
12050 if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
12051 return 0;
12052 return BLOCK_CHANGED;
12053 }
12054 return BLOCK_CHANGED;
12055}
12056
12057/*
12058 * This function is responsible for byte-swapping all of the indirect,
12059 * block pointers. It is also responsible for byte-swapping directories.
12060 */
12061static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
12062 struct ext2_inode *inode)
12063{
12064 errcode_t retval;
12065 struct swap_block_struct sb;
12066
12067 sb.ino = ino;
12068 sb.inode = inode;
12069 sb.dir_buf = block_buf + ctx->fs->blocksize*3;
12070 sb.errcode = 0;
12071 sb.isdir = 0;
12072 if (LINUX_S_ISDIR(inode->i_mode))
12073 sb.isdir = 1;
12074
12075 retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
12076 swap_block, &sb);
12077 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012078 bb_error_msg(_("while calling ext2fs_block_iterate"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012079 ctx->flags |= E2F_FLAG_ABORT;
12080 return;
12081 }
12082 if (sb.errcode) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012083 bb_error_msg(_("while calling iterator function"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012084 ctx->flags |= E2F_FLAG_ABORT;
12085 return;
12086 }
12087}
12088
12089static void swap_inodes(e2fsck_t ctx)
12090{
12091 ext2_filsys fs = ctx->fs;
12092 dgrp_t group;
12093 unsigned int i;
12094 ext2_ino_t ino = 1;
12095 char *buf, *block_buf;
12096 errcode_t retval;
12097 struct ext2_inode * inode;
12098
12099 e2fsck_use_inode_shortcuts(ctx, 1);
12100
12101 retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
12102 &buf);
12103 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012104 bb_error_msg(_("while allocating inode buffer"));
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012105 ctx->flags |= E2F_FLAG_ABORT;
12106 return;
12107 }
12108 block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
12109 "block interate buffer");
12110 for (group = 0; group < fs->group_desc_count; group++) {
12111 retval = io_channel_read_blk(fs->io,
12112 fs->group_desc[group].bg_inode_table,
12113 fs->inode_blocks_per_group, buf);
12114 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012115 bb_error_msg(_("while reading inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012116 group);
12117 ctx->flags |= E2F_FLAG_ABORT;
12118 return;
12119 }
12120 inode = (struct ext2_inode *) buf;
12121 for (i=0; i < fs->super->s_inodes_per_group;
12122 i++, ino++, inode++) {
12123 ctx->stashed_ino = ino;
12124 ctx->stashed_inode = inode;
12125
12126 if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
12127 ext2fs_swap_inode(fs, inode, inode, 0);
12128
12129 /*
12130 * Skip deleted files.
12131 */
12132 if (inode->i_links_count == 0)
12133 continue;
12134
12135 if (LINUX_S_ISDIR(inode->i_mode) ||
12136 ((inode->i_block[EXT2_IND_BLOCK] ||
12137 inode->i_block[EXT2_DIND_BLOCK] ||
12138 inode->i_block[EXT2_TIND_BLOCK]) &&
12139 ext2fs_inode_has_valid_blocks(inode)))
12140 swap_inode_blocks(ctx, ino, block_buf, inode);
12141
12142 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12143 return;
12144
12145 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12146 ext2fs_swap_inode(fs, inode, inode, 1);
12147 }
12148 retval = io_channel_write_blk(fs->io,
12149 fs->group_desc[group].bg_inode_table,
12150 fs->inode_blocks_per_group, buf);
12151 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012152 bb_error_msg(_("while writing inode table (group %d)"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012153 group);
12154 ctx->flags |= E2F_FLAG_ABORT;
12155 return;
12156 }
12157 }
12158 ext2fs_free_mem(&buf);
12159 ext2fs_free_mem(&block_buf);
12160 e2fsck_use_inode_shortcuts(ctx, 0);
12161 ext2fs_flush_icache(fs);
12162}
12163
Rob Landley7c94bed2006-05-03 21:58:45 +000012164#if defined(__powerpc__) && BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012165/*
12166 * On the PowerPC, the big-endian variant of the ext2 filesystem
12167 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
12168 * of each word. Thus a bitmap with only bit 0 set would be, as
12169 * a string of bytes, 00 00 00 01 00 ...
12170 * To cope with this, we byte-reverse each word of a bitmap if
12171 * we have a big-endian filesystem, that is, if we are *not*
12172 * byte-swapping other word-sized numbers.
12173 */
12174#define EXT2_BIG_ENDIAN_BITMAPS
12175#endif
12176
12177#ifdef EXT2_BIG_ENDIAN_BITMAPS
12178static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
12179{
12180 __u32 *p = (__u32 *) bmap->bitmap;
12181 int n, nbytes = (bmap->end - bmap->start + 7) / 8;
12182
12183 for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
12184 *p = ext2fs_swab32(*p);
12185}
12186#endif
12187
12188
12189#ifdef ENABLE_SWAPFS
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012190static void swap_filesys(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012191{
12192 ext2_filsys fs = ctx->fs;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012193 if (!(ctx->options & E2F_OPT_PREEN))
12194 printf(_("Pass 0: Doing byte-swap of filesystem\n"));
12195
Rob Landley3e72c592006-04-06 22:49:04 +000012196 /* Byte swap */
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012197
12198 if (fs->super->s_mnt_count) {
12199 fprintf(stderr, _("%s: the filesystem must be freshly "
12200 "checked using fsck\n"
12201 "and not mounted before trying to "
12202 "byte-swap it.\n"), ctx->device_name);
12203 ctx->flags |= E2F_FLAG_ABORT;
12204 return;
12205 }
12206 if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
12207 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
12208 EXT2_FLAG_SWAP_BYTES_WRITE);
12209 fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
12210 } else {
12211 fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
12212 fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
12213 }
12214 swap_inodes(ctx);
12215 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
12216 return;
12217 if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
12218 fs->flags |= EXT2_FLAG_SWAP_BYTES;
12219 fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
12220 EXT2_FLAG_SWAP_BYTES_WRITE);
12221
12222#ifdef EXT2_BIG_ENDIAN_BITMAPS
12223 e2fsck_read_bitmaps(ctx);
12224 ext2fs_swap_bitmap(fs->inode_map);
12225 ext2fs_swap_bitmap(fs->block_map);
12226 fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
12227#endif
12228 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
12229 ext2fs_flush(fs);
12230 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012231}
12232#endif /* ENABLE_SWAPFS */
12233
12234#endif
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012235
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012236/*
12237 * util.c --- miscellaneous utilities
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012238 */
12239
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012240
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012241void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
12242 const char *description)
12243{
12244 void *ret;
12245 char buf[256];
12246
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012247 ret = malloc(size);
12248 if (!ret) {
12249 sprintf(buf, "Can't allocate %s\n", description);
Rob Landley7c94bed2006-05-03 21:58:45 +000012250 bb_error_msg_and_die(buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012251 }
12252 memset(ret, 0, size);
12253 return ret;
12254}
12255
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012256static char *string_copy(const char *str, int len)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012257{
12258 char *ret;
12259
12260 if (!str)
12261 return NULL;
12262 if (!len)
12263 len = strlen(str);
12264 ret = malloc(len+1);
12265 if (ret) {
12266 strncpy(ret, str, len);
12267 ret[len] = 0;
12268 }
12269 return ret;
12270}
12271
12272#ifndef HAVE_CONIO_H
12273static int read_a_char(void)
12274{
12275 char c;
12276 int r;
12277 int fail = 0;
12278
12279 while(1) {
12280 if (e2fsck_global_ctx &&
12281 (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
12282 return 3;
12283 }
12284 r = read(0, &c, 1);
12285 if (r == 1)
12286 return c;
12287 if (fail++ > 100)
12288 break;
12289 }
12290 return EOF;
12291}
12292#endif
12293
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012294static int ask_yn(const char * string, int def)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012295{
12296 int c;
12297 const char *defstr;
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012298 static const char short_yes[] = "yY";
12299 static const char short_no[] = "nN";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012300
12301#ifdef HAVE_TERMIOS_H
12302 struct termios termios, tmp;
12303
12304 tcgetattr (0, &termios);
12305 tmp = termios;
12306 tmp.c_lflag &= ~(ICANON | ECHO);
12307 tmp.c_cc[VMIN] = 1;
12308 tmp.c_cc[VTIME] = 0;
12309 tcsetattr (0, TCSANOW, &tmp);
12310#endif
12311
12312 if (def == 1)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012313 defstr = "<y>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012314 else if (def == 0)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012315 defstr = "<n>";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012316 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012317 defstr = " (y/n)";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012318 printf("%s%s? ", string, defstr);
12319 while (1) {
12320 fflush (stdout);
12321 if ((c = read_a_char()) == EOF)
12322 break;
12323 if (c == 3) {
12324#ifdef HAVE_TERMIOS_H
12325 tcsetattr (0, TCSANOW, &termios);
12326#endif
12327 if (e2fsck_global_ctx &&
12328 e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
12329 puts("\n");
12330 longjmp(e2fsck_global_ctx->abort_loc, 1);
12331 }
12332 puts(_("cancelled!\n"));
12333 return 0;
12334 }
12335 if (strchr(short_yes, (char) c)) {
12336 def = 1;
12337 break;
12338 }
12339 else if (strchr(short_no, (char) c)) {
12340 def = 0;
12341 break;
12342 }
12343 else if ((c == ' ' || c == '\n') && (def != -1))
12344 break;
12345 }
12346 if (def)
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012347 puts("yes\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012348 else
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012349 puts ("no\n");
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012350#ifdef HAVE_TERMIOS_H
12351 tcsetattr (0, TCSANOW, &termios);
12352#endif
12353 return def;
12354}
12355
12356int ask (e2fsck_t ctx, const char * string, int def)
12357{
12358 if (ctx->options & E2F_OPT_NO) {
12359 printf (_("%s? no\n\n"), string);
12360 return 0;
12361 }
12362 if (ctx->options & E2F_OPT_YES) {
12363 printf (_("%s? yes\n\n"), string);
12364 return 1;
12365 }
12366 if (ctx->options & E2F_OPT_PREEN) {
12367 printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
12368 return def;
12369 }
12370 return ask_yn(string, def);
12371}
12372
12373void e2fsck_read_bitmaps(e2fsck_t ctx)
12374{
12375 ext2_filsys fs = ctx->fs;
12376 errcode_t retval;
12377
12378 if (ctx->invalid_bitmaps) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012379 bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012380 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012381 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012382 }
12383
12384 ehandler_operation(_("reading inode and block bitmaps"));
12385 retval = ext2fs_read_bitmaps(fs);
12386 ehandler_operation(0);
12387 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012388 bb_error_msg(_("while retrying to read bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012389 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012390 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012391 }
12392}
12393
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012394static void e2fsck_write_bitmaps(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012395{
12396 ext2_filsys fs = ctx->fs;
12397 errcode_t retval;
12398
12399 if (ext2fs_test_bb_dirty(fs)) {
12400 ehandler_operation(_("writing block bitmaps"));
12401 retval = ext2fs_write_block_bitmap(fs);
12402 ehandler_operation(0);
12403 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012404 bb_error_msg(_("while retrying to write block bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012405 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012406 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012407 }
12408 }
12409
12410 if (ext2fs_test_ib_dirty(fs)) {
12411 ehandler_operation(_("writing inode bitmaps"));
12412 retval = ext2fs_write_inode_bitmap(fs);
12413 ehandler_operation(0);
12414 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012415 bb_error_msg(_("while retrying to write inode bitmaps for %s"),
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012416 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000012417 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012418 }
12419 }
12420}
12421
12422void preenhalt(e2fsck_t ctx)
12423{
12424 ext2_filsys fs = ctx->fs;
12425
12426 if (!(ctx->options & E2F_OPT_PREEN))
12427 return;
12428 fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
12429 "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
12430 ctx->device_name);
12431 if (fs != NULL) {
12432 fs->super->s_state |= EXT2_ERROR_FS;
12433 ext2fs_mark_super_dirty(fs);
12434 ext2fs_close(fs);
12435 }
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012436 exit(EXIT_UNCORRECTED);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012437}
12438
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012439void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
12440 struct ext2_inode * inode, const char *proc)
12441{
12442 int retval;
12443
12444 retval = ext2fs_read_inode(ctx->fs, ino, inode);
12445 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012446 bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
12447 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012448 }
12449}
12450
12451extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
12452 struct ext2_inode * inode, int bufsize,
12453 const char *proc)
12454{
12455 int retval;
12456
12457 retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
12458 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012459 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12460 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012461 }
12462}
12463
12464extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
12465 struct ext2_inode * inode, const char *proc)
12466{
12467 int retval;
12468
12469 retval = ext2fs_write_inode(ctx->fs, ino, inode);
12470 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012471 bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
12472 bb_error_msg_and_die(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012473 }
12474}
12475
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012476blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
12477 io_manager manager)
12478{
12479 struct ext2_super_block *sb;
12480 io_channel io = NULL;
12481 void *buf = NULL;
12482 int blocksize;
12483 blk_t superblock, ret_sb = 8193;
12484
12485 if (fs && fs->super) {
12486 ret_sb = (fs->super->s_blocks_per_group +
12487 fs->super->s_first_data_block);
12488 if (ctx) {
12489 ctx->superblock = ret_sb;
12490 ctx->blocksize = fs->blocksize;
12491 }
12492 return ret_sb;
12493 }
12494
12495 if (ctx) {
12496 if (ctx->blocksize) {
12497 ret_sb = ctx->blocksize * 8;
12498 if (ctx->blocksize == 1024)
12499 ret_sb++;
12500 ctx->superblock = ret_sb;
12501 return ret_sb;
12502 }
12503 ctx->superblock = ret_sb;
12504 ctx->blocksize = 1024;
12505 }
12506
12507 if (!name || !manager)
12508 goto cleanup;
12509
12510 if (manager->open(name, 0, &io) != 0)
12511 goto cleanup;
12512
12513 if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
12514 goto cleanup;
12515 sb = (struct ext2_super_block *) buf;
12516
12517 for (blocksize = EXT2_MIN_BLOCK_SIZE;
12518 blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
12519 superblock = blocksize*8;
12520 if (blocksize == 1024)
12521 superblock++;
12522 io_channel_set_blksize(io, blocksize);
12523 if (io_channel_read_blk(io, superblock,
12524 -SUPERBLOCK_SIZE, buf))
12525 continue;
Rob Landley7c94bed2006-05-03 21:58:45 +000012526#if BB_BIG_ENDIAN
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012527 if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
12528 ext2fs_swap_super(sb);
12529#endif
12530 if (sb->s_magic == EXT2_SUPER_MAGIC) {
12531 ret_sb = superblock;
12532 if (ctx) {
12533 ctx->superblock = superblock;
12534 ctx->blocksize = blocksize;
12535 }
12536 break;
12537 }
12538 }
12539
12540cleanup:
12541 if (io)
12542 io_channel_close(io);
Rob Landleye7c43b62006-03-01 16:39:45 +000012543 ext2fs_free_mem(&buf);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012544 return (ret_sb);
12545}
12546
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012547
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012548/*
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012549 * This function runs through the e2fsck passes and calls them all,
12550 * returning restart, abort, or cancel as necessary...
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012551 */
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012552typedef void (*pass_t)(e2fsck_t ctx);
12553
12554static const pass_t e2fsck_passes[] = {
12555 e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
12556 e2fsck_pass5, 0 };
12557
12558#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
12559
12560static int e2fsck_run(e2fsck_t ctx)
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012561{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012562 int i;
12563 pass_t e2fsck_pass;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012564
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012565 if (setjmp(ctx->abort_loc)) {
12566 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
12567 return (ctx->flags & E2F_FLAG_RUN_RETURN);
12568 }
12569 ctx->flags |= E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012570
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012571 for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
12572 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12573 break;
12574 e2fsck_pass(ctx);
12575 if (ctx->progress)
12576 (void) (ctx->progress)(ctx, 0, 0, 0);
12577 }
12578 ctx->flags &= ~E2F_FLAG_SETJMP_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012579
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012580 if (ctx->flags & E2F_FLAG_RUN_RETURN)
12581 return (ctx->flags & E2F_FLAG_RUN_RETURN);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012582 return 0;
12583}
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012584
12585
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012586/*
12587 * unix.c - The unix-specific code for e2fsck
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012588 */
12589
12590
Mike Frysinger51a43b42005-09-24 07:11:16 +000012591/* Command line options */
12592static int swapfs;
12593#ifdef ENABLE_SWAPFS
12594static int normalize_swapfs;
12595#endif
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012596static int cflag; /* check disk */
Mike Frysinger51a43b42005-09-24 07:11:16 +000012597static int show_version_only;
12598static int verbose;
12599
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012600#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
12601
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012602static void show_stats(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012603{
12604 ext2_filsys fs = ctx->fs;
12605 int inodes, inodes_used, blocks, blocks_used;
12606 int dir_links;
12607 int num_files, num_links;
12608 int frag_percent;
12609
12610 dir_links = 2 * ctx->fs_directory_count - 1;
12611 num_files = ctx->fs_total_count - dir_links;
12612 num_links = ctx->fs_links_count - dir_links;
12613 inodes = fs->super->s_inodes_count;
12614 inodes_used = (fs->super->s_inodes_count -
12615 fs->super->s_free_inodes_count);
12616 blocks = fs->super->s_blocks_count;
12617 blocks_used = (fs->super->s_blocks_count -
12618 fs->super->s_free_blocks_count);
12619
12620 frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
12621 frag_percent = (frag_percent + 5) / 10;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012622
Mike Frysinger51a43b42005-09-24 07:11:16 +000012623 if (!verbose) {
12624 printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
12625 ctx->device_name, inodes_used, inodes,
12626 frag_percent / 10, frag_percent % 10,
12627 blocks_used, blocks);
12628 return;
12629 }
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012630 printf ("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
12631 100 * inodes_used / inodes);
12632 printf ("%8d non-contiguous inode%s (%0d.%d%%)\n",
12633 P_E2("", "s", ctx->fs_fragmented),
12634 frag_percent / 10, frag_percent % 10);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012635 printf (_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
12636 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012637 printf ("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
12638 (int) ((long long) 100 * blocks_used / blocks));
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012639 printf ("%8d large file%s\n", P_E2("", "s", ctx->large_files));
12640 printf ("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
12641 printf ("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
12642 printf ("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
12643 printf ("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
12644 printf ("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
12645 printf ("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
12646 printf ("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
12647 printf (" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
12648 printf ("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
12649 printf ("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012650}
12651
12652static void check_mount(e2fsck_t ctx)
12653{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012654 errcode_t retval;
12655 int cont;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012656
12657 retval = ext2fs_check_if_mounted(ctx->filesystem_name,
12658 &ctx->mount_flags);
12659 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000012660 bb_error_msg(_("while determining whether %s is mounted."),
Mike Frysinger51a43b42005-09-24 07:11:16 +000012661 ctx->filesystem_name);
12662 return;
12663 }
12664
12665 /*
12666 * If the filesystem isn't mounted, or it's the root filesystem
12667 * and it's mounted read-only, then everything's fine.
12668 */
12669 if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
12670 ((ctx->mount_flags & EXT2_MF_ISROOT) &&
12671 (ctx->mount_flags & EXT2_MF_READONLY)))
12672 return;
12673
12674 if (ctx->options & E2F_OPT_READONLY) {
12675 printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
12676 return;
12677 }
12678
12679 printf(_("%s is mounted. "), ctx->filesystem_name);
12680 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000012681 bb_error_msg_and_die(_("Cannot continue, aborting.\n\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000012682 printf(_("\n\n\007\007\007\007WARNING!!! "
12683 "Running e2fsck on a mounted filesystem may cause\n"
12684 "SEVERE filesystem damage.\007\007\007\n\n"));
12685 cont = ask_yn(_("Do you really want to continue"), -1);
12686 if (!cont) {
12687 printf (_("check aborted.\n"));
12688 exit (0);
12689 }
12690 return;
12691}
12692
12693static int is_on_batt(void)
12694{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012695 FILE *f;
12696 DIR *d;
12697 char tmp[80], tmp2[80], fname[80];
12698 unsigned int acflag;
12699 struct dirent* de;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012700
12701 f = fopen("/proc/apm", "r");
12702 if (f) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012703 if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012704 acflag = 1;
12705 fclose(f);
12706 return (acflag != 1);
12707 }
12708 d = opendir("/proc/acpi/ac_adapter");
12709 if (d) {
12710 while ((de=readdir(d)) != NULL) {
12711 if (!strncmp(".", de->d_name, 1))
12712 continue;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012713 snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
Mike Frysinger51a43b42005-09-24 07:11:16 +000012714 de->d_name);
12715 f = fopen(fname, "r");
12716 if (!f)
12717 continue;
12718 if (fscanf(f, "%s %s", tmp2, tmp) != 2)
12719 tmp[0] = 0;
12720 fclose(f);
12721 if (strncmp(tmp, "off-line", 8) == 0) {
12722 closedir(d);
12723 return 1;
12724 }
12725 }
12726 closedir(d);
12727 }
12728 return 0;
12729}
12730
12731/*
12732 * This routine checks to see if a filesystem can be skipped; if so,
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012733 * it will exit with EXIT_OK. Under some conditions it will print a
Mike Frysinger51a43b42005-09-24 07:11:16 +000012734 * message explaining why a check is being forced.
12735 */
12736static void check_if_skip(e2fsck_t ctx)
12737{
12738 ext2_filsys fs = ctx->fs;
12739 const char *reason = NULL;
12740 unsigned int reason_arg = 0;
12741 long next_check;
12742 int batt = is_on_batt();
12743 time_t now = time(0);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012744
Rob Landleyd8f66012006-05-05 17:29:09 +000012745 if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012746 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012747
Mike Frysinger51a43b42005-09-24 07:11:16 +000012748 if ((fs->super->s_state & EXT2_ERROR_FS) ||
12749 !ext2fs_test_valid(fs))
12750 reason = _(" contains a file system with errors");
12751 else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
12752 reason = _(" was not cleanly unmounted");
12753 else if ((fs->super->s_max_mnt_count > 0) &&
12754 (fs->super->s_mnt_count >=
12755 (unsigned) fs->super->s_max_mnt_count)) {
12756 reason = _(" has been mounted %u times without being checked");
12757 reason_arg = fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012758 if (batt && (fs->super->s_mnt_count <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012759 (unsigned) fs->super->s_max_mnt_count*2))
12760 reason = 0;
12761 } else if (fs->super->s_checkinterval &&
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012762 ((now - fs->super->s_lastcheck) >=
Mike Frysinger51a43b42005-09-24 07:11:16 +000012763 fs->super->s_checkinterval)) {
12764 reason = _(" has gone %u days without being checked");
12765 reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012766 if (batt && ((now - fs->super->s_lastcheck) <
Mike Frysinger51a43b42005-09-24 07:11:16 +000012767 fs->super->s_checkinterval*2))
12768 reason = 0;
12769 }
12770 if (reason) {
12771 fputs(ctx->device_name, stdout);
12772 printf(reason, reason_arg);
12773 fputs(_(", check forced.\n"), stdout);
12774 return;
12775 }
12776 printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
12777 fs->super->s_inodes_count - fs->super->s_free_inodes_count,
12778 fs->super->s_inodes_count,
12779 fs->super->s_blocks_count - fs->super->s_free_blocks_count,
12780 fs->super->s_blocks_count);
12781 next_check = 100000;
12782 if (fs->super->s_max_mnt_count > 0) {
12783 next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012784 if (next_check <= 0)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012785 next_check = 1;
12786 }
12787 if (fs->super->s_checkinterval &&
12788 ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
12789 next_check = 1;
12790 if (next_check <= 5) {
12791 if (next_check == 1)
12792 fputs(_(" (check after next mount)"), stdout);
12793 else
12794 printf(_(" (check in %ld mounts)"), next_check);
12795 }
12796 fputc('\n', stdout);
12797 ext2fs_close(fs);
12798 ctx->fs = NULL;
12799 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000012800 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012801}
12802
12803/*
12804 * For completion notice
12805 */
12806struct percent_tbl {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012807 int max_pass;
12808 int table[32];
Mike Frysinger51a43b42005-09-24 07:11:16 +000012809};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012810static const struct percent_tbl e2fsck_tbl = {
Mike Frysinger51a43b42005-09-24 07:11:16 +000012811 5, { 0, 70, 90, 92, 95, 100 }
12812};
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012813
Mike Frysinger51a43b42005-09-24 07:11:16 +000012814static char bar[128], spaces[128];
12815
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012816static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
Mike Frysinger51a43b42005-09-24 07:11:16 +000012817 int max)
12818{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012819 float percent;
12820
Mike Frysinger51a43b42005-09-24 07:11:16 +000012821 if (pass <= 0)
12822 return 0.0;
12823 if (pass > tbl->max_pass || max == 0)
12824 return 100.0;
12825 percent = ((float) curr) / ((float) max);
12826 return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
12827 + tbl->table[pass-1]);
12828}
12829
Rob Landleydfba7412006-03-06 20:47:33 +000012830void e2fsck_clear_progbar(e2fsck_t ctx)
Mike Frysinger51a43b42005-09-24 07:11:16 +000012831{
12832 if (!(ctx->flags & E2F_FLAG_PROG_BAR))
12833 return;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012834
Mike Frysinger51a43b42005-09-24 07:11:16 +000012835 printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
12836 ctx->stop_meta);
12837 fflush(stdout);
12838 ctx->flags &= ~E2F_FLAG_PROG_BAR;
12839}
12840
12841int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
12842 unsigned int dpynum)
12843{
12844 static const char spinner[] = "\\|/-";
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012845 int i;
12846 unsigned int tick;
12847 struct timeval tv;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012848 int dpywidth;
12849 int fixed_percent;
12850
12851 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
12852 return 0;
12853
12854 /*
12855 * Calculate the new progress position. If the
12856 * percentage hasn't changed, then we skip out right
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012857 * away.
Mike Frysinger51a43b42005-09-24 07:11:16 +000012858 */
12859 fixed_percent = (int) ((10 * percent) + 0.5);
12860 if (ctx->progress_last_percent == fixed_percent)
12861 return 0;
12862 ctx->progress_last_percent = fixed_percent;
12863
12864 /*
12865 * If we've already updated the spinner once within
12866 * the last 1/8th of a second, no point doing it
12867 * again.
12868 */
12869 gettimeofday(&tv, NULL);
12870 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
12871 if ((tick == ctx->progress_last_time) &&
12872 (fixed_percent != 0) && (fixed_percent != 1000))
12873 return 0;
12874 ctx->progress_last_time = tick;
12875
12876 /*
12877 * Advance the spinner, and note that the progress bar
12878 * will be on the screen
12879 */
12880 ctx->progress_pos = (ctx->progress_pos+1) & 3;
12881 ctx->flags |= E2F_FLAG_PROG_BAR;
12882
12883 dpywidth = 66 - strlen(label);
12884 dpywidth = 8 * (dpywidth / 8);
12885 if (dpynum)
12886 dpywidth -= 8;
12887
12888 i = ((percent * dpywidth) + 50) / 100;
12889 printf("%s%s: |%s%s", ctx->start_meta, label,
12890 bar + (sizeof(bar) - (i+1)),
12891 spaces + (sizeof(spaces) - (dpywidth - i + 1)));
12892 if (fixed_percent == 1000)
12893 fputc('|', stdout);
12894 else
12895 fputc(spinner[ctx->progress_pos & 3], stdout);
12896 printf(" %4.1f%% ", percent);
12897 if (dpynum)
12898 printf("%u\r", dpynum);
12899 else
12900 fputs(" \r", stdout);
12901 fputs(ctx->stop_meta, stdout);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012902
Mike Frysinger51a43b42005-09-24 07:11:16 +000012903 if (fixed_percent == 1000)
12904 e2fsck_clear_progbar(ctx);
12905 fflush(stdout);
12906
12907 return 0;
12908}
12909
12910static int e2fsck_update_progress(e2fsck_t ctx, int pass,
12911 unsigned long cur, unsigned long max)
12912{
12913 char buf[80];
12914 float percent;
12915
12916 if (pass == 0)
12917 return 0;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012918
Mike Frysinger51a43b42005-09-24 07:11:16 +000012919 if (ctx->progress_fd) {
12920 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
12921 write(ctx->progress_fd, buf, strlen(buf));
12922 } else {
12923 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
12924 e2fsck_simple_progress(ctx, ctx->device_name,
12925 percent, 0);
12926 }
12927 return 0;
12928}
12929
Mike Frysinger51a43b42005-09-24 07:11:16 +000012930static void reserve_stdio_fds(void)
12931{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012932 int fd;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012933
12934 while (1) {
"Vladimir N. Oleynik"6c35c7c2005-10-12 15:34:25 +000012935 fd = open(bb_dev_null, O_RDWR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012936 if (fd > 2)
12937 break;
12938 if (fd < 0) {
12939 fprintf(stderr, _("ERROR: Couldn't open "
12940 "/dev/null (%s)\n"),
12941 strerror(errno));
12942 break;
12943 }
12944 }
12945 close(fd);
12946}
12947
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012948static void signal_progress_on(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012949{
12950 e2fsck_t ctx = e2fsck_global_ctx;
12951
12952 if (!ctx)
12953 return;
12954
12955 ctx->progress = e2fsck_update_progress;
12956 ctx->progress_fd = 0;
12957}
12958
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012959static void signal_progress_off(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012960{
12961 e2fsck_t ctx = e2fsck_global_ctx;
12962
12963 if (!ctx)
12964 return;
12965
12966 e2fsck_clear_progbar(ctx);
12967 ctx->progress = 0;
12968}
12969
"Vladimir N. Oleynik"3ebb8952005-10-12 12:24:01 +000012970static void signal_cancel(int sig FSCK_ATTR((unused)))
Mike Frysinger51a43b42005-09-24 07:11:16 +000012971{
12972 e2fsck_t ctx = e2fsck_global_ctx;
12973
12974 if (!ctx)
12975 exit(FSCK_CANCELED);
12976
12977 ctx->flags |= E2F_FLAG_CANCEL;
12978}
Mike Frysinger51a43b42005-09-24 07:11:16 +000012979
12980static void parse_extended_opts(e2fsck_t ctx, const char *opts)
12981{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012982 char *buf, *token, *next, *p, *arg;
12983 int ea_ver;
12984 int extended_usage = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000012985
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000012986 buf = string_copy(opts, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000012987 for (token = buf; token && *token; token = next) {
12988 p = strchr(token, ',');
12989 next = 0;
12990 if (p) {
12991 *p = 0;
12992 next = p+1;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000012993 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000012994 arg = strchr(token, '=');
12995 if (arg) {
12996 *arg = 0;
12997 arg++;
12998 }
12999 if (strcmp(token, "ea_ver") == 0) {
13000 if (!arg) {
13001 extended_usage++;
13002 continue;
13003 }
13004 ea_ver = strtoul(arg, &p, 0);
13005 if (*p ||
13006 ((ea_ver != 1) && (ea_ver != 2))) {
13007 fprintf(stderr,
13008 _("Invalid EA version.\n"));
13009 extended_usage++;
13010 continue;
13011 }
13012 ctx->ext_attr_ver = ea_ver;
Mike Frysinger874af852006-03-08 07:03:27 +000013013 } else {
13014 fprintf(stderr, _("Unknown extended option: %s\n"),
13015 token);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013016 extended_usage++;
Mike Frysinger874af852006-03-08 07:03:27 +000013017 }
Mike Frysinger51a43b42005-09-24 07:11:16 +000013018 }
13019 if (extended_usage) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013020 bb_error_msg_and_die(
13021 "Extended options are separated by commas, "
Mike Frysinger51a43b42005-09-24 07:11:16 +000013022 "and may take an argument which\n"
13023 "is set off by an equals ('=') sign. "
Mike Frysinger874af852006-03-08 07:03:27 +000013024 "Valid extended options are:\n"
13025 "\tea_ver=<ea_version (1 or 2)>\n\n");
Mike Frysinger51a43b42005-09-24 07:11:16 +000013026 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013027}
Mike Frysinger51a43b42005-09-24 07:11:16 +000013028
13029
13030static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
13031{
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013032 int flush = 0;
13033 int c, fd;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013034 e2fsck_t ctx;
13035 errcode_t retval;
13036 struct sigaction sa;
13037 char *extended_opts = 0;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013038
13039 retval = e2fsck_allocate_context(&ctx);
13040 if (retval)
13041 return retval;
13042
13043 *ret_ctx = ctx;
13044
13045 setvbuf(stdout, NULL, _IONBF, BUFSIZ);
13046 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
13047 if (isatty(0) && isatty(1)) {
13048 ctx->interactive = 1;
13049 } else {
13050 ctx->start_meta[0] = '\001';
13051 ctx->stop_meta[0] = '\002';
13052 }
13053 memset(bar, '=', sizeof(bar)-1);
13054 memset(spaces, ' ', sizeof(spaces)-1);
13055 blkid_get_cache(&ctx->blkid, NULL);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013056
Mike Frysinger51a43b42005-09-24 07:11:16 +000013057 if (argc && *argv)
13058 ctx->program_name = *argv;
13059 else
13060 ctx->program_name = "e2fsck";
13061 while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
13062 switch (c) {
13063 case 'C':
13064 ctx->progress = e2fsck_update_progress;
13065 ctx->progress_fd = atoi(optarg);
13066 if (!ctx->progress_fd)
13067 break;
13068 /* Validate the file descriptor to avoid disasters */
13069 fd = dup(ctx->progress_fd);
13070 if (fd < 0) {
13071 fprintf(stderr,
13072 _("Error validating file descriptor %d: %s\n"),
13073 ctx->progress_fd,
13074 error_message(errno));
Rob Landley7c94bed2006-05-03 21:58:45 +000013075 bb_error_msg_and_die(_("Invalid completion information file descriptor"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013076 } else
13077 close(fd);
13078 break;
13079 case 'D':
13080 ctx->options |= E2F_OPT_COMPRESS_DIRS;
13081 break;
13082 case 'E':
13083 extended_opts = optarg;
13084 break;
13085 case 'p':
13086 case 'a':
13087 if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
13088 conflict_opt:
Rob Landley7c94bed2006-05-03 21:58:45 +000013089 bb_error_msg_and_die(_("Only one the options -p/-a, -n or -y may be specified."));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013090 }
13091 ctx->options |= E2F_OPT_PREEN;
13092 break;
13093 case 'n':
13094 if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
13095 goto conflict_opt;
13096 ctx->options |= E2F_OPT_NO;
13097 break;
13098 case 'y':
13099 if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
13100 goto conflict_opt;
13101 ctx->options |= E2F_OPT_YES;
13102 break;
13103 case 't':
Rob Landley3e72c592006-04-06 22:49:04 +000013104 /* FIXME - This needs to go away in a future path - will change binary */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013105 fprintf(stderr, _("The -t option is not "
13106 "supported on this version of e2fsck.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013107 break;
13108 case 'c':
13109 if (cflag++)
13110 ctx->options |= E2F_OPT_WRITECHECK;
13111 ctx->options |= E2F_OPT_CHECKBLOCKS;
13112 break;
13113 case 'r':
13114 /* What we do by default, anyway! */
13115 break;
13116 case 'b':
13117 ctx->use_superblock = atoi(optarg);
13118 ctx->flags |= E2F_FLAG_SB_SPECIFIED;
13119 break;
13120 case 'B':
13121 ctx->blocksize = atoi(optarg);
13122 break;
13123 case 'I':
13124 ctx->inode_buffer_blocks = atoi(optarg);
13125 break;
13126 case 'j':
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013127 ctx->journal_name = string_copy(optarg, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013128 break;
13129 case 'P':
13130 ctx->process_inode_size = atoi(optarg);
13131 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013132 case 'd':
13133 ctx->options |= E2F_OPT_DEBUG;
13134 break;
13135 case 'f':
13136 ctx->options |= E2F_OPT_FORCE;
13137 break;
13138 case 'F':
13139 flush = 1;
13140 break;
13141 case 'v':
13142 verbose = 1;
13143 break;
13144 case 'V':
13145 show_version_only = 1;
13146 break;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013147 case 'N':
13148 ctx->device_name = optarg;
13149 break;
13150#ifdef ENABLE_SWAPFS
13151 case 's':
13152 normalize_swapfs = 1;
13153 case 'S':
13154 swapfs = 1;
13155 break;
13156#else
13157 case 's':
13158 case 'S':
13159 fprintf(stderr, _("Byte-swapping filesystems "
13160 "not compiled in this version "
13161 "of e2fsck\n"));
13162 exit(1);
13163#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013164 default:
Rob Landley7c94bed2006-05-03 21:58:45 +000013165 bb_show_usage();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013166 }
13167 if (show_version_only)
13168 return 0;
13169 if (optind != argc - 1)
Rob Landley7c94bed2006-05-03 21:58:45 +000013170 bb_show_usage();
Rob Landleyd8f66012006-05-05 17:29:09 +000013171 if ((ctx->options & E2F_OPT_NO) &&
Mike Frysinger51a43b42005-09-24 07:11:16 +000013172 !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
13173 ctx->options |= E2F_OPT_READONLY;
13174 ctx->io_options = strchr(argv[optind], '?');
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013175 if (ctx->io_options)
Mike Frysinger51a43b42005-09-24 07:11:16 +000013176 *ctx->io_options++ = 0;
13177 ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
13178 if (!ctx->filesystem_name) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013179 bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
13180 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013181 }
13182 if (extended_opts)
13183 parse_extended_opts(ctx, extended_opts);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013184
Mike Frysinger51a43b42005-09-24 07:11:16 +000013185 if (flush) {
13186 fd = open(ctx->filesystem_name, O_RDONLY, 0);
13187 if (fd < 0) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013188 bb_error_msg(_("while opening %s for flushing"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013189 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013190 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013191 }
13192 if ((retval = ext2fs_sync_device(fd, 1))) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013193 bb_error_msg(_("while trying to flush %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013194 ctx->filesystem_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013195 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013196 }
13197 close(fd);
13198 }
13199#ifdef ENABLE_SWAPFS
Rob Landleyd8f66012006-05-05 17:29:09 +000013200 if (swapfs && cflag) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013201 fprintf(stderr, _("Incompatible options not "
13202 "allowed when byte-swapping.\n"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013203 exit(EXIT_USAGE);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013204 }
13205#endif
Mike Frysinger51a43b42005-09-24 07:11:16 +000013206 /*
13207 * Set up signal action
13208 */
13209 memset(&sa, 0, sizeof(struct sigaction));
13210 sa.sa_handler = signal_cancel;
13211 sigaction(SIGINT, &sa, 0);
13212 sigaction(SIGTERM, &sa, 0);
13213#ifdef SA_RESTART
13214 sa.sa_flags = SA_RESTART;
13215#endif
13216 e2fsck_global_ctx = ctx;
13217 sa.sa_handler = signal_progress_on;
13218 sigaction(SIGUSR1, &sa, 0);
13219 sa.sa_handler = signal_progress_off;
13220 sigaction(SIGUSR2, &sa, 0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013221
13222 /* Update our PATH to include /sbin if we need to run badblocks */
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013223 if (cflag)
13224 e2fs_set_sbin_path();
Mike Frysinger51a43b42005-09-24 07:11:16 +000013225 return 0;
13226}
13227
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013228static const char my_ver_string[] = E2FSPROGS_VERSION;
13229static const char my_ver_date[] = E2FSPROGS_DATE;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013230
Mike Frysinger51a43b42005-09-24 07:11:16 +000013231int e2fsck_main (int argc, char *argv[])
13232{
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013233 errcode_t retval;
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013234 int exit_value = EXIT_OK;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013235 ext2_filsys fs = 0;
13236 io_manager io_ptr;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013237 struct ext2_super_block *sb;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013238 const char *lib_ver_date;
13239 int my_ver, lib_ver;
13240 e2fsck_t ctx;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013241 struct problem_context pctx;
13242 int flags, run_result;
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013243
Mike Frysinger51a43b42005-09-24 07:11:16 +000013244 clear_problem_context(&pctx);
Rob Landley3e72c592006-04-06 22:49:04 +000013245
Mike Frysinger51a43b42005-09-24 07:11:16 +000013246 my_ver = ext2fs_parse_version_string(my_ver_string);
13247 lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
13248 if (my_ver > lib_ver) {
13249 fprintf( stderr, _("Error: ext2fs library version "
13250 "out of date!\n"));
13251 show_version_only++;
13252 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013253
Mike Frysinger51a43b42005-09-24 07:11:16 +000013254 retval = PRS(argc, argv, &ctx);
13255 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013256 bb_error_msg(_("while trying to initialize program"));
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013257 exit(EXIT_ERROR);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013258 }
13259 reserve_stdio_fds();
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013260
Mike Frysinger51a43b42005-09-24 07:11:16 +000013261 if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
13262 fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
13263 my_ver_date);
13264
13265 if (show_version_only) {
13266 fprintf(stderr, _("\tUsing %s, %s\n"),
13267 error_message(EXT2_ET_BASE), lib_ver_date);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013268 exit(EXIT_OK);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013269 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013270
Mike Frysinger51a43b42005-09-24 07:11:16 +000013271 check_mount(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013272
Mike Frysinger51a43b42005-09-24 07:11:16 +000013273 if (!(ctx->options & E2F_OPT_PREEN) &&
13274 !(ctx->options & E2F_OPT_NO) &&
13275 !(ctx->options & E2F_OPT_YES)) {
13276 if (!ctx->interactive)
Rob Landley7c94bed2006-05-03 21:58:45 +000013277 bb_error_msg_and_die(_("need terminal for interactive repairs"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013278 }
13279 ctx->superblock = ctx->use_superblock;
13280restart:
13281#ifdef CONFIG_TESTIO_DEBUG
13282 io_ptr = test_io_manager;
13283 test_io_backing_manager = unix_io_manager;
13284#else
13285 io_ptr = unix_io_manager;
13286#endif
13287 flags = 0;
13288 if ((ctx->options & E2F_OPT_READONLY) == 0)
13289 flags |= EXT2_FLAG_RW;
13290
13291 if (ctx->superblock && ctx->blocksize) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013292 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013293 flags, ctx->superblock, ctx->blocksize,
13294 io_ptr, &fs);
13295 } else if (ctx->superblock) {
13296 int blocksize;
13297 for (blocksize = EXT2_MIN_BLOCK_SIZE;
13298 blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013299 retval = ext2fs_open2(ctx->filesystem_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013300 ctx->io_options, flags,
13301 ctx->superblock, blocksize,
13302 io_ptr, &fs);
13303 if (!retval)
13304 break;
13305 }
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013306 } else
13307 retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013308 flags, 0, 0, io_ptr, &fs);
13309 if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
13310 !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
13311 ((retval == EXT2_ET_BAD_MAGIC) ||
13312 ((retval == 0) && ext2fs_check_desc(fs)))) {
13313 if (!fs || (fs->group_desc_count > 1)) {
13314 printf(_("%s trying backup blocks...\n"),
13315 retval ? _("Couldn't find ext2 superblock,") :
13316 _("Group descriptors look bad..."));
13317 get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
13318 if (fs)
13319 ext2fs_close(fs);
13320 goto restart;
13321 }
13322 }
13323 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013324 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013325 ctx->filesystem_name);
13326 if (retval == EXT2_ET_REV_TOO_HIGH) {
13327 printf(_("The filesystem revision is apparently "
13328 "too high for this version of e2fsck.\n"
13329 "(Or the filesystem superblock "
13330 "is corrupt)\n\n"));
13331 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
13332 } else if (retval == EXT2_ET_SHORT_READ)
13333 printf(_("Could this be a zero-length partition?\n"));
13334 else if ((retval == EPERM) || (retval == EACCES))
13335 printf(_("You must have %s access to the "
13336 "filesystem or be root\n"),
13337 (ctx->options & E2F_OPT_READONLY) ?
13338 "r/o" : "r/w");
13339 else if (retval == ENXIO)
13340 printf(_("Possibly non-existent or swap device?\n"));
13341#ifdef EROFS
13342 else if (retval == EROFS)
13343 printf(_("Disk write-protected; use the -n option "
13344 "to do a read-only\n"
13345 "check of the device.\n"));
13346#endif
13347 else
13348 fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
Rob Landley7c94bed2006-05-03 21:58:45 +000013349 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013350 }
13351 ctx->fs = fs;
13352 fs->priv_data = ctx;
13353 sb = fs->super;
13354 if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013355 bb_error_msg(_("while trying to open %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013356 ctx->filesystem_name);
13357 get_newer:
Rob Landley7c94bed2006-05-03 21:58:45 +000013358 bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013359 }
13360
13361 /*
13362 * Set the device name, which is used whenever we print error
13363 * or informational messages to the user.
13364 */
13365 if (ctx->device_name == 0 &&
13366 (sb->s_volume_name[0] != 0)) {
"Vladimir N. Oleynik"69d728b2005-09-28 15:16:22 +000013367 ctx->device_name = string_copy(sb->s_volume_name,
Mike Frysinger51a43b42005-09-24 07:11:16 +000013368 sizeof(sb->s_volume_name));
13369 }
13370 if (ctx->device_name == 0)
13371 ctx->device_name = ctx->filesystem_name;
13372
13373 /*
13374 * Make sure the ext3 superblock fields are consistent.
13375 */
13376 retval = e2fsck_check_ext3_journal(ctx);
13377 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013378 bb_error_msg(_("while checking ext3 journal for %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013379 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013380 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013381 }
13382
13383 /*
13384 * Check to see if we need to do ext3-style recovery. If so,
13385 * do it, and then restart the fsck.
13386 */
13387 if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
13388 if (ctx->options & E2F_OPT_READONLY) {
13389 printf(_("Warning: skipping journal recovery "
13390 "because doing a read-only filesystem "
13391 "check.\n"));
13392 io_channel_flush(ctx->fs->io);
13393 } else {
13394 if (ctx->flags & E2F_FLAG_RESTARTED) {
13395 /*
13396 * Whoops, we attempted to run the
13397 * journal twice. This should never
13398 * happen, unless the hardware or
13399 * device driver is being bogus.
13400 */
Rob Landley7c94bed2006-05-03 21:58:45 +000013401 bb_error_msg(_("unable to set superblock flags on %s\n"), ctx->device_name);
13402 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013403 }
13404 retval = e2fsck_run_ext3_journal(ctx);
13405 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013406 bb_error_msg(_("while recovering ext3 journal of %s"),
Mike Frysinger51a43b42005-09-24 07:11:16 +000013407 ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013408 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013409 }
13410 ext2fs_close(ctx->fs);
13411 ctx->fs = 0;
13412 ctx->flags |= E2F_FLAG_RESTARTED;
13413 goto restart;
13414 }
13415 }
13416
13417 /*
13418 * Check for compatibility with the feature sets. We need to
13419 * be more stringent than ext2fs_open().
13420 */
13421 if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
13422 (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013423 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013424 goto get_newer;
13425 }
13426 if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013427 bb_error_msg("(%s)", ctx->device_name);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013428 goto get_newer;
13429 }
13430#ifdef ENABLE_COMPRESSION
Rob Landley3e72c592006-04-06 22:49:04 +000013431 /* FIXME - do we support this at all? */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013432 if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
Rob Landley7c94bed2006-05-03 21:58:45 +000013433 bb_error_msg(_("Warning: compression support is experimental.\n"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013434#endif
13435#ifndef ENABLE_HTREE
13436 if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013437 bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
Mike Frysinger51a43b42005-09-24 07:11:16 +000013438 "but filesystem %s has HTREE directories.\n"),
13439 ctx->device_name);
13440 goto get_newer;
13441 }
13442#endif
13443
13444 /*
13445 * If the user specified a specific superblock, presumably the
13446 * master superblock has been trashed. So we mark the
13447 * superblock as dirty, so it can be written out.
13448 */
13449 if (ctx->superblock &&
13450 !(ctx->options & E2F_OPT_READONLY))
13451 ext2fs_mark_super_dirty(fs);
13452
13453 /*
13454 * We only update the master superblock because (a) paranoia;
13455 * we don't want to corrupt the backup superblocks, and (b) we
13456 * don't need to update the mount count and last checked
13457 * fields in the backup superblock (the kernel doesn't
13458 * update the backup superblocks anyway).
13459 */
13460 fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
13461
13462 ehandler_init(fs->io);
13463
13464 if (ctx->superblock)
13465 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
13466 ext2fs_mark_valid(fs);
13467 check_super_block(ctx);
13468 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013469 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013470 check_if_skip(ctx);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013471 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013472 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013473#ifdef ENABLE_SWAPFS
Rob Landley391a9042006-01-23 21:38:06 +000013474
13475#ifdef WORDS_BIGENDIAN
Rob Landley8b606342006-01-24 02:38:28 +000013476#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
Rob Landley391a9042006-01-23 21:38:06 +000013477#else
Rob Landley8b606342006-01-24 02:38:28 +000013478#define NATIVE_FLAG 0
Rob Landley391a9042006-01-23 21:38:06 +000013479#endif
13480
13481
Mike Frysinger51a43b42005-09-24 07:11:16 +000013482 if (normalize_swapfs) {
Rob Landley391a9042006-01-23 21:38:06 +000013483 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
Mike Frysinger51a43b42005-09-24 07:11:16 +000013484 fprintf(stderr, _("%s: Filesystem byte order "
13485 "already normalized.\n"), ctx->device_name);
Rob Landley7c94bed2006-05-03 21:58:45 +000013486 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013487 }
13488 }
13489 if (swapfs) {
13490 swap_filesys(ctx);
13491 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
Rob Landley7c94bed2006-05-03 21:58:45 +000013492 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013493 }
13494#endif
13495
13496 /*
13497 * Mark the system as valid, 'til proven otherwise
13498 */
13499 ext2fs_mark_valid(fs);
13500
13501 retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
13502 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013503 bb_error_msg(_("while reading bad blocks inode"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013504 preenhalt(ctx);
13505 printf(_("This doesn't bode well,"
13506 " but we'll try to go on...\n"));
13507 }
13508
13509 run_result = e2fsck_run(ctx);
13510 e2fsck_clear_progbar(ctx);
13511 if (run_result == E2F_FLAG_RESTART) {
13512 printf(_("Restarting e2fsck from the beginning...\n"));
13513 retval = e2fsck_reset_context(ctx);
13514 if (retval) {
Rob Landley7c94bed2006-05-03 21:58:45 +000013515 bb_error_msg(_("while resetting context"));
13516 bb_error_msg_and_die(0);
Mike Frysinger51a43b42005-09-24 07:11:16 +000013517 }
13518 ext2fs_close(fs);
13519 goto restart;
13520 }
13521 if (run_result & E2F_FLAG_CANCEL) {
13522 printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
13523 ctx->device_name : ctx->filesystem_name);
13524 exit_value |= FSCK_CANCELED;
13525 }
13526 if (run_result & E2F_FLAG_ABORT)
Rob Landley7c94bed2006-05-03 21:58:45 +000013527 bb_error_msg_and_die(_("aborted"));
Mike Frysinger51a43b42005-09-24 07:11:16 +000013528
Rob Landley3e72c592006-04-06 22:49:04 +000013529 /* Cleanup */
Mike Frysinger51a43b42005-09-24 07:11:16 +000013530 if (ext2fs_test_changed(fs)) {
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013531 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013532 if (!(ctx->options & E2F_OPT_PREEN))
13533 printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
13534 ctx->device_name);
13535 if (ctx->mount_flags & EXT2_MF_ISROOT) {
13536 printf(_("%s: ***** REBOOT LINUX *****\n"),
13537 ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013538 exit_value |= EXIT_DESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013539 }
13540 }
13541 if (!ext2fs_test_valid(fs)) {
13542 printf(_("\n%s: ********** WARNING: Filesystem still has "
13543 "errors **********\n\n"), ctx->device_name);
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013544 exit_value |= EXIT_UNCORRECTED;
13545 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013546 }
13547 if (exit_value & FSCK_CANCELED)
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013548 exit_value &= ~EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013549 else {
13550 show_stats(ctx);
13551 if (!(ctx->options & E2F_OPT_READONLY)) {
13552 if (ext2fs_test_valid(fs)) {
13553 if (!(sb->s_state & EXT2_VALID_FS))
"Vladimir N. Oleynik"d20cfbd2005-10-12 16:22:19 +000013554 exit_value |= EXIT_NONDESTRUCT;
Mike Frysinger51a43b42005-09-24 07:11:16 +000013555 sb->s_state = EXT2_VALID_FS;
13556 } else
13557 sb->s_state &= ~EXT2_VALID_FS;
13558 sb->s_mnt_count = 0;
13559 sb->s_lastcheck = time(NULL);
13560 ext2fs_mark_super_dirty(fs);
13561 }
13562 }
13563
13564 e2fsck_write_bitmaps(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013565
Mike Frysinger51a43b42005-09-24 07:11:16 +000013566 ext2fs_close(fs);
13567 ctx->fs = NULL;
13568 free(ctx->filesystem_name);
13569 free(ctx->journal_name);
13570 e2fsck_free_context(ctx);
"Vladimir N. Oleynik"3978e552005-09-27 11:43:29 +000013571
Mike Frysinger51a43b42005-09-24 07:11:16 +000013572 return exit_value;
13573}